Implements OllamaClientInterface against the Mistral Cloud API (/v1/chat/completions). Switch by toggling the alias in services.yaml and pointing AI_TEXT_MODEL / AI_VISION_MODEL env vars at the MISTRAL_* or OLLAMA_* counterparts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
79 lines
2.5 KiB
PHP
79 lines
2.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Infrastructure\AI;
|
|
|
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
|
|
|
final class MistralClient implements OllamaClientInterface
|
|
{
|
|
public function __construct(
|
|
private readonly HttpClientInterface $httpClient,
|
|
private readonly string $mistralApiKey,
|
|
private readonly string $mistralBaseUrl,
|
|
) {
|
|
}
|
|
|
|
public function generate(string $model, string $prompt): string
|
|
{
|
|
$response = $this->httpClient->request('POST', $this->mistralBaseUrl.'/v1/chat/completions', [
|
|
'headers' => [
|
|
'Authorization' => 'Bearer '.$this->mistralApiKey,
|
|
],
|
|
'json' => [
|
|
'model' => $model,
|
|
'messages' => [
|
|
['role' => 'user', 'content' => $prompt],
|
|
],
|
|
],
|
|
'timeout' => 120,
|
|
]);
|
|
|
|
/** @var array{choices: array{0: array{message: array{content: string}}}} $data */
|
|
$data = $response->toArray();
|
|
|
|
return $data['choices'][0]['message']['content'];
|
|
}
|
|
|
|
public function generateWithImage(string $model, string $prompt, string $imagePath): string
|
|
{
|
|
$imageData = base64_encode((string) file_get_contents($imagePath));
|
|
$mimeType = $this->guessMimeType($imagePath);
|
|
|
|
$response = $this->httpClient->request('POST', $this->mistralBaseUrl.'/v1/chat/completions', [
|
|
'headers' => [
|
|
'Authorization' => 'Bearer '.$this->mistralApiKey,
|
|
],
|
|
'json' => [
|
|
'model' => $model,
|
|
'messages' => [
|
|
[
|
|
'role' => 'user',
|
|
'content' => [
|
|
['type' => 'text', 'text' => $prompt],
|
|
['type' => 'image_url', 'image_url' => ['url' => 'data:'.$mimeType.';base64,'.$imageData]],
|
|
],
|
|
],
|
|
],
|
|
],
|
|
'timeout' => 180,
|
|
]);
|
|
|
|
/** @var array{choices: array{0: array{message: array{content: string}}}} $data */
|
|
$data = $response->toArray();
|
|
|
|
return $data['choices'][0]['message']['content'];
|
|
}
|
|
|
|
private function guessMimeType(string $path): string
|
|
{
|
|
return match (strtolower(pathinfo($path, PATHINFO_EXTENSION))) {
|
|
'jpg', 'jpeg' => 'image/jpeg',
|
|
'png' => 'image/png',
|
|
'gif' => 'image/gif',
|
|
'webp' => 'image/webp',
|
|
default => 'image/jpeg',
|
|
};
|
|
}
|
|
}
|