a tiny pet with a GPT-2 brain, living in your browser
tokenling docs prompt-format.md
7.8 kB
Markdown
at main

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).