feat(diagnostic): Info-Buttons mit Modal-Erklaerungen im Gehirn-Tab

Reusable Info-Modal-System: kleines (ℹ)-Button neben Ueberschriften, beim
Klick oeffnet ein Modal mit ausfuehrlicher Erklaerung. Fuer die 4 wichtigsten
Brain-Konzepte sind die Texte vor-definiert (INFO_TEXTS dict).

  - Gehirn — Status     online/offline, N Memories, Qdrant-Endpoint
  - Konversation        Rolling Window 50, Schwelle 60, Destillat-Logik,
                        Hinweis warum chat_backup ≠ conversation.jsonl
  - Memories            Hot vs. Cold, alle 8 Typen erklaert, semantische Suche
  - Bootstrap           Die drei Wege (Migration / Snapshot / Komplett-Gehirn)

Plus inline-ℹ-Button neben der "Konversation: N Turns"-Zeile in der
Status-Card, damit man dort wo's relevant ist sofort die Erklaerung findet.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 23:03:47 +02:00
parent 693542ef19
commit 950a9d009c
+102 -10
View File
@@ -120,6 +120,16 @@
/* Settings */
.settings-section { margin-bottom:20px; }
.settings-section h2 { margin-bottom:12px; }
/* Info-Button: kleines (i) neben Ueberschriften */
.info-btn { background:transparent; border:1px solid #0096FF; color:#0096FF; width:20px; height:20px;
border-radius:50%; padding:0; font-size:11px; font-weight:bold; cursor:pointer; margin-left:6px;
line-height:18px; text-align:center; vertical-align:middle; font-family:serif; }
.info-btn:hover { background:#0096FF; color:#fff; }
.info-btn-small { background:transparent; border:1px solid #0096FF44; color:#0096FF; width:14px; height:14px;
border-radius:50%; padding:0; font-size:9px; font-weight:bold; cursor:pointer; margin-left:4px;
line-height:11px; text-align:center; vertical-align:middle; font-family:serif; }
.info-btn-small:hover { background:#0096FF; color:#fff; }
.toggle { position:relative; width:40px; height:22px; flex-shrink:0; margin-left:8px; }
.toggle input { opacity:0; width:0; height:0; }
.toggle .slider { position:absolute; cursor:pointer; top:0; left:0; right:0; bottom:0;
@@ -689,24 +699,22 @@
<div id="tab-brain" class="main-tab">
<div class="settings-section">
<h2>Gehirn — Status</h2>
<h2>Gehirn — Status <button class="info-btn" onclick="showInfo('brain-status')" title="Was bedeutet was?"></button></h2>
<div class="card">
<div id="brain-status" style="font-size:12px;color:#8888AA;margin-bottom:8px;">(Lade...)</div>
<div id="conversation-status" style="font-size:12px;color:#8888AA;margin-bottom:8px;"></div>
<div id="conversation-status" style="font-size:12px;color:#8888AA;margin-bottom:8px;">
<button class="info-btn-small" onclick="showInfo('conversation')" title="Konversation — wie funktioniert das?"></button>
</div>
<div style="display:flex;gap:6px;flex-wrap:wrap;">
<button class="btn secondary" onclick="loadBrainStatus()" style="padding:4px 12px;font-size:11px;">Aktualisieren</button>
<button class="btn secondary" onclick="distillNow()" style="padding:4px 12px;font-size:11px;color:#FFD60A;border-color:#FFD60A;" title="Destilliert die aeltesten Turns sofort zu fact-Memories">⚗ Jetzt destillieren</button>
<button class="btn secondary" onclick="resetConversation()" style="padding:4px 12px;font-size:11px;color:#FF6B6B;border-color:#FF6B6B;" title="Leert ARIAs Rolling-Window (Brain) + die Chat-Anzeige (chat_backup). Memories bleiben in der Vector-DB.">🧹 Konversation komplett zurücksetzen</button>
</div>
<div style="margin-top:6px;font-size:10px;color:#555570;line-height:1.5;">
<strong>Konversation zurücksetzen:</strong> leert ARIAs Rolling-Window (Brain "vergisst" die letzten Turns)
UND die Chat-Anzeige hier (<code>chat_backup.jsonl</code>). Destillierte Facts + restliche Memories bleiben.
</div>
</div>
</div>
<div class="settings-section">
<h2>Bootstrap & Migration</h2>
<h2>Bootstrap & Migration <button class="info-btn" onclick="showInfo('bootstrap')" title="Was sind die drei Wege?"></button></h2>
<div class="card" style="line-height:1.6;">
<p style="color:#8888AA;font-size:12px;margin:0 0 12px;">
Drei Wege ARIA mit "Grundregeln" zu füttern — von leichtgewichtig bis Voll-Wiederherstellung.
@@ -758,7 +766,7 @@
<div class="settings-section">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">
<h2 style="margin:0;">Memories</h2>
<h2 style="margin:0;">Memories <button class="info-btn" onclick="showInfo('memories')" title="Hot vs. Cold — wie funktioniert das Gedaechtnis?"></button></h2>
<div>
<button class="btn secondary" onclick="resetBrainFilters();loadBrainMemoryList()" style="padding:4px 10px;font-size:11px;">Aktualisieren</button>
<button class="btn" onclick="openMemoryModal()" style="padding:4px 10px;font-size:11px;">+ Neu</button>
@@ -849,6 +857,17 @@
</div>
</div><!-- /tab-skills -->
<!-- Generisches Info-Modal — wird via openInfoModal(title, html) gefuellt -->
<div class="modal-overlay" id="info-modal">
<div class="modal-box" style="max-width:640px;">
<div class="modal-header">
<h3 id="info-modal-title">Info</h3>
<button class="modal-close" onclick="closeInfoModal()">&times;</button>
</div>
<div class="modal-body" id="info-modal-body" style="padding:16px;font-size:13px;color:#E0E0F0;line-height:1.6;"></div>
</div>
</div>
<!-- Memory-Modal (Neu + Editieren) -->
<div class="modal-overlay" id="memory-modal">
<div class="modal-box" style="max-width:640px;">
@@ -2876,14 +2895,15 @@
// Conversation-Stats (separater Endpoint)
const conv = document.getElementById('conversation-status');
if (!conv) return;
const infoBtn = `<button class="info-btn-small" onclick="showInfo('conversation')" title="Konversation — wie funktioniert das?"></button>`;
try {
const r2 = await fetch('/api/brain/conversation/stats');
if (!r2.ok) throw new Error('HTTP ' + r2.status);
const d2 = await r2.json();
const distillIcon = d2.needs_distill ? ' ⚠ Destillat bald fällig' : '';
conv.innerHTML = `Konversation: <strong>${d2.turns}</strong> Turns · Window: ${d2.max_window} · Schwelle: ${d2.distill_threshold}${distillIcon}`;
conv.innerHTML = `Konversation: <strong>${d2.turns}</strong> Turns · Window: ${d2.max_window} · Schwelle: ${d2.distill_threshold}${distillIcon} ${infoBtn}`;
} catch (e) {
conv.innerHTML = `Konversation: <span style="color:#555570;">${e.message}</span>`;
conv.innerHTML = `Konversation: <span style="color:#555570;">${e.message}</span> ${infoBtn}`;
}
}
@@ -3151,6 +3171,78 @@
return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
}
// ── Generisches Info-Modal — Aufruf: openInfoModal('Titel', '<p>HTML...</p>') ──
function openInfoModal(title, html) {
const t = document.getElementById('info-modal-title');
const b = document.getElementById('info-modal-body');
const m = document.getElementById('info-modal');
if (!t || !b || !m) return;
t.textContent = title;
b.innerHTML = html;
m.classList.add('open');
}
function closeInfoModal() {
const m = document.getElementById('info-modal');
if (m) m.classList.remove('open');
}
// Vor-definierte Info-Blocks
const INFO_TEXTS = {
'brain-status': {
title: 'Gehirn — Status',
html: `
<p><strong>online / offline</strong> — ob der <code>aria-brain</code> Container erreichbar ist (HTTP GET /health).</p>
<p><strong>N Memories</strong> — Anzahl der Punkte in der Vector-DB. Beinhaltet alle Typen: identity, rule, preference, tool, skill, fact, conversation, reminder.</p>
<p><strong>Qdrant: aria-qdrant:6333</strong> — Hostname + Port des Vector-DB-Containers. Der Brain spricht intern dorthin.</p>
`,
},
'conversation': {
title: 'Konversation — wie funktioniert das?',
html: `
<p><strong>Rolling Window:</strong> ARIA "sieht" pro Anfrage nur die letzten N Turns einer einzelnen, durchgehenden Konversation. Kein Sessions, kein Multi-Thread.</p>
<ul>
<li><strong>Turns</strong> — Anzahl aller Nachrichten (User + ARIA) seit dem letzten Destillat oder Reset.</li>
<li><strong>Window: 50</strong> — die letzten 50 Turns wandern in den Prompt. Aelteste fallen raus, sobald die Schwelle ueberschritten ist.</li>
<li><strong>Schwelle: 60</strong> — bei mehr als 60 Turns triggert Brain automatisch das Destillat (die 30 aeltesten werden zu fact-Memories verdichtet, Token-Budget bleibt konstant).</li>
</ul>
<p><strong>⚗ Jetzt destillieren:</strong> manueller Trigger fuer das Destillat (kostet einen Claude-Call). Verdichtet die aeltesten 30 Turns zu Fakten + entfernt sie aus dem Window.</p>
<p><strong>🧹 Konversation komplett zuruecksetzen:</strong> leert beides — ARIAs Rolling-Window (Brain) UND die Chat-Anzeige hier (chat_backup.jsonl). Destillierte Facts + alle anderen Memories in der Vector-DB <em>bleiben</em>.</p>
<p style="margin-top:8px;color:#FFD60A;font-size:12px;">⚠ Falls "Turns: 0" obwohl du oben Chat-Eintraege siehst: chat_backup.jsonl (Anzeige) und conversation.jsonl (Brain-Kontext) sind getrennte Stores. Alte chat_backup-Eintraege koennen aus OpenClaw-Zeit stammen. Reset-Button leert beides.</p>
`,
},
'memories': {
title: 'Memories — Hot vs. Cold',
html: `
<p><strong>Pinned (Hot Memory)</strong> 📌 — landet bei JEDER Anfrage im System-Prompt. Hier gehoeren rein: Identitaet, Sicherheitsregeln, Benutzer-Praeferenzen, Tool-Freigaben, Kern-Skills.</p>
<p><strong>Cold Memory</strong> — semantisch durchsucht. Pro Anfrage werden die 5 aehnlichsten Punkte zur User-Frage in den Prompt eingehaengt.</p>
<p><strong>Typen:</strong></p>
<ul>
<li><strong>identity</strong> — wer ARIA ist (Name, Persoenlichkeit)</li>
<li><strong>rule</strong> — Sicherheits-/Werte-Regeln</li>
<li><strong>preference</strong> — User-Profile</li>
<li><strong>tool</strong> — Tool-Freigaben + Infrastruktur</li>
<li><strong>skill</strong> — Faehigkeiten (verlinkt mit /data/skills/)</li>
<li><strong>fact</strong> — Wissens-Fakten (oft aus Destillaten)</li>
<li><strong>conversation</strong> — destillierte Konversations-Erkenntnisse</li>
<li><strong>reminder</strong> — Termine, Aufgaben</li>
</ul>
<p><strong>Such-Feld:</strong> semantische Suche via Embedder + Qdrant. Findet sinngemaess, nicht nur Stichworte.</p>
`,
},
'bootstrap': {
title: 'Bootstrap & Migration — die drei Wege',
html: `
<p><strong>1. Aus brain-import/ migrieren</strong> 🔵 — Parser fuer die <code>.md</code>-Dateien im Repo (AGENT.md, USER.md, TOOLING.md). Schreibt sie als atomare pinned Memories. Idempotent — Re-Run ersetzt nur die Migration-Punkte, eigene Memories bleiben.</p>
<p><strong>2. Bootstrap-Snapshot</strong> 🟡 — kleines JSON, NUR die pinned Memories. Export = aktueller Stand als Datei. Import = ALLE aktuell pinned werden ersetzt. Cold Memory bleibt unangetastet.</p>
<p><strong>3. Komplettes Gehirn</strong> 🔴 — tar.gz mit allem (Memories + Skills + Qdrant-DB). Backup + Restore. Import ueberschreibt ALLES.</p>
`,
},
};
function showInfo(key) {
const cfg = INFO_TEXTS[key];
if (cfg) openInfoModal(cfg.title, cfg.html);
}
function brainExport() {
// Browser folgt der Download-Header-Antwort automatisch
window.location.href = '/api/brain-export';