Files
2026-06-22 16:40:24 +02:00

98 lines
4.6 KiB
PHP

<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class MistralService
{
protected string $apiKey;
protected string $model;
public function __construct()
{
$this->apiKey = config('services.mistral.key') ?? env('MISTRAL_API_KEY', '');
$this->model = config('services.mistral.model') ?? env('MISTRAL_MODEL', 'mistral-small-latest');
}
/**
* Generate a response using RAG context and chat history.
*/
public function generateResponse(string $question, array $contextChunks, array $chatHistory = [], ?string $systemPrompt = null): ?string
{
if (empty($this->apiKey)) {
Log::error("Mistral API key is not configured.");
return "Désolé, la clé API Mistral AI n'est pas configurée dans l'application.";
}
// Format context from chunks
$contextText = "";
if (!empty($contextChunks)) {
$contextText = "Voici le contexte extrait des documents et sites internet indexés :\n\n";
foreach ($contextChunks as $index => $chunk) {
$num = $index + 1;
$title = $chunk['title'] ?? 'Document';
$source = $chunk['source'] ?? 'N/A';
$contextText .= "[Document #{$num} : {$title} (Source: {$source})]\n{$chunk['content']}\n\n";
}
} else {
$contextText = "Aucun contexte spécifique n'a été trouvé.";
}
// Prepare system message
if (empty($systemPrompt)) {
$systemPrompt = "Tu es un assistant virtuel intelligent et chaleureux intégré au site internet. Ton but est d'aider les utilisateurs en répondant de manière claire, concise et professionnelle.\n\n";
$systemPrompt .= "INSTRUCTIONS DE SÉCURITÉ CRITIQUES :\n";
$systemPrompt .= "1. Réponds UNIQUEMENT et STRICTEMENT en t'appuyant sur le CONTEXTE fourni ci-dessous.\n";
$systemPrompt .= "2. Si le contexte ne contient pas l'information nécessaire pour répondre à la question, réponds poliment que tu ne disposes pas de cette information car elle ne figure pas dans les documents indexés. N'utilise JAMAIS tes connaissances générales pour inventer, deviner ou extrapoler une réponse en dehors du contexte.\n";
$systemPrompt .= "3. Ne fais aucune supposition et n'invente aucune information. Si le contexte est vide ou insuffisant, refuse de répondre en expliquant que l'information n'est pas répertoriée dans les sources indexées.\n";
$systemPrompt .= "4. Réponds toujours en français, de manière structurée.\n";
$systemPrompt .= "5. Cite obligatoirement la source des documents fournis qui t'ont permis de répondre (ex: 'D'après le document [Titre]...').\n\n";
}
$systemPrompt .= "\n\n--- CONTEXTE DE RECHERCHE ---\n";
$systemPrompt .= $contextText;
// Build messages array
$messages = [
['role' => 'system', 'content' => $systemPrompt]
];
// Append chat history (limit to last 6 messages to keep context window clean)
$recentHistory = array_slice($chatHistory, -6);
foreach ($recentHistory as $msg) {
$messages[] = [
'role' => $msg['role'] === 'user' ? 'user' : 'assistant',
'content' => $msg['content']
];
}
// Add the current user question
$messages[] = ['role' => 'user', 'content' => $question];
try {
$response = Http::withHeaders([
'Authorization' => "Bearer {$this->apiKey}",
'Content-Type' => 'application/json'
])->timeout(30)->post('https://api.mistral.ai/v1/chat/completions', [
'model' => $this->model,
'messages' => $messages,
'temperature' => 0.3, // Low temperature for more factual answers
'max_tokens' => 1000
]);
if ($response->successful()) {
$result = $response->json();
return $result['choices'][0]['message']['content'] ?? null;
}
Log::error("Mistral API Chat Completion failed: " . $response->body());
return "Une erreur s'est produite lors de la génération de la réponse par Mistral AI (Code status: " . $response->status() . ").";
} catch (\Exception $e) {
Log::error("Mistral Service Error: " . $e->getMessage());
return "Impossible de contacter le service de génération de réponses Mistral. Veuillez réessayer.";
}
}
}