SuperSeller3000/src/Infrastructure/AI/MistralClient.php
Simon Kuehn d51efa057b feat: add MistralClient as switchable alternative to OllamaClient
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>
2026-05-17 16:03:16 +00:00

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',
};
}
}