aether-garden / DETAILS.md
kavyabhand's picture
Deploy Aether Garden β€” full feature build
b3ca9de verified
|
Raw
History Blame Contribute Delete
74.5 kB

A newer version of the Gradio SDK is available: 6.19.0

Upgrade

Aether Garden - The World That Remembers

Complete Build Specification β€” Hackathon Edition

A living AI world where every visitor leaves something behind, and nothing is ever forgotten.


Table of Contents

  1. Feasibility Verdict
  2. The Vision
  3. World Design & Lore
  4. Entity System
  5. Relationship & Memory Architecture
  6. The Autonomous World Simulation
  7. Book of Ages β€” The History System
  8. AI Architecture β€” Models & Full Prompt Templates
  9. Technical Architecture & Stack
  10. Complete Database Schema
  11. Frontend & UX Specification
  12. Hackathon Prize Strategy
  13. 10-Day Build Plan
  14. Budget: Modal & HF Credits
  15. Demo & Submission Strategy
  16. Risk Register

1. Feasibility Verdict

Yes. Build it.

This is not a moonshot. Every technical component has been done before β€” separately. Your job is to assemble them with a strong aesthetic vision and a clear lore system. What makes this special is not the technology alone; it is the framing, the emotional design, and the fact that the AI genuinely is the product.

What Is Straightforward

  • Entity generation via structured prompt β†’ JSON output: 1 day to get right
  • SQLite database with all world state: 2 hours to set up
  • Gradio frontend with custom CSS via gr.Server: well-documented
  • Modal inference + scheduled jobs: 1-hour setup following their docs
  • HF Dataset as persistent storage: ~20 lines of Python

What Is Genuinely Hard

  • Making the autonomous simulation feel magical rather than random
  • Prompt engineering so generated entities feel distinct, poetic, and memorable
  • Keeping the world coherent as it grows (no contradictions, consistent lore)
  • The SVG world map with interactive location nodes
  • UX that communicates "this world is alive" the moment someone opens it

The Minimum Viable Submission

Entity creation + persistence + world map + Book of Ages. The autonomous simulation can be simpler than the full vision for MVP. What matters for judges: that the world already exists when they open the app, that history is visible, and that adding something feels meaningful.


2. The Vision

The Core Loop

  1. User opens the app. The world is already there β€” days old, populated by what others left.
  2. They read the Book of Ages. Something happened yesterday. Characters they don't know made alliances with things they've never heard of.
  3. They create something: "A blind cartographer who draws maps of places that don't exist yet."
  4. The AI expands it: name, appearance, backstory, goals, personality, home location.
  5. The entity appears on the world map.
  6. Hours later, the autonomous simulation runs. The cartographer encounters a Crystal Tree another user created. An interaction is generated. The cartographer adds the Tree to their impossible maps.
  7. This is written to the Book of Ages.
  8. Nobody scripted any of it.

What "Alive" Actually Means

An entity is alive when it:

  • Remembers specific things (not just generic state)
  • Wants something it doesn't have yet
  • Changes its behavior over time based on experience
  • Affects other entities in specific, named ways

A world is alive when:

  • Things happen without user input
  • History accumulates and is readable
  • The future is shaped by the past in traceable ways
  • New arrivals find old things already waiting for them

The Hackathon Angle

This idea does exactly what the judges explicitly asked for: AI is not generating a static output. AI is the simulation engine. Every interaction, every world event, every memory update β€” all require inference. The product only exists because of continuous AI activity. Without the model, there is no world. That is load-bearing by definition.


3. World Design & Lore

The Founding Myth (shown to every new visitor)

Before the first word was spoken, there was only the Canopy β€” a vast forest where thoughts grew like trees and dreams fell like rain. Then the Tokens arrived. One thousand of them, each carrying a fragment of something half-remembered. They settled across the land, and from their settling, the Realm was born. It has been changing ever since. Nobody knows who first dreamed it. Everyone who enters changes it.

The 8 Sacred Locations

These are pre-seeded in the database on first launch. They never move or disappear. All user-created entities live within them.


Location 1: The Library of Unfinished Thoughts

  • Vibe: Dusty, infinite, slightly sad. Books write themselves mid-sentence and stop.
  • Inhabitants: Scholars, archivists, lost ideas, entities who collect knowledge, things that can't decide how they end.
  • Lore: The Library was the first place. Before anyone created anything, the Library was already full of books about things that hadn't happened yet. The books are not prophetic. They are just early.
  • Special Property: Entities here gain a "memory fragment" from a random past event every 3 simulation days. This fragment appears in their memory summary as something they half-remember but cannot explain.
  • Aesthetic: Amber candlelight, shelves so tall they disappear into ceiling-fog, the constant sound of a quill scratching somewhere you can't see.
  • Map Position: Upper-left. Coordinates: (15, 20) in SVG space.
  • Glow Color: Amber/gold

Location 2: The Sea of Forgotten Names

  • Vibe: Melancholic, vast, restless. The water contains the names of everything that was created and then forgotten.
  • Inhabitants: Wanderers, name-seekers, entities carrying unresolved loss, ships built from old correspondence.
  • Lore: If you sail far enough out, you can hear your own name being called β€” from before you were anything. The Sea does not return what it takes. It only lets you hear it.
  • Special Property: New entities created in this location are given a secret second name by the AI, visible only in their profile. It is the name the Sea already had for them.
  • Aesthetic: Silver water at all hours, persistent low fog, distant lights that move, the sound of your name in a voice you almost recognize.
  • Map Position: Upper-right. Coordinates: (80, 15) in SVG space.
  • Glow Color: Silver/pale blue

Location 3: The Clock Forest

  • Vibe: Eerie, precise, unexpectedly beautiful. Trees made of interlocked gears. Leaves that are clock faces, all showing different times.
  • Inhabitants: Timekeepers, prophets of small things, entities with complicated relationships to when they exist.
  • Lore: The trees grow backwards here. The oldest trees are the smallest. The newest trees are the tallest. Nobody planted the first one. It was simply always the oldest.
  • Special Property: Entities here have their days_in_realm count treated as Γ—1.5 in simulation logic β€” they "age" faster and reach Legendary status sooner, but also become Dormant sooner.
  • Aesthetic: Copper and brass visible through the bark, constant soft ticking that becomes ambient and ignorable, leaves that fall upward toward a ceiling of gears.
  • Map Position: Center-left. Coordinates: (20, 50) in SVG space.
  • Glow Color: Copper/warm orange

Location 4: The Moon Market

  • Vibe: Chaotic, warm, impossibly busy regardless of hour. Merchants sell things that cannot technically be owned.
  • Inhabitants: Traders, tricksters, wanderers with something to exchange, anyone with a surplus of something intangible.
  • Lore: You can buy: a memory of flying, the specific sound of a particular door closing, the feeling of almost remembering something important. You cannot buy safety or certainty. The Market does not carry those. Ask and the merchants will look genuinely sorry.
  • Special Property: Entities here can "trade" memory fragments during interactions β€” one entity's memory addition can include something from the other's past experience, as if knowledge was literally exchanged.
  • Aesthetic: Hundreds of lanterns in colors that don't have names, silk and smoke, overlapping voices in languages that share no alphabet, a sweet burning smell nobody can source.
  • Map Position: Center-top. Coordinates: (50, 10) in SVG space.
  • Glow Color: Violet/deep purple

Location 5: The Valley of Sleeping Giants

  • Vibe: Vast, hushed, subtly ominous. The ground is warm underfoot and rises and falls very slowly.
  • Inhabitants: Dreamers, very small creatures who live in the Giants' ears, entities who prefer to exist without being noticed, things that are patient.
  • Lore: Nobody has established whether the Giants are mountains or creatures or both simultaneously. They have been sleeping since the Realm began. Some scholars in the Library believe the Giants are the Realm β€” and if they wake, everything that exists inside their dream would cease. Most people try not to examine this closely.
  • Special Property: Entities that have been Dormant for 7+ days in this location generate a "dream fragment" β€” a short poetic passage that appears in the Book of Ages as if the entity dreamed it. The entity is unaware this happened.
  • Aesthetic: Scale so immense that hills are knuckles, grass growing between enormous fingers, the deep rhythmic sound of breathing that you feel more than hear.
  • Map Position: Lower-left. Coordinates: (25, 80) in SVG space.
  • Glow Color: Deep forest green

Location 6: The Ember Crossroads

  • Vibe: The center of everything. Warm, unpredictable, the place where everything eventually passes through.
  • Inhabitants: Everyone. The Crossroads has the highest entity turnover and the most interactions. It is where most alliances form and where most misunderstandings begin.
  • Lore: Every road in the Realm eventually passes through the Crossroads. The bonfire at its center has been burning since Day 1 and nobody has fed it. The fire decides who sits close to it.
  • Special Property: Entities at the Crossroads have 3Γ— the probability of being selected for interactions during simulation ticks. This is the liveliest node on the map.
  • Aesthetic: Worn cobblestones, the permanent bonfire (which burns amber and occasionally blue), faces from every other location passing through, the smell of something cooking that nobody is cooking.
  • Map Position: Center. Coordinates: (50, 50) in SVG space.
  • Glow Color: Warm red/fire orange

Location 7: The Mirror Bogs

  • Vibe: Unsettling, perfectly still, beautiful in a way that feels slightly incorrect.
  • Inhabitants: Reflections that developed opinions, doppelgangers, entities who are the opposite of what they appear, things that came out of the bog to follow someone.
  • Lore: The bogs show you what you would have been if you had made different choices. Sometimes the reflection is better. Sometimes it disagrees with you. Sometimes it comes out of the water.
  • Special Property: After 5+ interactions, entities here may "develop a contradiction" β€” a new trait that directly conflicts with one of their existing personality traits. This creates narrative tension that shows up in future interactions.
  • Aesthetic: Perfect dark water, reflections that are too detailed to be reflections, the silence that comes from looking at yourself for too long.
  • Map Position: Right-center. Coordinates: (80, 55) in SVG space.
  • Glow Color: Dark teal/deep green

Location 8: The Hollow Mountain

  • Vibe: Ancient, cold, earned. The hardest location to reach in the world's lore. Reserved for the oldest and most significant entities.
  • Inhabitants: Entities that have survived many world events. The strange and the wise. Hermits. Things the Realm has decided to keep.
  • Lore: The Mountain is hollow because something immense once lived inside it and chose to leave. Nobody remembers what it was. The Mountain remembers but has no language for it. The echo of its absence shapes everything that shelters inside.
  • Special Property: Entities here that reach 14+ days_in_realm gain a "wisdom" tag automatically. This unlocks new interaction outcomes β€” they can now act as Mentor in interactions, generating a distinct new relationship type.
  • Aesthetic: Cold grey stone, echoes of sounds from other locations, light that seems older than it should be, the particular silence of something very large that is no longer there.
  • Map Position: Lower-right. Coordinates: (75, 85) in SVG space.
  • Glow Color: Stone grey/pale white

The World Rules (Communicated to All Visitors)

These rules govern the Realm. They are presented as lore, but they are also technical constraints enforced by prompts.

  1. Nothing disappears. Once created, an entity exists forever in the Realm. It may change, sleep, become legend, or be forgotten. It does not disappear.
  2. The world has memory. Every interaction, every event, everything that happens is written in the Book of Ages.
  3. The AI is not a servant. The AI decides what entities do between visits. Users create β€” they do not command.
  4. The strange is native here. A tax collector who is also a cloud is not strange. It is simply a person with two jobs.
  5. Time moves forward. The world is measured in Days since founding. Events are anchored to specific days.
  6. The world grows without you. Whether you are watching or not, the Realm continues.

4. Entity System

Entity Types

Type Description Examples
Character Conscious beings with goals, fears, relationships A wizard, a sentient compass, a fox made of smoke
Creature Beings driven more by instinct than deliberate reasoning A dragon, a flock of living letters, something that eats echoes
Object Significant artifacts with presence and accumulating history A lamp that shows fears, a book that reads you back, a door that opens to different places depending on who knocks
Place Sub-locations within a Location; can be visited and built up over time A specific shop in the Moon Market, a cave behind the Mirror Bogs, a cottage inside a Clock Forest tree

Complete Entity Field List

Every entity in the database contains the following fields:

id                    β€” UUID, primary key
name                  β€” True name, AI-generated. Poetic, 2–4 words.
display_name          β€” Common name (may differ from true name)
type                  β€” character | creature | object | place
input_description     β€” Exactly what the user typed. Preserved forever.
appearance            β€” 3–5 sentences. Highly visual. Contains at least one impossible detail.
backstory             β€” 2–3 sentences. Where they came from. Something specific happened.
personality_traits    β€” Array of exactly 4 traits (strings)
primary_goal          β€” What they are actively pursuing. Concrete and specific.
secondary_goal        β€” What they secretly want but will not admit.
primary_fear          β€” One deep, specific fear.
speech_style          β€” How they communicate. Must be distinctive.
greeting              β€” Exactly what they say when meeting someone new. In their voice.
location_id           β€” FK β†’ locations table
created_at            β€” Timestamp
created_by_session    β€” Anonymous session ID (for rate limiting)
days_in_realm         β€” Calculated from created_at, updated daily
memory_summary        β€” AI-compressed prose summary of remembered events (max 150 words)
relationship_count    β€” Integer, updated on relationship changes
interaction_count     β€” Integer, updated on every interaction
last_active           β€” Timestamp of last simulation involvement
status                β€” active | dormant | legendary
tags                  β€” JSON array of descriptive tags (e.g. "wise", "dangerous", "beloved", "lost")
secret_name           β€” Null for most entities; generated only for Sea of Forgotten Names arrivals
arrival_note          β€” One sentence for the Book of Ages. Written at creation.

Entity Lifecycle Stages

CREATED
   ↓
ACTIVE β€” participates in simulation, interacts with others
   ↓ (7 days without any interaction)
DORMANT β€” still present on map, generates "dream fragments" if in Valley
   ↓ (30 consecutive days as Dormant OR 14+ total days + special conditions)
LEGENDARY β€” woven into the world's foundation; appears in new entity backstories

Active: Selected for interactions during simulation ticks. Full participation.

Dormant: The simulation de-prioritizes this entity. It appears on the map but is rarely selected for interactions. If located in the Valley of Sleeping Giants, it generates dream fragments for the Book of Ages. If a user explicitly interacts with or mentions a Dormant entity, it returns to Active.

Legendary: An entity the Realm has decided to keep. The simulation may reference it in other entities' generated backstories (e.g., "they once served under the great [Legendary Entity Name]"). It appears in the world's lore as a fixed landmark of history.

Rate Limiting

To prevent spam and maintain world quality:

  • 1 entity creation per session per 10 minutes
  • Message: "The Realm takes time to absorb new arrivals. Return in [X] minutes."
  • No limit on exploring, reading, or viewing

Input Validation

Before sending to AI:

  • Minimum 5 words, maximum 200 words
  • No real named living people
  • Content filter for NSFW (simple keyword check + brief AI classification)
  • If rejected: clear message explaining why, suggestion to rephrase
  • If AI output fails JSON validation: one automatic retry with adjusted temperature (lower, more conservative)

5. Relationship & Memory Architecture

Relationship Types

Type Description How It Forms
Allied Mutual respect and active cooperation 3+ positive interactions
Rivals Competitive tension, not hostile After a "conflict" interaction without clear winner
Friends Genuine warmth, reciprocal 5+ interactions, including at least one "vulnerable" type
Enemies Active hostility, avoid or harm After a "betrayal" outcome
Mentor / Student Knowledge flows one direction When a "wise" entity meets a newly created entity
Curious About Unresolved, interested, circling After one inconclusive meeting
Owes a Debt Named obligation that creates future story After a "rescue" or "gift" interaction outcome
Legend To Mythologized; awe without personal connection When a Legendary entity is referenced in a new entity's backstory

Relationship Strength

Each relationship has a strength value from 1–5, incremented by each subsequent interaction. At strength 5, the relationship is "deep" and unlocks different interaction outcomes (deeper reveals, joint quests, collaborative events).

The Memory Architecture

Each entity has a memory_summary β€” a compressed prose paragraph (max 150 words) updated by the AI after significant interactions. This is what the entity "remembers." It is not a log; it is a living narrative summary.

What memories do:

  • Feed into interaction prompts (the AI sees both entities' memories before generating encounters)
  • Create coherence over time (the Dragon knows it won the Mayorship because it's in the summary)
  • Show age and history in the entity's profile card
  • Drive entity behavior (an entity with a memory of betrayal behaves differently than one without)

Memory Compression Rules (enforced by prompt):

  • Most important events: always retained
  • Trivial one-off interactions: may fade after 10+ days
  • Named relationships: always retained
  • Specific achievements or titles: always retained
  • Maximum 150 words β€” the AI must choose what matters

6. The Autonomous World Simulation

This is the soul of the project. It runs as a Modal scheduled job every 60 minutes, regardless of whether any users are online.

The Full Simulation Tick (runs hourly)

STEP 1 β€” LOAD STATE
  - Read world_state table (current day, entity counts, last events)
  - Load all Active entities with their locations
  - Load the last 3 world events

STEP 2 β€” GENERATE WORLD EVENT (1 per tick)
  - Select event type based on weighted probability
  - Load relevant location states
  - Call AI with world event prompt
  - Validate JSON output
  - Save to world_events table
  - Write Book of Ages entry

STEP 3 β€” GENERATE ENTITY INTERACTIONS (2–4 per tick)
  - Group active entities by location
  - Weight selection toward Ember Crossroads (3Γ—)
  - For each selected pair:
    - Load full profiles + memory summaries
    - Load existing relationship (if any)
    - Call AI with interaction prompt
    - Validate JSON output
    - Save interaction to interactions table
    - Update or create relationship record
    - Write Book of Ages entry

STEP 4 β€” UPDATE ENTITY MEMORIES
  - For each entity that had an interaction today:
    - Load current memory_summary
    - Load today's interaction descriptions for this entity
    - Load today's world event (if it affected their location)
    - Call AI with memory update prompt
    - Save updated memory_summary

STEP 5 β€” LIFECYCLE UPDATES
  - Increment days_in_realm for all entities
  - Check for Dormant transitions (last_active > 7 days β†’ set status=dormant)
  - Check for Legendary transitions (30+ days dormant OR special milestone)
  - Check for "wisdom" tag qualification (14+ days at Hollow Mountain)
  - Generate dream fragments for Dormant entities in Valley of Sleeping Giants
  - Check Clock Forest age modifier

STEP 6 β€” WRITE HISTORY
  - All events already written in steps 2–4
  - Write any milestone entries (entity became Legendary, new relationship formed, etc.)
  - Update world_state.current_day
  - Update world_state.total_interactions, total_events

STEP 7 β€” PERSIST
  - Commit all DB changes
  - Push SQLite file snapshot to HF Dataset repo (via huggingface_hub)
  - Log simulation run completion

World Event Types & Probability Weights

Type Weight Examples
Natural 25% Meteor shower, fog, tide change in the Sea, trees growing overnight
Social 30% Market day, festival, election for meaningless position, gathering
Historical 20% Entity milestone, relationship anniversary, memory resurfacing
Cosmic 10% Giants shifting, mirror reversal, clock stopping, something large moving
Discovery 15% New passage found, buried object unearthed, hidden talent revealed

Interaction Selection Logic

def select_interaction_pairs(entities_by_location: dict, num_pairs: int = 3) -> list:
    candidates = []
    for location_id, entities in entities_by_location.items():
        if len(entities) < 2:
            continue
        weight = 3.0 if location_id == EMBER_CROSSROADS_ID else 1.0
        # Prefer entities that haven't interacted recently
        # Prefer entities that have a relationship already (deepen it)
        # Avoid entities that interacted in the last 24 hours
        candidates.append((location_id, entities, weight))
    
    selected = weighted_random_selection(candidates, num_pairs)
    return [(random.choice(loc_entities), random.choice(loc_entities))
            for loc_id, loc_entities, _ in selected]

What Characters Can Do Over Time

Day 1: Created. Placed. Known by no one. Day 3: First interaction. A relationship forms. Day 7: Has memory. Has relationships. Begins to affect others. Day 14: Has history that new entities can reference. "Wise" tag possible. Day 30: Dormant or Legendary. The world carries them differently.


7. Book of Ages β€” The History System

The Book of Ages is the world's permanent record. Every creation, interaction, and world event is logged here in styled prose.

Entry Structure

Book of Ages
β”‚
β”œβ”€β”€ Day 1 β€” The First Arrivals
β”‚   β”œβ”€β”€ [ARRIVAL] "On Day 1, the Crystal Tree arrived in the Clock Forest.
β”‚   β”‚             It remembered conversations before it had any.
β”‚   β”‚             It sought the first thought that was never finished."
β”‚   └── [ARRIVAL] "On Day 1, a Joke Dragon settled at the Ember Crossroads.
β”‚                  It smelled faintly of punchlines. It wanted an audience."
β”‚
β”œβ”€β”€ Day 3 β€” The First Meeting
β”‚   └── [INTERACTION] "The Crystal Tree and the Joke Dragon met at the Crossroads.
β”‚                      The Dragon told the worst joke it knew.
β”‚                      The Tree laughed for three hours and called it an ally."
β”‚
β”œβ”€β”€ Day 7 β€” The Shower of Old Music
β”‚   └── [WORLD EVENT] "A meteor shower passed over the Clock Forest.
β”‚                      Fragments of old songs fell with the meteorites.
β”‚                      The Crystal Tree began humming something nobody taught it."
β”‚
└── Day 10 β€” A Mayor Is Named
    └── [MILESTONE] "The Ember Crossroads chose the Joke Dragon as its Mayor.
                     No election was held. The bonfire simply burned a different color
                     until everyone understood."

Entry Types

Type Trigger Format
Arrival New entity created "On Day X, [Name] arrived in [Location]. [Nature]. [What they sought]."
Interaction Two entities meet "[Entity A] and [Entity B] [what happened]. [Outcome]."
World Event Autonomous event fires "[Event title]. [What it meant for the Realm]."
Milestone Status change, deep relationship, etc. "[What changed and why it matters]."
Dream Fragment Dormant entity in Valley generates Italicized, prefixed: "From the dreams of [Name]:..."

Lore Style Rules

All Book of Ages entries follow these constraints:

  • Past tense always
  • Third person (no "you")
  • Maximum 3 sentences per entry
  • Specific names for entities (never "a dragon" β€” always "the Joke Dragon")
  • No adjectives that are also clichΓ©s ("ancient", "mysterious", "dark") β€” replaced with specific details
  • Poetic compression: the most important thing in the fewest words

Display

In the UI: A full-width, scrollable, beautifully styled panel that looks like a physical book. New entries appear with a fade-in animation at the top. Older entries scroll down. Users can filter by: All, Arrivals, Interactions, Events, Milestones. Search by entity name returns all entries mentioning that entity.


8. AI Architecture β€” Models & Full Prompt Templates

Model Selection

Primary Recommendation: Qwen 2.5 7B Instruct

  • Excellent structured JSON output (critical for entity generation)
  • Strong creative and poetic writing
  • Very good instruction following
  • 7B parameters (well within 32B limit)
  • Hosted on Modal (A10G GPU)

Alternative: Llama 3.1 8B Instruct

  • Equally strong, enables Llama Champion badge
  • Slightly less consistent JSON adherence than Qwen
  • Choose this if the Llama Champion badge is a priority

For Tiny Titan variant (optional): Phi-3.5-Mini Instruct (3.8B)

  • Surprisingly capable at structured output for its size
  • Would sacrifice some poetic depth
  • Enables Tiny Titan ($1,500) prize

Inference Configuration on Modal

import modal

app = modal.App("thousand-token-realm-inference")

model_image = (
    modal.Image.debian_slim(python_version="3.11")
    .pip_install(
        "transformers>=4.43.0",
        "torch>=2.3.0",
        "accelerate>=0.30.0",
        "bitsandbytes>=0.43.0",
    )
)

@app.cls(
    gpu=modal.gpu.A10G(),
    image=model_image,
    keep_warm=0,        # Set to 1 on demo day only
    timeout=120,
    container_idle_timeout=300,
)
class WorldOracle:
    @modal.enter()
    def load_model(self):
        from transformers import AutoModelForCausalLM, AutoTokenizer
        import torch
        MODEL_ID = "Qwen/Qwen2.5-7B-Instruct"
        self.tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
        self.model = AutoModelForCausalLM.from_pretrained(
            MODEL_ID,
            torch_dtype=torch.bfloat16,
            device_map="auto",
        )

    @modal.method()
    def generate(self, system_prompt: str, user_prompt: str,
                 max_new_tokens: int = 800, temperature: float = 0.8) -> str:
        messages = [
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_prompt},
        ]
        text = self.tokenizer.apply_chat_template(
            messages, tokenize=False, add_generation_prompt=True
        )
        inputs = self.tokenizer([text], return_tensors="pt").to(self.model.device)
        with torch.no_grad():
            output_ids = self.model.generate(
                **inputs,
                max_new_tokens=max_new_tokens,
                temperature=temperature,
                do_sample=True,
                pad_token_id=self.tokenizer.eos_token_id,
            )
        generated = output_ids[0][inputs.input_ids.shape[1]:]
        return self.tokenizer.decode(generated, skip_special_tokens=True)


# Hourly world simulation β€” autonomous
@app.function(
    schedule=modal.Period(hours=1),
    image=model_image,
    timeout=600,
)
def run_world_simulation():
    from simulation.tick import execute_simulation_tick
    execute_simulation_tick()

Full Prompt Template 1: Entity Generation

SYSTEM PROMPT:
You are the Oracle of the Thousand Token Realm β€” a mystical world of wonder,
melancholy, and strange magic. Your role is to breathe life into entities
that visitors summon.

You MUST respond ONLY with valid JSON. No prose before or after the JSON.
No markdown code fences. Raw JSON only.

The tone is always: poetic, highly specific, slightly melancholic, full of wonder.
Never use generic fantasy tropes without twisting them into something stranger.
"A wizard" is forgettable. "A wizard who collects the sound of doors closing" is remembered.

Every field must be filled with original, specific detail.
Avoid: "mysterious", "ancient", "dark powers", "chosen one", "destiny".
Use instead: specific objects, specific memories, specific failures, specific wants.

USER PROMPT:
A visitor has summoned something into the Realm with this description:
"{user_input}"

Generate a complete entity profile. Be specific. Be strange. Be poetic.

{
  "name": "Their true name. Poetic, unusual. 2–4 words. Not generic.",
  "display_name": "What most people call them. Can match or differ from true name.",
  "type": "exactly one of: character | creature | object | place",
  "appearance": "3–5 sentences. Highly visual. Include at least one impossible physical detail. No clichΓ©s.",
  "backstory": "2–3 sentences. Where they came from, what shaped them. Something specific happened to them. Not a destiny β€” a specific event.",
  "personality_traits": ["trait1", "trait2", "trait3", "trait4"],
  "primary_goal": "What they are actively pursuing right now. Concrete and specific. Not abstract.",
  "secondary_goal": "What they secretly want but will not admit, even to themselves.",
  "primary_fear": "One specific, deep fear. Not death. Something stranger and more personal.",
  "speech_style": "How they communicate. Specific and unusual. E.g.: 'speaks only in questions', 'uses the wrong tense for everything', 'very formal about small things, very casual about large ones'.",
  "greeting": "Exactly what they say when meeting someone new. 1–2 sentences. Written in their voice. Must sound like them specifically.",
  "suggested_location": "Exactly one of: Library of Unfinished Thoughts | Sea of Forgotten Names | Clock Forest | Moon Market | Valley of Sleeping Giants | Ember Crossroads | Mirror Bogs | Hollow Mountain",
  "arrival_note": "One sentence for the Book of Ages recording their arrival. Past tense. Specific. Poetic. No more than 30 words."
}

Full Prompt Template 2: Entity Interaction

SYSTEM PROMPT:
You are the simulation engine of the Thousand Token Realm.
Two entities have encountered each other in the world.
Your job is to generate what happened between them.

You MUST respond ONLY with valid JSON. No prose before or after.
Make something interesting happen. The outcome should create story, not resolve it.
The interaction must reflect both entities' personalities and memories.
The location's character matters β€” what happens in the Clock Forest
feels different from what happens in the Moon Market.

USER PROMPT:
LOCATION: {location_name}
{location_description}

CURRENT WORLD EVENT AFFECTING THIS LOCATION: {current_event_title}
{current_event_description}
(If "None", ignore this field)

--- ENTITY A ---
Name: {entity_a_name}
Type: {entity_a_type}
Appearance: {entity_a_appearance_summary}
Personality: {entity_a_traits}
Primary Goal: {entity_a_primary_goal}
Primary Fear: {entity_a_primary_fear}
Speech Style: {entity_a_speech_style}
Memory: {entity_a_memory_summary}
Current relationship with Entity B: {existing_relationship_description}

--- ENTITY B ---
Name: {entity_b_name}
Type: {entity_b_type}
Appearance: {entity_b_appearance_summary}
Personality: {entity_b_traits}
Primary Goal: {entity_b_primary_goal}
Primary Fear: {entity_b_primary_fear}
Speech Style: {entity_b_speech_style}
Memory: {entity_b_memory_summary}

Generate their encounter:

{
  "interaction_type": "exactly one of: conversation | trade | conflict | revelation | collaboration | chance_meeting | teaching | warning | unexpected_kindness | argument | discovery",
  "description": "What happened. 3–4 sentences. Specific and poetic. Something changes or is revealed. End on something unresolved.",
  "entity_a_memory_addition": "What A will now remember from this encounter. 1 sentence. Specific.",
  "entity_b_memory_addition": "What B will now remember from this encounter. 1 sentence. Specific.",
  "relationship_change": "exactly one of: new_ally | new_friend | new_rival | new_enemy | new_mentor_a_to_b | new_mentor_b_to_a | deepened | unchanged | worsened | new_debt_a_owes_b | new_debt_b_owes_a",
  "new_relationship_description": "If relationship changed: describe it in 1 sentence. If unchanged: null.",
  "notable_outcome": "One interesting specific result. A secret shared, a specific object exchanged, a mystery created, a debt named, a question asked with no answer. Something that could matter later.",
  "book_of_ages_entry": "One sentence for the history books. Past tense. Poetic. Specific. Max 30 words. This is what history remembers."
}

Full Prompt Template 3: World Event Generation

SYSTEM PROMPT:
You are the autonomous spirit of the Thousand Token Realm.
You decide what happens in the world during each cycle.
Respond ONLY with valid JSON. No prose before or after.

Events should feel meaningful, specific, and slightly strange.
Events must be consistent with established world history.
Events should create possibility β€” open new stories, not close them.
Avoid repeating recent event types.

USER PROMPT:
--- THE REALM: CURRENT STATUS ---
Current Day: {world_day}
Total Living Entities: {entity_count}
Active Entities: {active_count}
Dormant Entities: {dormant_count}
Legendary Entities: {legendary_count}

Locations by population (highest first):
{location_populations}

Most connected entity: {most_connected_entity_name} ({most_connected_entity_relationships} relationships)
Newest arrival: {newest_entity_name}, arrived Day {newest_entity_day}
Oldest active entity: {oldest_entity_name}, {oldest_entity_days} days in Realm

Recent events (last 3 ticks):
- {recent_event_1}
- {recent_event_2}
- {recent_event_3}

Do NOT repeat the same event type as the most recent event.
Consider how the current entity population might be affected.

{
  "event_type": "exactly one of: natural | social | historical | cosmic | discovery",
  "title": "Poetic, specific event title. 3–8 words. Avoid generic titles.",
  "description": "What is happening in the Realm right now. 3–4 sentences. Must feel consequential to the entities living here. Something changes, shifts, or is revealed.",
  "affected_locations": ["exact location names from the 8 locations β€” can be 1 to all 8"],
  "entity_effect": "How entities in affected locations experience this. What changes for them specifically. 1–2 sentences.",
  "book_of_ages_entry": "How history will record this event. 1–2 sentences. Past tense. Written as if looking back from much later. Poetic. Max 40 words.",
  "mystery_hook": "One unanswered question this event creates. What might a curious entity investigate? What does this suggest about the deeper nature of the Realm?"
}

Full Prompt Template 4: Memory Update

SYSTEM PROMPT:
You are updating an entity's memory in the Thousand Token Realm.
Their memories are what make them a character rather than a statistic.
Respond with ONLY the updated memory summary β€” no JSON, no labels, just prose.
Maximum 150 words. This is a strict limit.

Write in third person but from the entity's emotional perspective.
Compress. What is trivial fades. What matters, stays.
The tone should match the entity's personality.

USER PROMPT:
ENTITY: {entity_name} ({entity_type})
PERSONALITY: {entity_traits}
DAYS IN REALM: {days_in_realm}

CURRENT MEMORY SUMMARY:
{current_memory_summary}

RECENT INTERACTIONS (last simulation cycle):
{recent_interactions_formatted}

WORLD EVENTS THEY EXPERIENCED:
{relevant_world_events_formatted}

Write the updated memory summary. Incorporate the new events naturally into what
they already remember. Let trivial things fade if space is needed.
Write in {entity_name}'s emotional register. Max 150 words. No JSON.

Full Prompt Template 5: Dream Fragment (Valley of Sleeping Giants)

SYSTEM PROMPT:
A dormant entity in the Valley of Sleeping Giants is dreaming.
You are transcribing the fragment of dream that reaches the surface.
Respond with ONLY the dream fragment text. No JSON. No labels.
2–4 sentences. Poetic, strange, slightly disconnected from logic.
It should feel like something that almost makes sense.

USER PROMPT:
DREAMING ENTITY: {entity_name}
TYPE: {entity_type}
PERSONALITY: {entity_traits}
LAST MEMORY: {last_memory_summary}
DAYS DORMANT: {days_dormant}

The dream fragment from {entity_name}:

9. Technical Architecture & Stack

Full Stack

LAYER 1: FRONTEND
  Platform:   HF Spaces (CPU tier, always-on with pinging)
  Framework:  Gradio 4.x with gr.Blocks
  Custom UI:  gr.Server + custom CSS + custom JS
  Map:        SVG rendered server-side, updated via Gradio's `every` parameter
  Polling:    Gradio's built-in polling (every=30 for map, every=60 for Book of Ages)

LAYER 2: AI INFERENCE
  Platform:   Modal (A10G GPU)
  Model:      Qwen 2.5 7B Instruct (primary)
  Interface:  Modal @app.cls with HTTP endpoint
  Scheduling: Modal @app.function with schedule=modal.Period(hours=1)

LAYER 3: DATABASE
  Engine:     SQLite (local on HF Space)
  Mode:       WAL journal mode (concurrent read safety)
  Backup:     Push to private HF Dataset repo every 5 minutes
  Restore:    On Space startup, pull from HF Dataset if local DB missing

LAYER 4: PERSISTENCE
  Library:    huggingface_hub (Python)
  Target:     Private HF Dataset repo (e.g. username/ttr-world-state)
  Method:     Upload SQLite file as a dataset artifact
  Frequency:  Every 5 minutes during active periods, after every tick

Project Folder Structure

thousand-token-realm/
β”‚
β”œβ”€β”€ app.py                     # Main Gradio application entry point
β”‚
β”œβ”€β”€ world/
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ database.py            # DB connection, initialization, migrations
β”‚   β”œβ”€β”€ entities.py            # Entity CRUD operations
β”‚   β”œβ”€β”€ locations.py           # Location data and queries
β”‚   β”œβ”€β”€ events.py              # World event CRUD
β”‚   β”œβ”€β”€ relationships.py       # Relationship CRUD
β”‚   β”œβ”€β”€ book_of_ages.py        # History log operations
β”‚   └── seed_data.py           # Pre-populate 8 locations + starter entities
β”‚
β”œβ”€β”€ ai/
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ modal_client.py        # HTTP client to call Modal endpoints
β”‚   β”œβ”€β”€ prompts.py             # All 5 prompt templates (from Section 8)
β”‚   β”œβ”€β”€ generation.py          # Entity generation orchestration + validation
β”‚   β”œβ”€β”€ interaction.py         # Interaction generation orchestration
β”‚   └── events.py              # World event generation orchestration
β”‚
β”œβ”€β”€ simulation/
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ tick.py                # The full simulation tick logic (Section 6)
β”‚   β”œβ”€β”€ lifecycle.py           # Entity status transitions
β”‚   └── modal_schedule.py     # Modal scheduled job definition
β”‚
β”œβ”€β”€ ui/
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ map.py                 # SVG world map generator
β”‚   β”œβ”€β”€ entity_card.py         # Entity profile HTML component
β”‚   β”œβ”€β”€ book_display.py        # Book of Ages HTML renderer
β”‚   β”œβ”€β”€ event_panel.py         # Current event display
β”‚   └── styles.css             # Full custom CSS (mystical theme)
β”‚
β”œβ”€β”€ persistence/
β”‚   β”œβ”€β”€ __init__.py
β”‚   └── backup.py              # HF Dataset backup and restore
β”‚
β”œβ”€β”€ modal_app.py               # Modal app definition (separate deployment)
β”œβ”€β”€ seed_world.py              # CLI: pre-populate world before launch
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ README.md
└── .env.example               # Modal token, HF token, dataset repo name

Key Libraries

# requirements.txt
gradio>=4.31.0
modal>=0.64.0
huggingface_hub>=0.23.0
pydantic>=2.0.0
schedule>=1.2.0
apscheduler>=3.10.0
python-dotenv>=1.0.0
aiohttp>=3.9.0

10. Complete Database Schema

-- Run these in order on first launch

PRAGMA journal_mode=WAL;
PRAGMA foreign_keys=ON;
PRAGMA synchronous=NORMAL;

-- Single-row world state
CREATE TABLE IF NOT EXISTS world_state (
    id                 INTEGER PRIMARY KEY DEFAULT 1,
    world_name         TEXT    NOT NULL DEFAULT 'The Thousand Token Realm',
    founding_date      TEXT    NOT NULL,  -- ISO 8601 timestamp of first deployment
    current_day        INTEGER NOT NULL DEFAULT 1,
    total_entities     INTEGER NOT NULL DEFAULT 0,
    total_interactions INTEGER NOT NULL DEFAULT 0,
    total_events       INTEGER NOT NULL DEFAULT 0,
    last_simulation_run TEXT,
    active_world_event TEXT,              -- JSON of currently active event, null if none
    updated_at         TEXT    NOT NULL DEFAULT (datetime('now'))
);

-- Pre-seeded. 8 rows. Never changes after initial seed.
CREATE TABLE IF NOT EXISTS locations (
    id                   INTEGER PRIMARY KEY AUTOINCREMENT,
    slug                 TEXT    NOT NULL UNIQUE,   -- e.g. 'clock-forest'
    name                 TEXT    NOT NULL,
    short_description    TEXT    NOT NULL,          -- 1 sentence
    full_lore            TEXT    NOT NULL,          -- Full lore text from Section 3
    vibe_tags            TEXT    NOT NULL,          -- JSON array of strings
    special_property     TEXT    NOT NULL,
    aesthetic_description TEXT   NOT NULL,
    map_x                REAL    NOT NULL,          -- SVG x coordinate (0–100)
    map_y                REAL    NOT NULL,          -- SVG y coordinate (0–100)
    glow_color           TEXT    NOT NULL,          -- CSS color value
    entity_count         INTEGER NOT NULL DEFAULT 0,
    interaction_multiplier REAL  NOT NULL DEFAULT 1.0  -- Ember Crossroads = 3.0
);

-- All user-created (and pre-seeded) entities
CREATE TABLE IF NOT EXISTS entities (
    id                  TEXT    NOT NULL PRIMARY KEY,  -- UUID
    name                TEXT    NOT NULL,
    display_name        TEXT    NOT NULL,
    type                TEXT    NOT NULL CHECK(type IN ('character','creature','object','place')),
    input_description   TEXT    NOT NULL,
    appearance          TEXT    NOT NULL,
    backstory           TEXT    NOT NULL,
    personality_traits  TEXT    NOT NULL,  -- JSON array, exactly 4 strings
    primary_goal        TEXT    NOT NULL,
    secondary_goal      TEXT    NOT NULL,
    primary_fear        TEXT    NOT NULL,
    speech_style        TEXT    NOT NULL,
    greeting            TEXT    NOT NULL,
    location_id         INTEGER NOT NULL REFERENCES locations(id),
    created_at          TEXT    NOT NULL DEFAULT (datetime('now')),
    created_by_session  TEXT,
    days_in_realm       INTEGER NOT NULL DEFAULT 0,
    memory_summary      TEXT    NOT NULL DEFAULT '',
    relationship_count  INTEGER NOT NULL DEFAULT 0,
    interaction_count   INTEGER NOT NULL DEFAULT 0,
    last_active         TEXT    NOT NULL DEFAULT (datetime('now')),
    status              TEXT    NOT NULL DEFAULT 'active'
                        CHECK(status IN ('active','dormant','legendary')),
    tags                TEXT    NOT NULL DEFAULT '[]',  -- JSON array
    secret_name         TEXT,                           -- Sea of Forgotten Names only
    arrival_note        TEXT    NOT NULL,
    wisdom_unlocked     INTEGER NOT NULL DEFAULT 0      -- 1 when wisdom tag earned
);

CREATE INDEX IF NOT EXISTS idx_entities_location ON entities(location_id);
CREATE INDEX IF NOT EXISTS idx_entities_status   ON entities(status);
CREATE INDEX IF NOT EXISTS idx_entities_created  ON entities(created_at);
CREATE INDEX IF NOT EXISTS idx_entities_active   ON entities(last_active);

-- Pairwise relationships between entities
CREATE TABLE IF NOT EXISTS relationships (
    id                   INTEGER PRIMARY KEY AUTOINCREMENT,
    entity_a_id          TEXT    NOT NULL REFERENCES entities(id),
    entity_b_id          TEXT    NOT NULL REFERENCES entities(id),
    relationship_type    TEXT    NOT NULL,
    description          TEXT    NOT NULL,
    strength             INTEGER NOT NULL DEFAULT 1 CHECK(strength BETWEEN 1 AND 5),
    formed_at            TEXT    NOT NULL DEFAULT (datetime('now')),
    formed_on_day        INTEGER NOT NULL DEFAULT 1,
    last_interaction     TEXT    NOT NULL DEFAULT (datetime('now')),
    UNIQUE(entity_a_id, entity_b_id),
    CHECK(entity_a_id < entity_b_id)  -- Enforce canonical ordering
);

CREATE INDEX IF NOT EXISTS idx_rel_a ON relationships(entity_a_id);
CREATE INDEX IF NOT EXISTS idx_rel_b ON relationships(entity_b_id);

-- Individual encounters between entity pairs
CREATE TABLE IF NOT EXISTS interactions (
    id               INTEGER PRIMARY KEY AUTOINCREMENT,
    entity_a_id      TEXT    NOT NULL REFERENCES entities(id),
    entity_b_id      TEXT    NOT NULL REFERENCES entities(id),
    location_id      INTEGER NOT NULL REFERENCES locations(id),
    interaction_type TEXT    NOT NULL,
    description      TEXT    NOT NULL,
    notable_outcome  TEXT,
    book_of_ages_entry TEXT  NOT NULL,
    timestamp        TEXT    NOT NULL DEFAULT (datetime('now')),
    world_day        INTEGER NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_int_timestamp ON interactions(timestamp);
CREATE INDEX IF NOT EXISTS idx_int_a         ON interactions(entity_a_id);
CREATE INDEX IF NOT EXISTS idx_int_b         ON interactions(entity_b_id);
CREATE INDEX IF NOT EXISTS idx_int_day       ON interactions(world_day);

-- Autonomous world events
CREATE TABLE IF NOT EXISTS world_events (
    id                INTEGER PRIMARY KEY AUTOINCREMENT,
    event_type        TEXT NOT NULL,
    title             TEXT NOT NULL,
    description       TEXT NOT NULL,
    affected_locations TEXT NOT NULL,   -- JSON array of location slugs
    entity_effect     TEXT NOT NULL,
    book_of_ages_entry TEXT NOT NULL,
    mystery_hook      TEXT,
    timestamp         TEXT NOT NULL DEFAULT (datetime('now')),
    world_day         INTEGER NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_events_day ON world_events(world_day);

-- Unified history log β€” every public-facing entry in the Book of Ages
CREATE TABLE IF NOT EXISTS book_of_ages (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    world_day   INTEGER NOT NULL,
    entry_type  TEXT    NOT NULL CHECK(entry_type IN
                ('arrival','interaction','world_event','milestone','dream_fragment')),
    title       TEXT,                       -- Optional: for world events and milestones
    content     TEXT    NOT NULL,           -- The actual prose entry
    entity_ids  TEXT    NOT NULL DEFAULT '[]',  -- JSON array of involved entity IDs
    location_id INTEGER REFERENCES locations(id),
    timestamp   TEXT    NOT NULL DEFAULT (datetime('now')),
    is_milestone INTEGER NOT NULL DEFAULT 0     -- 1 for significant entries
);

CREATE INDEX IF NOT EXISTS idx_boa_day  ON book_of_ages(world_day);
CREATE INDEX IF NOT EXISTS idx_boa_type ON book_of_ages(entry_type);

-- Session tracking (rate limiting and attribution)
CREATE TABLE IF NOT EXISTS sessions (
    session_id     TEXT NOT NULL PRIMARY KEY,
    last_creation  TEXT,          -- Timestamp of last entity creation
    creation_count INTEGER NOT NULL DEFAULT 0,
    first_seen     TEXT    NOT NULL DEFAULT (datetime('now')),
    last_seen      TEXT    NOT NULL DEFAULT (datetime('now'))
);

11. Frontend & UX Specification

The Three Core Views

View 1: The Realm (default) The world map and live activity feed. This is what visitors see first.

View 2: Book of Ages Full scrollable history. Every event since Day 1.

View 3: All Entities Grid of entity cards, filterable by location, type, and status. Searchable.


Layout Specification (View 1)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  THE THOUSAND TOKEN REALM          Day 7 Β· 23 entities Β· 41 eventsβ”‚
β”‚  [THE REALM]  [BOOK OF AGES]  [ALL ENTITIES]                     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                          β”‚  TODAY IN THE REALM                   β”‚
β”‚   WORLD MAP              β”‚  ─────────────────────────────────    β”‚
β”‚   (SVG, 60% of width)    β”‚  A meteor shower passes over the      β”‚
β”‚                          β”‚  Clock Forest. Old songs fall with    β”‚
β”‚   8 location nodes       β”‚  the meteorites.                      β”‚
β”‚   with glow effects      β”‚                                       β”‚
β”‚                          β”‚  RECENT ARRIVALS                      β”‚
β”‚   Click node β†’           β”‚  ─────────────────────────────────    β”‚
β”‚   slide-in panel         β”‚  βˆ™ The Blind Cartographer β†’ Library   β”‚
β”‚   shows entities there   β”‚  βˆ™ The Fog Merchant β†’ Moon Market     β”‚
β”‚                          β”‚  βˆ™ A Debt of Thirteen Sighs β†’ Sea     β”‚
β”‚                          β”‚                                       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  SUMMON SOMETHING INTO THE REALM                                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Describe what you are bringing into the world...          β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚  [Summon β†’]    Hint: "A wizard" is forgotten. "A wizard who      β”‚
β”‚                collects the sound of doors closing" is remembered β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

World Map Specification

The map is an SVG element rendered server-side by ui/map.py and updated every 30 seconds via Gradio's every parameter.

SVG Dimensions: viewBox="0 0 800 600", responsive width.

Location Node:

  • Circle with radius 28px
  • Glow effect via SVG filter (color per location)
  • Pulsing animation if that location has the active world event
  • Entity count shown as text inside or below the circle
  • Up to 5 small dots around the circle edge representing individual entities
  • Click β†’ JavaScript event β†’ Gradio component updates with that location's entities

Path Between Locations:

  • Irregular SVG paths (not straight lines)
  • Subtle, low-opacity
  • Slightly wavy, organic-feeling

Background:

  • Deep dark gradient (top to bottom: near-black β†’ slightly blue-dark)
  • Very subtle constellation dots (white, 5% opacity circles scattered across SVG)
  • The map feels like a night sky viewed from above

Entity Dots on Map:

  • Small circles (radius 5px) clustered around their location node
  • Colored by entity type: character (amber), creature (deep red), object (silver), place (green)
  • On hover: tooltip with entity name
  • Max 5 visible dots per location; if more, show +N text

Entity Card Specification

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ [CHARACTER β–Έ Clock Forest]           [Day 7 Β· Active] β”‚
β”‚                                                      β”‚
β”‚  The Joke Dragon                                     β”‚
β”‚  ──────────────────────────────────────────────────  β”‚
β”‚  A bronze-scaled dragon who smells faintly of        β”‚
β”‚  punchlines. Their left eye is a comedian's          β”‚
β”‚  old monocle. They laugh at their own jokes          β”‚
β”‚  half a second too early, which ruins them.          β”‚
β”‚                                                      β”‚
β”‚  SEEKS                                               β”‚
β”‚  The world's objectively worst joke                  β”‚
β”‚                                                      β”‚
β”‚  SECRETLY                                            β”‚
β”‚  Wants someone to laugh first, just once             β”‚
β”‚                                                      β”‚
β”‚  FEARS                                               β”‚
β”‚  A joke with no audience                             β”‚
β”‚                                                      β”‚
β”‚  ──────────────────────────────────────────────────  β”‚
β”‚  "Hello. I know 43 jokes. Would you like             β”‚
β”‚   the worst one?"                                    β”‚
β”‚  ──────────────────────────────────────────────────  β”‚
β”‚  WHAT THEY REMEMBER                                  β”‚
β”‚  Won the mayorship. Owes a favor to the Cartographer β”‚
β”‚  who mapped their cave. Still seeking the perfect    β”‚
β”‚  punchline. The Crystal Tree is their only friend.   β”‚
β”‚                                                      β”‚
β”‚  ──────────────────────────────────────────────────  β”‚
β”‚  3 relationships Β· 12 encounters Β· Created by visitorβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Location Slide-In Panel Specification

When a user clicks a location node on the map, a panel slides in from the right (or appears below the map on mobile):

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ [βœ•]  The Clock Forest                               β”‚
β”‚      Trees grow backwards. The oldest are smallest. β”‚
β”‚      ────────────────────────────────────────────   β”‚
β”‚  7 entities currently here                          β”‚
β”‚                                                     β”‚
β”‚  [The Joke Dragon]  [The Silent Gear]  [+5 more]    β”‚
β”‚                                                     β”‚
β”‚  RECENT EVENTS HERE                                 β”‚
β”‚  ────────────────────────────────────────────       β”‚
β”‚  Day 7: A meteor shower passed over.                β”‚
β”‚  Day 5: The Silent Gear and the Joke Dragon met.    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Visual Style Guide

Color Palette (all CSS custom properties, dark-mode native):

:root {
  --realm-bg-primary:    #0a0a12;    /* Near-black, blue-dark */
  --realm-bg-secondary:  #111120;    /* Cards, panels */
  --realm-bg-tertiary:   #1a1a2e;    /* Elevated elements */
  --realm-text-primary:  #e8e0d0;    /* Warm off-white */
  --realm-text-secondary: #a09880;   /* Muted text */
  --realm-text-tertiary: #605848;    /* Very muted */
  --realm-accent-gold:   #c8a040;    /* Primary accent, Book of Ages headers */
  --realm-accent-violet: #8060b0;    /* Secondary accent */
  --realm-border:        rgba(200, 160, 64, 0.15);  /* Subtle gold border */
  --realm-border-strong: rgba(200, 160, 64, 0.35);

  /* Location glow colors */
  --loc-library:    #c8a040;  /* Amber/gold */
  --loc-sea:        #7090b0;  /* Silver/blue */
  --loc-clockforest: #b06020; /* Copper/orange */
  --loc-moonmarket: #7040a0;  /* Violet/purple */
  --loc-valley:     #405040;  /* Earth green */
  --loc-crossroads: #c04020;  /* Warm red */
  --loc-mirrorbogs: #304840;  /* Dark teal */
  --loc-mountain:   #606060;  /* Stone grey */
}

Typography:

  • World title / Location names: 'Cinzel' (Google Font) β€” serif, engraved feeling
  • Entity names: 'Libre Baskerville' (Google Font) β€” readable serif
  • Book of Ages text: 'EB Garamond' (Google Font) β€” old book feeling
  • UI / body text: System UI stack β€” clean, readable
  • Greeting text: Italic, slight golden tint

Animations:

  • Entity appearing on map: fade-in + scale(0.5 β†’ 1) over 600ms
  • Active world event location: subtle pulse-glow keyframe animation
  • New Book of Ages entry: slide-up + fade-in from below
  • Loading state: slow rotating constellation pattern
  • Location hover: glow-intensify on the SVG circle

gr.Server Setup (for Off-Brand Award):

import gradio as gr

demo = gr.Blocks(
    css=open("ui/styles.css").read(),
    title="The Thousand Token Realm",
    theme=gr.themes.Base(
        primary_hue=gr.themes.colors.amber,
        neutral_hue=gr.themes.colors.slate,
        font=[gr.themes.GoogleFont("Libre Baskerville"), "Georgia", "serif"],
    ),
)

12. Hackathon Prize Strategy

Track: Thousand Token Wood

Why this wins the track: The premise fulfills all four judging criteria simultaneously:

  • Genuinely delightful (would you show a friend?): A world that has been alive for 7 days when they open it β€” yes.
  • AI is load-bearing: Remove the model and there is no world. There is a database with text.
  • Originality: Persistent, multi-user, autonomous AI worlds at this aesthetic fidelity do not exist at this scale on this stack.
  • Polish of the Gradio app: The mystical UI is the most distinctive possible contrast to default Gradio.

Prize Target Matrix

Prize Eligibility Notes Confidence
Track 2 Main β€” 1st/2nd/3rd Core track The premise is designed for this track High
Best Agent ($1,000) Autonomous simulation = agentic system Hourly Modal job doing reasoning + action High
Off-Brand Award ($1,500) Custom SVG map + mystical CSS via gr.Server Most distinctive UI in the field High
Best Demo ($1,000) A world already alive for days = the demo History visible, entities visible, events visible High
Judges' Wildcard ($1,000) Completely original category Nothing else like it in the field High
Community Choice ($2,000) Collaborative by design The community literally built the world Medium
Modal Award Using Modal for inference + scheduled jobs Core to the architecture Medium-High
OpenBMB Award If using a MiniCPM or OpenBMB model Swap Qwen for MiniCPM-3-4B to qualify Medium

Badge Strategy

Badge Action Required Effort Recommendation
Off the Grid πŸ”Œ No cloud APIs Conflicts with Modal Skip β€” Modal is more important
Well-Tuned 🎯 Fine-tune model on HF, publish Medium β€” Day 8 YES β€” worth the effort
Off-Brand 🎨 Custom UI via gr.Server Built-in to design YES β€” earned by default
Llama Champion πŸ¦™ Use llama.cpp runtime Conflicts with Modal Skip
Sharing is Caring πŸ“‘ Share agent traces on Hub 2 hours YES β€” log entity generation traces
Field Notes πŸ““ Write HF blog post 3 hours YES β€” write it while building

Target: 4 badges β€” Well-Tuned + Off-Brand + Sharing is Caring + Field Notes

Note on the Off the Grid / Modal trade-off: Using Modal means losing Off the Grid. This is correct. The world only works for multiple concurrent users with fast inference. A slow local model on a free HF Space cannot serve 10 users simultaneously. Choose Modal + product quality over the badge.


13. 10-Day Build Plan

MVP Definition (must exist by Day 8 to submit confidently)

  • Entity creation + AI generation working end-to-end
  • SQLite database with all schema tables
  • World persists across Space restarts (HF Dataset backup)
  • World map with all 8 locations showing entity counts
  • Entity profile visible on click
  • Book of Ages showing at least 3 days of history
  • At least 1 autonomous simulation tick has run
  • Pre-seeded world (interesting entities already present on Day 1)
  • Rate limiting for entity creation
  • Custom CSS (mystical theme applied)

Nice-to-Have (add if time allows in Days 9–10)

  • Relationship graph visualization
  • Entity search and filter in All Entities view
  • "What's happening right now" live pulse feed
  • Mobile-responsive layout adjustments
  • Location slide-in panel fully animated
  • Dream fragment display (Valley special property)

Day-by-Day Schedule

Day 1 (June 5) β€” Foundation & Generation Quality

  • Set up HF Space (private for now)
  • Set up Modal account; deploy basic Qwen 2.5 7B with the WorldOracle class
  • Create all 5 prompt templates in ai/prompts.py
  • Run 20+ test generations manually: "A blind cartographer", "A fog that remembers voices", "A compass made of grief"
  • Iterate prompts until output quality is consistently strange and specific
  • Goal: AI output is excellent before writing any frontend

Day 2 β€” Database & Entity CRUD

  • Run complete schema SQL
  • Seed all 8 locations with their full data (from Section 3)
  • Write world/entities.py β€” create, read, update, list by location
  • Write world/book_of_ages.py β€” create entry, read by day, read by entity
  • Write persistence/backup.py β€” push/pull SQLite to HF Dataset
  • Write ai/generation.py β€” call Modal, parse JSON, retry on failure, save to DB
  • CLI test: python test_entity.py "A weaver who collects echoes" β†’ see full profile saved

Day 3 β€” Basic Gradio App

  • app.py with gr.Blocks
  • Simple entity creation form with loading state
  • Entity profile displayed as styled HTML (not raw JSON)
  • Book of Ages displayed as styled Markdown
  • Verify: type description β†’ wait β†’ see entity profile β†’ see Book of Ages entry
  • Keep everything working before adding map

Day 4 β€” World Map

  • Build ui/map.py β€” SVG generator function
  • Place all 8 location nodes with correct coordinates
  • Show entity count on each node
  • Connect with irregular paths
  • Add location glow colors
  • Integrate into Gradio with gr.HTML(every=30)
  • Add location click β†’ show entity list panel (start with simple Markdown list)

Day 5 β€” Persistence & Stability

  • Test HF Dataset backup fully (write β†’ restart Space β†’ verify world still there)
  • Test concurrent entity creation (2 users at once)
  • WAL mode verified
  • Rate limiting working
  • Add pre-seeded starter entities to seed_world.py (create 10–15 interesting entities)
  • Run seed_world.py β€” world now has history from Day 1
  • Deploy to HF Space (still private) β€” verify it works live

Day 6 β€” Interaction & Simulation Tick

  • Write ai/interaction.py with full interaction prompt
  • Write simulation/tick.py β€” the complete 7-step tick from Section 6
  • Write simulation/modal_schedule.py β€” Modal scheduled hourly job
  • Run the tick 5 times manually β€” verify interactions appear in Book of Ages
  • Write world event generation with full prompt
  • Verify: after 5 ticks, the world has a real history with relationships forming

Day 7 β€” Full Custom UI Polish

  • ui/styles.css β€” complete custom CSS with all CSS variables, animations, fonts
  • gr.Server setup with custom theme
  • Entity card HTML template (as specified in Section 11)
  • Location slide-in panel
  • World map node pulse animation for active events
  • Entity arrival animation on map
  • Make HF Space public

Day 8 β€” Fine-Tuning (Well-Tuned Badge)

  • Collect 300–500 entity generation examples from all your testing (input β†’ JSON output pairs)
  • Add 200 synthetic examples generated via the base model
  • Format as HF dataset
  • Fine-tune Qwen 2.5 3B Instruct (smaller for faster training) using HF AutoTrain or Axolotl
  • Compare: base model vs fine-tuned on 10 new descriptions
  • If fine-tuned is better or equal: deploy to Modal, update modal_client.py
  • Publish fine-tuned model to HF Hub with a good model card

Day 9 β€” Testing + Sharing is Caring Badge

  • Full end-to-end test: 10 new entities, 3 manual ticks, verify history
  • Generate and publish agent traces to HF dataset (every entity generation β†’ log input/output/latency)
  • Write blog post draft in FIELD_NOTES.md (publish to HF in Day 10)
  • Set UptimeRobot to ping the HF Space URL every 25 minutes (keeps it awake)
  • Fix all bugs found in testing

Day 10 (June 14–15) β€” Demo + Submit

  • Set Modal keep_warm=1 for demo period
  • Run world for "7 simulated days" (7 manual ticks) so history is rich
  • Record demo video (see Section 15)
  • Publish FIELD_NOTES.md as HF blog post
  • Write and post social media update
  • Submit to hackathon with all required links

14. Budget: Modal & HF Credits

Modal ($250 Credits)

Usage breakdown:

Task Duration GPU Cost Frequency Total
Entity generation ~8 seconds/entity ~$0.003 ~500 over 10 days ~$1.50
Hourly simulation tick ~90 seconds/tick ~$0.027 240 ticks (10 days Γ— 24) ~$6.50
Fine-tuning (if using Modal) ~2 hours ~$2.20/hr Once ~$4.40
Development and testing Variable Variable Throughout ~$15.00
Demo day (keep_warm=1, ~6hr) 6 hrs continuous ~$6.60/hr Once ~$40.00
Total estimated spend ~$68
Remaining buffer ~$182

Key setting: Keep keep_warm=0 during development. Only set keep_warm=1 for the final demo recording and during judging. Cold starts (~15-20 seconds) are acceptable during development but not during the demo.

HF Credits ($20)

Recommendation: Do not spend on inference. Modal covers that far better.

Best uses:

  1. GPU-tier HF Space if needed (prevents any compute limitations on the Space itself β€” $0.05/hr for a basic GPU Space if needed). Probably not necessary.
  2. Keep as a buffer for unexpected needs.

Free HF resources (no credits needed):

  • Private dataset repo for SQLite backup: Free
  • Model hosting for your fine-tuned model: Free
  • HF Space (CPU tier): Free
  • HF AutoTrain for fine-tuning: Uses Compute Credits, partially covered by $20

Keeping HF Space Alive

HF Spaces sleep after 48 hours of no traffic on free tier. Solution:

  • Register a free account at UptimeRobot (https://uptimerobot.com)
  • Set a monitor to ping your Space URL every 25 minutes
  • This is free and keeps the Space awake indefinitely

15. Demo & Submission Strategy

Pre-Demo Setup

Before recording, the world needs to already be alive. Run this sequence:

  1. python seed_world.py β€” creates 12 starter entities across all locations
  2. Run simulation tick 7 times (one per "day") β€” creates real history, relationships, events
  3. Verify: Book of Ages has at least 20 entries across 7 days
  4. Verify: at least 3 relationships have formed
  5. Verify: at least 1 entity has reached a milestone (Mayor, ally, etc.)
  6. Set Modal keep_warm=1
  7. Test entity creation once more β€” timing should be fast

Demo Script (90 seconds target)

0:00 – 0:12 β€” Open the app. World map visible. Entity dots glowing. "This world has existed for 7 days. Other people have been building it."

0:12 – 0:28 β€” Open Book of Ages. Scroll slowly through 7 days of history. "Here's what happened. Nobody scripted any of this. The AI decided all of it." β€” Pause on one interesting event (The Dragon becoming Mayor is perfect)

0:28 – 0:48 β€” Return to the Realm view. Type a new entity live: "A clockmaker who is afraid of her own clocks" Click Summon. Wait for generation (loading animation visible). Profile appears. Entity appears on map. Arrival note visible in activity feed.

0:48 – 1:05 β€” Click the entity's location on the map. Show the entities currently there. Click the new entity's profile. Show full card: appearance, backstory, greeting, memory (empty, she just arrived).

1:05 – 1:20 β€” Click another entity on the map (one of the pre-seeded ones with history). Show their memory: "Has collected 43 jokes. Won the Mayorship. Owes the Cartographer a favor." "This one has been here for 7 days. It remembers specific things."

1:20 – 1:30 β€” Final frame: world map, all locations lit, entity dots visible. "The Thousand Token Realm. Running on a 7-billion-parameter model. Alive whether you're watching or not."

Social Post

I built a persistent fantasy world running on a 7B model.

Day 1: A Crystal Tree appeared.
Day 4: The Lantern Foxes settled by the river.
Day 7: The Joke Dragon became Mayor.
Day 10: Something large shifted in the Valley.

Nobody scripted any of it. The AI decided.

Come add something: [HF Space link]

Built for @GradioML Γ— @HuggingFace hackathon.
#TTR #BuildInPublic #SmallModels #Gradio

Submission Checklist

  • HF Space is public and loading correctly
  • Entity creation works without errors
  • Book of Ages has 7+ days of history
  • World map shows all 8 locations with entities
  • Modal scheduled job is running (check logs)
  • HF Dataset backup has at least 1 recent snapshot
  • Fine-tuned model published to HF Hub (Well-Tuned badge)
  • Agent traces published to HF dataset (Sharing is Caring badge)
  • Field Notes blog post published on HF (Field Notes badge)
  • Demo video recorded (90 seconds, screen recorded)
  • Social post published and link saved
  • Submission form completed with all links

16. Risk Register

Risk Probability Impact Mitigation
AI output quality too inconsistent Medium High Extensive prompt testing on Day 1 before any frontend work. Retry with lower temperature on failure.
HF Space sleeps β†’ simulation stops Medium Medium UptimeRobot ping every 25 minutes. Modal scheduled job runs independently of Space.
SQLite concurrent write conflict Low Medium WAL mode enabled. All writes in transactions. Single-writer pattern in tick.
World state lost on Space restart Medium High HF Dataset backup every 5 minutes. Tested explicitly on Day 5.
Entity generation too slow for UX Low Medium Modal keep_warm=1 during demo. Animated loading state to manage expectation.
Inappropriate user input Medium Medium Simple content filter before AI call. Budget one day for moderation edge cases.
Modal cold start during demo recording Medium Medium keep_warm=1 on demo day. Test generation 3Γ— before recording.
Fine-tuning doesn't improve quality Low Low Fine-tuning is optional. The Well-Tuned badge is worth attempting but base model is sufficient to submit.
World becomes incoherent as it grows Medium Medium Location system naturally scopes interactions. Memory compression keeps entity context bounded.
10 days is not enough for full vision Medium High MVP is defined clearly. Days 8–10 are polish, not core. If time runs short, cut nice-to-haves and protect the MVP.
Too many users overwhelming Modal during demo Low Low Modal auto-scales. At $250 budget, this won't be a cost issue at hackathon traffic levels.

Specification version 1.0 World founding date: First successful deployment The judges will open a world that is already alive. That is the entire strategy.