Files
Sophia/web/index.html
dtoro bae084cd76 Implement Sophia MVP stages 4-7 (introspection, queries, persistence, polish)
Brings the system from "engrams cluster + form synapses" to a complete
end-to-end demo: ingest text, watch it cluster, ask questions, restart
with state intact.

- Stage 4: birth introspection (taxonomy/goals/open_questions via LLM,
  bounded by the global parallel-op semaphore), per-engram memory log,
  click-to-inspect side panel.
- Stage 5: queries as conversations. POST /api/galaxy/:id/query embeds
  the question, materializes a pinned Query-Engram at the donut center,
  runs broadcast retrieval (global cosine scan + 1-hop synaptic
  expansion with attenuation) and fans out responder LLM calls. The
  integrator runs every 2s on accumulated snippets and streams the
  refining answer back over SSE; responders briefly transition to
  Conversing on the WS bus so the right dots light up.
- Stage 6: snapshot persistence. sled-backed store keyed by galaxy id,
  JSON-encoded values (bincode chokes on internally-tagged enums like
  Manifest/MemoryKind), 60s periodic snapshot task, hydrate-on-boot,
  DELETE /api/galaxy/:id wired through. State survives kill -9.
- Stage 7: HUD additions (sim ticks/sec, LLM queue depth, FPS) via a
  new GET /api/stats polled at 1Hz. `sophia demo` subcommand boots the
  server then auto-ingests a 50-paragraph corpus baked into the binary
  with include_str!. README quickstart added.

Token caps for query_responder/integrator bumped (gemma-4-e4b is a
thinking model — output budget must cover hidden reasoning + visible
answer, otherwise content comes back empty). Pinned engrams skip
physics; their tick scheduling is also skipped at materialization so
they stay perfectly still at the donut center.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 11:53:11 +02:00

277 lines
8.9 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sophia</title>
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
background: #050608;
color: #cfd6df;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
overflow: hidden;
}
#stage {
position: fixed;
inset: 0;
}
#hud {
position: fixed;
top: 12px;
left: 12px;
font-size: 12px;
line-height: 1.5;
background: rgba(0,0,0,0.4);
padding: 8px 10px;
border-radius: 4px;
pointer-events: none;
}
#hud .k { opacity: 0.55; }
#controls {
position: fixed;
top: 12px;
right: 12px;
display: flex;
flex-direction: column;
gap: 6px;
font-size: 12px;
background: rgba(0,0,0,0.4);
padding: 8px 10px;
border-radius: 4px;
}
#controls .ctrl-row {
display: flex;
gap: 6px;
align-items: center;
}
#controls input, #controls button {
font: inherit;
background: #11151b;
color: #cfd6df;
border: 1px solid #2a323d;
padding: 3px 8px;
border-radius: 3px;
}
#controls input { width: 64px; }
#controls button { cursor: pointer; }
#controls button:hover { background: #1a2129; }
#controls button:disabled { opacity: 0.5; cursor: progress; }
#ingest {
position: fixed;
bottom: 12px;
right: 12px;
width: 340px;
background: rgba(0,0,0,0.55);
padding: 10px;
border-radius: 4px;
font-size: 12px;
}
#ingest textarea {
width: 100%;
height: 96px;
font: inherit;
resize: vertical;
background: #0c1014;
color: #cfd6df;
border: 1px solid #2a323d;
border-radius: 3px;
padding: 6px;
box-sizing: border-box;
}
#ingest .row { display: flex; justify-content: space-between; align-items: center; margin-top: 6px; }
#ingest .hint { opacity: 0.55; font-size: 11px; }
#ingest button {
font: inherit;
background: #1a2129;
color: #cfd6df;
border: 1px solid #2a323d;
padding: 4px 10px;
border-radius: 3px;
cursor: pointer;
}
#ingest button:hover { background: #232c36; }
#ingest button:disabled { opacity: 0.5; cursor: progress; }
#query {
position: fixed;
bottom: 12px;
left: 12px;
width: 380px;
background: rgba(0,0,0,0.55);
padding: 10px;
border-radius: 4px;
font-size: 12px;
}
#query-form { display: flex; gap: 6px; }
#query-input {
flex: 1;
font: inherit;
background: #0c1014;
color: #cfd6df;
border: 1px solid #2a323d;
border-radius: 3px;
padding: 5px 8px;
}
#query-submit {
font: inherit;
background: #1a2129;
color: #cfd6df;
border: 1px solid #2a323d;
padding: 4px 12px;
border-radius: 3px;
cursor: pointer;
}
#query-submit:hover { background: #232c36; }
#query-submit:disabled { opacity: 0.5; cursor: progress; }
#query-status {
margin-top: 6px;
opacity: 0.55;
font-size: 11px;
min-height: 1.2em;
}
#query-answer {
margin-top: 4px;
white-space: pre-wrap;
line-height: 1.45;
max-height: 180px;
overflow-y: auto;
padding: 6px 8px;
background: rgba(255,255,255,0.03);
border-radius: 3px;
min-height: 2em;
}
#query-answer:empty::before {
content: "ask what your knowledge knows…";
opacity: 0.4;
}
#inspector {
position: fixed;
top: 12px;
left: 50%;
transform: translateX(-50%);
width: 460px;
max-height: 70vh;
overflow-y: auto;
background: rgba(0,0,0,0.78);
padding: 12px 14px;
border-radius: 4px;
border: 1px solid #2a323d;
font-size: 12px;
line-height: 1.45;
display: none;
}
#inspector.open { display: block; }
#inspector h3 {
margin: 0 0 6px;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.06em;
opacity: 0.6;
font-weight: 500;
}
#inspector .field { margin-bottom: 8px; }
#inspector .label {
opacity: 0.45;
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.08em;
margin-bottom: 2px;
}
#inspector .value { color: #e1e6ec; }
#inspector .muted { color: #6b7480; }
#inspector ul {
margin: 2px 0 0 14px;
padding: 0;
}
#inspector ul li { margin-bottom: 1px; }
#inspector .row {
display: flex;
justify-content: space-between;
align-items: center;
}
#inspector .close {
cursor: pointer;
background: transparent;
border: none;
color: #cfd6df;
font-size: 16px;
line-height: 1;
padding: 0 4px;
opacity: 0.7;
}
#inspector .close:hover { opacity: 1; }
#inspector .manifest {
white-space: pre-wrap;
max-height: 8em;
overflow-y: auto;
font-size: 11px;
opacity: 0.85;
}
#inspector .id {
font-family: ui-monospace, monospace;
font-size: 10px;
opacity: 0.5;
word-break: break-all;
}
</style>
</head>
<body>
<canvas id="stage"></canvas>
<div id="hud">
<div><span class="k">sophia</span> v0.0.1</div>
<div><span class="k">stage</span> <span id="hud-stage">2 — slate + lm studio</span></div>
<div><span class="k">galaxy</span> <span id="hud-galaxy"></span></div>
<div><span class="k">torus</span> <span id="hud-torus"></span></div>
<div><span class="k">engrams</span> <span id="hud-count">0</span></div>
<div><span class="k">synapses</span> <span id="hud-synapses">0</span></div>
<div><span class="k">sim</span> <span id="hud-ticks"></span></div>
<div><span class="k">llm queue</span> <span id="hud-llm-queue"></span></div>
<div><span class="k">fps</span> <span id="hud-fps"></span></div>
<div><span class="k">lm studio</span> <span id="hud-lm"></span></div>
<div><span class="k">health</span> <span id="hud-health"></span></div>
</div>
<div id="controls">
<div class="ctrl-row">
<input id="seed-n" type="number" min="1" max="5000" value="200" title="number of synthetic engrams" />
<button id="seed-btn">seed</button>
</div>
<div class="ctrl-row">
<input id="resize-major" type="number" min="1" max="10000" step="10" value="100" title="major radius (donut hole)" />
<input id="resize-minor" type="number" min="1" max="2000" step="5" value="30" title="minor radius (tube)" />
<button id="resize-btn">resize</button>
</div>
</div>
<div id="inspector">
<div class="row">
<h3>engram</h3>
<button class="close" id="inspector-close" title="close">×</button>
</div>
<div class="field id" id="inspector-id"></div>
<div class="field"><span class="label">state</span><div class="value" id="inspector-state"></div></div>
<div class="field"><span class="label">manifest</span><div class="manifest" id="inspector-manifest"></div></div>
<div class="field"><span class="label">taxonomy</span><div class="value" id="inspector-taxonomy"></div></div>
<div class="field"><span class="label">goals</span><div class="value" id="inspector-goals"></div></div>
<div class="field"><span class="label">open questions</span><div class="value" id="inspector-questions"></div></div>
<div class="field"><span class="label">memories</span><div class="value" id="inspector-memories"></div></div>
</div>
<div id="query">
<form id="query-form">
<input id="query-input" type="text" placeholder="What did we ingest about…?" autocomplete="off" />
<button id="query-submit" type="submit">ask</button>
</form>
<div id="query-status"></div>
<div id="query-answer"></div>
</div>
<div id="ingest">
<textarea id="ingest-text" placeholder="One engram per line. Empty lines are skipped.&#10;e.g.&#10;Pasta carbonara uses guanciale, eggs, pecorino, and pepper.&#10;Backpropagation computes gradients of a loss with respect to weights.&#10;The fall of Constantinople occurred in 1453."></textarea>
<div class="row">
<span class="hint">embeds via LM Studio, then spawns at center</span>
<button id="ingest-btn">ingest</button>
</div>
</div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>