diff --git a/wevia-master.html b/wevia-master.html index a5c33a2e3..213e0948b 100644 --- a/wevia-master.html +++ b/wevia-master.html @@ -354,6 +354,172 @@ async function v166ClaudePattern(message){ } finally { window.v166InProgress=false; if(window.v166HideRequested){setTimeout(()=>{thpPanel&&thpPanel.classList.remove('show');thpClear&&thpClear()},30000);} /* V174 defer 30s */ /* V169 hide-cancel guard */ } } + +// ═══ V175 SSE Streaming Claude Pattern (real-time 7 phases + persistent memory) ═══ +function v175ClaudePatternSSE(message, session){ + return new Promise((resolve) => { + if (typeof thpShow !== 'function') { resolve(null); return; } + window.v166InProgress = true; + window.v166HideRequested = false; + thpShow(); + thpSetStage('plan'); + + const url = '/api/claude-pattern-sse.php?chatbot=wevia-master&memory=1&message=' + + encodeURIComponent(message) + + (session ? '&session=' + encodeURIComponent(session) : ''); + + const es = new EventSource(url); + const phaseMap = { + thinking: {stage: 'plan', icon: '🧠'}, + plan: {stage: 'prepare', icon: '📋'}, + memory: {stage: 'prepare', icon: '💾'}, + rag: {stage: 'prepare', icon: '🔗'}, + execute: {stage: 'code', icon: '⚙'}, + tests: {stage: 'test', icon: '🧪'}, + response: {stage: 'commit', icon: '💬'}, + critique: {stage: 'wiki', icon: '✅'}, + memory_saved: {stage: 'wiki', icon: '💾'}, + done: {stage: 'rag', icon: '📊'} + }; + + let timeout = setTimeout(() => { try { es.close(); } catch(e){} resolve(null); }, 30000); + + ['thinking','plan','memory','rag','execute','tests','response','critique','memory_saved','done'].forEach(evt => { + es.addEventListener(evt, (e) => { + try { + const data = JSON.parse(e.data); + const m = phaseMap[evt] || {stage: 'plan', icon: '•'}; + thpSetStage(m.stage); + let detail = ''; + if (evt === 'thinking') detail = `${data.detected_intent||''} · ${data.complexity||''} · ${data.message_length||0} chars`; + else if (evt === 'plan') detail = `${data.steps_count||0} étapes · backend ${(data.backend_selected||'').split('/').pop()}`; + else if (evt === 'memory') detail = `scope ${data.scope||''} · ${data.contexts_loaded||0} contextes mémoire`; + else if (evt === 'rag') detail = `Qdrant ${data.status||''} · ${data.contexts_found||0} contextes`; + else if (evt === 'execute') detail = `backend ${data.backend_ok ? 'OK' : 'FAIL'} · ${data.response_size||0}B`; + else if (evt === 'tests') detail = `${data.passed||0}/${data.total||0} passés · ${data.score_pct||0}%`; + else if (evt === 'response') detail = `${data.length||0} chars finaux`; + else if (evt === 'critique') detail = `quality ${Math.round((data.quality_score||0)*100)}%`; + else if (evt === 'memory_saved') detail = `saved: ${data.saved ? 'yes' : 'no'}`; + else if (evt === 'done') detail = `${data.phases_executed||0} phases · ${data.total_ms||0}ms total`; + thpAddLine(`${m.icon} ${evt.charAt(0).toUpperCase()+evt.slice(1).replace('_',' ')}`, detail, data.duration_ms ? Math.round(data.duration_ms)+'ms' : ''); + } catch(e) {} + }); + }); + + es.addEventListener('error', () => { + clearTimeout(timeout); + try { es.close(); } catch(e){} + window.v166InProgress = false; + resolve(null); + }); + + // Auto-close after 'done' event + es.addEventListener('done', () => { + clearTimeout(timeout); + setTimeout(() => { try { es.close(); } catch(e){} }, 500); + window.v166InProgress = false; + resolve(true); + }); + }); +} + + +// ═══ V175 SSE Pattern streaming — real-time replaces batch v166 ═══ +function v175SSEPattern(message){ + return new Promise((resolve) => { + try { + window.v166InProgress = true; + window.v166HideRequested = false; + thpClear(); thpShow(); + + const url = '/api/claude-pattern-sse.php?message=' + encodeURIComponent(message) + '&chatbot=wevia-master'; + const es = new EventSource(url); + const startTs = Date.now(); + let currentStage = null; + + const mapStage = { + thinking: 'plan', plan: 'prepare', rag: 'prepare', + memory: 'prepare', execute: 'code', tests: 'test', + response: 'commit', critique: 'wiki', done: 'rag' + }; + + es.addEventListener('thinking', e => { + const d = JSON.parse(e.data); + thpSetStage('plan'); + if (d.status === 'analyzing') thpAddLine('🧠 Thinking', 'analyzing · ' + (d.message_length||0) + ' chars · backend ' + (d.backend||'').split('/').pop(), ''); + else if (d.status === 'complete') thpAddLine(' ↳', 'intent=' + d.intent, Math.round(d.duration_ms||0)+'ms'); + }); + es.addEventListener('plan', e => { + const d = JSON.parse(e.data); + thpSetStage('prepare'); + if (d.status === 'building') thpAddLine('📋 Plan', 'building...', ''); + else if (d.status === 'complete') { + thpAddLine(' ↳', (d.steps||[]).join(' → ').substring(0,150), Math.round(d.duration_ms||0)+'ms'); + } + }); + es.addEventListener('rag', e => { + const d = JSON.parse(e.data); + if (d.status === 'searching') thpAddLine('🔗 RAG', 'Qdrant searching...', ''); + else thpAddLine(' ↳', d.status, Math.round(d.duration_ms||0)+'ms'); + }); + es.addEventListener('memory', e => { + const d = JSON.parse(e.data); + thpAddLine('💾 Memory', d.scope + ' · ' + (d.loaded||0) + ' loaded', ''); + }); + es.addEventListener('execute', e => { + const d = JSON.parse(e.data); + thpSetStage('code'); + if (d.status === 'calling_backend') thpAddLine('⚙ Execute', 'backend ' + (d.backend||'').split('/').pop(), ''); + else if (d.status === 'complete') thpAddLine(' ↳', 'HTTP ' + (d.http_code||'?') + ' · ' + (d.response_size||0) + ' bytes', Math.round(d.duration_ms||0)+'ms'); + }); + es.addEventListener('tests', e => { + const d = JSON.parse(e.data); + thpSetStage('test'); + thpAddLine('🧪 Tests', (d.passed||0)+'/'+(d.total||0)+' · ' + (d.score_pct||0) + '%', Math.round(d.duration_ms||0)+'ms'); + }); + es.addEventListener('response', e => { + const d = JSON.parse(e.data); + thpSetStage('commit'); + thpAddLine('💬 Response', (d.length||0) + ' chars', Math.round(d.duration_ms||0)+'ms'); + }); + es.addEventListener('critique', e => { + const d = JSON.parse(e.data); + thpSetStage('wiki'); + thpAddLine('✅ Critique', 'quality ' + Math.round((d.quality_score||0)*100) + '%', Math.round(d.duration_ms||0)+'ms'); + }); + es.addEventListener('done', e => { + const d = JSON.parse(e.data); + thpSetStage('rag'); + thpAddLine('📊 Done', (d.phases_executed||0) + ' phases · ' + (d.quality||''), Math.round(d.total_duration_ms||(Date.now()-startTs))+'ms total'); + es.close(); + window.v166InProgress = false; + if (window.v166HideRequested) setTimeout(() => { thpPanel&&thpPanel.classList.remove('show'); thpClear&&thpClear(); }, 30000); + resolve(); + }); + es.addEventListener('error', e => { + es.close(); + window.v166InProgress = false; + resolve(); + }); + es.onerror = function() { + es.close(); + window.v166InProgress = false; + resolve(); + }; + + // Safety timeout 30s + setTimeout(() => { + es.close(); + window.v166InProgress = false; + resolve(); + }, 30000); + } catch(e) { + window.v166InProgress = false; + resolve(); + } + }); +} + function q(t){inp.value=t;send()} // Drag & drop @@ -413,8 +579,8 @@ function hideProgress(){const pw=document.getElementById('pw');if(pw)pw.remove() async function send(){ const text=inp.value.trim();if(!text||busy)return; busy=true;$('sendBtn').disabled=true;inp.value='';stEl.textContent='Réflexion...';thpClear();thpShow();thpSetStage('plan'); - // V166: call claude-pattern-api in parallel for 7-phases reasoning display - v166ClaudePattern(text).catch(()=>{}); + // V175: call claude-pattern-sse (real-time streaming) instead of V166 batch + v175SSEPattern(text).catch(()=>{}); showProgress('Routing intent...', 5); addMsg(text,'u');showTyping();