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>
277 lines
8.9 KiB
HTML
277 lines
8.9 KiB
HTML
<!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. e.g. Pasta carbonara uses guanciale, eggs, pecorino, and pepper. Backpropagation computes gradients of a loss with respect to weights. 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>
|