index.html (19479B)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="utf-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1"> 6 <title>semif-api — decision readout</title> 7 <link rel="stylesheet" href="style.css"> 8 </head> 9 <body> 10 11 <header> 12 <div class="brand"> 13 <h1>semif-api</h1> 14 <p class="tagline">semantic decision readout — native option-slot logits, not generated text</p> 15 </div> 16 <div id="health" class="health" aria-live="polite"> 17 <span class="dot" id="health-dot"></span> 18 <span id="health-text">checking…</span> 19 <button type="button" id="health-refresh" class="ghost" title="Re-check /healthz">↻</button> 20 <select id="model-picker" class="model-picker" hidden aria-label="model selector" 21 title="Served models — switching unloads the current model and loads the new one on the next request"></select> 22 <span id="model-note" class="model-note" role="status"></span> 23 </div> 24 </header> 25 26 <nav class="tabs" role="tablist"> 27 <button type="button" class="tab is-active" id="tab-single" role="tab" aria-selected="true" 28 aria-controls="panel-single">Single decision <code>/decide</code></button> 29 <button type="button" class="tab" id="tab-batch" role="tab" aria-selected="false" 30 aria-controls="panel-batch">Shared state <code>/decide-batch</code></button> 31 <button type="button" class="tab" id="tab-game" role="tab" aria-selected="false" 32 aria-controls="panel-game">Platformer demo</button> 33 <button type="button" class="tab" id="tab-self" role="tab" aria-selected="false" 34 aria-controls="panel-self">Platformer SL</button> 35 <button type="button" class="tab" id="tab-room" role="tab" aria-selected="false" 36 aria-controls="panel-room">Puzzle room</button> 37 <button type="button" class="tab" id="tab-roomself" role="tab" aria-selected="false" 38 aria-controls="panel-roomself">Puzzle room SL</button> 39 </nav> 40 41 <main> 42 <!-- ── request ────────────────────────────────────────────────── --> 43 <section class="col" id="request"> 44 45 <form id="panel-single" class="panel" novalidate> 46 <div class="field"> 47 <label for="s-id">id</label> 48 <input id="s-id" class="mono" value="adhoc" autocomplete="off" spellcheck="false"> 49 </div> 50 51 <div class="field"> 52 <div class="field-head"> 53 <label for="s-state">state <span class="hint">the evidence</span></label> 54 <span class="state-mode"> 55 <input type="radio" name="s-state-mode" id="s-state-text" value="text" checked> 56 <label for="s-state-text">text</label> 57 <input type="radio" name="s-state-mode" id="s-state-json" value="json"> 58 <label for="s-state-json">JSON</label> 59 </span> 60 </div> 61 <textarea id="s-state" rows="7" spellcheck="false" 62 placeholder="Evidence the model must decide from — prose, or a JSON object/array in JSON mode."></textarea> 63 <p class="note" id="s-state-note"></p> 64 </div> 65 66 <div class="field"> 67 <label for="s-question">question <span class="hint">the criterion applied to the evidence</span></label> 68 <input id="s-question" autocomplete="off" placeholder="Does this evidence support…?"> 69 </div> 70 71 <div class="field"> 72 <div class="field-head"> 73 <label>options <span class="hint">2–16, ids must be unique</span></label> 74 <button type="button" class="ghost add-option">+ option</button> 75 </div> 76 <div class="options" id="s-options"></div> 77 </div> 78 79 <div class="actions"> 80 <button type="submit" class="primary" id="s-submit">Decide</button> 81 <button type="button" id="s-curl">Copy as curl</button> 82 <button type="button" id="s-clear">Clear</button> 83 </div> 84 </form> 85 86 <form id="panel-batch" class="panel" novalidate hidden> 87 <p class="constraint"> 88 Every decision below is scored against the same evidence. Torch shares a prefill; 89 the llama backend scores sequentially and lets the server reuse cached prefixes. 90 </p> 91 92 <div class="field"> 93 <div class="field-head"> 94 <label for="b-state">shared state</label> 95 <span class="state-mode"> 96 <input type="radio" name="b-state-mode" id="b-state-text" value="text" checked> 97 <label for="b-state-text">text</label> 98 <input type="radio" name="b-state-mode" id="b-state-json" value="json"> 99 <label for="b-state-json">JSON</label> 100 </span> 101 </div> 102 <textarea id="b-state" rows="7" spellcheck="false"></textarea> 103 <p class="note" id="b-state-note"></p> 104 </div> 105 106 <div class="field"> 107 <div class="field-head"> 108 <label>decisions <span class="hint">ids must be unique across the batch</span></label> 109 <button type="button" class="ghost" id="b-add-decision">+ decision</button> 110 </div> 111 <div id="b-decisions"></div> 112 </div> 113 114 <div class="actions"> 115 <button type="submit" class="primary" id="b-submit">Run batch</button> 116 <button type="button" id="b-curl">Copy as curl</button> 117 <button type="button" id="b-clear">Clear</button> 118 </div> 119 </form> 120 121 <div class="presets"> 122 <span class="presets-label">Load example</span> 123 <div id="preset-buttons"></div> 124 </div> 125 </section> 126 127 <!-- ── response ───────────────────────────────────────────────── --> 128 <section class="col" id="response"> 129 <div id="idle" class="placeholder"> 130 <p>No request sent yet.</p> 131 <p class="dim">Probabilities are <em>conditional option scores</em>: use them to rank options and 132 set coarse thresholds, never as calibrated confidence.</p> 133 </div> 134 <div id="pending" class="placeholder" hidden> 135 <p class="spinner" aria-hidden="true">◌</p> 136 <p id="pending-text">scoring…</p> 137 <p class="dim">GPU work serializes on a per-request lock — concurrent requests queue, they do not 138 speed each other up.</p> 139 </div> 140 <div id="error" class="error" hidden role="alert"></div> 141 <div id="result" hidden></div> 142 </section> 143 144 <!-- ── platformer demo (third tab; see game.js + game-rules.js) ────── --> 145 <section id="panel-game" class="demo-panel" role="tabpanel" aria-labelledby="tab-game" hidden> 146 <div class="game-stage"> 147 <h2 id="level-label">Level</h2> 148 <canvas id="cv" width="960" height="200"></canvas> 149 <div class="controls"> 150 <button type="button" id="btn-start" class="primary">▶ Let SemIf play</button> 151 <button type="button" id="btn-reset">↺ Reset</button> 152 <button type="button" id="btn-randomize" title="Shuffle pit positions to a new random seed">⚄ Randomize</button> 153 <label class="seed" title="Level seed — type one and press Enter to reproduce that exact layout">seed<input type="text" id="seed" class="seed-input" inputmode="numeric" autocomplete="off" spellcheck="false" placeholder="default"></label> 154 <div class="seg" role="group" aria-label="state text mode" title="Guided injects a hint about the right move; Unguided gives only the prose facts; Run-length encodes the terrain as run-length segments (-N floor, #N pit) the model must parse; ASCII replaces the readout with a single symbolic row (- floor, # pit, * you, ! flag). Hardest last. Applies from the next decision."> 155 <button type="button" id="mode-guided" class="on">Guided</button><button type="button" id="mode-unguided">Unguided</button><button type="button" id="mode-runlength">Run-length</button><button type="button" id="mode-ascii">ASCII</button> 156 </div> 157 <span class="status" id="status">idle — press Start</span> 158 </div> 159 <p class="game-hint">Manual drive (when idle): <code>→</code>/<code>D</code> run right, <code>space</code>/<code>W</code> jump. 160 Physics pauses while a decision is pending. In <b>Guided</b> mode the state text includes a hint about the right move; 161 in <b>Unguided</b> mode only the facts are given and the model has to decide on its own; 162 in <b>Run-length</b> mode the terrain is a compact run-length encoding 163 (<code>-N</code> floor, <code>#N</code> pit) the model must parse; 164 in <b>ASCII</b> mode the facts are a single symbolic row (<code>-</code> floor, <code>#</code> pit, <code>*</code> you, <code>!</code> flag) 165 the model must decode — the hardest of the four.<br> 166 <b>⚄ Randomize</b> moves the three pits to new (always-solvable) positions; the seed box shows the seed so a 167 layout can be reproduced — type a seed and press Enter.</p> 168 </div> 169 <aside class="game-side"> 170 <h2>State text — rendered from, and sent verbatim to, /decide</h2> 171 <pre id="state-view"></pre> 172 <h2>Decision log</h2> 173 <div id="log" class="log"></div> 174 </aside> 175 </section> 176 <!-- ── no-rules platformer (fourth tab; see self-game.js + self-rules.js) ── --> 177 <section id="panel-self" class="demo-panel" role="tabpanel" aria-labelledby="tab-self" hidden> 178 <div class="game-stage"> 179 <h2 id="self-level-label">Level</h2> 180 <canvas id="self-cv" width="960" height="200"></canvas> 181 <div class="controls"> 182 <button type="button" id="self-start" class="primary">▶ Let SemIf play</button> 183 <button type="button" id="self-reset">↺ Reset</button> 184 <button type="button" id="self-forget" title="Discard the learned rules and the action transcript">✕ Forget rules</button> 185 <button type="button" id="self-randomize" title="Shuffle pit positions to a new random seed">⚄ Randomize</button> 186 <label class="seed" title="Level seed — type one and press Enter to reproduce that exact layout">seed<input type="text" id="self-seed" class="seed-input" inputmode="numeric" autocomplete="off" spellcheck="false" placeholder="default"></label> 187 <div class="seg" role="group" aria-label="bare state text mode" title="All three modes send the same neutral observation with no rules: Prose lists the terrain ahead as counts, Run-length encodes it as segments (-N ground, #N hole), ASCII as one symbolic row (- ground, # hole, * you, ! flag). The model must learn how to play from /plan. Applies from the next decision."> 188 <button type="button" id="self-mode-prose" class="on">Prose</button><button type="button" id="self-mode-runlength">Run-length</button><button type="button" id="self-mode-ascii">ASCII</button> 189 </div> 190 <span class="status" id="self-status">idle — press Start</span> 191 </div> 192 <p class="game-hint">Same physics as the Platformer demo, but the decision model is given <b>no rules</b> — only a 193 neutral observation of the level and the actions <code>run</code> / <code>jump</code>, plus an 194 <code>insufficient</code> escape hatch. When it picks <code>insufficient</code> with p ≥ 0.99, or the player 195 falls into a pit, or it keeps requesting jumps with none left, the client calls <code>/plan</code>: a reasoning 196 chat completion that reviews the transcript of completed actions and writes a set of rules. The rules then lead 197 every later observation — that is the only way the model learns how to play. Every trigger restarts the level 198 with the learned rules: the planner writes full game rules, not a fix for one stuck spot, so the model gets a 199 fresh attempt to apply them from the start. Reset restarts the level and keeps the learned rules; 200 ✕ Forget rules clears them. Manual drive (when idle): <code>→</code>/<code>D</code> run right, 201 <code>space</code>/<code>W</code> jump.</p> 202 </div> 203 <aside class="game-side"> 204 <h2>State text — rendered from, and sent verbatim to, /decide</h2> 205 <pre id="self-state-view"></pre> 206 <h2>Rules learned from /plan (editable)</h2> 207 <textarea id="self-rules-view" class="rules-edit" rows="7" spellcheck="false" 208 placeholder="(none yet — rules learned from /plan appear here; edit or paste to set them by hand)"></textarea> 209 <h2>Run stats</h2> 210 <pre id="self-stats">(run not started)</pre> 211 <h2>Decision log</h2> 212 <div id="self-log" class="log"></div> 213 </aside> 214 </section> 215 <!-- ── puzzle room demo (fifth tab; see room.js + room-rules.js) ──── --> <section id="panel-room" class="demo-panel" role="tabpanel" aria-labelledby="tab-room" hidden> 216 <div class="game-stage"> 217 <h2 id="room-label">Room</h2> 218 <canvas id="room-cv" width="960" height="420"></canvas> 219 <div class="controls"> 220 <button type="button" id="room-start" class="primary">▶ Let SemIf solve</button> 221 <button type="button" id="room-reset">↺ Reset</button> 222 <button type="button" id="room-randomize" title="New random room; the seed box shows it so the layout can be reproduced">⚄ Randomize</button> 223 <label class="seed" title="Room seed — type one and press Enter to reproduce that exact layout">seed<input type="text" id="room-seed" class="seed-input" inputmode="numeric" autocomplete="off" spellcheck="false" placeholder="default"></label> 224 <div class="seg" role="group" aria-label="room state text mode" title="Guided FPP describes your view plus a compass hint toward the current objective; FPP gives only what you see and remember; Map hands the model the whole top-down layout. Applies from the next decision."> 225 <button type="button" id="room-mode-guided" class="on">Guided FPP</button><button type="button" id="room-mode-fpp">FPP</button><button type="button" id="room-mode-map">Map</button> 226 </div> 227 <span class="status" id="room-status">idle — press Start</span> 228 </div> 229 <p class="game-hint">Manual drive (when idle): <code>←</code>/<code>A</code> turn left, <code>→</code>/<code>D</code> turn right, <code>↑</code>/<code>W</code> step forward. 230 The first-person view is yours — the model only ever sees the text in the side pane: the four adjacent cells and what is in view, 231 so it must build its own map by dead-reckoning. It must find the key, unlock the door in the dividing wall, and reach the exit, 232 one action per decision, within the step budget. In <b>Guided FPP</b> the state includes a compass hint toward the key/door/exit; 233 in <b>FPP</b> only what you see and what happened; in <b>Map</b> it gets the whole layout. 234 <b>⚄ Randomize</b> builds a new (always-solvable) room; the seed box reproduces it — type a seed and press Enter.</p> 235 </div> 236 <aside class="game-side"> 237 <h2>State text — rendered from, and sent verbatim to, /decide</h2> 238 <pre id="room-state-view"></pre> 239 <h2>Decision log</h2> 240 <div id="room-log" class="log"></div> 241 </aside> 242 </section> 243 <!-- ── self-learning puzzle room (sixth tab; see room-self-game.js + room-self-rules.js) ── --> 244 <section id="panel-roomself" class="demo-panel" role="tabpanel" aria-labelledby="tab-roomself" hidden> 245 <div class="game-stage"> 246 <h2 id="rs-label">Room</h2> 247 <canvas id="rs-cv" width="960" height="420"></canvas> 248 <div class="controls"> 249 <button type="button" id="rs-start" class="primary">▶ Let SemIf play</button> 250 <button type="button" id="rs-reset">↺ Reset</button> 251 <button type="button" id="rs-forget" title="Discard the learned rules and the action transcript">✕ Forget rules</button> 252 <button type="button" id="rs-randomize" title="New random room; the seed box shows it so the layout can be reproduced">⚄ Randomize</button> 253 <label class="seed" title="Room seed — type one and press Enter to reproduce that exact layout">seed<input type="text" id="rs-seed" class="seed-input" inputmode="numeric" autocomplete="off" spellcheck="false" placeholder="default"></label> 254 <div class="seg" role="group" aria-label="bare room state text mode" title="Both modes send the same neutral observation with no rules: FPP describes all four adjacent cells (open floor included), whatever your 120° view cone currently sees, and bearings back to any landmark it has discovered (key, locked door, exit) that is out of sight — bearings only, no guidance, and a bearing whose straight line is walled reads "(blocked)" — plus your carry status and step count; Map hands over the whole top-down layout with a neutral symbol legend. There is deliberately no event log: a list of the actor's own recent actions is imitation bait for a single-pass reader, not a memory. The model must learn what to do from /plan. Applies from the next decision."> 255 <button type="button" id="rs-mode-fpp" class="on">FPP</button><button type="button" id="rs-mode-map">Map</button> 256 </div> 257 <span class="status" id="rs-status">idle — press Start</span> 258 </div> 259 <p class="game-hint">Same room as the Puzzle room demo, but the decision model is given <b>no rules</b> — only a neutral 260 observation (all four adjacent cells, in-view bearings, carry status and a step count — or the whole top-down map) 261 and the actions <code>forward</code> / <code>left</code> / <code>right</code>, plus an <code>insufficient</code> 262 escape hatch. When <code>insufficient</code> is its leading option at all, or the step budget runs out, the client 263 calls <code>/plan</code>: a reasoning chat completion that reviews the transcript of completed actions and writes a 264 set of rules. The rules then lead every later observation — that is the only way the model learns how to play. 265 Either trigger restarts the room with the learned rules: the planner writes full game rules, not a fix for one 266 stuck spot, so the model gets a fresh attempt to apply them from the start. 267 Reset restarts the room and keeps the learned rules; ✕ Forget rules clears them. Manual drive (when idle): 268 <code>←</code>/<code>A</code> turn left, <code>→</code>/<code>D</code> turn right, <code>↑</code>/<code>W</code> step forward.</p> 269 </div> 270 <aside class="game-side"> 271 <h2>State text — rendered from, and sent verbatim to, /decide</h2> 272 <pre id="rs-state-view"></pre> 273 <h2>Rules learned from /plan (editable)</h2> 274 <textarea id="rs-rules-view" class="rules-edit" rows="7" spellcheck="false" 275 placeholder="(none yet — rules learned from /plan appear here; edit or paste to set them by hand)"></textarea> 276 <h2>Run stats</h2> 277 <pre id="rs-stats">(run not started)</pre> 278 <h2>Decision log</h2> 279 <div id="rs-log" class="log"></div> 280 </aside> 281 </section> 282 </main> 283 284 <footer> 285 <p>Readout reads logits at the declared answer slots; there is no decoding loop and no output tokens. 286 Prompts over <code id="foot-max-tokens">max_tokens</code> are rejected, never silently truncated.</p> 287 </footer> 288 289 <script src="game-rules.js"></script> 290 <script src="game.js"></script> 291 <script src="planner.js"></script> 292 <script src="self-rules.js"></script> 293 <script src="self-game.js"></script> 294 <script src="room-rules.js"></script> 295 <script src="room.js"></script> 296 <script src="room-self-rules.js"></script> 297 <script src="room-self-game.js"></script> 298 <script src="app.js"></script> 299 </body> 300 </html>