Issues (17)

src/OllamaTowerInstruct.php (1 issue)

Labels
Severity
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
It seems like $output can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

113
        $decoded = json_decode(/** @scrutinizer ignore-type */ $output, true);
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