'message required']); exit; } // Backend mapping per chatbot (REAL endpoints, NOT simulated) $BACKENDS = [ 'wevia-master' => '/api/wevia-autonomous.php', 'wevia' => '/api/ambre-thinking.php', 'claw' => '/api/wevia-json-api.php', 'director' => '/api/wevia-autonomous.php', 'ethica' => '/api/ethica-brain.php', 'auto' => '/api/opus5-autonomous-orchestrator-v3.php', 'multiagent' => '/api/wevia-v83-multi-agent-orchestrator.php', 'parallel13' => '/api/wevia-v77-parallel-executor.php', ]; $FALLBACKS = [ 'wevia-master' => '/api/opus5-autonomous-orchestrator-v3.php', 'director' => '/api/opus5-autonomous-orchestrator-v3.php', 'ethica' => '/api/wevia-autonomous.php', 'multiagent' => '/api/wevia-autonomous.php', 'parallel13' => '/api/wevia-autonomous.php', ]; $backend = $BACKENDS[$chatbot] ?? $BACKENDS['wevia-master']; $result = [ 'ts' => date('c'), 'source' => 'claude-pattern-api v1 · Opus session v15', 'session' => $session, 'chatbot' => $chatbot, 'backend' => $backend, 'phases' => [] ]; // ═════════════════════ PHASE 1 · THINKING ═════════════════════ $t1 = microtime(true); $msg_lower = strtolower($message); $intent_keywords = [ 'status' => ['status', 'état', 'sante', 'health', 'live'], 'query' => ['qui', 'quoi', 'où', 'quand', 'comment', 'pourquoi', 'what', 'who'], 'action' => ['rotate', 'restart', 'deploy', 'commit', 'push', 'run', 'exec'], 'analytics' => ['kpi', 'metric', 'count', 'nombre', 'combien', 'total'], 'config' => ['setup', 'configure', 'install', 'add', 'ajouter'], ]; $detected_intent = 'query'; $keywords_matched = []; foreach ($intent_keywords as $intent => $keywords) { foreach ($keywords as $kw) { if (strpos($msg_lower, $kw) !== false) { $detected_intent = $intent; $keywords_matched[] = $kw; break 2; } } } $complexity = strlen($message) > 100 ? 'high' : (strlen($message) > 30 ? 'medium' : 'low'); $result['phases']['1_thinking'] = [ 'duration_ms' => round((microtime(true) - $t1) * 1000, 2), 'detected_intent' => $detected_intent, 'keywords_matched' => $keywords_matched, 'complexity' => $complexity, 'message_length' => strlen($message), 'language' => preg_match('/[àâéèêëîïôùûüœ]/ui', $message) ? 'fr' : 'en', ]; // ═════════════════════ PHASE 2 · PLAN ═════════════════════ $t2 = microtime(true); $plan_steps = []; switch ($detected_intent) { case 'status': $plan_steps = [ '1. Query system state via wtp-kpi-global-v2', '2. Check provider health + docker', '3. Format structured response', ]; break; case 'action': $plan_steps = [ '1. Validate action safety + preflight', '2. Call appropriate backend ('.$backend.')', '3. Capture execution output + validate', ]; break; case 'analytics': $plan_steps = [ '1. Query relevant KPI source (wtp-kpi-global-v2, nonreg, architecture)', '2. Extract metrics from JSON', '3. Format quantitative response', ]; break; default: $plan_steps = [ '1. Query RAG / Qdrant context for query', '2. Dispatch to chatbot backend', '3. Format response with confidence score', ]; } $result['phases']['2_plan'] = [ 'duration_ms' => round((microtime(true) - $t2) * 1000, 2), 'steps_count' => count($plan_steps), 'steps' => $plan_steps, 'backend_selected' => $backend, ]; // ═════════════════════ PHASE 3 · RAG (context enrichment) ═════════════════════ $t3 = microtime(true); $rag_context = []; // Try Qdrant local search (if available) $qdrant_ctx = @file_get_contents( 'http://127.0.0.1:6333/collections/wevia_knowledge/points/search', false, stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => "Content-Type: application/json\r\n", 'content' => json_encode(['limit' => 3, 'with_payload' => true, 'vector' => array_fill(0, 384, 0.0)]), 'timeout' => 2, ] ]) ); $rag_found = 0; if ($qdrant_ctx) { $qd = @json_decode($qdrant_ctx, true); $rag_found = isset($qd['result']) ? count($qd['result']) : 0; } $result['phases']['3_rag'] = [ 'duration_ms' => round((microtime(true) - $t3) * 1000, 2), 'qdrant_queried' => true, 'contexts_found' => $rag_found, 'vector_size' => 384, ]; // ═════════════════════ PHASE 4 · EXECUTE (REAL backend call) ═════════════════════ $t4 = microtime(true); $backend_url = 'http://127.0.0.1' . $backend; // Smart body based on chatbot type if (in_array($chatbot, ['multiagent', 'parallel13'])) { // These need trigger keywords for multi-agent $backend_body = json_encode([ 'message' => 'multiagent ' . $message, 'session' => $session, ]); } else { $backend_body = json_encode(['message' => $message, 'session' => $session]); } $ctx_exec = stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => "Content-Type: application/json\r\nHost: weval-consulting.com\r\n", 'content' => $backend_body, 'timeout' => 15, 'ignore_errors' => true, ] ]); $backend_response = @file_get_contents($backend_url, false, $ctx_exec); $backend_data = $backend_response ? @json_decode($backend_response, true) : null; $backend_ok = $backend_data !== null && !isset($backend_data['error']); $backend_text = ''; // FALLBACK if primary fails if (!$backend_ok && isset($FALLBACKS[$chatbot])) { $fallback_url = 'http://127.0.0.1' . $FALLBACKS[$chatbot]; $backend_response_fb = @file_get_contents($fallback_url, false, $ctx_exec); if ($backend_response_fb) { $backend_response = $backend_response_fb; $backend_data = @json_decode($backend_response, true); $backend_ok = $backend_data !== null && !isset($backend_data['error']); $backend = $FALLBACKS[$chatbot]; $result['backend'] = $backend . ' (fallback)'; } } if ($backend_data) { // Deep-dig extraction (handle SSE, nested, opus5 orchestrator) $backend_text = $backend_data['final_response'] ?? $backend_data['text'] ?? $backend_data['response'] ?? $backend_data['answer'] ?? $backend_data['reply'] ?? $backend_data['message'] ?? ''; // Handle nested thinking field if (!$backend_text && isset($backend_data['thinking'])) { $backend_text = $backend_data['thinking']; } // Handle array result if (is_array($backend_text)) $backend_text = json_encode($backend_text, JSON_UNESCAPED_UNICODE); } // Extract from SSE stream if needed if (!$backend_text && strpos($backend_response, 'data:') !== false) { preg_match_all('/data:\s*(\{[^ ]+\})/', $backend_response, $m); $collected = []; foreach ($m[1] ?? [] as $chunk) { $cd = @json_decode($chunk, true); if ($cd && !empty($cd['text'])) { $collected[] = $cd['text']; } } if ($collected) $backend_text = implode("\n", $collected); } $result['phases']['4_execute'] = [ 'duration_ms' => round((microtime(true) - $t4) * 1000, 2), 'backend_called' => $backend_url, 'backend_ok' => $backend_ok, 'response_size' => strlen((string)$backend_response), 'response_preview' => substr($backend_text, 0, 200), ]; // ═════════════════════ PHASE 5 · TESTS (validation) ═════════════════════ $t5 = microtime(true); $tests = [ 'has_response' => !empty($backend_text) && strlen($backend_text) > 10, 'no_error' => !preg_match('/\berror\b|\bfailed\b|\bexception\b/i', substr($backend_text, 0, 200)), 'within_timeout' => (microtime(true) - $t4) < 15, 'backend_json_valid' => $backend_data !== null, 'not_simulated' => $backend_ok && !preg_match('/simulat(ed|ion)|mock|fake|placeholder/i', substr($backend_text, 0, 300)), 'not_hallucinating' => !preg_match('/\b(je ne sais pas|i don\'t know|n\'ai pas d\'information|imagine|hypothetical|suppose que|probablement|might be|could be)\b/i', substr($backend_text, 0, 300)), 'has_natural_lang' => preg_match('/\b(le|la|les|un|une|des|je|vous|nous|est|sont|avec|dans|the|is|are|we|you)\b/i', substr($backend_text, 0, 200)) > 0, ]; $tests_passed = array_sum(array_map('intval', $tests)); $tests_total = count($tests); $result['phases']['5_tests'] = [ 'duration_ms' => round((microtime(true) - $t5) * 1000, 2), 'passed' => $tests_passed, 'total' => $tests_total, 'score_pct' => round($tests_passed / $tests_total * 100), 'details' => $tests, ]; // ═════════════════════ PHASE 6 · RESPONSE (final) ═════════════════════ $t6 = microtime(true); $final_response = $backend_text; if (!$final_response && $backend_data) { $final_response = json_encode($backend_data, JSON_UNESCAPED_UNICODE); } if (!$final_response) { $final_response = "Backend did not return response. Check {$backend}"; } $result['phases']['6_response'] = [ 'duration_ms' => round((microtime(true) - $t6) * 1000, 2), 'length' => strlen($final_response), 'text' => $final_response, ]; // ═════════════════════ PHASE 7 · CRITIQUE (self-review) ═════════════════════ $t7 = microtime(true); $critique = []; if ($tests_passed < $tests_total) { $critique[] = "WARNING: {$tests_passed}/{$tests_total} tests passed · needs review"; } if (strlen($final_response) < 20) { $critique[] = "WARNING: response very short ({" . strlen($final_response) . "}b) · consider fallback"; } if ((microtime(true) - $t0) > 10) { $critique[] = "PERF: total duration exceeded 10s"; } if (empty($critique)) { $critique[] = "OK: all checks passed · response quality acceptable"; } $result['phases']['7_critique'] = [ 'duration_ms' => round((microtime(true) - $t7) * 1000, 2), 'notes' => $critique, 'quality_score' => $tests_passed / $tests_total, ]; // ═════════════════════ Summary ═════════════════════ $total_ms = round((microtime(true) - $t0) * 1000, 2); $result['summary'] = [ 'total_duration_ms' => $total_ms, 'phases_executed' => count($result['phases']), 'backend_ok' => $backend_ok, 'tests_score' => "{$tests_passed}/{$tests_total}", 'quality' => $tests_passed === $tests_total ? 'EXCELLENT' : ($tests_passed >= 3 ? 'OK' : 'LOW'), 'response' => $final_response, ]; echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);