AUTO-BACKUP 20260422-1720

This commit is contained in:
opus
2026-04-22 17:20:03 +02:00
parent d8ee40f38e
commit e41ea138ed
6 changed files with 564 additions and 5 deletions

View File

@@ -0,0 +1,38 @@
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] });
const ctx = await browser.newContext({ ignoreHTTPSErrors: true, viewport: {width: 1280, height: 800} });
const page = await ctx.newPage();
const logs = [];
page.on('console', m => logs.push(`${m.type()}: ${m.text().substring(0,200)}`));
await page.goto('https://weval-consulting.com/wevia.html?cb=' + Date.now(), { waitUntil: 'networkidle' });
await page.waitForTimeout(2500);
// Type intents_pool
const input = await page.locator('#msgInput').first();
await input.click({force:true});
await input.fill('intents_pool');
await input.press('Enter');
console.log('SENT intents_pool');
await page.waitForTimeout(6000);
const state = await page.evaluate(() => {
const bodyText = document.body.innerText;
return {
seesPoolTotal: /POOL TOTAL/i.test(bodyText),
seesWeviaAssistant: /Je suis WEVIA Assistant IA/i.test(bodyText),
seesTools377: /Tool registry.*377|377.*Tool/i.test(bodyText),
seesCapacities: /2450 capacit/i.test(bodyText),
lastMsgText: (document.querySelectorAll('.msg-bot, .msg-assistant, .ambre-phase-content, .msg')[0]?.innerText || '').substring(0, 400),
};
});
console.log('STATE:', JSON.stringify(state, null, 2));
console.log('CONSOLE LOGS (last 10):');
for (const l of logs.slice(-10)) console.log(' ', l);
await page.screenshot({path:'/tmp/v180-test.png', fullPage:true});
await browser.close();
})();

View File

@@ -1,5 +1,5 @@
{
"generated_at": "2026-04-22T17:15:01.675526",
"generated_at": "2026-04-22T17:20:01.400129",
"stats": {
"total": 50,
"pending": 32,

View File

@@ -1,17 +1,17 @@
{
"ok": true,
"agent": "V42_MQL_Scoring_Agent_REAL",
"ts": "2026-04-22T15:10:01+00:00",
"ts": "2026-04-22T15:20:01+00:00",
"status": "DEPLOYED_AUTO",
"deployed": true,
"algorithm": "weighted_behavioral_signals",
"signals_tracked": {
"wtp_engagement": 80,
"wtp_engagement": 78,
"chat_engagement": 0,
"roi_tool": 0,
"email_opened": 0
},
"avg_score": 20,
"avg_score": 19.5,
"mql_threshold": 50,
"sql_threshold": 75,
"leads_captured": 48,

View File

@@ -1,7 +1,7 @@
{
"ok": true,
"version": "V83-business-kpi",
"ts": "2026-04-22T15:15:44+00:00",
"ts": "2026-04-22T15:19:45+00:00",
"summary": {
"total_categories": 8,
"total_kpis": 64,

BIN
proofs/v180-test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

521
wevia-chat-v2.html Normal file
View File

@@ -0,0 +1,521 @@
<!DOCTYPE html>
<html lang="fr" data-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>WEVIA Chat v2 · Direct Execution</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root{
--bg:#080c14; --s1:#0d1219; --s2:#131b27; --s3:#1a2435;
--tx:#e4e8f0; --dim:#8899af; --dim2:#556677;
--ac:#10b981; --ac2:rgba(16,185,129,.12);
--cy:#06b6d4; --vi:#8b5cf6; --bl:#3b82f6; --w:#f59e0b; --rd:#ef4444;
--bd:rgba(255,255,255,.06);
--mono:'JetBrains Mono',monospace; --r:10px;
}
*{box-sizing:border-box;margin:0;padding:0}
body{background:var(--bg);color:var(--tx);font-family:'Inter',-apple-system,sans-serif;height:100vh;overflow:hidden;font-size:14px}
/* LAYOUT 3 colonnes: sidebar + chat + context */
.app{display:grid;grid-template-columns:280px 1fr 360px;height:100vh;gap:0}
/* SIDEBAR */
.sidebar{background:var(--s1);border-right:1px solid var(--bd);overflow-y:auto;padding:16px 14px}
.brand{padding:8px 6px 18px;border-bottom:1px solid var(--bd);margin-bottom:16px}
.brand-title{font-weight:800;font-size:20px;color:var(--ac);letter-spacing:-0.5px}
.brand-sub{font-size:11px;color:var(--dim);letter-spacing:2px;margin-top:2px;text-transform:uppercase}
.brand-v{display:inline-block;margin-top:6px;padding:2px 8px;background:linear-gradient(135deg,var(--ac),var(--cy));color:#000;font-size:9px;font-weight:800;border-radius:4px;letter-spacing:1px}
.s-group{margin-bottom:18px}
.s-title{font-size:10px;font-weight:700;color:var(--dim2);letter-spacing:1.5px;margin-bottom:8px;padding:0 4px}
.s-item{display:flex;align-items:center;gap:10px;padding:7px 8px;border-radius:8px;cursor:pointer;font-size:13px;color:var(--dim);transition:all .15s}
.s-item:hover{background:var(--s2);color:var(--tx)}
.s-item .ico{font-size:16px;width:22px;text-align:center}
.s-item .t{flex:1}
.s-item .d{font-size:10px;color:var(--dim2)}
.s-item.active{background:var(--ac2);color:var(--ac)}
/* TOP NAV */
.topnav{padding:4px 0 14px;border-bottom:1px solid var(--bd);margin-bottom:14px}
.topnav a{display:inline-block;padding:5px 10px;margin-right:4px;background:var(--s2);color:var(--dim);border-radius:6px;text-decoration:none;font-size:11px;font-weight:600}
.topnav a:hover{background:var(--s3);color:var(--ac)}
/* CHAT CENTRAL */
.chat{display:flex;flex-direction:column;background:var(--bg);position:relative}
.chat-header{padding:14px 22px;border-bottom:1px solid var(--bd);display:flex;align-items:center;gap:12px}
.ch-avatar{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--ac),var(--cy));display:flex;align-items:center;justify-content:center;font-size:18px;font-weight:800;color:#000}
.ch-info{flex:1}
.ch-title{font-weight:700;font-size:15px}
.ch-sub{font-size:11px;color:var(--dim);margin-top:2px}
.ch-sub .dot{display:inline-block;width:7px;height:7px;background:var(--ac);border-radius:50%;margin-right:4px;animation:pulse 1.5s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
.ch-stats{display:flex;gap:14px;font-size:10px;color:var(--dim)}
.ch-stats b{color:var(--ac);font-weight:700}
.messages{flex:1;overflow-y:auto;padding:22px;scroll-behavior:smooth}
.welcome{text-align:center;padding:60px 20px;color:var(--dim)}
.welcome h1{font-size:26px;font-weight:800;color:var(--tx);margin-bottom:8px}
.welcome p{font-size:14px;max-width:520px;margin:0 auto 20px}
.welcome-cmds{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;max-width:680px;margin:24px auto}
.wc{padding:14px;background:var(--s2);border:1px solid var(--bd);border-radius:10px;cursor:pointer;text-align:left;transition:all .15s}
.wc:hover{border-color:var(--ac);background:var(--s3);transform:translateY(-1px)}
.wc-t{font-weight:600;color:var(--ac);font-size:13px;margin-bottom:4px}
.wc-d{font-size:11px;color:var(--dim)}
.msg{margin-bottom:20px;max-width:88%;animation:slideIn .3s}
@keyframes slideIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}
.msg.u{margin-left:auto}
.msg-role{font-size:10px;color:var(--dim2);margin-bottom:4px;font-weight:600;letter-spacing:1px;text-transform:uppercase}
.msg.u .msg-role{text-align:right}
.bubble{padding:13px 17px;border-radius:12px;line-height:1.55;font-size:14px;word-break:break-word}
.msg.u .bubble{background:linear-gradient(135deg,var(--bl),var(--vi));color:#fff;border-bottom-right-radius:4px}
.msg.a .bubble{background:var(--s2);border:1px solid var(--bd);border-bottom-left-radius:4px;color:var(--tx)}
.msg.a .bubble pre{white-space:pre-wrap;font-family:var(--mono);font-size:12.5px;line-height:1.6;color:var(--tx)}
.msg-meta{font-size:10px;color:var(--dim2);margin-top:6px;display:flex;gap:10px;font-family:var(--mono)}
.msg.u .msg-meta{justify-content:flex-end}
.msg-meta .tag{padding:2px 7px;background:var(--s3);border-radius:4px;font-weight:600}
.msg-meta .tag.ac{color:var(--ac);background:var(--ac2)}
.msg-meta .tag.cy{color:var(--cy);background:rgba(6,182,212,.1)}
.msg-meta .tag.vi{color:var(--vi);background:rgba(139,92,246,.1)}
/* THINKING PANEL v162 */
.thinking-panel{margin:8px 0 16px;padding:12px 16px;background:linear-gradient(135deg,rgba(16,185,129,.05),rgba(139,92,246,.05));border:1px solid rgba(16,185,129,.2);border-radius:10px;font-family:var(--mono);font-size:12px;color:var(--dim);display:none}
.thinking-panel.show{display:block;animation:slideIn .3s}
.thp-hdr{display:flex;align-items:center;gap:8px;padding-bottom:8px;border-bottom:1px dashed rgba(16,185,129,.15);margin-bottom:8px}
.thp-ico{width:12px;height:12px;border-radius:50%;background:radial-gradient(circle,var(--ac) 30%,transparent 70%);animation:pulse 1.5s infinite}
.thp-title{color:var(--ac);font-weight:600;font-size:10px;letter-spacing:1.5px;text-transform:uppercase;flex:1}
.thp-time{font-size:10px;color:var(--dim2)}
.thp-step{display:flex;align-items:center;gap:10px;padding:4px 0;font-size:11px}
.thp-step .s-ico{width:14px;text-align:center}
.thp-step .s-name{color:var(--tx);font-weight:500;min-width:80px}
.thp-step .s-det{flex:1;color:var(--dim);font-size:10.5px}
.thp-step.active .s-ico{animation:spin 1s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.thp-step.done .s-ico{color:var(--ac)}
/* PROGRESS */
.progress{display:none;padding:8px 12px;background:var(--s2);border-radius:8px;font-size:11px;color:var(--dim);margin:4px 0;font-family:var(--mono)}
.progress.show{display:block}
.progress-bar{height:3px;background:var(--s3);border-radius:2px;overflow:hidden;margin-top:5px}
.progress-fill{height:100%;background:linear-gradient(90deg,var(--ac),var(--cy));transition:width .3s;border-radius:2px}
/* INPUT */
.input-wrap{padding:14px 22px 18px;border-top:1px solid var(--bd);background:var(--s1)}
.input-box{display:flex;gap:10px;background:var(--s2);border:1px solid var(--bd);border-radius:14px;padding:4px 4px 4px 16px;align-items:flex-end}
.input-box:focus-within{border-color:var(--ac)}
#msgInput{flex:1;background:none;border:0;color:var(--tx);font-family:inherit;font-size:14px;resize:none;min-height:24px;max-height:140px;padding:10px 0;outline:0;line-height:1.5}
#sendBtn{background:linear-gradient(135deg,var(--ac),var(--cy));color:#000;border:0;width:40px;height:40px;border-radius:10px;cursor:pointer;font-size:17px;font-weight:800;display:flex;align-items:center;justify-content:center}
#sendBtn:hover{transform:scale(1.04)}
#sendBtn:disabled{opacity:.5;cursor:wait}
.hint{font-size:10px;color:var(--dim2);margin-top:6px;text-align:center}
/* CONTEXT PANEL (right) */
.context{background:var(--s1);border-left:1px solid var(--bd);overflow-y:auto}
.ctx-tabs{display:flex;padding:10px 12px 0;border-bottom:1px solid var(--bd);gap:2px}
.ctx-tab{padding:8px 12px;background:none;border:0;color:var(--dim);font-size:11px;font-weight:600;cursor:pointer;border-bottom:2px solid transparent;font-family:inherit}
.ctx-tab.active{color:var(--ac);border-bottom-color:var(--ac)}
.ctx-tab:hover{color:var(--tx)}
.ctx-content{padding:14px;display:none}
.ctx-content.active{display:block}
.ctx-log{font-family:var(--mono);font-size:11px;line-height:1.8;color:var(--dim)}
.ctx-log .t{color:var(--ac);font-weight:600}
.ctx-log .d{color:var(--dim2)}
.ctx-kpi{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.kpi-card{background:var(--s2);padding:12px;border-radius:8px;border:1px solid var(--bd)}
.kpi-label{font-size:9px;color:var(--dim);letter-spacing:1px;text-transform:uppercase;margin-bottom:4px}
.kpi-value{font-size:20px;font-weight:800;color:var(--ac);font-family:var(--mono)}
.kpi-card.w .kpi-value{color:var(--w)}
.kpi-card.cy .kpi-value{color:var(--cy)}
.kpi-card.vi .kpi-value{color:var(--vi)}
.scroll::-webkit-scrollbar{width:6px}
.scroll::-webkit-scrollbar-thumb{background:var(--s3);border-radius:3px}
/* Mobile */
@media (max-width:1100px){.app{grid-template-columns:1fr}.sidebar,.context{display:none}}
.badge-v2{position:fixed;top:14px;right:14px;padding:6px 12px;background:linear-gradient(135deg,var(--w),var(--rd));color:#000;border-radius:20px;font-size:10px;font-weight:800;letter-spacing:1px;z-index:10;box-shadow:0 4px 14px rgba(245,158,11,.3)}
</style>
</head>
<body>
<div class="badge-v2">v2 DIRECT · 0 LLM FALLBACK</div>
<div class="app">
<!-- ===== SIDEBAR ===== -->
<div class="sidebar scroll">
<div class="brand">
<div class="brand-title">WEVIA</div>
<div class="brand-sub">Chat v2 · Direct</div>
<div class="brand-v">WAVE-269 · 0 HALLUC</div>
</div>
<div class="topnav">
<a href="/weval-technology-platform.html">WTP</a>
<a href="/all-ia-hub.html">IA Hub</a>
<a href="/wevia-orchestrator.html">Arena</a>
<a href="/wevia-master.html">Master</a>
</div>
<div class="s-group">
<div class="s-title">IA AGENTS</div>
<div class="s-item" onclick="quickCmd('ethica status')"><span class="ico">💊</span><span class="t">Ethica HCP</span></div>
<div class="s-item" onclick="quickCmd('deerflow skills')"><span class="ico">🦌</span><span class="t">DeerFlow</span></div>
<div class="s-item" onclick="quickCmd('paperclip status')"><span class="ico">📎</span><span class="t">Paperclip</span></div>
<div class="s-item" onclick="quickCmd('consensus')"><span class="ico">⚖️</span><span class="t">Consensus</span></div>
<div class="s-item" onclick="quickCmd('blade status')"><span class="ico"></span><span class="t">Blade IA</span></div>
<div class="s-item" onclick="quickCmd('director')"><span class="ico">👁️</span><span class="t">Director</span></div>
<div class="s-item" onclick="quickCmd('wedroid')"><span class="ico">🔧</span><span class="t">WEDROID</span></div>
<div class="s-item" onclick="quickCmd('openclaw')"><span class="ico">🐙</span><span class="t">OpenClaw</span></div>
<div class="s-item" onclick="quickCmd('wevcode')"><span class="ico">⚙️</span><span class="t">WEVCODE</span></div>
</div>
<div class="s-group">
<div class="s-title">ACTIONS</div>
<div class="s-item" onclick="quickCmd('audit complet')"><span class="ico">🔍</span><span class="t">Audit Complet</span></div>
<div class="s-item" onclick="quickCmd('auto-fix')"><span class="ico">🔧</span><span class="t">Auto-Fix</span></div>
<div class="s-item" onclick="quickCmd('nonreg')"><span class="ico">🧪</span><span class="t">NonReg</span></div>
<div class="s-item" onclick="quickCmd('benchmark')"><span class="ico">📊</span><span class="t">Benchmark</span></div>
<div class="s-item" onclick="quickCmd('security scan')"><span class="ico">🛡️</span><span class="t">Security</span></div>
<div class="s-item" onclick="quickCmd('disk clean')"><span class="ico">🧹</span><span class="t">Disk Clean</span></div>
<div class="s-item" onclick="quickCmd('guardian')"><span class="ico">🔒</span><span class="t">Guardian</span></div>
<div class="s-item" onclick="quickCmd('git push')"><span class="ico">📂</span><span class="t">Git Push</span></div>
<div class="s-item" onclick="quickCmd('crons status')"><span class="ico"></span><span class="t">Crons</span></div>
</div>
<div class="s-group">
<div class="s-title">OUTILS</div>
<div class="s-item" onclick="quickCmd('intents_pool')"><span class="ico">🎯</span><span class="t">Intents Pool</span><span class="d">2450</span></div>
<div class="s-item" onclick="quickCmd('wiring map')"><span class="ico">🔌</span><span class="t">Wiring Map</span></div>
<div class="s-item" onclick="quickCmd('rag status')"><span class="ico">🧠</span><span class="t">RAG Status</span></div>
<div class="s-item" onclick="quickCmd('quelle heure')"><span class="ico"></span><span class="t">Heure Serveur</span></div>
<div class="s-item" onclick="quickCmd('orchestrate')"><span class="ico">🎼</span><span class="t">Orchestrate</span></div>
<div class="s-item" onclick="quickCmd('multiagent parallele: nonreg + l99 + git + ethica')"><span class="ico"></span><span class="t">Multi-Agent</span></div>
</div>
</div>
<!-- ===== CHAT CENTER ===== -->
<div class="chat">
<div class="chat-header">
<div class="ch-avatar">W</div>
<div class="ch-info">
<div class="ch-title">WEVIA Master AI · Chat v2</div>
<div class="ch-sub"><span class="dot"></span>Connecté · Direct Endpoint · 0 LLM Fallback</div>
</div>
<div class="ch-stats">
<div>Providers <b id="stProviders">17</b></div>
<div>Tools <b id="stTools">649</b></div>
<div>Agents <b id="stAgents">906</b></div>
</div>
</div>
<div class="messages scroll" id="messages">
<div class="welcome" id="welcome">
<h1>👋 Bienvenue sur WEVIA Chat v2</h1>
<p>Version <b>directe</b> sans LLM fallback buggé. Chaque commande retourne <b>la vraie donnée</b> du backend — zéro hallucination, zéro simulation.</p>
<div class="welcome-cmds">
<div class="wc" onclick="quickCmd('intents_pool')">
<div class="wc-t">🎯 intents_pool</div>
<div class="wc-d">Liste les 2450 capacités mobilisables</div>
</div>
<div class="wc" onclick="quickCmd('quelle heure')">
<div class="wc-t">⌚ quelle heure</div>
<div class="wc-d">Heure réelle du serveur WEVAL</div>
</div>
<div class="wc" onclick="quickCmd('audit complet')">
<div class="wc-t">🔍 audit complet</div>
<div class="wc-d">Diagnostic infrastructure live</div>
</div>
<div class="wc" onclick="quickCmd('orchestrate')">
<div class="wc-t">🎼 orchestrate</div>
<div class="wc-d">Multi-agent status temps réel</div>
</div>
<div class="wc" onclick="quickCmd('multiagent parallele: nonreg + l99 + git + ethica + security')">
<div class="wc-t">⚡ multiagent parallele</div>
<div class="wc-d">5 agents en parallèle (live data)</div>
</div>
<div class="wc" onclick="quickCmd('cable un intent pour afficher uptime quand on dit uptime. Commande: uptime')">
<div class="wc-t">🛠️ cable un intent</div>
<div class="wc-d">Auto-wire NL (crée intent à la volée)</div>
</div>
</div>
</div>
</div>
<div class="input-wrap">
<div class="progress" id="progress">
<div id="progressText">Routing intent...</div>
<div class="progress-bar"><div class="progress-fill" id="progressFill" style="width:0%"></div></div>
</div>
<div class="input-box">
<textarea id="msgInput" placeholder="Tape une commande ou pose une question... Entrée pour envoyer." rows="1"
onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();send()}"></textarea>
<button id="sendBtn" onclick="send()"></button>
</div>
<div class="hint">Entrée = envoyer · Shift+Entrée = nouvelle ligne · Chaque réponse = donnée live du backend</div>
</div>
</div>
<!-- ===== CONTEXT PANEL ===== -->
<div class="context scroll">
<div class="ctx-tabs">
<button class="ctx-tab active" onclick="ctxTab('thinking',event)">Thinking</button>
<button class="ctx-tab" onclick="ctxTab('kpi',event)">KPI Live</button>
<button class="ctx-tab" onclick="ctxTab('history',event)">History</button>
</div>
<div class="ctx-content active" id="ctx-thinking">
<div class="ctx-log" id="thinkingLog">
<div class="d">--:--:--</div>
<div>En attente d'une requête...</div>
</div>
</div>
<div class="ctx-content" id="ctx-kpi">
<div class="ctx-kpi">
<div class="kpi-card"><div class="kpi-label">NonReg</div><div class="kpi-value" id="kpiNonreg">153/153</div></div>
<div class="kpi-card cy"><div class="kpi-label">L99</div><div class="kpi-value" id="kpiL99">341/341</div></div>
<div class="kpi-card vi"><div class="kpi-label">Tools</div><div class="kpi-value" id="kpiTools">649</div></div>
<div class="kpi-card w"><div class="kpi-label">Pool</div><div class="kpi-value" id="kpiPool">2450</div></div>
</div>
<div style="margin-top:16px;padding:12px;background:var(--s2);border-radius:8px;font-size:11px;color:var(--dim);font-family:var(--mono);line-height:1.7">
<div style="color:var(--ac);font-weight:700;margin-bottom:6px">LIVE STATUS</div>
<div id="kpiLive">Loading...</div>
</div>
</div>
<div class="ctx-content" id="ctx-history">
<div id="historyList" style="font-size:11px;color:var(--dim)">Historique de session (s'affiche au fur et à mesure des messages)</div>
</div>
</div>
</div>
<script>
// ============= STATE =============
const state = {
busy: false,
sessionId: 'chatv2-' + Date.now().toString(36),
history: [],
startTime: null
};
// ============= UTILS =============
function $(id){return document.getElementById(id)}
function now(){return new Date().toLocaleTimeString('fr-FR',{hour:'2-digit',minute:'2-digit',second:'2-digit'})}
function esc(s){return (s||'').replace(/[&<>]/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;'}[m]))}
// ============= THINKING LOG =============
function thLog(icon, name, detail, status){
const log = $('thinkingLog');
const t = now();
const statusClass = status === 'done' ? 'done' : (status === 'active' ? 'active' : '');
log.innerHTML += `<div class="thp-step ${statusClass}" style="margin:6px 0">
<span class="s-ico">${icon}</span>
<span class="s-name">${name}</span>
<span class="s-det">${detail}</span>
<span class="d">${t}</span>
</div>`;
log.scrollTop = log.scrollHeight;
}
function thClear(){
$('thinkingLog').innerHTML = '';
}
// ============= PROGRESS =============
function progress(text, pct){
const p = $('progress');
p.classList.add('show');
$('progressText').textContent = text;
$('progressFill').style.width = pct + '%';
}
function progressHide(){ $('progress').classList.remove('show'); }
// ============= CONTEXT TABS =============
function ctxTab(name, ev){
document.querySelectorAll('.ctx-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.ctx-content').forEach(c => c.classList.remove('active'));
ev.target.classList.add('active');
$('ctx-' + name).classList.add('active');
}
// ============= ADD MESSAGE =============
function addMsg(text, role, meta){
// Hide welcome
const w = $('welcome'); if(w) w.style.display = 'none';
const msgs = $('messages');
const el = document.createElement('div');
el.className = 'msg ' + role;
const roleName = role === 'u' ? 'Vous' : 'WEVIA';
let metaHtml = '';
if(meta){
const tags = [];
if(meta.engine) tags.push(`<span class="tag ac">${esc(meta.engine)}</span>`);
if(meta.intent) tags.push(`<span class="tag cy">${esc(meta.intent)}</span>`);
if(meta.time) tags.push(`<span class="tag vi">${esc(meta.time)}</span>`);
if(meta.status) tags.push(`<span class="tag">${esc(meta.status)}</span>`);
if(tags.length) metaHtml = `<div class="msg-meta">${tags.join('')}</div>`;
}
// Detect if response is data-like (contains === or newlines) -> render as <pre>
const isData = role === 'a' && (text.includes('===') || text.split('\n').length > 3 || text.includes('['));
const bubbleContent = isData
? `<pre>${esc(text)}</pre>`
: esc(text).replace(/\n/g, '<br>');
el.innerHTML = `
<div class="msg-role">${roleName}</div>
<div class="bubble">${bubbleContent}</div>
${metaHtml}
`;
msgs.appendChild(el);
msgs.scrollTop = msgs.scrollHeight;
// Add to history panel
const hist = $('historyList');
if(hist){
const preview = esc(text).substring(0, 80);
hist.innerHTML = `<div style="padding:8px;background:var(--s2);border-radius:6px;margin-bottom:6px;border-left:3px solid ${role==='u'?'var(--bl)':'var(--ac)'}">
<div style="color:${role==='u'?'var(--bl)':'var(--ac)'};font-weight:600;font-size:10px">${roleName.toUpperCase()} · ${now()}</div>
<div style="margin-top:3px">${preview}${text.length>80?'...':''}</div>
</div>` + hist.innerHTML;
}
state.history.push({role, text, meta, t: Date.now()});
}
// ============= SEND =============
async function send(){
const inp = $('msgInput');
const text = inp.value.trim();
if(!text || state.busy) return;
state.busy = true;
state.startTime = Date.now();
$('sendBtn').disabled = true;
inp.value = '';
inp.style.height = 'auto';
thClear();
thLog('🧠', 'Thinking', `analyzing · ${text.length} chars`, 'active');
progress('Routing intent...', 10);
addMsg(text, 'u');
thLog('📋', 'Plan', 'Direct endpoint routing (no LLM fallback)', 'done');
thLog('🔗', 'API', 'POST /api/wevia-master-api.php', 'active');
progress('Querying backend...', 35);
try {
const t0 = Date.now();
const res = await fetch('/api/wevia-master-api.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
message: text,
session: state.sessionId,
history: state.history.slice(-10).map(h => ({role: h.role==='u'?'user':'assistant', content: h.text}))
}),
signal: AbortSignal.timeout(60000)
});
progress('Processing response...', 70);
const raw = await res.text();
const elapsed = Date.now() - t0;
thLog('⚙', 'Execute', `HTTP ${res.status} · ${raw.length} bytes · ${elapsed}ms`, 'done');
// Parse response (try JSON first, then SSE)
let answer = '', engine = '', intent = '', tool = '';
try {
const j = JSON.parse(raw);
answer = j.content || j.response || j.text || j.message || raw;
engine = j.engine || j.provider || '';
intent = j.intent || '';
tool = j.tool || '';
} catch(e) {
// Try SSE
for (const line of raw.split('\n')) {
if (line.startsWith('data: ')) {
try {
const d = JSON.parse(line.substring(6));
if ((d.type === 'answer' || d.type === 'chunk') && (d.text || d.content)) {
answer += (d.text || d.content);
engine = d.engine || engine;
intent = d.intent || intent;
}
} catch(e2) {}
}
}
if(!answer) answer = raw;
}
thLog('💬', 'Response', `${answer.length} chars · engine=${engine||tool||'direct'}`, 'done');
progress('Done', 100);
setTimeout(progressHide, 800);
addMsg(answer, 'a', {
engine: engine || tool || 'direct',
intent: intent,
time: (elapsed/1000).toFixed(2) + 's',
status: 'REAL'
});
} catch(e) {
thLog('❌', 'Error', e.message, 'done');
progressHide();
addMsg('⚠️ Erreur réseau: ' + e.message, 'a', {engine: 'error'});
}
state.busy = false;
$('sendBtn').disabled = false;
inp.focus();
}
// ============= QUICK COMMANDS =============
function quickCmd(cmd){
$('msgInput').value = cmd;
send();
}
// ============= AUTO-RESIZE TEXTAREA =============
$('msgInput').addEventListener('input', function(){
this.style.height = 'auto';
this.style.height = Math.min(this.scrollHeight, 140) + 'px';
});
// ============= LIVE KPI LOAD =============
async function loadKPI(){
try {
const nr = await fetch('/api/nonreg-api.php?cat=all').then(r => r.json());
if(nr && nr.summary) {
$('kpiNonreg').textContent = nr.summary.pass + '/' + nr.summary.total;
}
} catch(e){}
try {
const imr = await fetch('/api/infra-monitor-api.php').then(r => r.json());
if(imr){
let live = '';
if(imr.load) live += 'LOAD: ' + JSON.stringify(imr.load).substring(0,60) + '\n';
if(imr.disk) live += 'DISK: ' + JSON.stringify(imr.disk).substring(0,60) + '\n';
if(imr.docker) live += 'DOCKER: ' + (Array.isArray(imr.docker)?imr.docker.length:'?') + ' containers\n';
$('kpiLive').textContent = live || 'Infra live data loaded';
}
} catch(e){ $('kpiLive').textContent = 'KPI refresh every 30s'; }
}
loadKPI();
setInterval(loadKPI, 30000);
$('msgInput').focus();
console.log('WEVIA Chat v2 ready. Session:', state.sessionId);
</script>
</body>
</html>