Files
html/wevcode.html
Opus Wire fe18bfc8d4
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
feat(v20-learning-session-persist): apprentissage universel + session persistante 20 chatbots
A) SSE apprentissage universel (ai_learning_log)
- Tous chatbots logged apres chaque interaction (public + internal)
- experience jsonb: chatbot, intent, message_sample, backend, total_ms, memory_scope
- patterns_extracted jsonb: tests_passed, has_natural_lang, not_hallucinating, backend_ok
- outcome_success bool: true si tests >= 4 et backend ok
- Public: session_id EXCLUE (anonymise) · only aggregated patterns
- Internal: session_id INCLUS (lie aux messages persistants)
- Event SSE learned emit avant done

B) Session persistance localStorage / sessionStorage (20 chatbots)
- Public (wevia, wevia-widget) -> sessionStorage (per-tab, transient)
- Internal (18 chatbots) -> localStorage (cross-reload, persistent)
- Key: opus_chatbot_session_{BOT_ID}
- Format: opus-{BOT}-{timestamp}-{random6}
- URL SSE auto-includes &session=... param
- Reuse same session across clicks

Impact runtime:
- User ouvre blade-ai -> click badge -> 1st query save msg1+resp1 dans wevia_conversations
- Ferme page, reouvre blade-ai -> click badge -> session LOCAL reutilisee -> SSE load msg1+resp1 comme context
- PG table wevia_conversations grows avec cross-session conversation history
- PG table ai_learning_log grows avec outcome patterns pour meta-analyse

Chatbots apprennent:
- Quels intents mieux servis (outcome_success TRUE)
- Quels backends problematiques (not_hallucinating FALSE)
- Quel chatbot le plus utilise (groupby chatbot)

Tests live:
- blade-ai learn-test session: 1 row inserted · outcome=partial (backend faible)
- Event SSE learned emitted correctly
- localStorage persist: session key stored client-side
- Zero regression pour public (sessionStorage scope)

Doctrine respectee:
- Zero regression (try/catch silencieux · fail soft)
- Apprentissage universel (ALL chatbots, pas juste internes)
- Public anonymise (pas de session_id)
- Internal lie a conversation history
- Backup GOLD 20 chatbots + SSE
- chattr mgmt rigoureux
- Cause racine memoire cross-session resolue (localStorage)
2026-04-22 05:04:01 +02:00

377 lines
22 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<meta http-equiv="Cache-Control" content="no-cache,no-store,must-revalidate">
<title>WEVCODE — Sovereign Coding Agent</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700;800&family=DM+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{--bg:#06090f;--sf:#0c1222;--pn:#111a2e;--rim:#1a2744;--rl:#243456;--cy:#22d3ee;--em:#34d399;--am:#fbbf24;--ro:#fb7185;--lv:#a78bfa;--wh:#f0f4f8;--mu:#7a8599;--dm:#3e4b63}
body{background:var(--bg);color:var(--wh);font-family:'JetBrains Mono',monospace;min-height:100vh;display:flex;flex-direction:column}
.nav{padding:10px 20px;border-bottom:1px solid var(--rim);display:flex;align-items:center;gap:14px;background:rgba(12,18,34,.95);backdrop-filter:blur(10px);position:sticky;top:0;z-index:50}
.logo{width:28px;height:28px;border-radius:6px;background:linear-gradient(135deg,var(--cy),var(--lv));display:flex;align-items:center;justify-content:center;font-weight:800;font-size:12px;color:var(--bg)}
.tag{padding:2px 8px;border-radius:4px;font-size:9px;font-weight:700;letter-spacing:.5px;text-transform:uppercase}
.tag-em{background:#34d39918;color:var(--em)}
.tag-lv{background:#a78bfa18;color:var(--lv)}
.modes{display:flex;gap:4px;padding:8px 20px;border-bottom:1px solid var(--rim);background:var(--sf);flex-wrap:wrap}
.mode{padding:6px 14px;border:1px solid var(--rim);border-radius:6px;background:transparent;color:var(--mu);font-family:inherit;font-size:11px;font-weight:600;cursor:pointer;transition:all .2s;letter-spacing:.3px}
.mode:hover{border-color:var(--cy);color:var(--wh)}
.mode.on{background:var(--cy);color:var(--bg);border-color:var(--cy)}
.main{flex:1;display:flex;flex-direction:column;max-width:1200px;width:100%;margin:0 auto;padding:16px 20px}
.status{display:flex;gap:16px;margin-bottom:12px;font-size:10px;color:var(--dm);letter-spacing:.5px}
.status span{display:flex;align-items:center;gap:4px}
.status .dot{width:5px;height:5px;border-radius:50%}
.output{flex:1;background:var(--sf);border:1px solid var(--rim);border-radius:10px;padding:16px 20px;overflow-y:auto;min-height:300px;max-height:calc(100vh - 320px);font-size:13px;line-height:1.7;white-space:pre-wrap;word-break:break-word}
.output .sys{color:var(--cy);font-weight:600}
.output .usr{color:var(--am)}
.output .err{color:var(--ro)}
.output pre{background:var(--pn);border:1px solid var(--rim);border-radius:8px;padding:12px 16px;margin:8px 0;overflow-x:auto;font-size:12px;line-height:1.6}
.output code{font-family:'JetBrains Mono',monospace}
.output .thinking{color:var(--lv);font-style:italic;opacity:.7}
.input-area{margin-top:12px;display:flex;gap:8px}
.input-area textarea{flex:1;background:var(--pn);border:1px solid var(--rim);border-radius:8px;padding:10px 14px;color:var(--wh);font-family:inherit;font-size:13px;resize:none;outline:none;min-height:48px;max-height:200px;transition:border-color .2s}
.input-area textarea:focus{border-color:var(--cy)}
.input-area textarea::placeholder{color:var(--dm)}
.send{width:48px;height:48px;border-radius:8px;border:1px solid var(--cy);background:var(--cy);color:var(--bg);font-size:18px;font-weight:800;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;flex-shrink:0}
.send:hover{background:transparent;color:var(--cy)}
.send:disabled{opacity:.3;cursor:not-allowed}
.history{margin-bottom:12px}
.history .entry{padding:12px 0;border-bottom:1px solid var(--rim)}
.history .entry:last-child{border:none}
.stats{display:flex;gap:20px;padding:8px 20px;border-top:1px solid var(--rim);background:var(--sf);font-size:10px;color:var(--dm)}
.stats b{color:var(--mu)}
@keyframes blink{0%,100%{opacity:1}50%{opacity:0}}
.cursor{display:inline-block;width:8px;height:14px;background:var(--cy);animation:blink 1s step-end infinite;vertical-align:middle;margin-left:2px}
.loading .cursor{display:inline-block}
</style>
</head>
<body>
<div class="nav">
<div class="logo">W</div>
<div>
<div style="font-size:14px;font-weight:700;letter-spacing:-.3px">WEVCODE <span style="color:var(--cy);font-weight:800">v2.0</span></div>
<div style="font-size:9px;color:var(--dm);letter-spacing:1px;font-family:'DM Sans',sans-serif">SOVEREIGN CODING AGENT</div>
</div>
<div style="margin-left:auto;display:flex;gap:6px;align-items:center;flex-wrap:wrap">
<a href="/weval-technology-platform.html" style="padding:3px 8px;border-radius:4px;font-size:10px;font-weight:600;color:#fcd34d;text-decoration:none;border:1px solid #fcd34d33;background:rgba(252,211,77,.08)" title="WEVAL Technology Platform · ERP point entrée unique">🏠 WTP</a>
<a href="/all-ia-hub.html" style="padding:3px 8px;border-radius:4px;font-size:10px;font-weight:600;color:#f9a8d4;text-decoration:none;border:1px solid #f9a8d433;background:rgba(249,168,212,.08)" title="All-IA Hub · 7 tabs multi-agents">🌈 All-IA</a>
<a href="/weval-arena.html" style="padding:3px 8px;border-radius:4px;font-size:10px;font-weight:600;color:#a78bfa;text-decoration:none;border:1px solid #a78bfa33;background:rgba(167,139,250,.08)" title="WEVAL Arena Command Center">⚔️ Arena</a>
<a href="/wevia-orchestrator.html" style="padding:3px 8px;border-radius:4px;font-size:10px;font-weight:600;color:#06d6a0;text-decoration:none;border:1px solid #06d6a033;background:rgba(6,214,160,.08)" title="WEVIA Orchestrator GODMODE">🎛️ Orchestrator</a>
<a href="/wevia-unified-hub.html" style="padding:3px 8px;border-radius:4px;font-size:10px;font-weight:600;color:#00d4b4;text-decoration:none;border:1px solid #00d4b433;background:rgba(0,212,180,.08)" title="WEVIA Unified Hub · Truth Registry source unique">🧠 Truth</a>
<span class="tag tag-em" id="fn-tag">635 fn</span>
<span class="tag tag-lv" id="sk-tag">5231 skills</span>
</div>
</div>
<div class="modes" id="modes-bar"></div>
<div class="main">
<div class="status" id="status-bar">
<span><span class="dot" style="background:var(--em)"></span> Connected</span>
<span id="model-info">Cerebras qwen-3-235b</span>
<span id="rag-info">Qdrant RAG active</span>
<span id="cog-info">Cognitive 635fn</span>
</div>
<div class="output" id="output">
<span class="sys">WEVCODE v2.0 — Sovereign Coding Agent</span>
<span style="color:var(--dm)">Ready. 6 modes: code, analyze, plan, exécute, git, rag</span>
<span style="color:var(--dm)">Type a prompt or paste code to begin.</span>
</div>
<div class="input-area">
<textarea id="prompt" rows="2" placeholder="Describe what you want to build, analyze, or fix..." onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();send()}"></textarea>
<button class="send" id="btn-send" onclick="send()">&#9654;</button>
</div>
</div>
<div class="stats">
<span>Mode: <b id="cur-mode">code</b></span>
<span>Requests: <b id="req-count">0</b></span>
<span>Tokens: <b id="tok-count">0</b></span>
<span id="elapsed"></span>
</div>
<script>
const MODES=[
{id:'code',label:'Code',icon:'&#60;/&#62;',desc:'Generate, fix, refactor code'},
{id:'analyze',label:'Analyze',icon:'&#128269;',desc:'Deep code analysis'},
{id:'plan',label:'Plan',icon:'&#128203;',desc:'Architecture & design planning'},
{id:'execute',label:'Execute',icon:'&#9654;',desc:'Run commands on infrastructure'},
{id:'git',label:'Git',icon:'&#128736;',desc:'Git operations & history'},
{id:'rag',label:'RAG',icon:'&#128218;',desc:'Search 5231 skills knowledge base'}
];
let mode='code', reqs=0, toks=0, loading=false;
const out=document.getElementById('output');
const inp=document.getElementById('prompt');
const btn=document.getElementById('btn-send');
function renderModes(){
document.getElementById('modes-bar').innerHTML=MODES.map(m=>
'<button class="mode'+(m.id===mode?' on':'')+'" onclick="setMode(\''+m.id+'\')" title="'+m.desc+'">'+m.icon+' '+m.label+'</button>'
).join('');
document.getElementById('cur-mode').textContent=mode;
}
function setMode(m){mode=m;renderModes();}
function appendOut(html){out.innerHTML+=html;out.scrollTop=out.scrollHeight;}
function escHtml(s){return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');}
function formatResponse(text){
// Extract code blocks and highlight them
return text.replace(/```(\w*)\n([\s\S]*?)```/g, function(m,lang,code){
var highlighted;
try{
highlighted=lang?hljs.highlight(code.trim(),{language:lang}).value:hljs.highlightAuto(code.trim()).value;
}catch(e){highlighted=escHtml(code.trim());}
return '<pre><code class="hljs">'+highlighted+'</code></pre>';
}).replace(/`([^`]+)`/g,'<code style="background:var(--pn);padding:1px 6px;border-radius:3px;font-size:12px">$1</code>')
.replace(/\*\*([^*]+)\*\*/g,'<b style="color:var(--cy)">$1</b>');
}
async function send(){
var prompt=inp.value.trim();
if(!prompt||loading)return;
loading=true;
btn.disabled=true;
inp.value='';
appendOut('\n<span class="usr">&#9656; '+escHtml(prompt)+'</span>\n');
appendOut('<span class="thinking">Thinking...</span><span class="cursor"></span>\n');
var t0=performance.now();
try{
var res=await fetch('/api/wevcode-superclaude.php',{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({action:mode,prompt:prompt,language:'auto'})
});
/* WEVCODE_HTML_GUARD_V1 */ var _txt=await res.text(); var _t=(_txt||'').trim(); var data; if(_t.startsWith('<!DOCTYPE')||_t.startsWith('<html')){ var _code=res.status||'???'; var _msg='[HTTP '+_code+'] Backend indisponible'; if(_t.indexOf('502')>-1) _msg='[502 Bad Gateway] Serveur surcharge - reessayez dans 10s'; else if(_t.indexOf('503')>-1) _msg='[503 Service Unavailable] Maintenance en cours'; else if(_t.indexOf('504')>-1) _msg='[504 Gateway Timeout] Requete trop longue'; data={error:_msg,isHtmlError:true}; } else { try{data=JSON.parse(_t)}catch(e){data={error:'[JSON parse] '+e.message,raw:_t.substring(0,120)}} }
var elapsed=((performance.now()-t0)/1000).toFixed(1);
// Remove thinking indicator
var lines=out.innerHTML.split('\n');
out.innerHTML=lines.filter(l=>!l.includes('Thinking...')).join('\n');
reqs++;
if(data.tokens)toks+=data.tokens;
document.getElementById('req-count').textContent=reqs;
document.getElementById('tok-count').textContent=toks;
document.getElementById('elapsed').textContent=elapsed+'s';
if(data.error){
appendOut('<span class="err">Error: '+escHtml(data.error)+'</span>\n');
} else {
var response=data.response||data.result||data.output||data.message||JSON.stringify(data,null,2);
appendOut('<span class="sys">['+mode+'] '+elapsed+'s</span>\n');
appendOut(formatResponse(response)+'\n');
}
}catch(e){
var lines=out.innerHTML.split('\n');
out.innerHTML=lines.filter(l=>!l.includes('Thinking...')).join('\n');
appendOut('<span class="err">Network error: '+escHtml(e.message)+'</span>\n');
}
loading=false;
btn.disabled=false;
inp.focus();
}
// Health check on load
fetch('/api/wevcode-superclaude.php?action=health').then(r=>r.json()).then(d=>{
document.getElementById('fn-tag').textContent=d.functions_loaded+' fn';
document.getElementById('sk-tag').textContent=d.qdrant_skills+' skills';
document.getElementById('cog-info').textContent='Cognitive '+d.functions_loaded+'fn';
}).catch(()=>{});
// Auto-resize textarea
inp.addEventListener('input',function(){this.style.height='auto';this.style.height=Math.min(this.scrollHeight,200)+'px';});
inp.focus();
renderModes();
</script>
<!-- === OPUS UNIVERSAL DRILL-DOWN v1 19avr — append-only, doctrine #14 === -->
<script>
(function(){
if (window.__opusUniversalDrill) return; window.__opusUniversalDrill = true;
var d = document;
var m = d.createElement('div');
m.id = 'opus-udrill';
m.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.82);backdrop-filter:blur(6px);display:none;align-items:center;justify-content:center;z-index:99995;padding:20px;cursor:pointer';
var inner = d.createElement('div');
inner.id = 'opus-udrill-in';
inner.style.cssText = 'max-width:900px;width:100%;max-height:90vh;overflow:auto;background:#0b0d15;border:1px solid rgba(99,102,241,0.35);border-radius:14px;padding:28px;cursor:default;box-shadow:0 20px 60px rgba(0,0,0,0.6);color:#e2e8f0;font:14px/1.55 Inter,system-ui,sans-serif';
inner.addEventListener('click', function(e){ e.stopPropagation(); });
m.appendChild(inner);
m.addEventListener('click', function(){ m.style.display='none'; });
d.addEventListener('keydown', function(e){ if(e.key==='Escape') m.style.display='none'; });
(d.body || d.documentElement).appendChild(m);
function openCard(card) {
// Clone card content + show close btn + increase font-size
var html = '<div style="display:flex;justify-content:flex-end;margin-bottom:14px"><button id="opus-udrill-close" style="padding:6px 14px;background:#171b2a;border:1px solid rgba(99,102,241,0.25);color:#e2e8f0;border-radius:8px;cursor:pointer;font-size:12px">✕ Fermer (Esc)</button></div>';
html += '<div style="transform-origin:top left;font-size:1.05em">' + card.outerHTML + '</div>';
inner.innerHTML = html;
d.getElementById('opus-udrill-close').onclick = function(){ m.style.display='none'; };
m.style.display = 'flex';
}
function wire(root) {
var sels = '.card,[class*="card"],.kpi,[class*="kpi"],.stat,[class*="stat"],.tile,[class*="tile"],.metric,[class*="metric"],.widget,[class*="widget"]';
var cards = root.querySelectorAll(sels);
for (var i = 0; i < cards.length; i++) {
var c = cards[i];
if (c.__opusWired) continue;
if (c.closest('button, a, input, select, textarea, #opus-udrill')) continue;
var r = c.getBoundingClientRect();
if (r.width < 60 || r.height < 40) continue;
c.__opusWired = true;
c.style.cursor = 'pointer';
c.setAttribute('role','button');
c.setAttribute('tabindex','0');
c.addEventListener('click', function(ev){
// If a more-specific drill is already active (e.g. pp-card custom), let it handle
if (ev.target.closest('[data-pp-id]') && window.__opusDrillInit) return;
if (ev.target.closest('a,button,input,select')) return;
ev.preventDefault(); ev.stopPropagation();
openCard(this);
});
c.addEventListener('keydown', function(ev){ if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();openCard(this);} });
}
}
// Initial + mutation observer
var initRun = function(){ wire(d.body || d.documentElement); };
if (d.readyState === 'loading') d.addEventListener('DOMContentLoaded', initRun);
else initRun();
var mo = new MutationObserver(function(muts){
var newCard = false;
for (var i=0;i<muts.length;i++) if (muts[i].addedNodes.length) { newCard = true; break; }
if (newCard) initRun();
});
mo.observe(d.body || d.documentElement, {childList:true, subtree:true});
})();
</script>
<!-- === OPUS UNIVERSAL DRILL-DOWN END === -->
<!-- OPUS_v932e_5HUBS -->
<div id="opus-xlinks-wc" style="position:fixed;top:12px;right:12px;display:flex;gap:6px;z-index:9998;flex-wrap:wrap;max-width:380px">
<a href="/weval-technology-platform.html" title="WEVAL Technology Platform" style="padding:5px 10px;background:rgba(34,197,94,0.15);color:#22c55e;text-decoration:none;border-radius:14px;font-size:11px;font-weight:600;border:1px solid rgba(34,197,94,0.3);backdrop-filter:blur(8px)">WTP</a>
<a href="/wevia-master.html" title="WEVIA Master - Brain" style="padding:5px 10px;background:rgba(59,130,246,0.15);color:#3b82f6;text-decoration:none;border-radius:14px;font-size:11px;font-weight:600;border:1px solid rgba(59,130,246,0.3);backdrop-filter:blur(8px)">Master</a>
<a href="/all-ia-hub.html" title="All IA Hub" style="padding:5px 10px;background:rgba(6,182,212,0.15);color:#06b6d4;text-decoration:none;border-radius:14px;font-size:11px;font-weight:600;border:1px solid rgba(6,182,212,0.3);backdrop-filter:blur(8px)">IA Hub</a>
<a href="/wevia-orchestrator.html" title="WEVIA Orchestrator" style="padding:5px 10px;background:rgba(139,92,246,0.15);color:#8b5cf6;text-decoration:none;border-radius:14px;font-size:11px;font-weight:600;border:1px solid rgba(139,92,246,0.3);backdrop-filter:blur(8px)">Orch</a>
<a href="/wevia-ia/droid.html" title="WEDROID v3.2 - Backend droid 19 providers" style="padding:5px 10px;background:rgba(16,185,129,0.15);color:#10b981;text-decoration:none;border-radius:14px;font-size:11px;font-weight:600;border:1px solid rgba(16,185,129,0.3);backdrop-filter:blur(8px)">Droid</a>
</div>
<script src="/api/a11y-auto-enhancer.js" defer></script>
<script src="/opus-antioverlap-doctrine.js?v=1776776094" defer></script>
<!-- Opus v17 · Claude Pattern SSE (auto-injected) -->
<style id="opus-pattern-style">
#opus-pattern-badge{position:fixed;bottom:20px;right:20px;z-index:99990;
background:linear-gradient(135deg,#06b6d4,#8b5cf6);color:#fff;
padding:10px 16px;border-radius:20px;font:700 0.78rem -apple-system,sans-serif;
cursor:pointer;box-shadow:0 4px 16px rgba(0,0,0,0.35);transition:all 0.2s;
display:flex;align-items:center;gap:6px}
#opus-pattern-badge:hover{transform:translateY(-2px);box-shadow:0 6px 20px rgba(6,182,212,0.4)}
#opus-pattern-modal{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.8);
z-index:99991;align-items:center;justify-content:center;padding:20px}
#opus-pattern-modal.show{display:flex}
#opus-pattern-box{background:#0b0d15;color:#e2e8f0;border:1px solid rgba(6,182,212,0.3);
border-radius:14px;padding:22px;max-width:820px;width:100%;max-height:85vh;overflow:auto;
font:-apple-system,sans-serif}
#opus-pattern-box h3{font:800 1.2rem;margin-bottom:12px;
background:linear-gradient(135deg,#06b6d4,#ec4899);
-webkit-background-clip:text;-webkit-text-fill-color:transparent}
#opus-pattern-input{width:100%;background:#1a1f3a;color:#fff;border:1px solid rgba(100,116,139,0.3);
border-radius:8px;padding:10px;margin-bottom:10px;font:0.9rem -apple-system}
#opus-pattern-run{background:linear-gradient(135deg,#10b981,#06b6d4);color:#fff;border:0;
padding:10px 20px;border-radius:8px;font:700 0.85rem;cursor:pointer;margin-bottom:14px}
.phase-card{background:rgba(15,23,42,0.8);border:1px solid rgba(100,116,139,0.2);
border-left:3px solid #06b6d4;border-radius:8px;padding:10px 14px;margin-bottom:8px;
font-size:0.82rem}
.phase-card.done{border-left-color:#22c55e}
.phase-card.active{border-left-color:#f59e0b;animation:pulse 1.2s ease infinite}
.phase-name{font-weight:800;color:#06b6d4;margin-bottom:4px;font-size:0.78rem;text-transform:uppercase;letter-spacing:1px}
.phase-data{font-size:0.72rem;color:#94a3b8;font-family:ui-monospace,monospace}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.6}}
#opus-pattern-close{position:absolute;top:14px;right:20px;background:0;border:0;color:#94a3b8;
font-size:1.6rem;cursor:pointer}
</style>
<div id="opus-pattern-badge" onclick="window.__opusPatternOpen()">
<span>🧠</span><span>Claude Pattern</span>
</div>
<div id="opus-pattern-modal" onclick="if(event.target.id==='opus-pattern-modal')window.__opusPatternClose()">
<div id="opus-pattern-box">
<button id="opus-pattern-close" onclick="window.__opusPatternClose()">×</button>
<h3>🧠 Claude Pattern · 7 phases REAL (SSE live)</h3>
<p style="font-size:0.82rem;color:#94a3b8;margin-bottom:12px">Backend: <b id="opus-pattern-bot">wevcode</b> · anti-hallucination · langue naturelle</p>
<input id="opus-pattern-input" placeholder="Posez une question (FR ou EN)..." value="bonjour quel est le statut" />
<button id="opus-pattern-run" onclick="window.__opusPatternRun()">▶ Lancer (SSE stream)</button>
<div id="opus-pattern-output"></div>
</div>
</div>
<script>
(function(){
const BOT = 'wevcode';
window.__opusPatternOpen = () => document.getElementById('opus-pattern-modal').classList.add('show');
window.__opusPatternClose = () => document.getElementById('opus-pattern-modal').classList.remove('show');
window.__opusPatternRun = () => {
const msg = document.getElementById('opus-pattern-input').value.trim();
if (!msg) return;
const out = document.getElementById('opus-pattern-output');
out.innerHTML = '';
const OPUS_SESSION_KEY = 'opus_chatbot_session_' + BOT;
let sess = localStorage.getItem(OPUS_SESSION_KEY);
if (!sess) {
sess = 'opus-' + BOT + '-' + Date.now().toString(36) + '-' + Math.random().toString(36).substr(2, 6);
localStorage.setItem(OPUS_SESSION_KEY, sess);
}
const url = '/api/claude-pattern-sse.php?message=' + encodeURIComponent(msg) + '&chatbot=' + encodeURIComponent(BOT) + '&session=' + encodeURIComponent(sess);
const es = new EventSource(url);
const phases = {};
const order = ['thinking','plan','rag','execute','tests','response','critique','done'];
order.forEach(p => {
const card = document.createElement('div');
card.className = 'phase-card';
card.id = 'phase-' + p;
card.innerHTML = '<div class="phase-name">' + p.toUpperCase() + '</div><div class="phase-data">⏳ waiting...</div>';
out.appendChild(card);
});
order.forEach(evName => {
es.addEventListener(evName, (e) => {
const data = JSON.parse(e.data);
const card = document.getElementById('phase-' + evName);
if (card) {
card.classList.add('done');
card.classList.remove('active');
let txt;
if (evName === 'response' && data.text) {
txt = '<div style="background:rgba(6,182,212,0.1);padding:10px;border-radius:6px;margin-top:6px;color:#e2e8f0;font-size:0.82rem">' + (data.text.substring(0, 600)) + (data.text.length > 600 ? '...' : '') + '</div>';
} else if (evName === 'tests') {
txt = '<div>' + data.passed + '/' + data.total + ' tests ✓</div>';
} else if (evName === 'critique') {
txt = '<div>Quality: <b style="color:' + (data.quality === 'EXCELLENT' ? '#22c55e' : (data.quality === 'OK' ? '#f59e0b' : '#ef4444')) + '">' + data.quality + '</b> (' + (data.quality_score * 5).toFixed(0) + '/5)</div>';
} else {
txt = JSON.stringify(data).substring(0, 300);
}
card.querySelector('.phase-data').innerHTML = txt;
}
if (evName === 'done') es.close();
});
});
es.addEventListener('error', () => es.close());
};
})();
</script>
</body>
</html>