feat: web switch for her inner voice (Dolphin/3090 | Qwen-32B/MI50 | Off)

Her introspection (reflect/think) voice is now switchable live from the web
settings, read each cycle by the dream loop — so Brian can flip it off the 3090
before gaming without touching config or restarting.

- memory: runtime key/value settings table + get_setting/set_setting.
- self_state: INTROSPECTION_MODES (dolphin=local/dolphin3:8b, mi50=Qwen-32B,
  off=paused) + introspection_target()/set_introspection_mode(); default "dolphin".
  reflect() resolves from the live setting and SKIPS entirely when off.
- thoughts.think(): same resolution + skip-when-off.
- server: GET/POST /settings/introspection.
- index.html: "Inner Voice (introspection)" selector in Settings, applies instantly.
- tests: routing (dolphin/mi50), off-skip for think + reflect. Suite 77, ruff clean.

Default = Dolphin on the 3090 (richer voice). Flip to MI50 or Off in Settings
before gaming — that was the GPU-contention culprit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-22 19:16:35 +00:00
parent a705e573a9
commit a7966e4bab
7 changed files with 139 additions and 8 deletions
+32
View File
@@ -169,6 +169,17 @@
</select>
</div>
<div class="settings-section" style="margin-top: 24px;">
<h4>Inner Voice (introspection)</h4>
<p class="settings-desc">Which model runs her reflections & thoughts (her dream loop).
Dolphin is richer but shares the 3090 — switch to MI50 or Off before gaming.</p>
<select id="introspectionMode">
<option value="dolphin">Dolphin · 3090 (richer voice)</option>
<option value="mi50">Qwen-32B · MI50 (gaming-safe)</option>
<option value="off">Off (pause her thinking)</option>
</select>
</div>
<div class="settings-section" style="margin-top: 24px;">
<h4>Session Management</h4>
<p class="settings-desc">Manage your saved chat sessions:</p>
@@ -979,10 +990,31 @@
}
}
// Inner-voice (introspection) switch — applies instantly, read live by the dream loop.
const introspectionSel = document.getElementById("introspectionMode");
async function loadIntrospection() {
try {
const r = await fetch("/settings/introspection", { cache: "no-store" });
const d = await r.json();
if (d.mode) introspectionSel.value = d.mode;
} catch (e) {}
}
if (introspectionSel) {
introspectionSel.addEventListener("change", async () => {
try {
await fetch("/settings/introspection", {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ mode: introspectionSel.value })
});
} catch (e) {}
});
}
// Show modal and load session list
settingsBtn.addEventListener("click", () => {
settingsModal.classList.add("show");
loadSessionList(); // Refresh session list when opening settings
loadIntrospection(); // reflect the current inner-voice setting
});
// Sidebar "Settings" from another page navigates here with ?settings=1.