diff --git a/scripts/brain/cortex_intent.py b/scripts/brain/cortex_intent.py index f943818830..951c681c19 100644 --- a/scripts/brain/cortex_intent.py +++ b/scripts/brain/cortex_intent.py @@ -35,6 +35,17 @@ def _norm(text: str) -> str: def detect_intent(message: str) -> CortexIntent: m = _norm(message) + if m.startswith("/code") and any(k in m for k in [ + "playtest", "app", "application", "html", "interface", + "calculatrice", "todo", "kanban", "dashboard" + ]): + return CortexIntent( + intent="playtest_code_task", + confidence="high", + route_reason="playtest_builder_direct", + dashboard_context=DASHBOARD_CONTEXT, + ) + if any(k in m for k in [ "recherche web", "cherche sur le web", "actualité", "actualités", "actu", "news", "récent", "récente", "aujourd'hui", "maintenant", diff --git a/scripts/brain/dashboard/brain_gpu.html b/scripts/brain/dashboard/brain_gpu.html index 7a9ab10b8e..2f1afc74b3 100644 --- a/scripts/brain/dashboard/brain_gpu.html +++ b/scripts/brain/dashboard/brain_gpu.html @@ -171,14 +171,14 @@
' + target.replace(/';
+ if (info) info.innerHTML = 'Chargement : ' + target.replace(/';
+ try {
+ if (/^https?:\/\/127\.0\.0\.1:8765\/playtests\/.+\.html$/i.test(target) || /^\/playtests\/.+\.html$/i.test(target)) {
+ const probe = await fetch(target, {cache: 'no-store'});
+ if (!probe.ok) throw new Error('HTTP ' + probe.status);
+ }
+ if (f) f.src = target;
+ if (info) info.innerHTML = 'Chargé : ' + target.replace(/';
+ } catch (e) {
+ if (info) info.innerHTML = 'Échec Playtest : ' + String(e.message || e).replace(/';
+ }
};
window._playtestSetFromCode = (result) => {
- // result = sortie du dev_command /code. On cherche un chemin de fichier
- // créé/modifié dans la sortie et on le propose en preview.
const info = document.getElementById('playtest-info');
if (!info) return;
- // Heuristique : fichiers HTML, MD, ou path générique
+ if (result && typeof result === 'object') {
+ const meta = result.meta || {};
+ if (meta.playtest_url || meta.auto_open_playtest) {
+ const target = meta.playtest_url || (result.response && (result.response.match(/https?:\/\/127\.0\.0\.1:8765\/playtests\/[^\s]+\.html/i) || [])[0]);
+ if (target) {
+ info.innerHTML = `Livrable Playtest généré : ${target.replace(/`;
+ window._togglePlaytest();
+ window._playtestLoad(target);
+ return;
+ }
+ }
+ }
const m = (result || '').match(/[A-Z]:[\\\/][^\s'"`]+\.(html|md|js|ts|py)/i);
if (m) {
const p = m[0];
@@ -2121,6 +2158,51 @@ window._playtestSetFromCode = (result) => {
}
};
+window._consortiumUi = { hideEmpty: true, lastOnly: false };
+window._toggleConsortiumEmpty = () => {
+ window._consortiumUi.hideEmpty = !window._consortiumUi.hideEmpty;
+ const btn = document.getElementById('consortium-toggle-empty');
+ if (btn) btn.textContent = window._consortiumUi.hideEmpty ? 'afficher réponses vides' : 'masquer réponses vides';
+ window._renderConsortium(window._consortiumCache || {ok:false});
+};
+window._toggleConsortiumLastOnly = () => {
+ window._consortiumUi.lastOnly = !window._consortiumUi.lastOnly;
+ const btn = document.getElementById('consortium-toggle-last');
+ if (btn) btn.textContent = window._consortiumUi.lastOnly ? 'tous les rounds' : 'dernier round seulement';
+ window._renderConsortium(window._consortiumCache || {ok:false});
+};
+window._consortiumShowJson = () => {
+ const raw = JSON.stringify(window._consortiumCache || {}, null, 2);
+ window._openBrainDetail({
+ title: 'Consortium JSON brut',
+ subtitle: 'Source: /api/cortex/judges',
+ chips: [{label:'raw json', color:'#9fc0ff'}],
+ body: raw,
+ });
+};
+window._consortiumExplainWhy = () => {
+ const meta = window._lastChatMeta || {};
+ const lines = [
+ `intent: ${meta.intent || 'n/a'}`,
+ `complexité: ${meta.complexity || 'n/a'}`,
+ `routing_decision: ${meta.routing_decision || meta.v2_path || 'n/a'}`,
+ `backend: ${meta.selected_backend || meta.backend || 'n/a'}`,
+ `raison: ${meta.selection_reason || meta.route_reason || 'n/a'}`,
+ `router_used: ${String(!!meta.router_used)}`,
+ `judge_used: ${String(!!meta.judge_used)}`,
+ `history: ${meta.history_used ? 'oui' : 'non'} (${meta.history_count || 0})`,
+ ];
+ window._openBrainDetail({
+ title: 'Pourquoi ce modèle ?',
+ subtitle: 'Métadonnées du dernier tour',
+ chips: [
+ {label:`backend ${(meta.selected_backend || meta.backend || 'n/a')}`, color:'#9fc0ff'},
+ {label:`route ${(meta.routing_decision || meta.v2_path || 'n/a')}`, color:'#8fd5b8'},
+ ],
+ body: lines.join('\n'),
+ });
+};
+
window._renderConsortium = (data) => {
const ranking = document.getElementById('consortium-ranking');
const rounds = document.getElementById('consortium-rounds');
@@ -2130,27 +2212,46 @@ window._renderConsortium = (data) => {
rounds.innerHTML = 'Impossible de lire le consortium pour le moment.';
return;
}
- ranking.innerHTML = (data.ranking || []).slice(0, 5).map((row, idx) =>
- `${idx + 1}. ${row.model}${row.win_rate}% · ${row.avg_latency_s}s`
- ).join('');
- rounds.innerHTML = (data.rounds || []).slice().reverse().map((round, idx) => {
+ const rankingRows = (data.ranking || []).filter(row => Number(row.rounds || 0) > 0);
+ ranking.innerHTML = rankingRows.length
+ ? rankingRows.slice(0, 5).map((row, idx) =>
+ `${idx + 1}. ${row.model}${row.win_rate}% · ${row.avg_latency_s}s`
+ ).join('')
+ : 'Pas assez de scores réels pour afficher un classement fiable.';
+ let sourceRounds = (data.rounds || []).slice().reverse();
+ if (window._consortiumUi.lastOnly) sourceRounds = sourceRounds.slice(0, 1);
+ rounds.innerHTML = sourceRounds.map((round, idx) => {
const responses = round.responses || {};
const latencies = round.latencies || {};
const scores = round.scores || {};
- const models = Object.keys(responses);
+ const statuses = round.statuses || {};
+ let models = Object.keys({...responses, ...latencies, ...scores, ...statuses});
+ if (window._consortiumUi.hideEmpty) {
+ models = models.filter((model) => {
+ const resp = responses[model];
+ return !!(resp && String(resp).trim());
+ });
+ }
const rows = models.map((model) => {
const isWinner = model === round.winner;
const score = scores[model];
const latency = latencies[model];
- const why = score != null
- ? `score ${Number(score).toFixed ? Number(score).toFixed(1) : score}`
- : `latence ${latency != null ? Number(latency).toFixed(1) + 's' : 'n/a'}`;
+ const rawStatus = statuses[model];
+ const inferredStatus = rawStatus || (!responses[model] ? (latency >= 59 ? 'timeout' : 'empty_response') : 'ok');
+ const statusColor = inferredStatus === 'ok' ? '#8fd5b8' : inferredStatus.includes('timeout') ? '#ffb36b' : inferredStatus.includes('error') ? '#ff8a8a' : '#d7b36f';
+ const finalScore = round.final_scores && round.final_scores[model];
+ const why = [
+ `status ${inferredStatus}`,
+ latency != null ? `latence ${Number(latency).toFixed(1)}s` : 'latence n/a',
+ score != null ? `score juge ${Number(score).toFixed(1)}` : null,
+ finalScore != null ? `score final ${Number(finalScore).toFixed(2)}` : null,
+ ].filter(Boolean).join(' · ');
return `
${model}${isWinner ? ' · gagnant' : ''}
- ${why}
+ ${why}
- ${(responses[model] || '(vide)').replace(/[&<>]/g, c => ({'&':'&','<':'<','>':'>'}[c]))}
+ ${(responses[model] || 'empty_response').replace(/[&<>]/g, c => ({'&':'&','<':'<','>':'>'}[c]))}
`;
}).join('');
const question = (round.question || '').replace(/[&<>]/g, c => ({'&':'&','<':'<','>':'>'}[c]));
@@ -2160,7 +2261,8 @@ window._renderConsortium = (data) => {
winner ${round.winner || 'n/a'}
${question || '(question indisponible)'}
- ${rows}
+ ${round.route || round.v2_path || 'route non documentée'}
+ ${rows || 'Tous les candidats ont été masqués par le filtre actuel.'}
`;
}).join('') || 'Aucun round récent.';
};
@@ -2291,6 +2393,61 @@ function _shortName(p) {
if (!p) return '?';
return p.split('/').pop().split('\\').pop().replace(/\.md$/, '').slice(0, 32);
}
+function _brainAttr(obj) {
+ return JSON.stringify(obj)
+ .replace(/&/g, '&')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''')
+ .replace(//g, '>');
+}
+window._openBrainDetail = (opts) => {
+ const modal = document.getElementById('brain-detail-modal');
+ if (!modal) return;
+ document.getElementById('brain-detail-title').textContent = opts.title || 'Détail';
+ document.getElementById('brain-detail-subtitle').textContent = opts.subtitle || '';
+ const meta = document.getElementById('brain-detail-meta');
+ meta.innerHTML = (opts.chips || []).map(ch =>
+ `${String(ch.label || '').replace(/[&<>]/g, c => ({'&':'&','<':'<','>':'>'}[c]))}`
+ ).join('');
+ document.getElementById('brain-detail-body').textContent = opts.body || '';
+ modal.style.display = 'flex';
+};
+window._closeBrainDetail = () => {
+ const modal = document.getElementById('brain-detail-modal');
+ if (modal) modal.style.display = 'none';
+};
+window._showBrainSourceDetail = async (item) => {
+ const chips = [];
+ if (item.type) chips.push({label:`type ${item.type}`, color:'#9fc0ff'});
+ if (item.score != null) chips.push({label:`score ${Number(item.score).toFixed(3)}`, color:'#8fd5b8'});
+ if (item.weight != null) chips.push({label:`force ${Number(item.weight).toFixed(3)}`, color:'#f6ad55'});
+ if (item.learningRate != null) chips.push({label:`learning_rate ${Number(item.learningRate).toFixed(3)}`, color:'#fbd38d'});
+ if (item.coactivationCount != null) chips.push({label:`coactivations ${item.coactivationCount}`, color:'#d6bcfa'});
+ if (item.lastSeen) chips.push({label:`last_seen ${item.lastSeen}`, color:'#a0aec0'});
+ window._openBrainDetail({
+ title: item.title || _shortName(item.source || item.a || item.b || ''),
+ subtitle: item.source || [item.a, item.b].filter(Boolean).join(' ↔ ') || 'source introuvable',
+ chips,
+ body: 'Chargement de la source markdown…',
+ });
+ let excerpt = 'source introuvable';
+ if (item.source) {
+ try {
+ const r = await fetch(`/api/node-content?id=${encodeURIComponent(item.source)}`);
+ const d = await r.json();
+ excerpt = (d.content || 'source introuvable').slice(0, 2400);
+ } catch (e) {
+ excerpt = 'source introuvable';
+ }
+ }
+ const details = [];
+ if (item.description) details.push(item.description);
+ if (item.a || item.b) details.push(`Liaison: ${(item.a || '?')} ↔ ${(item.b || '?')}`);
+ if (item.tooltip) details.push(item.tooltip);
+ details.push('\nExtrait markdown:\n' + excerpt);
+ document.getElementById('brain-detail-body').textContent = details.join('\n\n');
+};
function _kindOf(p) {
// Devine la nature d'un nœud à partir de son chemin
const s = (p||'').toLowerCase();
@@ -2320,12 +2477,15 @@ function refreshCerebralView() {
+ `Activation : ${v.toFixed(3)} (sur 1.0)`
+ `Demi-vie restante avant extinction : ~${Math.max(0, halfLife)} s`
+ `Pourquoi éveillé : un retrieve_context, un A* ou la boucle de pensée vagabonde l'a touché. Plus la valeur est haute, plus il vient d'être réactivé.`;
- return `
+ const detail = _brainAttr({title:_shortName(p), source:p, type:k, score:Number(v.toFixed(3)), lastSeen:'récent', description:'Activation neurale récente. Cette barre peut décroître avec le temps.'});
+ return `
+
${(v).toFixed(2)}
+
`;
}).join('');
}
@@ -2346,7 +2506,8 @@ function refreshCerebralView() {
+ `Force : ${e.strength.toFixed(3)}`
+ `Soit ~${coActivations} co-activations cumulées (HEBBIAN_LR=0.01).`
+ `Plus on les pense ensemble, plus le lien grossit. Persisté sur disque, survit aux redémarrages. C'est la mémoire long terme.`;
- return `
+ const detail = _brainAttr({title:`${_shortName(e.a)} ↔ ${_shortName(e.b)}`, source:e.a, a:e.a, b:e.b, type:'hebbian', weight:Number(e.strength.toFixed(3)), learningRate:0.01, coactivationCount:coActivations, lastSeen:'récent', description:'Ces deux notes ont été co-activées plusieurs fois. Force actuelle persistée sur disque.', tooltip:`Ces deux notes ont été co-activées ${coActivations} fois. Force actuelle: ${e.strength.toFixed(3)}.`});
+ return `
↔
@@ -2426,6 +2587,7 @@ function refreshCerebralView() {
// ── 4b. World model — concepts qui grandissent (croissance Hebbian récente) ──
const wcont = document.getElementById('cb-worldmodel');
if (wcont) {
+ window._worldModelStable = window._worldModelStable || {};
// On combine : top hebbian edges (force) + activations actuelles pour deviner
// les concepts en croissance dans le world model.
const heb = (acts.top_hebbian_edges || []).slice(0, 12);
@@ -2435,7 +2597,12 @@ function refreshCerebralView() {
concept[e.b] = (concept[e.b] || 0) + e.strength;
});
active.forEach(([p, v]) => { concept[p] = (concept[p] || 0) + v * 0.5; });
- const top = Object.entries(concept).sort((a,b) => b[1]-a[1]).slice(0, 5);
+ Object.entries(concept).forEach(([p, score]) => {
+ const prev = window._worldModelStable[p] || 0;
+ window._worldModelStable[p] = Math.max(prev, score);
+ });
+ const stableEntries = Object.entries(window._worldModelStable).sort((a,b) => b[1]-a[1]).slice(0, 5);
+ const top = stableEntries.length ? stableEntries : Object.entries(concept).sort((a,b) => b[1]-a[1]).slice(0, 5);
if (!top.length) {
wcont.innerHTML = 'Apprentissage stagne — Cortex n\'a rien renforcé récemment';
} else {
@@ -2446,7 +2613,8 @@ function refreshCerebralView() {
+ `Score de croissance : ${score.toFixed(3)}`
+ `= activation cumulée + force des liens Hebbian.`
+ `Plus ce score grandit, plus Cortex consolide ce concept dans son world model. Si tous les scores sont bas → l'apprentissage est en pause (augmente l'activité ou attends la pensée vagabonde).`;
- return `
+ const detail = _brainAttr({title:_shortName(p), source:p, type:'concept_consolidated', score:Number(score.toFixed(3)), lastSeen:'stable', description:'Concept consolidé ou en croissance. Cette vue garde la meilleure consolidation observée et ne décroît pas artificiellement.'});
+ return `