Something went wrong. Try again.
a tiny pet with a GPT-2 brain, living in your browser
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187"""Shared tokenling world model: state headers, event strings, tool results.
The shared rules (core/src/world.rs) build exactly the same strings. Keep the two insync; the fine-tuned model only understands what it saw in training."""import randomimport re
STAGES = ["baby", "kid", "teen", "adult"]PERSONALITIES = [ "bouncy", "shy", "curious", "sleepy", "dramatic", "foodie", "silly", "sweet", "grumpy", "brave", "artsy", "dreamy", "chatty", "clingy", "sassy", "polite",]FOODS = ["strawberry", "cookie", "rice ball", "carrot", "pudding", "apple"]GIFTS = ["flower", "pebble", "star", "leaf", "shell", "heart", "drawing", "feather"]DREAMS = FOODS + GIFTS + ["ball", "moon", "sun", "cloud", "rain", "frog", "cat", "ghost", "music", "sparkle"]EMOTES = ["happy", "love", "sad", "sleepy", "wave", "dance", "spin", "blush", "grumpy", "wow", "giggle", "cry"]WANTS = ["snack", "play", "bath", "sleep", "pats", "medicine"]WALKS = ["left", "right"]DOODLES = ["heart", "star", "strawberry", "cloud", "moon", "sun", "flower", "cookie", "frog", "rain", "music", "cat", "ghost", "sparkle"]WEATHER_DAY = ["sunny", "cloudy", "rainy", "windy", "snowy", "foggy"]WEATHER_NIGHT = ["clear", "cloudy", "rainy", "windy", "snowy", "starry"]TOYS = ["ball", "yarn", "tiny drum", "rubber duck", "blocks"]
# Tool grammar. Effects change the app; lookups get a {result} appended by the# app before the model continues the same line.EFFECT_TOOLS = {"emote", "want", "remember", "gift", "sleep", "dream", "diary", "walk", "yes", "no"}# [yes]/[no] answer an offer event line and may appear nowhere else.ANSWER_TOOLS = {"yes", "no"}LOOKUP_TOOLS = {"clock", "recall", "look", "yesterday"}TAG_RE = re.compile(r"\[(\w+)(?::([^\]\n]{1,80}))?\](\{[^}\n]*\})?")
def part_of_day(h): if 5 <= h < 12: return "morning" if 12 <= h < 17: return "afternoon" if 17 <= h < 21: return "evening" return "night"
def level_words(s): """Stats are 0-100 in the app; the model sees words.""" def pick(v, words): return words[min(len(words) - 1, v * len(words) // 101)] return { "tummy": pick(s["tummy"], ["starving", "hungry", "peckish", "full", "stuffed"]), "fun": pick(s["fun"], ["sad", "bored", "okay", "happy", "giddy"]), "energy": pick(s["energy"], ["exhausted", "sleepy", "okay", "awake", "hyper"]), "clean": pick(s["clean"], ["stinky", "messy", "clean", "sparkly"]), }
def header(kind, st): """kind: 'chat' or 'diary'. Must match header() in core/src/world.rs.""" w = level_words(st["stats"]) lines = [f"## tokenling {kind}", f"pet: {st['pet']} · {st['stage']} · day {st['day']} · {', '.join(st['traits'])}", f"owner: {st['owner']}"] if kind == "chat": now = f"now: {st['hh']:02d}:{st['mm']:02d} {part_of_day(st['hh'])}, {st['weather']}" if st.get("asleep"): now += f", {st['pet']} is asleep" if st.get("sick"): now += f", {st['pet']} is sick" lines.append(now) lines.append(f"needs: tummy {w['tummy']}, fun {w['fun']}, energy {w['energy']}, {w['clean']}") else: lines.append(f"weather: {st['weather']}") if st["memories"]: lines.append("remembers: " + "; ".join(st["memories"][-3:])) today = [t for t in st["today"] if kind != "chat" or not t.startswith(st["owner"] + " said: ")] if today: lines.append("today: " + "; ".join(today[-6:])) lines.append("---") return "\n".join(lines) + "\n"
def words(text): out = set() for wd in re.findall(r"[a-z0-9']+", text.lower()): wd = wd.strip("'") if len(wd) < 3 or wd in STOP: continue if wd.endswith("s") and len(wd) > 3: wd = wd[:-1] out.add(wd) return out
STOP = {"the", "and", "you", "your", "are", "was", "has", "have", "for", "with", "what", "who", "does", "did", "about", "that", "this", "like", "likes", "she", "her", "his", "him", "they", "them", "named", "name", "is"}
def recall(memories, query): q = words(query) hits = [m for m in reversed(memories) if q & words(m)] return "; ".join(hits[:2]) if hits else "nothing"
def look(st): shelf = ", ".join(st["shelf"][-4:]) if st["shelf"] else "empty" out = f"shelf: {shelf} · toy: {st['toy']} · window: {st['weather']}" if st["poop"]: out += f" · poop x{st['poop']}" return out
def clock(st): return f"{st['hh']:02d}:{st['mm']:02d} {part_of_day(st['hh'])}"
def tool_result(st, name, arg): if name == "clock": return clock(st) if name == "look": return look(st) if name == "recall": return recall(st["memories"], arg or "") if name == "yesterday": return st["yesterday"] or "no diary yet" return None
# Event lines inserted into chat by the app. Python regexes accept exactly the# formats core/src/world.rs emits. Offers ask the pet to decide: its next line must# answer with [yes] or [no].def event_patterns(owner, pet): o, p = re.escape(owner), re.escape(pet) foods = "|".join(FOODS) return [ rf"\* {o} offered {p} (a |an )({foods}) \*", rf"\* {o} offered {p} (a bath|medicine) \*", rf"\* {o} asked {p} to play catch \*", rf"\* {o} patted {p} \*", rf"\* {o} played catch with {p} \([0-9]+ stars?\) \*", rf"\* {o} turned (off|on) the lights \*", rf"\* {p} woke up \*", rf"\* {p} pooped \*", rf"\* {o} cleaned up the poop \*", rf"\* {o} came back after [0-9]+ (minutes|hours|days?) \*", rf"\* {p} grew into a (kid|teen|adult)! \*", ]
def offer_patterns(owner, pet): """The subset of event lines that the pet must answer with [yes] or [no].""" o, p = re.escape(owner), re.escape(pet) return [rf"\* {o} offered {p} .+ \*", rf"\* {o} asked {p} to play catch \*"]
def a(food): return ("an " if food[0] in "aeiou" else "a ") + food
def dream_thing(item): return item if item in ("music", "rain") else "the " + item if item in ("moon", "sun") else a(item)
def wake_chance(energy, kind, n=1): """Body disturbance roll; n counts this disturbance (reset after 5 min).""" return min(0.9, max(0, {"talk": 0.03, "touch": 0.10, "light": 0.20}[kind] + 0.007 * energy + 0.12 * (n - 1)))
def event_examples(owner, pet): f = random.choice(FOODS) return [ f"* {owner} offered {pet} {a(f)} *", f"* {owner} offered {pet} a bath *", f"* {owner} offered {pet} medicine *", f"* {owner} asked {pet} to play catch *", f"* {owner} played catch with {pet} ({random.randint(0, 9)} stars) *", f"* {owner} patted {pet} *", f"* {owner} turned off the lights *", f"* {owner} turned on the lights *", f"* {pet} woke up *", f"* {pet} pooped *", f"* {owner} cleaned up the poop *", f"* {owner} came back after {random.choice(['20 minutes', '3 hours', '2 days'])} *", ]