apiKey = ChatConfig::get('gemini_api_key', ''); $this->model = ChatConfig::get('gemini_model', 'gemini-3.5-flash'); // v1 for newer models, v1beta as fallback $ver = preg_match('/gemini-(2\.5|3[\.\d]*)/', $this->model) ? 'v1' : 'v1beta'; $this->apiUrl = "https://generativelanguage.googleapis.com/{$ver}/models/{$this->model}:generateContent"; } private function buildRequest(array $contents, array $genConfig = []): array { return [ 'contents' => $contents, 'generationConfig' => array_merge([ 'temperature' => 0.1, 'maxOutputTokens' => 2048, 'thinkingConfig' => ['thinkingBudget' => 0], ], $genConfig), ]; } /** * Detecta la intención del texto del usuario y la mapea a una acción del sistema. * Retorna ['action' => string, 'data' => array] o null si no se puede detectar. */ public function detectar(string $texto, ?int $usuarioId = null, string $canal = 'web'): ?array { if (! $this->apiKey || ChatConfig::get('gemini_habilitado', '0') !== '1') { return null; } $prompt = $this->buildPrompt($texto); $inicio = microtime(true); try { $response = Http::withHeaders(['Content-Type' => 'application/json']) ->timeout(8) ->post("{$this->apiUrl}?key={$this->apiKey}", $this->buildRequest([['parts' => [['text' => $prompt]]]]) ); $tiempoMs = (int) ((microtime(true) - $inicio) * 1000); if (! $response->successful()) { Log::warning('[Gemini Intent] API error: ' . $response->body()); AiUsageLog::registrar([ 'servicio' => 'gemini_intent', 'canal' => $canal, 'usuario_id'=> $usuarioId, 'tiempo_ms' => $tiempoMs, 'resultado' => 'error', 'detalle' => ['error' => substr($response->body(), 0, 300), 'texto' => $texto], ]); 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_intent', 'canal' => $canal, 'usuario_id' => $usuarioId, 'input_tokens' => $inputTokens, 'output_tokens'=> $outputTokens, 'tiempo_ms' => $tiempoMs, 'resultado' => 'ok', 'detalle' => ['texto' => $texto, 'accion' => $resultado['action'] ?? null], ]); return $resultado; } catch (\Throwable $e) { $tiempoMs = (int) ((microtime(true) - $inicio) * 1000); Log::warning('[Gemini Intent] Excepcion: ' . $e->getMessage()); AiUsageLog::registrar([ 'servicio' => 'gemini_intent', 'canal' => $canal, 'usuario_id'=> $usuarioId, 'tiempo_ms' => $tiempoMs, 'resultado' => 'error', 'detalle' => ['error' => $e->getMessage(), 'texto' => $texto], ]); return null; } } private function buildPrompt(string $texto): string { $promptExtra = ChatConfig::get('gemini_prompt_extra', ''); return << 'menu.principal', 'data' => []]; } return [ 'action' => $json['action'], 'data' => is_array($json['data'] ?? null) ? $json['data'] : [], ]; } }