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' => 100], $genConfig), ]; // thinkingConfig goes at root level (NOT inside generationConfig) for 2.5+ models if (preg_match('/gemini-(2\.5|3[\.\d]*)/', $this->model)) { $body['thinkingConfig'] = ['thinkingBudget' => 0]; } return $body; } /** * 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'] : [], ]; } }