diff --git a/src/Infrastructure/AI/MistralClient.php b/src/Infrastructure/AI/MistralClient.php index fca0a0a..cae4427 100644 --- a/src/Infrastructure/AI/MistralClient.php +++ b/src/Infrastructure/AI/MistralClient.php @@ -74,26 +74,30 @@ final class MistralClient implements OllamaClientInterface { $messages = [['role' => 'user', 'content' => $prompt]]; - $response = $this->httpClient->request('POST', $this->mistralBaseUrl.'/v1/chat/completions', [ - 'headers' => ['Authorization' => 'Bearer '.$this->mistralApiKey], - 'json' => [ - 'model' => $model, - 'tools' => [['type' => 'web_search']], - 'messages' => $messages, - ], - 'timeout' => 120, - ]); + try { + $response = $this->httpClient->request('POST', $this->mistralBaseUrl.'/v1/chat/completions', [ + 'headers' => ['Authorization' => 'Bearer '.$this->mistralApiKey], + 'json' => [ + 'model' => $model, + 'tools' => [['type' => 'web_search']], + 'messages' => $messages, + ], + 'timeout' => 120, + ]); + + /** @var array{choices: array{0: array{finish_reason: string, message: array{content: ?string, tool_calls?: list}}}} $data */ + $data = $response->toArray(); + } catch (\Throwable) { + // Model does not support web_search tool — fall back to plain generation + return $this->generate($model, $prompt); + } - /** @var array{choices: array{0: array{finish_reason: string, message: array{content: ?string, tool_calls?: list}}}} $data */ - $data = $response->toArray(); $choice = $data['choices'][0]; if ('tool_calls' !== $choice['finish_reason']) { return (string) $choice['message']['content']; } - // Append assistant's tool call turn, then return empty tool results so - // Mistral's servers can complete the search and produce the final answer. $messages[] = $choice['message']; foreach ($choice['message']['tool_calls'] ?? [] as $toolCall) { $messages[] = [ @@ -103,20 +107,24 @@ final class MistralClient implements OllamaClientInterface ]; } - $final = $this->httpClient->request('POST', $this->mistralBaseUrl.'/v1/chat/completions', [ - 'headers' => ['Authorization' => 'Bearer '.$this->mistralApiKey], - 'json' => [ - 'model' => $model, - 'tools' => [['type' => 'web_search']], - 'messages' => $messages, - ], - 'timeout' => 120, - ]); + try { + $final = $this->httpClient->request('POST', $this->mistralBaseUrl.'/v1/chat/completions', [ + 'headers' => ['Authorization' => 'Bearer '.$this->mistralApiKey], + 'json' => [ + 'model' => $model, + 'tools' => [['type' => 'web_search']], + 'messages' => $messages, + ], + 'timeout' => 120, + ]); - /** @var array{choices: array{0: array{message: array{content: string}}}} $finalData */ - $finalData = $final->toArray(); + /** @var array{choices: array{0: array{message: array{content: string}}}} $finalData */ + $finalData = $final->toArray(); - return $finalData['choices'][0]['message']['content']; + return $finalData['choices'][0]['message']['content']; + } catch (\Throwable) { + return $this->generate($model, $prompt); + } } private function guessMimeType(string $path): string