'user', 'content' => $input]); $result = self::callLlm($systemPrompt, $ctxId, $company); if ($result === null) { return null; } ConversationContext::addAiMessage($ctxId, ['role' => 'assistant', 'content' => $result['content'] ?? '']); return [ 'action' => 'send', 'type' => 'text', 'to' => $context['from'], 'payload' => json_encode(['text' => $result['content'] ?? '']), ]; } private static function callLlm(string $systemPrompt, int $ctxId, array $company): ?array { $provider = env('AI_PROVIDER', 'openai'); $history = ConversationContext::getAiHistory($ctxId); return match ($provider) { 'openai' => self::callOpenAI($systemPrompt, $history, $company), 'gemini' => self::callGemini($systemPrompt, $history), 'mock' => self::mockResponse($history), default => self::callOpenAI($systemPrompt, $history, $company), }; } private static function callOpenAI(string $systemPrompt, array $history, array $company): ?array { $apiKey = env('OPENAI_API_KEY', ''); if ($apiKey === '') { return null; } $model = env('OPENAI_MODEL', 'gpt-4o-mini'); $messages = [ ['role' => 'system', 'content' => $systemPrompt], ]; foreach ($history as $msg) { $messages[] = [ 'role' => $msg['role'] ?? 'user', 'content' => $msg['content'] ?? '', ]; } $payload = json_encode([ 'model' => $model, 'messages' => $messages, 'max_tokens' => (int)env('AI_MAX_TOKENS', '500'), 'temperature' => 0.7, ]); $ch = curl_init('https://api.openai.com/v1/chat/completions'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $payload, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $apiKey, ], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($httpCode !== 200 || $response === false) { WpWebhook::log('ERROR', "OpenAI API error: {$error} HTTP:{$httpCode}"); return [ 'content' => 'Lo siento, tengo problemas para procesar tu mensaje. Por favor intenta de nuevo más tarde.', ]; } $data = json_decode($response, true); $text = $data['choices'][0]['message']['content'] ?? null; if ($text === null) { return [ 'content' => 'No pude generar una respuesta adecuada. ¿Puedes reformular tu pregunta?', ]; } return ['content' => $text]; } private static function callGemini(string $systemPrompt, array $history): ?array { $apiKey = env('GEMINI_API_KEY', ''); if ($apiKey === '') return null; $model = env('GEMINI_MODEL', 'gemini-2.0-flash'); $maxTokens = (int)env('AI_MAX_TOKENS', '500'); // Gemini usa "contents" con roles "user"/"model" $contents = []; foreach ($history as $msg) { $role = ($msg['role'] ?? 'user') === 'assistant' ? 'model' : 'user'; $contents[] = ['role' => $role, 'parts' => [['text' => $msg['content'] ?? '']]]; } // Si el historial está vacío o el último es del model, Gemini requiere que el último sea user if (empty($contents)) { $contents[] = ['role' => 'user', 'parts' => [['text' => '']]]; } $payload = json_encode([ 'system_instruction' => ['parts' => [['text' => $systemPrompt]]], 'contents' => $contents, 'generationConfig' => ['maxOutputTokens' => $maxTokens, 'temperature' => 0.7], ]); $url = "https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent?key={$apiKey}"; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $payload, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($httpCode !== 200 || $response === false) { return ['content' => 'Lo siento, tengo problemas para procesar tu mensaje. Por favor intenta de nuevo.']; } $data = json_decode($response, true); $text = $data['candidates'][0]['content']['parts'][0]['text'] ?? null; return $text !== null ? ['content' => $text] : ['content' => 'No pude generar una respuesta. ¿Puedes reformular tu pregunta?']; } private static function mockResponse(array $history): array { $last = end($history); $input = $last['content'] ?? ''; $responses = [ 'hola' => '¡Hola! ¿En qué puedo ayudarte hoy?', 'gracias' => '¡De nada! Si necesitas algo más, estoy aquí.', 'adios' => '¡Hasta luego! Que tengas un excelente día.', 'default' => "Gracias por tu mensaje. He recibido: \"{$input}\". Un asesor se pondrá en contacto contigo pronto.", ]; $normalized = mb_strtolower(trim($input)); $found = $responses['default']; foreach ($responses as $keyword => $response) { if (str_contains($normalized, $keyword)) { $found = $response; break; } } return ['content' => $found]; } private static function defaultPrompt(array $company): string { $name = $company['display_name'] ?? $company['name'] ?? 'la empresa'; return <<