1 | <?php |
||
2 | |||
3 | namespace LeKoala\Multilingual; |
||
4 | |||
5 | use Exception; |
||
6 | |||
7 | /** |
||
8 | * A simple ollama client to query towerinstruct model |
||
9 | * @link https://ollama.com/thinkverse/towerinstruct |
||
10 | * @link https://huggingface.co/Unbabel/TowerInstruct-7B-v0.2 |
||
11 | */ |
||
12 | class OllamaTowerInstruct |
||
13 | { |
||
14 | final public const BASE_URL = 'http://localhost:11434'; |
||
15 | final public const BASE_MODEL = 'thinkverse/towerinstruct'; |
||
16 | |||
17 | protected ?string $model; |
||
18 | protected ?string $url; |
||
19 | |||
20 | public function __construct(?string $model = null, ?string $url = null) |
||
21 | { |
||
22 | $this->model = $model ?? self::BASE_MODEL; |
||
23 | $this->url = $url ?? self::BASE_URL; |
||
24 | } |
||
25 | |||
26 | public function expandLang(string $lang): string |
||
27 | { |
||
28 | $lc = strtolower($lang); |
||
29 | // English, Portuguese, Spanish, French, German, Dutch, Italian, Korean, Chinese, Russian |
||
30 | return match ($lc) { |
||
31 | 'en' => 'English', |
||
32 | 'fr' => 'French', |
||
33 | 'nl' => 'Dutch', |
||
34 | 'it' => 'Italian', |
||
35 | 'de' => 'German', |
||
36 | 'pt' => 'Portuguese', |
||
37 | 'ko' => 'Korean', |
||
38 | 'zh' => 'Chinese', |
||
39 | 'ru' => 'Russian', |
||
40 | default => $lang |
||
41 | }; |
||
42 | } |
||
43 | |||
44 | public function translate(?string $string, string $to, string $from) |
||
45 | { |
||
46 | /* |
||
47 | messages = [ |
||
48 | {"role": "user", "content": "Translate the following text from Portuguese into English.\nPortuguese: Um grupo de investigadores lançou um novo modelo para tarefas relacionadas com tradução.\nEnglish:"}, |
||
49 | ] |
||
50 | */ |
||
51 | |||
52 | $string = $string ?? ''; |
||
53 | $from = $this->expandLang($from); |
||
54 | $to = $this->expandLang($to); |
||
55 | |||
56 | $prompt = "Translate the following text from $from into $to and keep variables between {} as is.\n$from: $string\n$to:"; |
||
57 | |||
58 | $result = $this->generate($prompt); |
||
59 | |||
60 | $response = $result['response'] ?? ''; |
||
61 | |||
62 | // Avoid extra space |
||
63 | $response = trim($response); |
||
64 | |||
65 | // Make sure we don't get any extra ending dot |
||
66 | $endsWithDot = str_ends_with($string, '.'); |
||
67 | $translationEndsWithDot = str_ends_with($response, '.'); |
||
68 | if (!$endsWithDot && $translationEndsWithDot) { |
||
69 | $response = rtrim($response, '.'); |
||
70 | } |
||
71 | |||
72 | // No crazy {} |
||
73 | $includesParen = str_contains($string, '{}'); |
||
74 | $translationIncludesParen = str_contains($string, '{}'); |
||
75 | if (!$includesParen && $translationIncludesParen) { |
||
76 | $response = trim(str_replace('{}', '', $response)); |
||
77 | } |
||
78 | |||
79 | // No spaces in { } |
||
80 | $response = str_replace('{ ', '{', $response); |
||
81 | $response = str_replace(' }', '}', $response); |
||
82 | |||
83 | return $response; |
||
84 | } |
||
85 | |||
86 | /** |
||
87 | * @param null|array<int> $context |
||
88 | * @return array{model:string,created_at:string,response:string,done:bool,done_reason:string,context:array<int>,total_duration:int,load_duration:int,prompt_eval_count:int,prompt_eval_duration:int,eval_count:int,eval_duration:int} |
||
89 | */ |
||
90 | public function generate(string $prompt, ?array $context = null) |
||
91 | { |
||
92 | $data = [ |
||
93 | 'model' => $this->model, |
||
94 | 'prompt' => $prompt, |
||
95 | 'stream' => false, |
||
96 | ]; |
||
97 | if (!empty($context)) { |
||
98 | $data['context'] = $context; |
||
99 | } |
||
100 | |||
101 | $url = $this->url . '/api/generate'; |
||
102 | $ch = curl_init(); |
||
103 | |||
104 | curl_setopt($ch, CURLOPT_URL, $url); |
||
105 | curl_setopt($ch, CURLOPT_POST, true); |
||
106 | curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); |
||
107 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
||
108 | |||
109 | $output = curl_exec($ch); |
||
110 | |||
111 | curl_close($ch); |
||
112 | |||
113 | $decoded = json_decode($output, true); |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
114 | |||
115 | if (!$decoded) { |
||
116 | throw new Exception("Failed to decode json: " . json_last_error_msg()); |
||
117 | } |
||
118 | |||
119 | //@phpstan-ignore-next-line |
||
120 | return $decoded; |
||
121 | } |
||
122 | } |
||
123 |