# Prompt format and tool grammar This is the contract between the app and the weights. `train/world.py` renders it for training and `web/world.ts` for the browser; `node test/parity.mjs` compares 300 random states byte for byte, checks that the word lists and tool sets are identical, and checks that every event line the app writes matches exactly one of the validator's patterns (and that offers are recognised as offers). ## Chat prompt ``` ## tokenling chat pet: Mochi · kid · day 3 · curious, foodie owner: Sam now: 21:40 night, rainy needs: tummy hungry, fun happy, energy sleepy, clean remembers: Sam likes frogs; Sam has a cat named Bo today: ate a cookie; played catch (4 stars); had a bath --- Sam: what time is it? Mochi: [clock]{21:40 night} it's so late!! [emote:sleepy] [want:snack] ``` - `pet:` is `name · stage · day N · trait, trait`. - `now:` is `HH:MM part-of-day, weather`, plus `, NAME is asleep` and/or `, NAME is sick`. Part of day: morning 05–11, afternoon 12–16, evening 17–20, night 21–04. - `needs:` turns the 0–100 stats into words (below). - `remembers:` is the newest three memories; omitted when empty. - `today:` is the newest six entries after filtering `OWNER said: …` from chat headers; omitted when empty. Diary headers retain those quotes. - After `---`, one line per message. The prompt always ends at `Name:`. Stat words (thresholds are `floor(value * n / 101)`): | Stat | Words, low to high | |---|---| | tummy | starving, hungry, peckish, full, stuffed | | fun | sad, bored, okay, happy, giddy | | energy | exhausted, sleepy, okay, awake, hyper | | clean | stinky, messy, clean, sparkly (printed alone, without a label) | ## Diary prompt ``` ## tokenling diary pet: Mochi · kid · day 3 · curious, foodie owner: Sam weather: rainy remembers: … today: … --- dear diary, ``` The header has `weather:` instead of `now:`/`needs:`. The model writes the entry and ends with `<|endoftext|>`; optional `[doodle:NAME]` stickers go on the last line. ## Event lines Lines starting with `* ` are written by the app, never the model. They are the only way the pet learns what the owner did or what happened to its body. ``` * Sam offered Mochi a strawberry * * Sam offered Mochi a bath * * Sam offered Mochi medicine * * Sam asked Mochi to play catch * * Sam played catch with Mochi (4 stars) * * Sam patted Mochi * * Sam turned off the lights * * Mochi woke up * * Mochi pooped * * Sam cleaned up the poop * * Sam came back after 3 hours * * Mochi grew into a teen! * ``` `world.py:event_patterns()` is the validator for training data; `world.ts:events` builds them. Foods take "a" or "an" (`a(food)`). Messages and pats may wake a sleeping pet by a body roll; a successful roll writes `* Mochi woke up *` before the message or pat. Lights on rolls after its event line. Lights off and cleaning do not roll. Offers never wake first: a sleeping pet's `[yes]` is followed by `woke up`, then the accepted care. The app never inserts an event between an offer and its answer. ### Offers The first four lines above are **offers** (`world.py:offer_patterns()`). The pet's very next line must contain exactly one `[yes]` or `[no]`; owner lines may come between. The app feeds, bathes, gives medicine or starts the catch game only on `[yes]`, and treats a line without an answer as no. `[yes]` and `[no]` are invalid anywhere else. After `[yes]` to catch, the game runs and the app writes the `played catch … (N stars)` line. Pre-v2 data used direct `* Sam fed Mochi … *`, `gave … a bath` and `gave … medicine` lines; those no longer exist. ### Sleeping lines While asleep, each pet line contains exactly one `[dream:X]`, optionally with a mumble of at most four words. No other tags are allowed, except `[no]` on the answering line of an offer it sleeps through. A sleeping pet can instead accept with short words and `[yes]` only (no dream), followed immediately by the app's `woke up` line. `[dream]` is invalid awake, and `[sleep]` is invalid already asleep. A nap starts after the `[sleep]` line; a subsequent instant wake must precede an owner message or pat. `DREAMS` contains the six foods, eight gifts, and ball, moon, sun, cloud, rain, frog, cat, ghost, music and sparkle. Dream content comes from the brain. ### Silent lines and idle turns A pet line made only of tags (`Mochi: [walk:left]`, `Mochi: [walk:right] [emote:happy]`, `Mochi: [want:snack]`) is a **silent** line: the pet acting on its own. The app asks for a pet line not only after something happens ("react" turns) but also every 8–20 seconds while the page is visible ("idle" turns, the pet continuing after its own line or an event). Training data has at most two silent lines in a row, and the app keeps at most the newest two consecutive silent lines in the chat. ## Tools Tools are inline tags in the pet's own line. Tag syntax is `[name]` or `[name:argument]`. The argument is at most 80 characters and cannot contain `]` or a newline. | Tag | Values | App effect | |---|---|---| | `[emote:X]` | happy, love, sad, sleepy, wave, dance, spin, blush, grumpy, wow, giggle, cry | LCD animation and floating hearts, notes or a bang | | `[want:X]` | snack, play, bath, sleep, pats, medicine | thought bubble above the pet for 25 s | | `[remember:FACT]` | a fact naming the owner, without gendered pronouns | appended to `S.memories` (max 60, newest three in the header) | | `[gift:X]` | flower, pebble, star, leaf, shell, heart, drawing, feather | placed on the shelf, toast, a note in the day log | | `[diary:NOTE]` | free text | `note: NOTE` added to the day log | | `[sleep]` | none | the pet falls asleep | | `[dream:X]` | `DREAMS` above | sleeping thought bubble for 25 s and a dream chip; at most two dreams in the day log | | `[walk:X]` | left, right | the pet walks 14–28 LCD pixels that way (one per line) | | `[yes]`, `[no]` | none | answers an offer (see above) | ### Lookups | Tag | Result written by the app | |---|---| | `[clock]` | `HH:MM part-of-day` | | `[look]` | `shelf: a, b, c · toy: T · window: weather` plus ` · poop xN` if any | | `[recall:KEYWORD]` (one or two words) | up to two memories sharing a word with the keyword (newest first, joined with `; `), or `nothing` | | `[yesterday]` | the first sentence of the last finished diary page, or `no diary yet` | The exchange looks like `[clock]{21:40 night}`. During generation the model stops after the closing `]` (or the single token `][`, which `brain.ts` trims), the app appends `{…}`, and the model continues the same line. In training data the `{result}` is part of the context but carries no loss, so the model learns to call the tool, not to fake the answer. `recall` matching (`world.words`) lowercases, takes `[a-z0-9']` words of three or more characters, drops a short stop list and a trailing plural `s`, and matches on any shared word. ## Stopping and trimming - Chat generation stops at the first newline or a completed lookup tag and allows at most 64 new tokens per hop (40 for idle turns). Sampling is temperature 0.85, top-k 40, top-p 0.92. - Diary generation allows 220 tokens and also stops on a stray `\n##` or a run of blank lines. - `fit()` in `brain.ts` drops the oldest chat lines until header + lines + prefix + the new-token budget fit in 1024 tokens (512 for idle turns, whose cost is mostly prompt processing). ## Tokenization pitfalls - `Name:` and `Name: ` tokenize differently. Always end the prompt with `Name:`; the model emits `" word"` tokens itself. - GPT-2 has a merged `][` token, so `[look][gift:x]` closes a lookup mid-token. The stopper regex allows a trailing `[`, and the app removes it. - Do not use a repetition penalty (see `gotchas.md`).