Files
html/js/wevia-stream-client.js
2026-04-14 21:40:01 +02:00

111 lines
5.0 KiB
JavaScript

/**
* WEVIA Sovereign Stream Client v1.0
* SSE streaming + real exec via WEVIA Master
* Usage: weviaStream('message', document.getElementById('output'))
*/
window.weviaStream = function(message, container, opts) {
opts = opts || {};
var system = opts.system || 'Tu es WEVIA, IA souveraine de WEVAL Consulting Casablanca. Reponds en francais, professionnel et naturel comme un collegue expert. Si on te demande une action serveur, genere la commande entre ```bash et ```.';
var maxTokens = opts.maxTokens || 2000;
var onDone = opts.onDone || function(){};
var onToken = opts.onToken || null;
var responseDiv = document.createElement('div');
responseDiv.className = 'wevia-stream-msg';
responseDiv.innerHTML = '<span class="wevia-thinking-dot"></span> Analyse...';
container.appendChild(responseDiv);
var textBuffer = '';
var provider = '';
var startTime = performance.now();
fetch('/api/sovereign/v1/chat/completions', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
model: 'auto',
messages: [
{role: 'system', content: system},
{role: 'user', content: message}
],
max_tokens: maxTokens,
stream: true
})
}).then(function(response) {
var reader = response.body.getReader();
var decoder = new TextDecoder();
var buf = '';
function read() {
return reader.read().then(function(result) {
if (result.done) { finish(); return; }
buf += decoder.decode(result.value, {stream: true});
var lines = buf.split('\n');
buf = lines.pop();
lines.forEach(function(line) {
if (!line.startsWith('data: ') || line === 'data: [DONE]') {
if (line === 'data: [DONE]') { finish(); return; }
return;
}
try {
var d = JSON.parse(line.substring(6));
if (d.type === 'token' && d.content) {
textBuffer += d.content;
if (!provider && d.provider) provider = d.provider;
responseDiv.innerHTML = renderMarkdown(textBuffer) + '<span class="wevia-cursor">|</span>';
if (container.scrollHeight) container.scrollTop = container.scrollHeight;
if (onToken) onToken(d.content, textBuffer);
}
} catch(e) {}
});
return read();
});
}
return read();
}).catch(function(err) {
responseDiv.innerHTML = '<div class="wevia-error">Erreur: ' + err.message + '</div>';
});
function finish() {
var elapsed = ((performance.now() - startTime) / 1000).toFixed(1);
var html = renderMarkdown(textBuffer);
// Detect and execute bash blocks
var bashBlocks = textBuffer.match(/```bash\n([\s\S]*?)```/g);
if (bashBlocks && bashBlocks.length > 0) {
bashBlocks.forEach(function(block) {
var cmd = block.replace(/```bash\n/, '').replace(/```$/, '').trim();
html += '<div class="wevia-exec"><div class="exec-header">Execution: ' + escHtml(cmd.substring(0,80)) + '</div><div class="exec-output" id="exec-' + Date.now() + '">En cours...</div></div>';
// Execute via WEVIA Master
fetch('/api/wevia-autonomous.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({message: cmd})
}).then(function(r){return r.text()}).then(function(t){
var lines = t.split('\n');
var result = '';
lines.forEach(function(l){
if (l.startsWith('data: ') && l !== 'data: [DONE]') {
try { var d = JSON.parse(l.substring(6)); if(d.text) result = d.text; } catch(e){}
}
});
var execDiv = responseDiv.querySelector('.exec-output:last-of-type');
if (execDiv) execDiv.textContent = result || 'Commande executee';
});
});
}
html += '<div class="wevia-meta">' + escHtml(provider) + ' · ' + elapsed + 's</div>';
responseDiv.innerHTML = html;
onDone({text: textBuffer, provider: provider, elapsed: elapsed});
}
function renderMarkdown(md) {
return md
.replace(/```(\w*)\n([\s\S]*?)```/g, '<pre class="code-block"><code>$2</code></pre>')
.replace(/`([^`]+)`/g, '<code class="inline-code">$1</code>')
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
.replace(/\n/g, '<br>');
}
function escHtml(t) { var d=document.createElement('div');d.textContent=t;return d.innerHTML; }
return responseDiv;
};