AUTO-BACKUP 20260422-1720
This commit is contained in:
38
api/ambre-pw-tests/v180_test.js
Normal file
38
api/ambre-pw-tests/v180_test.js
Normal 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();
|
||||
})();
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
BIN
proofs/v180-test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
521
wevia-chat-v2.html
Normal file
521
wevia-chat-v2.html
Normal 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=>({'&':'&','<':'<','>':'>'}[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>
|
||||
Reference in New Issue
Block a user