apiKey = ChatConfig::get('gemini_api_key', ''); $this->model = ChatConfig::get('gemini_model', 'gemini-2.0-flash'); $this->apiUrl = "https://generativelanguage.googleapis.com/v1beta/models/{$this->model}:generateContent"; } private function buildRequest(array $contents, array $genConfig = []): array { $body = [ 'contents' => $contents, 'generationConfig' => array_merge(['temperature' => 0.1, 'maxOutputTokens' => 200], $genConfig), ]; if (preg_match('/gemini-(2\.5|3[\.\d]*)/', $this->model)) { $body['thinkingConfig'] = ['thinkingBudget' => 0]; } return $body; } /** * Extrae datos de pago de una imagen de comprobante. * * @param string $base64 Imagen en base64 * @param string $mimeType MIME type (image/jpeg, image/png, etc.) * @return array|null ['banco', 'valor', 'referencia', 'fecha', 'hora', 'remitente'] o null */ public function extraerPago(string $base64, string $mimeType, ?int $usuarioId = null, string $canal = 'telegram'): ?array { if (! $this->apiKey) { Log::warning('[Gemini Vision] No hay API key configurada.'); return null; } $prompt = $this->buildPrompt(); $inicio = microtime(true); try { $contents = [[ 'parts' => [ ['text' => $prompt], ['inline_data' => ['mime_type' => $mimeType, 'data' => $base64]], ], ]]; $response = Http::withHeaders(['Content-Type' => 'application/json']) ->timeout(15) ->post("{$this->apiUrl}?key={$this->apiKey}", $this->buildRequest($contents)); $tiempoMs = (int) ((microtime(true) - $inicio) * 1000); if (! $response->successful()) { Log::warning('[Gemini Vision] API error: ' . $response->body()); AiUsageLog::registrar([ 'servicio' => 'gemini_vision', 'canal' => $canal, 'usuario_id'=> $usuarioId, 'tiempo_ms' => $tiempoMs, 'resultado' => 'error', 'detalle' => ['error' => substr($response->body(), 0, 300)], ]); return null; } $inputTokens = $response->json('usageMetadata.promptTokenCount', 0); $outputTokens = $response->json('usageMetadata.candidatesTokenCount', 0); $text = $response->json('candidates.0.content.parts.0.text', ''); $resultado = $this->parseRespuesta($text); AiUsageLog::registrar([ 'servicio' => 'gemini_vision', 'canal' => $canal, 'usuario_id' => $usuarioId, 'input_tokens' => $inputTokens, 'output_tokens'=> $outputTokens, 'tiempo_ms' => $tiempoMs, 'resultado' => $resultado ? 'ok' : 'error', 'detalle' => $resultado ? ['banco' => $resultado['banco'], 'valor' => $resultado['valor']] : ['raw' => substr($text, 0, 200)], ]); return $resultado; } catch (\Throwable $e) { $tiempoMs = (int) ((microtime(true) - $inicio) * 1000); Log::error('[Gemini Vision] Excepcion: ' . $e->getMessage()); AiUsageLog::registrar([ 'servicio' => 'gemini_vision', 'canal' => $canal, 'usuario_id'=> $usuarioId, 'tiempo_ms' => $tiempoMs, 'resultado' => 'error', 'detalle' => ['error' => $e->getMessage()], ]); return null; } } private function buildPrompt(): string { return << $json['banco'] ?? null, 'valor' => $json['valor'] ?? null, 'referencia' => $json['referencia'] ?? null, 'fecha' => $json['fecha'] ?? null, 'hora' => $json['hora'] ?? null, 'remitente' => $json['remitente'] ?? null, ]; } }