impmementation de gemini vertex & maistral ai
This commit is contained in:
@@ -6,16 +6,23 @@ use App\Models\Attachment;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Google\Auth\Credentials\ServiceAccountCredentials;
|
||||
|
||||
class GeminiService
|
||||
{
|
||||
protected ?string $apiKey;
|
||||
protected string $model;
|
||||
protected string $region;
|
||||
protected ?string $projectId;
|
||||
protected ?string $credentialsPath;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->apiKey = config('services.gemini.key');
|
||||
$this->model = config('services.gemini.model', 'gemini-1.5-flash');
|
||||
$this->region = config('services.gemini.region', 'us-central1');
|
||||
$this->projectId = config('services.gemini.project_id');
|
||||
$this->credentialsPath = config('services.gemini.credentials_path');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,10 +58,6 @@ class GeminiService
|
||||
*/
|
||||
protected function analyzeContent(string $fileContent, string $mimeType, string $fileName): array
|
||||
{
|
||||
if (empty($this->apiKey)) {
|
||||
throw new \Exception("La clé API Gemini n'est pas configurée dans l'environnement.");
|
||||
}
|
||||
|
||||
// Encodage Base64 pour l'API Gemini
|
||||
$base64Data = base64_encode($fileContent);
|
||||
|
||||
@@ -86,15 +89,86 @@ Règles importantes :
|
||||
3. Ne réponds QUE par le JSON brut, valide, sans aucun texte explicatif avant ou après, et sans bloc de code markdown.
|
||||
4. Assure-toi que les montants et quantités sont des nombres.";
|
||||
|
||||
$url = "https://generativelanguage.googleapis.com/v1beta/models/{$this->model}:generateContent?key={$this->apiKey}";
|
||||
// Détection automatique de Vertex AI vs Gemini (Google AI Studio)
|
||||
$isVertex = false;
|
||||
$credentials = null;
|
||||
|
||||
$resolvedPath = $this->credentialsPath;
|
||||
if ($resolvedPath && !str_starts_with($resolvedPath, '{')) {
|
||||
if (!file_exists($resolvedPath) && file_exists(base_path($resolvedPath))) {
|
||||
$resolvedPath = base_path($resolvedPath);
|
||||
}
|
||||
}
|
||||
|
||||
if ($resolvedPath && (file_exists($resolvedPath) || str_starts_with($resolvedPath, '{'))) {
|
||||
$isVertex = true;
|
||||
$credentials = $resolvedPath;
|
||||
} elseif ($this->apiKey) {
|
||||
$resolvedApiKey = $this->apiKey;
|
||||
if (!str_starts_with($resolvedApiKey, '{')) {
|
||||
if (!file_exists($resolvedApiKey) && file_exists(base_path($resolvedApiKey))) {
|
||||
$resolvedApiKey = base_path($resolvedApiKey);
|
||||
}
|
||||
}
|
||||
if (str_starts_with($resolvedApiKey, '{') || (file_exists($resolvedApiKey) && str_ends_with($resolvedApiKey, '.json'))) {
|
||||
$isVertex = true;
|
||||
$credentials = $resolvedApiKey;
|
||||
}
|
||||
}
|
||||
|
||||
Log::info("Début de l'analyse OCR Gemini pour le fichier : {$fileName}");
|
||||
if ($isVertex) {
|
||||
Log::info("Début de l'analyse OCR Vertex AI pour le fichier : {$fileName}");
|
||||
|
||||
// Extraction du Project ID à partir des identifiants si non fourni explicitement
|
||||
$projectId = $this->projectId;
|
||||
if (!$projectId && $credentials) {
|
||||
try {
|
||||
$jsonContent = file_exists($credentials) ? file_get_contents($credentials) : $credentials;
|
||||
$decodedCreds = json_decode($jsonContent, true);
|
||||
$projectId = $decodedCreds['project_id'] ?? null;
|
||||
} catch (\Exception $e) {
|
||||
Log::warning("Impossible d'extraire le project_id de la configuration : " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$response = Http::withHeaders([
|
||||
'Content-Type' => 'application/json',
|
||||
])->post($url, [
|
||||
if (!$projectId) {
|
||||
throw new \Exception("Le Project ID Google Cloud est requis pour Vertex AI.");
|
||||
}
|
||||
|
||||
// Génération du token OAuth2 avec le SDK
|
||||
try {
|
||||
$scopes = ['https://www.googleapis.com/auth/cloud-platform'];
|
||||
$sa = new ServiceAccountCredentials($scopes, $credentials);
|
||||
$token = $sa->fetchAuthToken();
|
||||
$accessToken = $token['access_token'];
|
||||
} catch (\Exception $e) {
|
||||
Log::error("Erreur d'authentification Vertex AI : " . $e->getMessage());
|
||||
throw new \Exception("Échec de l'authentification Vertex AI : " . $e->getMessage());
|
||||
}
|
||||
|
||||
// Vertex AI Endpoint
|
||||
$url = "https://{$this->region}-aiplatform.googleapis.com/v1/projects/{$projectId}/locations/{$this->region}/publishers/google/models/{$this->model}:generateContent";
|
||||
|
||||
$headers = [
|
||||
'Authorization' => 'Bearer ' . $accessToken,
|
||||
'Content-Type' => 'application/json',
|
||||
];
|
||||
} else {
|
||||
if (empty($this->apiKey)) {
|
||||
throw new \Exception("La clé API Gemini n'est pas configurée dans l'environnement.");
|
||||
}
|
||||
|
||||
Log::info("Début de l'analyse OCR Gemini (Google AI Studio) pour le fichier : {$fileName}");
|
||||
$url = "https://generativelanguage.googleapis.com/v1beta/models/{$this->model}:generateContent?key={$this->apiKey}";
|
||||
$headers = [
|
||||
'Content-Type' => 'application/json',
|
||||
];
|
||||
}
|
||||
|
||||
$response = Http::withHeaders($headers)->post($url, [
|
||||
'contents' => [
|
||||
[
|
||||
'role' => 'user',
|
||||
'parts' => [
|
||||
['text' => $prompt],
|
||||
[
|
||||
@@ -112,25 +186,25 @@ Règles importantes :
|
||||
]);
|
||||
|
||||
if ($response->failed()) {
|
||||
Log::error("Échec de l'appel Gemini API : " . $response->body());
|
||||
throw new \Exception("Erreur de communication avec l'API Gemini : " . $response->status());
|
||||
Log::error("Échec de l'appel API : " . $response->body());
|
||||
throw new \Exception("Erreur de communication avec l'API Google : " . $response->status());
|
||||
}
|
||||
|
||||
$data = $response->json();
|
||||
$textResult = $data['candidates'][0]['content']['parts'][0]['text'] ?? null;
|
||||
|
||||
if (!$textResult) {
|
||||
Log::error("Gemini n'a renvoyé aucun contenu exploitable.");
|
||||
Log::error("Google API n'a renvoyé aucun contenu exploitable.");
|
||||
throw new \Exception("L'analyse du devis n'a retourné aucun résultat.");
|
||||
}
|
||||
|
||||
$decoded = json_decode($textResult, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
Log::error("Impossible de parser le JSON retourné par Gemini : " . $textResult);
|
||||
Log::error("Impossible de parser le JSON retourné par l'API : " . $textResult);
|
||||
throw new \Exception("Le format des données retournées par l'OCR est invalide.");
|
||||
}
|
||||
|
||||
Log::info("OCR Gemini réussi pour le fichier : {$fileName}");
|
||||
Log::info("OCR Google réussi pour le fichier : {$fileName}");
|
||||
|
||||
return $decoded;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user