Something went wrong. Try again.
a tiny pet with a GPT-2 brain, living in your browser
Something went wrong. Try again.
5.9 kB · 171 lines
TSX
at cloud-pet
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172import { useState } from "preact/hooks";import { DAY, MIN, STATS } from "../app/config.ts";import { copyText, setAge } from "../app/debug.ts";import { writeDiary } from "../app/diary.ts";import { editState, petEngine } from "../app/engine.ts";import { age, dayNum, worldState } from "../app/simulation.ts";import { brain, store, ui, view } from "../app/store.ts";import { header, offerThing, stageAges, wakeChance } from "../core.ts";import type { Disturbance } from "../types.ts";import { canRunLocally, isRemote } from "../app/hosting.ts";export function Debug() { const s = store.state, pet = s.pet; const [days, setDays] = useState(() => (age() / DAY).toFixed(2)); const n = petEngine().disturbancesAt(Date.now()) + 1; return ( <fieldset style={{ border: "none", padding: 0, margin: 0, minWidth: 0 }} disabled={!canRunLocally()} > {isRemote() && ( <p class="muted small"> This is the server's last synchronized state. Switch to local mode to use body debug controls. </p> )} <h2>Pet</h2> {pet ? ( <> <p class="muted small">{`brain ${brain.id || "none"} · ${pet.stage}, day ${dayNum()} (${(age() / DAY).toFixed(2)} days old) · offer open: ${s.offer ? offerThing(s.offer) : "none"}`}</p> <div class="pet-row"> {stageAges() .reverse() .map(([stage, ms]) => ( <button key={stage} type="button" class={stage === pet.stage ? "primary" : "text-button"} onClick={() => { setAge(ms + MIN); setDays((age() / DAY).toFixed(2)); }} > {stage} </button> ))} </div> <label for="debug-age">age in days</label> <div class="pet-row"> <input id="debug-age" type="number" min={0} step={0.25} value={days} inputMode="decimal" onInput={(e) => setDays(e.currentTarget.value)} /> <button type="button" class="text-button" onClick={() => setAge(Math.max(0, +days || 0) * DAY)} > set age </button> </div> <div class="debug-grid"> {STATS.map((k) => ( <div class="debug-stat" key={k}> <label for={`debug-${k}`}>{k}</label> <input id={`debug-${k}`} type="range" min={0} max={100} value={Math.round(s.stats[k])} onInput={(e) => { const value = +e.currentTarget.value; editState((state) => { state.stats[k] = value; }); }} /> <span>{Math.round(s.stats[k])}</span> </div> ))} </div> <label class="pet-check"> <input type="checkbox" checked={s.sick} onChange={(e) => { const sick = e.currentTarget.checked; editState((state) => { state.sick = sick; }); }} /> sick </label> <p class="muted small" data-debug-wake=""> {`${s.asleep ? "Asleep" : "Awake"} · next disturbance #${n}: `} {(["talk", "touch", "light"] as Disturbance[]) .map((k) => `${k} ${Math.round(wakeChance(s.stats.energy, k, n) * 100)}%`) .join(" · ")} </p> <p class="muted small"> Write today's page using the log so far, even if it is short. An early page skips bedtime writing; rewrite it later to include new events. </p> <button type="button" class="text-button" data-debug-diary="" disabled={brain.busy || brain.state !== "ready" || !!view.game} onClick={() => writeDiary(s.today.date, true)} > {s.diary.some((e) => e.date === s.today.date) ? "rewrite today's page" : "write today's page"} </button> </> ) : ( <p class="muted small">No pet yet.</p> )} <h2>Context</h2> <p class="muted small"> The last prompts sent to the brain, newest first; what it wrote is highlighted. Only kept until the page reloads. </p> <div class="pet-row"> <button type="button" class="primary" disabled={!ui.debugLog.length} onClick={() => { const d = ui.debugLog.at(-1); if (d) void copyText(d.prompt + d.text); }} > copy latest </button> <button type="button" class="text-button" disabled={!pet} onClick={() => copyText(header("chat", worldState()) + s.chat.map((l) => l + "\n").join("")) } > copy full chat </button> </div> {ui.debugLog .slice() .reverse() .map((d) => ( <details key={d.id}> <summary>{`${d.at.toLocaleTimeString()} · ${d.mode} · ${d.promptTokens} prompt tokens · stopped: ${d.reason}`}</summary> <pre class="debug-pre"> {d.prompt} <span class="debug-out">{d.text}</span> </pre> <button type="button" class="text-button" onClick={() => copyText(d.prompt + d.text)}> copy </button> </details> ))} </fieldset> );}