Files
html/wevia-chat-v2.html
Opus 9a5f534d8b
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
phase30 doctrine 167 cascade enrich 6 pages - 18 pages UX doctrine 60 total
6 pages centrales enrichies via cascade Cerebras:
- wevia-chat-v2 (+1328B)
- sovereign-monitor (+1271B)
- wevia-audit (+1266B)
- wevia-console (+1263B)
- wevia-autonomy-dashboard (+1292B)
- wevia-business-visual-studio (+1300B)

Handler /var/www/html/api/enrich-hub-cascade.sh:
- Try Cerebras qwen-3-235b primary
- Fallback Ollama llama3.2 LOCAL (zero rate limit)
- GOLD backup + chattr handling + lint
- Markers DOCTRINE-60-UX-ENRICH idempotent

Intent wevia_enrich_hub_cascade_cerebras_ollama wired pour chat NL.
Total: 18 pages UX doctrine 60 (12 avant + 6 aujourd hui).

Cascade zero-rate-limit effective: Cerebras OK + Ollama llama3.2 ready.
Disk 87% stable apres recovery phase 29 +19GB.
2026-04-24 10:44:16 +02:00

520 lines
26 KiB
HTML

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>WEVIA Chat V2 — Sovereign AI (Wave 268)</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
:root{
--bg:#0b0d15; --bg2:#1a1f3a; --bg3:#1e293b;
--tx:#e2e8f0; --tx2:#94a3b8; --tx3:#64748b;
--cyan:#06b6d4; --green:#10b981; --purple:#8b5cf6; --orange:#f59e0b; --pink:#ec4899;
--border:rgba(255,255,255,0.08); --shadow:0 4px 20px rgba(0,0,0,0.4);
}
body{font:14px/1.5 -apple-system,"Segoe UI",Inter,sans-serif;background:var(--bg);color:var(--tx);height:100vh;overflow:hidden;display:flex;flex-direction:column}
/* TOP NAV — portals */
.topnav{display:flex;align-items:center;gap:10px;padding:10px 16px;background:linear-gradient(90deg,rgba(6,182,212,0.08),rgba(139,92,246,0.08));border-bottom:1px solid var(--border);flex-shrink:0}
.topnav .brand{font-weight:800;font-size:16px;background:linear-gradient(135deg,var(--cyan),var(--green));-webkit-background-clip:text;-webkit-text-fill-color:transparent;margin-right:12px}
.topnav .wave{font-size:10px;padding:3px 8px;background:rgba(245,158,11,0.15);color:var(--orange);border-radius:10px;border:1px solid rgba(245,158,11,0.3);font-weight:700;letter-spacing:0.5px}
.portal{padding:5px 12px;background:rgba(255,255,255,0.04);color:var(--tx);text-decoration:none;border-radius:14px;font-size:11px;font-weight:600;border:1px solid var(--border);transition:all 0.15s}
.portal:hover{background:rgba(255,255,255,0.08);transform:translateY(-1px)}
.portal.hub{color:var(--cyan);border-color:rgba(6,182,212,0.3);background:rgba(6,182,212,0.1)}
.portal.arena{color:var(--purple);border-color:rgba(139,92,246,0.3);background:rgba(139,92,246,0.1)}
.portal.wtp{color:var(--green);border-color:rgba(16,185,129,0.3);background:rgba(16,185,129,0.1)}
.portal.wevcode{color:var(--pink);border-color:rgba(236,72,153,0.3);background:rgba(236,72,153,0.1)}
.topnav .spacer{flex:1}
.status-pill{display:flex;align-items:center;gap:6px;padding:4px 10px;background:rgba(16,185,129,0.1);border:1px solid rgba(16,185,129,0.3);border-radius:12px;font-size:11px;color:var(--green);font-weight:600}
.dot-g{width:7px;height:7px;border-radius:50%;background:var(--green);box-shadow:0 0 8px var(--green);animation:pulse 2s infinite}
@keyframes pulse{50%{opacity:0.5}}
/* MAIN GRID */
.app{display:grid;grid-template-columns:260px 1fr 340px;flex:1;min-height:0}
/* SIDEBAR */
.sidebar{background:var(--bg2);border-right:1px solid var(--border);overflow-y:auto;padding:16px}
.sb-head{padding-bottom:14px;border-bottom:1px solid var(--border);margin-bottom:14px}
.sb-head h2{font-size:18px;background:linear-gradient(135deg,var(--cyan),var(--purple));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
.sb-head small{color:var(--tx3);font-size:10px;letter-spacing:1px}
.sb-label{font-size:10px;color:var(--tx3);letter-spacing:1.5px;text-transform:uppercase;margin:14px 0 6px;font-weight:700}
.sb-item{display:flex;align-items:center;gap:8px;width:100%;padding:8px 10px;background:transparent;border:1px solid transparent;border-radius:8px;color:var(--tx);font-size:12px;cursor:pointer;text-align:left;transition:all 0.12s;margin-bottom:2px;font-family:inherit}
.sb-item:hover{background:rgba(6,182,212,0.08);border-color:rgba(6,182,212,0.2);color:var(--cyan)}
.sb-item .ic{font-size:14px;flex-shrink:0}
.sb-foot{margin-top:20px;padding:12px;background:rgba(0,0,0,0.3);border-radius:8px;font-size:10px;color:var(--tx2);line-height:1.6}
.sb-foot .val{color:var(--cyan);font-weight:700}
/* CHAT */
.chat{display:flex;flex-direction:column;min-height:0}
.chat-header{padding:12px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;background:rgba(6,182,212,0.03)}
.chat-header h3{font-size:15px}
.chat-header .badges{display:flex;gap:8px;align-items:center}
.badge{padding:3px 8px;background:rgba(6,182,212,0.1);color:var(--cyan);border-radius:10px;font-size:10px;font-weight:700;border:1px solid rgba(6,182,212,0.3)}
.badge.green{background:rgba(16,185,129,0.1);color:var(--green);border-color:rgba(16,185,129,0.3)}
.msgs{flex:1;overflow-y:auto;padding:20px;display:flex;flex-direction:column;gap:14px}
.msg{display:flex;flex-direction:column;max-width:85%;animation:slideIn 0.3s}
@keyframes slideIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
.msg.u{align-self:flex-end}
.msg.a{align-self:flex-start;max-width:92%}
.msg .role{font-size:10px;color:var(--tx3);margin-bottom:4px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase}
.msg .bubble{padding:12px 16px;border-radius:14px;line-height:1.55;word-wrap:break-word}
.msg.u .bubble{background:linear-gradient(135deg,var(--cyan),#0891b2);color:#fff;border-bottom-right-radius:4px}
.msg.a .bubble{background:var(--bg3);border:1px solid var(--border);border-bottom-left-radius:4px}
.msg.a .bubble pre{background:rgba(0,0,0,0.4);padding:10px;border-radius:8px;overflow-x:auto;font-size:12px;margin:6px 0;color:#cbd5e1;white-space:pre-wrap}
.msg.a .bubble code{background:rgba(6,182,212,0.15);padding:1px 5px;border-radius:4px;color:var(--cyan);font-size:12px}
.msg .meta{font-size:10px;color:var(--tx3);margin-top:4px;display:flex;gap:10px;font-family:monospace}
.msg .meta .eng{color:var(--green);font-weight:700}
.msg.a.thinking .bubble{background:rgba(6,182,212,0.05);border-style:dashed;color:var(--tx2);font-style:italic}
/* SUGGESTED CHIPS */
.suggestions{padding:0 20px 14px;display:flex;gap:6px;flex-wrap:wrap;border-bottom:1px solid var(--border)}
.chip{padding:5px 10px;background:rgba(139,92,246,0.08);color:var(--purple);border:1px solid rgba(139,92,246,0.25);border-radius:12px;font-size:11px;cursor:pointer;transition:all 0.12s;font-family:inherit}
.chip:hover{background:rgba(139,92,246,0.2);transform:translateY(-1px)}
/* INPUT */
.input-area{padding:14px 20px;border-top:1px solid var(--border);background:var(--bg2)}
.input-row{display:flex;gap:10px;align-items:flex-end;background:var(--bg3);border:1px solid var(--border);border-radius:14px;padding:6px;transition:all 0.15s}
.input-row:focus-within{border-color:var(--cyan);box-shadow:0 0 0 3px rgba(6,182,212,0.1)}
textarea#inp{flex:1;background:transparent;color:var(--tx);border:0;outline:0;resize:none;padding:10px 12px;font:14px/1.4 inherit;max-height:120px;min-height:40px}
.btn-send{background:linear-gradient(135deg,var(--cyan),var(--green));color:#000;border:0;width:40px;height:40px;border-radius:10px;cursor:pointer;font-size:16px;font-weight:700;transition:all 0.12s;flex-shrink:0;display:flex;align-items:center;justify-content:center}
.btn-send:hover{transform:scale(1.05)}
.btn-send:disabled{opacity:0.4;cursor:wait}
.input-hint{font-size:10px;color:var(--tx3);margin-top:6px;text-align:center}
/* RIGHT PANEL — Live Context */
.context-panel{background:var(--bg2);border-left:1px solid var(--border);overflow-y:auto;padding:16px}
.cp-tabs{display:flex;gap:4px;margin-bottom:14px;border-bottom:1px solid var(--border);padding-bottom:10px}
.cp-tab{padding:6px 12px;background:transparent;border:1px solid transparent;color:var(--tx2);font-size:11px;border-radius:8px;cursor:pointer;font-weight:600;font-family:inherit;transition:all 0.12s}
.cp-tab.active{background:rgba(6,182,212,0.1);color:var(--cyan);border-color:rgba(6,182,212,0.3)}
.cp-section{margin-bottom:14px}
.cp-section h4{font-size:10px;color:var(--tx3);letter-spacing:1px;text-transform:uppercase;margin-bottom:8px;font-weight:700}
.cp-row{display:flex;justify-content:space-between;font-size:11px;padding:4px 0;border-bottom:1px dashed var(--border)}
.cp-row .k{color:var(--tx2)}
.cp-row .v{color:var(--cyan);font-weight:700;font-family:monospace}
.log-entry{font-size:10px;padding:4px 8px;background:rgba(0,0,0,0.2);border-left:2px solid var(--cyan);margin:3px 0;border-radius:0 4px 4px 0;font-family:monospace;color:var(--tx2)}
.log-entry .ts{color:var(--tx3)}
.log-entry .engine{color:var(--green);font-weight:700}
/* RESPONSIVE */
@media (max-width:1100px){.context-panel{display:none}.app{grid-template-columns:240px 1fr}}
@media (max-width:780px){.sidebar{display:none}.app{grid-template-columns:1fr}}
/* PULSING DOT during request */
.typing{display:inline-flex;gap:3px;align-items:center}
.typing span{width:6px;height:6px;background:var(--cyan);border-radius:50%;animation:bounce 1.4s infinite}
.typing span:nth-child(2){animation-delay:0.2s}
.typing span:nth-child(3){animation-delay:0.4s}
@keyframes bounce{0%,80%,100%{transform:translateY(0);opacity:0.4}40%{transform:translateY(-6px);opacity:1}}
</style>
<!-- DOCTRINE-60-UX-ENRICH cerebras-qwen-235b 20260424-104048 --><style id="doctrine60-ux-wevia-chat-v2">
body::before {
content: '';
position: fixed;
width: 100%;
height: 100%;
background: radial-gradient(ellipse at center, rgba(0,0,0,0.12) 0%, rgba(0,0,0,0) 70%);
z-index: -1;
pointer-events: none;
}
.card, .btn, .kpi, .panel {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.5s cubic-bezier(0.4, 0, 0.2, 1), transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
.enter-stagger {
opacity: 1;
transform: translateY(0);
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.pulse, .active, .live-indicator, .online {
animation: pulse 3s ease-in-out infinite;
}
.card:hover {
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
border-color: var(--accent);
}
.modal, .chat, .speech, .overlay {
backdrop-filter: blur(12px);
}
</style>
</head>
<body>
<nav class="topnav">
<span class="brand">WEVIA Chat V2</span>
<span class="wave">WAVE 268 · DIRECT BACKEND</span>
<a href="/all-ia-hub.html" class="portal hub" title="All-IA Hub">All-IA Hub</a>
<a href="/wevia-orchestrator.html" class="portal arena" title="Arena Orchestrator">Arena</a>
<a href="/weval-technology-platform.html" class="portal wtp" title="WTP Hub">WTP Hub</a>
<a href="/wevcode.html" class="portal wevcode" title="WEVCODE">WEVCODE</a>
<a href="/wevia-master.html" class="portal" title="Legacy wevia-master">Legacy</a>
<span class="spacer"></span>
<span class="status-pill"><span class="dot-g"></span> <span id="statusText">Connecté · Zero LLM fallback</span></span>
</nav>
<div class="app">
<aside class="sidebar">
<div class="sb-head">
<h2>WEVIA</h2>
<small>CHAT V2 · DIRECT BACKEND</small>
</div>
<div class="sb-label">IA Agents</div>
<button class="sb-item" onclick="q('ethica combien de HCP par pays')"><span class="ic">💊</span>Ethica HCP</button>
<button class="sb-item" onclick="q('deerflow recherche tendances')"><span class="ic">🦌</span>DeerFlow</button>
<button class="sb-item" onclick="q('paperclip status goals')"><span class="ic">📎</span>Paperclip</button>
<button class="sb-item" onclick="q('consensus stratgie')"><span class="ic">⚖️</span>Consensus</button>
<button class="sb-item" onclick="q('blade desktop status')"><span class="ic"></span>Blade IA</button>
<button class="sb-item" onclick="q('director supervision')"><span class="ic">👁️</span>Director</button>
<button class="sb-item" onclick="q('wedroid backend diagnostic')"><span class="ic">🔧</span>WEDROID</button>
<button class="sb-item" onclick="q('openclaw ollama models')"><span class="ic">🐙</span>OpenClaw</button>
<button class="sb-item" onclick="q('wevcode assistant code')"><span class="ic">⚙️</span>WEVCODE</button>
<button class="sb-item" onclick="q('nuclei scan scurit')"><span class="ic">🔬</span>Nuclei</button>
<div class="sb-label">Actions</div>
<button class="sb-item" onclick="q('audit complet RAM disk Docker')"><span class="ic">🔍</span>Audit Complet</button>
<button class="sb-item" onclick="q('auto-fix repare tout')"><span class="ic">🔧</span>Auto-Fix</button>
<button class="sb-item" onclick="q('lance nonreg')"><span class="ic">🧪</span>NonReg</button>
<button class="sb-item" onclick="q('benchmark classement')"><span class="ic">📊</span>Benchmark</button>
<button class="sb-item" onclick="q('scuris firewall')"><span class="ic">🛡️</span>Security</button>
<button class="sb-item" onclick="q('nettoie le disque')"><span class="ic">🧹</span>Disk Clean</button>
<button class="sb-item" onclick="q('lance guardian')"><span class="ic">🔒</span>Guardian</button>
<button class="sb-item" onclick="q('git push status')"><span class="ic">📂</span>Git Push</button>
<button class="sb-item" onclick="q('consolide les crons')"><span class="ic"></span>Crons</button>
<button class="sb-item" onclick="q('test fonctionnel')"><span class="ic"></span>Func Test</button>
<button class="sb-item" onclick="q('vacuum ethica')"><span class="ic">💊</span>Vacuum DB</button>
<div class="sb-label">Outils</div>
<button class="sb-item" onclick="q('intents_pool')"><span class="ic">🔌</span>Intents Pool</button>
<button class="sb-item" onclick="q('rag status qdrant')"><span class="ic">🧠</span>RAG Status</button>
<button class="sb-item" onclick="q('cherche weval consulting')"><span class="ic">🔎</span>Recherche Web</button>
<button class="sb-item" onclick="q('traduis en anglais bonjour')"><span class="ic">🌍</span>Traduction</button>
<button class="sb-item" onclick="q('diagramme architecture')"><span class="ic">📐</span>Diagramme</button>
<button class="sb-item" onclick="q('scraping extraction web')"><span class="ic">🕷️</span>Scraping</button>
<button class="sb-item" onclick="q('port scan ouverts')"><span class="ic">🔌</span>Port Scan</button>
<button class="sb-item" onclick="q('value chain processus')"><span class="ic">📈</span>Value Chain</button>
<button class="sb-item" onclick="q('orchestrate')"><span class="ic">🎭</span>Orchestrate 35 agents</button>
<button class="sb-item" onclick="q('quelle heure')"><span class="ic">⏱️</span>Heure serveur</button>
<div class="sb-foot">
<span id="prov">17</span> providers | 0€<br>
<span id="stats">2450 capacites · 906 agents · 12 cascade</span>
</div>
</aside>
<main class="chat">
<div class="chat-header">
<h3>💬 WEVIA Chat V2 · <span style="color:var(--tx3);font-size:12px;font-weight:400">Direct backend, no LLM fallback</span></h3>
<div class="badges">
<span class="badge green">Backend: chat-v2-direct</span>
<span class="badge">Zero hallucination</span>
</div>
</div>
<div class="msgs" id="msgs">
<div class="msg a">
<div class="role">WEVIA</div>
<div class="bubble">
<b>🏆 Chat V2 prêt</b><br>
Cette page appelle <code>wevia-chat-v2-direct.php</code> en direct et affiche la réponse <b>brute du backend</b> — aucun LLM fallback ne peut polluer la sortie.<br><br>
Clique un bouton sidebar, une suggestion, ou tape ta commande.<br>
Essaie : <code>intents_pool</code> · <code>orchestrate</code> · <code>quelle heure</code> · <code>multiagent parallele: nonreg + l99 + git</code>
</div>
</div>
</div>
<div class="suggestions" id="suggestions">
<button class="chip" onclick="q('intents_pool')">🔌 Intents pool (2450 capacités)</button>
<button class="chip" onclick="q('orchestrate')">🎭 Orchestrate 35 agents</button>
<button class="chip" onclick="q('quelle heure')">⏱️ Quelle heure</button>
<button class="chip" onclick="q('audit complet RAM disk Docker')">🔍 Audit complet</button>
<button class="chip" onclick="q('lance nonreg')">🧪 NonReg</button>
<button class="chip" onclick="q('multiagent parallele: nonreg + l99 + git + ethica')">🌐 Multi-agent</button>
</div>
<div class="input-area">
<div class="input-row">
<textarea id="inp" rows="1" placeholder="Demandez à WEVIA... (ex: intents_pool, orchestrate, quelle heure, audit complet)" onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();send()}"></textarea>
<button class="btn-send" id="sendBtn" onclick="send()"></button>
</div>
<div class="input-hint">Enter envoyer · Shift+Enter nouvelle ligne · Zéro LLM fallback · Réponse backend brute</div>
</div>
</main>
<aside class="context-panel">
<div class="cp-tabs">
<button class="cp-tab active" onclick="showTab('live')">Live Log</button>
<button class="cp-tab" onclick="showTab('stats')">Stats</button>
<button class="cp-tab" onclick="showTab('help')">Help</button>
</div>
<div id="tab-live">
<div class="cp-section">
<h4>⚡ Activity Log</h4>
<div id="liveLog">
<div class="log-entry"><span class="ts">--:--:--</span> En attente d'une requête...</div>
</div>
</div>
</div>
<div id="tab-stats" style="display:none">
<div class="cp-section">
<h4>📊 Stats Backend</h4>
<div class="cp-row"><span class="k">Endpoint</span><span class="v">wevia-master-api.php</span></div>
<div class="cp-row"><span class="k">Fallback</span><span class="v">ZERO LLM</span></div>
<div class="cp-row"><span class="k">Session</span><span class="v" id="sessId">--</span></div>
<div class="cp-row"><span class="k">Requêtes</span><span class="v" id="reqCount">0</span></div>
<div class="cp-row"><span class="k">Erreurs</span><span class="v" id="errCount">0</span></div>
<div class="cp-row"><span class="k">Temps moyen</span><span class="v" id="avgTime">--</span></div>
</div>
<div class="cp-section">
<h4>🔌 Intents Wired (WAVE-267)</h4>
<div class="cp-row"><span class="k">intents_pool</span><span class="v"></span></div>
<div class="cp-row"><span class="k">quelle heure</span><span class="v"></span></div>
<div class="cp-row"><span class="k">multiagent NL</span><span class="v"></span></div>
<div class="cp-row"><span class="k">orchestrate</span><span class="v"></span></div>
<div class="cp-row"><span class="k">auto-wire NL</span><span class="v"></span></div>
</div>
</div>
<div id="tab-help" style="display:none">
<div class="cp-section">
<h4>💡 Exemples</h4>
<div style="font-size:11px;line-height:1.7;color:var(--tx2)">
<code>intents_pool</code> → liste capacités<br>
<code>orchestrate</code> → 35 agents live<br>
<code>quelle heure</code> → heure serveur<br>
<code>lance nonreg</code> → score 153/153<br>
<code>audit complet</code> → RAM disk Docker<br>
<code>ethica combien HCP</code> → par pays<br>
<code>multiagent parallele: X + Y</code> → orchestration<br>
<code>cable un intent pour X quand on dit Y. Commande: Z</code> → auto-wire<br>
</div>
</div>
<div class="cp-section">
<h4>🎯 Avantages V2</h4>
<div style="font-size:11px;line-height:1.7;color:var(--tx2)">
✅ Réponse backend brute affichée<br>
✅ Zero LLM fallback qui pollue<br>
✅ Zero hallucination<br>
✅ Pattern plan/exec visible<br>
✅ Logs temps réel<br>
✅ Compatible tous intents WAVE-267<br>
</div>
</div>
</div>
</aside>
</div>
<script>
var sessionId = "v2-" + Date.now() + "-" + Math.random().toString(36).substring(2,8);
var reqCount = 0, errCount = 0, totalTime = 0;
document.getElementById("sessId").textContent = sessionId.substring(0,20);
function q(text) {
document.getElementById("inp").value = text;
send();
}
function showTab(tab) {
["live","stats","help"].forEach(t => {
document.getElementById("tab-" + t).style.display = (t === tab) ? "block" : "none";
});
document.querySelectorAll(".cp-tab").forEach(b => b.classList.remove("active"));
event.target.classList.add("active");
}
function addMsg(text, role, meta) {
var div = document.createElement("div");
div.className = "msg " + role;
var html = '<div class="role">' + (role === "u" ? "Vous" : "WEVIA") + '</div>';
html += '<div class="bubble">' + formatContent(text) + '</div>';
if (meta) {
html += '<div class="meta">';
if (meta.engine) html += '<span class="eng">' + meta.engine + '</span>';
if (meta.time) html += '<span>' + meta.time + '</span>';
if (meta.tool) html += '<span>tool: ' + meta.tool + '</span>';
html += '</div>';
}
div.innerHTML = html;
var msgs = document.getElementById("msgs");
msgs.appendChild(div);
msgs.scrollTop = msgs.scrollHeight;
return div;
}
function formatContent(text) {
if (!text) return "<i>(vide)</i>";
text = String(text).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
// Detect code blocks (triple backtick or === HEADER ===)
if (text.indexOf("\n") >= 0 || text.indexOf("===") >= 0 || text.indexOf("[") === 0) {
// Preserve as pre if multiline + structured
if (text.split("\n").length > 2) {
return "<pre>" + text + "</pre>";
}
}
return text.replace(/\n/g, "<br>").replace(/`([^`]+)`/g, "<code>$1</code>").replace(/\*\*(.+?)\*\*/g, "<b>$1</b>");
}
function addLog(entry, engine) {
var log = document.getElementById("liveLog");
var ts = new Date().toLocaleTimeString("fr-FR");
var div = document.createElement("div");
div.className = "log-entry";
div.innerHTML = '<span class="ts">' + ts + '</span> ' + (engine ? '<span class="engine">[' + engine + ']</span> ' : '') + entry;
log.insertBefore(div, log.firstChild);
// Keep max 30 entries
while (log.children.length > 30) log.removeChild(log.lastChild);
}
var busy = false;
async function send() {
if (busy) return;
var inp = document.getElementById("inp");
var text = inp.value.trim();
if (!text) return;
busy = true;
document.getElementById("sendBtn").disabled = true;
inp.value = "";
// Hide suggestions after first send
document.getElementById("suggestions").style.display = "none";
addMsg(text, "u");
addLog("→ " + text.substring(0,60));
var thinking = addMsg('<span class="typing"><span></span><span></span><span></span></span> Thinking...', "a thinking");
var t0 = Date.now();
var gotResponse = false;
try {
// Decide endpoint: multi-agent triggers SSE orchestrator, others = master-api
var useMultiAgent = /multiagent|tout finir|full scan|orchestr|en parallele/i.test(text);
var endpoint = useMultiAgent && !/multiagent\s+parallele|cable\s+un?\s+intent|intents?_pool|quelle\s+heure/i.test(text) ? "/api/wevia-sse-orchestrator.php?msg=" + encodeURIComponent(text) : "/api/wevia-chat-v2-direct.php";
addLog("POST " + (useMultiAgent ? "sse-orch" : "master-api"), useMultiAgent ? "SSE-Orch" : "Direct");
var opts = useMultiAgent
? {method:"GET"}
: {method:"POST", headers:{"Content-Type":"application/json"}, body:JSON.stringify({message:text, session:sessionId, history:[], no_llm_fallback:true})};
var r = await fetch(endpoint, opts);
var raw = await r.text();
var elapsed = Date.now() - t0;
// Try JSON direct (master-api)
var answer = "", engine = "", tool = "";
try {
var j = JSON.parse(raw);
answer = j.content || j.response || j.text || j.answer || "";
engine = j.engine || j.provider || "direct";
tool = j.tool || "";
} catch(e) {
// Parse SSE lines (sse-orchestrator)
var lines = raw.split("\n");
var ssebits = [];
var agentsList = [];
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
if (line.indexOf("data: ") !== 0) continue;
try {
var d = JSON.parse(line.substring(6));
if (d.type === "agent") {
agentsList.push("[" + (d.id || d.name || "agent") + "] " + (d.result || d.text || d.output || ""));
} else if (d.type === "done") {
// End marker
} else if (d.type === "answer" && d.text) {
answer = d.text;
engine = d.engine || engine;
tool = d.intent || d.tool || "";
break;
} else if (d.type === "chunk" || d.type === "llm_chunk") {
answer += d.text || "";
engine = d.engine || engine;
} else if (d.type === "roster") {
ssebits.push("Roster: " + JSON.stringify(d));
} else if (d.type === "start") {
ssebits.push("Started: " + (d.task || ""));
}
} catch(e){}
}
if (!answer && agentsList.length > 0) {
answer = "ORCHESTRATOR: " + agentsList.length + " agents\n\n" + agentsList.join("\n");
engine = engine || "SSE-Orch";
}
if (!answer && ssebits.length) answer = ssebits.join("\n");
}
// Remove thinking placeholder
thinking.remove();
if (!answer) {
addMsg("⚠️ Backend returned empty. Raw: " + raw.substring(0,300), "a", {engine:"empty", time:elapsed+"ms"});
errCount++;
} else {
addMsg(answer, "a", {engine:engine||"direct", tool:tool, time:elapsed+"ms"});
addLog("✅ " + (tool || engine || "response") + " " + answer.length + " chars", engine || "ok");
gotResponse = true;
}
reqCount++;
totalTime += elapsed;
} catch(e) {
thinking.remove();
addMsg("❌ Erreur: " + e.message, "a", {engine:"error", time:(Date.now()-t0)+"ms"});
addLog("ERR: " + e.message.substring(0,80), "error");
errCount++;
}
document.getElementById("reqCount").textContent = reqCount;
document.getElementById("errCount").textContent = errCount;
if (reqCount > 0) document.getElementById("avgTime").textContent = Math.round(totalTime/reqCount) + "ms";
busy = false;
document.getElementById("sendBtn").disabled = false;
document.getElementById("inp").focus();
}
// Auto-grow textarea
document.getElementById("inp").addEventListener("input", function(e){
this.style.height = "auto";
this.style.height = Math.min(this.scrollHeight, 120) + "px";
});
// Focus on load
setTimeout(() => document.getElementById("inp").focus(), 200);
addLog("Chat V2 loaded · session " + sessionId.substring(0,16), "init");
</script>
<!-- DOCTRINE-60-UX-JS --><script id="doctrine60-ux-js-wevia-chat-v2">
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry, index) => {
if (entry.isIntersecting) {
setTimeout(() => {
entry.target.classList.add('enter-stagger');
}, index * 80);
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.card, .btn, .kpi, .panel').forEach(el => observer.observe(el));
</script>
</body>
</html>