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>
This commit is contained in:
2026-04-28 11:53:11 +02:00
parent 8688f632bf
commit bae084cd76
36 changed files with 3137 additions and 58 deletions

View File

@@ -94,6 +94,127 @@
}
#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>
@@ -105,6 +226,9 @@
<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>
@@ -119,6 +243,27 @@
<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">