| 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
Loading history...
|
|||
| 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 |