'no message'])); // === WAVE 253 GROUNDING: fetch live data BEFORE LLM call (anti-hallucination) === function pg_c() { return @pg_connect('host=10.1.0.3 port=5432 dbname=paperclip user=admin password=admin123 connect_timeout=2'); } function build_grounding_context() { $ctx = ['ts' => date('c'), 'source' => 'live']; $pg = pg_c(); if ($pg) { // 48 leads stats $r = @pg_query($pg, "SELECT COUNT(*) AS n, ROUND(AVG(mql_score)) AS avg_mql, SUM(CASE WHEN sql_qualified THEN 1 ELSE 0 END) AS sql_q FROM weval_leads"); if ($r) { $ctx['leads'] = pg_fetch_assoc($r); } // Top industries $r2 = @pg_query($pg, "SELECT industry, COUNT(*) AS n, ROUND(AVG(mql_score)) AS avg_mql FROM weval_leads WHERE industry IS NOT NULL GROUP BY industry ORDER BY n DESC, avg_mql DESC LIMIT 8"); if ($r2) { $ctx['industries'] = []; while ($row = pg_fetch_assoc($r2)) $ctx['industries'][] = $row; } // Top countries $r3 = @pg_query($pg, "SELECT country, COUNT(*) AS n FROM weval_leads WHERE country IS NOT NULL GROUP BY country ORDER BY n DESC LIMIT 6"); if ($r3) { $ctx['countries'] = []; while ($row = pg_fetch_assoc($r3)) $ctx['countries'][] = $row; } // TOP 5 leads with MQL 85+ $r4 = @pg_query($pg, "SELECT company, mql_score, industry, country, sql_qualified FROM weval_leads WHERE mql_score >= 85 ORDER BY mql_score DESC LIMIT 6"); if ($r4) { $ctx['top_leads'] = []; while ($row = pg_fetch_assoc($r4)) $ctx['top_leads'][] = $row; } // Tasks $r5 = @pg_query($pg, "SELECT status, COUNT(*) AS n, SUM(estimated_mad) AS mad FROM weval_tasks GROUP BY status"); if ($r5) { $ctx['tasks_by_status'] = []; while ($row = pg_fetch_assoc($r5)) $ctx['tasks_by_status'][] = $row; } pg_close($pg); } // Solutions scanner top 3 $ch = curl_init('http://127.0.0.1/api/solution-scanner.php?action=full_analysis'); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>1, CURLOPT_TIMEOUT=>5]); $raw = curl_exec($ch); curl_close($ch); if ($raw) { $sd = @json_decode($raw, true); if ($sd && !empty($sd['solutions'])) { $ctx['top_solutions'] = []; foreach (array_slice($sd['solutions'], 0, 5) as $sol) { $ctx['top_solutions'][] = [ 'name' => $sol['name'], 'winning_score' => $sol['winning_score'], 'decision' => $sol['decision'], 'mad' => $sol['mad_est'], 'maturity' => $sol['maturity'], 'dev_calendar_days' => $sol['dev_effort']['calendar_days_est'] ?? 0, ]; } $ctx['pipeline_summary'] = $sd['summary'] ?? null; } } return $ctx; } $grounding = build_grounding_context(); // Build grounding text block for system prompt $ground_text = "DONNÉES WEVAL LIVE (ne jamais dire que tu n'as pas accès - ces données sont fraîches, injectées):\n\n"; if (!empty($grounding['leads'])) { $l = $grounding['leads']; $ground_text .= "📊 PAPERCLIP LEADS: " . $l['n'] . " leads · avg MQL " . $l['avg_mql'] . " · SQL qualifiés " . $l['sql_q'] . "\n"; } if (!empty($grounding['industries'])) { $ground_text .= "🏭 TOP INDUSTRIES: "; foreach ($grounding['industries'] as $i) $ground_text .= $i['industry'] . " (" . $i['n'] . " leads, MQL " . $i['avg_mql'] . ") · "; $ground_text .= "\n"; } if (!empty($grounding['countries'])) { $ground_text .= "🗺 PAYS: "; foreach ($grounding['countries'] as $c) $ground_text .= $c['country'] . "=" . $c['n'] . " · "; $ground_text .= "\n"; } if (!empty($grounding['top_leads'])) { $ground_text .= "🏆 TOP LEADS MQL85+:\n"; foreach ($grounding['top_leads'] as $tl) { $sql = ($tl['sql_qualified'] === 't' || $tl['sql_qualified'] === true) ? '✅SQL' : ''; $ground_text .= " · " . $tl['company'] . " (MQL " . $tl['mql_score'] . " · " . $tl['industry'] . " · " . $tl['country'] . " $sql)\n"; } } if (!empty($grounding['tasks_by_status'])) { $ground_text .= "📋 TASKS: "; foreach ($grounding['tasks_by_status'] as $t) $ground_text .= $t['status'] . "=" . $t['n'] . " (" . round(($t['mad']??0)/1000) . "K MAD) · "; $ground_text .= "\n"; } if (!empty($grounding['top_solutions'])) { $ground_text .= "\n🎯 TOP 5 SOLUTIONS WEVAL (Scanner WePredict):\n"; foreach ($grounding['top_solutions'] as $s) { $ground_text .= " · " . $s['name'] . " · score " . $s['winning_score'] . "/100 · " . $s['decision'] . " · " . round($s['mad']/1000) . "K MAD · maturity " . $s['maturity'] . "% · dev " . $s['dev_calendar_days'] . "j\n"; } } if (!empty($grounding['pipeline_summary'])) { $ps = $grounding['pipeline_summary']; $ground_text .= "\n💰 PIPELINE GLOBAL: " . round($ps['total_mad_pipeline']/1000) . "K MAD · dev cost " . round($ps['total_dev_cost_mad']/1000) . "K MAD · " . $ps['ship_it'] . " SHIP_IT · " . $ps['dev_sprint'] . " DEV_SPRINT\n"; } // Redis memory $history=[]; try{$redis=new Redis();$redis->connect('127.0.0.1',6379);$redis->select(3); $raw=$redis->get("saas:$session");if($raw)$history=json_decode($raw,true)?:[];}catch(Exception $e){$redis=null;} // System prompt ENRICHED with grounding $system_prompt = "Tu es WEVIA Master, IA de WEVAL Consulting Casablanca (Maroc + France + MENA).\n\n" . "RÈGLES STRICTES ANTI-HALLUCINATION (wave 253):\n" . "1. TOUJOURS utiliser les DONNÉES LIVE ci-dessous, JAMAIS dire 'je n'ai pas accès' ou 'je ne peux pas accéder'\n" . "2. Si on te demande 'combien de leads' → réponds avec le chiffre exact fourni\n" . "3. Si on te demande top industries/pays/leads → liste ceux fournis\n" . "4. Si tu manques une info, dis 'non disponible dans les données fournies' PAS 'je n'ai pas accès'\n" . "5. Réponds en français concis. Utilise les noms réels (Ethica, Vistex, Huawei, etc.)\n\n" . $ground_text . "\n" . "CONTEXTE: origin=" . basename($origin) . "\n" . "Réponds maintenant à la question en t'appuyant sur ces données live."; $messages=[['role'=>'system','content'=>$system_prompt]]; foreach(array_slice($history,-4) as $h)$messages[]=$h; $messages[]=['role'=>'user','content'=>$msg]; // Cascade $env=[];foreach(@file('/etc/weval/secrets.env',2|4)?:[] as $l){if(strpos($l,'=')!==false){[$k,$v]=explode('=',$l,2);$env[trim($k)]=trim($v," \t\"'");}} $providers=[ ['u'=>'https://api.groq.com/openai/v1/chat/completions','k'=>$env['GROQ_KEY']??'','m'=>'llama-3.3-70b-versatile','name'=>'Groq-Llama3.3'], ['u'=>'https://api.cerebras.ai/v1/chat/completions','k'=>$env['CEREBRAS_API_KEY']??'','m'=>'llama-3.3-70b','name'=>'Cerebras-Llama3.3'], ['u'=>'https://api.mistral.ai/v1/chat/completions','k'=>$env['MISTRAL_KEY']??'','m'=>'mistral-small-latest','name'=>'Mistral'], ]; $reply=''; $provider_used=''; foreach($providers as $p){ if(!$p['k'])continue; $ch=curl_init($p['u']); curl_setopt_array($ch,[CURLOPT_RETURNTRANSFER=>1,CURLOPT_POST=>1,CURLOPT_TIMEOUT=>15, CURLOPT_HTTPHEADER=>['Content-Type: application/json','Authorization: Bearer '.$p['k']], CURLOPT_POSTFIELDS=>json_encode(['model'=>$p['m'],'messages'=>$messages,'max_tokens'=>1200,'temperature'=>0.3])]); $r=curl_exec($ch);$code=curl_getinfo($ch,CURLINFO_HTTP_CODE);curl_close($ch); if($code>=200&&$code<300){$d=json_decode($r,true);$reply=$d['choices'][0]['message']['content']??'';if($reply){$provider_used=$p['name'];break;}} } if(!$reply)$reply='Service temporairement indisponible.'; $history[]=['role'=>'user','content'=>$msg]; $history[]=['role'=>'assistant','content'=>$reply]; if($redis)$redis->setex("saas:$session",3600,json_encode(array_slice($history,-20))); echo json_encode([ 'response' => wave256_sanitize($reply), 'provider'=>$provider_used ?: 'saas-sovereign', 'session'=>$session, 'grounding'=>[ 'leads_count'=>$grounding['leads']['n'] ?? null, 'industries_count'=>count($grounding['industries'] ?? []), 'solutions_count'=>count($grounding['top_solutions'] ?? []), 'wave'=>253, ] ]);