Spaces:
Runtime error
Runtime error
| title: The Oracle | |
| emoji: ๐ฎ | |
| colorFrom: indigo | |
| colorTo: yellow | |
| sdk: gradio | |
| sdk_version: 6.16.0 | |
| python_version: '3.13' | |
| app_file: app.py | |
| pinned: false | |
| license: mit | |
| short_description: Oracle An Akinator-style guessing game | |
| models: | |
| - bartowski/Llama-3.2-1B-Instruct-GGUF | |
| - bartowski/Llama-3.2-3B-Instruct-GGUF | |
| tags: | |
| - build-small-hackathon | |
| - thousand-token-wood | |
| - llama | |
| - llama-cpp | |
| - gradio | |
| - game | |
| - guessing-game | |
| - offline-first | |
| - on-device | |
| # ๐ฎ The Oracle | |
| Think of an **animal, a fruit, or a vegetable**. The Oracle asks simple yes/no | |
| questions and divines exactly what you're thinking โ an Akinator-style | |
| mind-reader. **Built with Llama** ๐ฆ and designed to run **fully offline**. | |
| Built for the **Build Small Hackathon ยท An Adventure in Thousand Token Wood**. | |
| ## Model | |
| - **[Llama 3.2 1B Instruct](https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF)** | |
| (`Llama-3.2-1B-Instruct-Q4_K_M.gguf`) โ what the live Space runs โ through the | |
| **llama.cpp** runtime (in-process via `llama-cpp-python`). Just **1B parameters**, | |
| far under the โค32B limit and the โค4B Tiny Titan bar. It's used only to phrase | |
| questions and to derive attributes when learning a new item; all deduction is | |
| pure deterministic code, so a 1B model is plenty. | |
| - Also supports **[Llama 3.2 3B Instruct](https://huggingface.co/bartowski/Llama-3.2-3B-Instruct-GGUF)** | |
| (the default) for slightly richer phrasing โ set via `ORACLE_LLAMA_REPO` / | |
| `ORACLE_LLAMA_FILE`. | |
| Because questions are pre-generated once at boot and cached (persisted in the | |
| Storage Bucket), the model size barely affects gameplay โ the live cache holds | |
| all 42 questions, so each turn is an instant lookup. | |
| ## Badges & prizes we're targeting | |
| | Target | Status | How | | |
| |--------|--------|-----| | |
| | ๐ **Off the Grid** | โ | no cloud APIs; the engine + cached questions run fully offline | | |
| | ๐จ **Off-Brand** | โ | fully custom crystal-ball UI (art, buttons, reveal), not default Gradio | | |
| | ๐ **Field Notes** | โ | the design journey above + the two deep-dive docs / this write-up | | |
| | ๐ฆ **Llama Champion** | โ | the model runs through the llama.cpp runtime (`ORACLE_QUESTION_LLM=1`) | | |
| | ๐ **Tiny Titan** *(special award)* | ๐ฏ | built on a genuinely tiny โค4B model (Llama-3.2-3B) | | |
| | ๐ก Sharing is Caring | optional | the attribute database is shareable as a dataset | | |
| | ๐ฏ Well-Tuned | โ๏ธ n/a | deliberately no fine-tuned model โ accuracy comes from the engine | | |
| ## Demo | |
| [](https://youtu.be/U5UNzHBfJ1k) | |
| ## X post | |
| https://x.com/i/status/2064387556376981633 | |
| ## Why it fits the track | |
| The game spends almost no tokens per turn. A **deterministic engine** does the | |
| deduction over a small attribute database, and a tiny **Llama 3.2 3B** model is | |
| used only to phrase questions in natural language (pre-generated at boot, so | |
| gameplay is instant). Small model, small token budget, no network required. | |
| ## Design journey | |
| **Attempt 1 โ one model did everything (ask *and* guess).** It asked repetitive, | |
| elimination-style questions, never narrowed down, and games didn't end. | |
| **Attempt 2 โ two models: one writes questions, one eliminates items from a list.** | |
| Questions got better, but elimination was slow and wrong on large lists, and the | |
| model's facts couldn't be trusted (it called a tomato a root vegetable). | |
| **Final โ facts in a JSON attribute database; a deterministic engine does all the | |
| elimination; the small model only phrases the questions** (generated once at boot | |
| and cached, so gameplay is instant). A Teach mode lets it learn new items from | |
| Wikipedia, so the database grows on its own. | |
| ```mermaid | |
| flowchart TD | |
| A[Boot] --> B(Model pre-generates natural questions, cached) | |
| B --> C(Gameplay: deterministic elimination engine) | |
| C --> D(Learning: model extracts facts from Wikipedia) | |
| ``` | |
| ## How it works | |
| ``` | |
| answers so far โโบ engine.filter_candidates โโบ engine.choose_attribute โโบ look up cached question โโบ ask | |
| โโบ 1 left โ guess 0 left โ discovery/teach | |
| ``` | |
| (the cached questions are written by Llama once at boot โ see **[How question generation works](./docs/QUESTION_GENERATION.md)**) | |
| - **engine.py** โ pure-Python core. Filters candidates by the answers (exact, no | |
| model), then picks the attribute that best splits the set (max info gain). | |
| Auto-reloads the JSON if it changes on disk. | |
| - **data/\*.json** โ the attribute database (animals / fruits / vegetables); the | |
| single source of truth. Every item defines every attribute. | |
| - **question_maker.py** โ the only place the LLM is used: turn an attribute into a | |
| natural yes/no question. Pre-generated at boot and cached (instant in-game), with | |
| built-in phrasing as fallback. Never decides elimination. | |
| - **llm.py** โ runs the model through the **llama.cpp** runtime (in-process via | |
| `llama-cpp-python`, or an HTTP `llama-server` for local dev), with thread caps, | |
| a timeout, and warmup so it can never hang the game. | |
| - **discovery.py** โ when the Oracle doesn't know an item, the player teaches it; | |
| attributes are filled from the player's answers, the LLM, and the existing DB, | |
| so the new item is complete and guessable next time. Also explains *why* a wrong | |
| answer threw it off. | |
| - **app.py** โ `gradio.Server`: `@app.api("next")` per turn, `@app.api("learn")` | |
| for teaching, serves `index.html`. | |
| - **index.html** โ a fully custom crystal-ball UI (image art, category pick, | |
| image answer buttons, "I'm not sure", dramatic reveal, Teach mode). | |
| - **check_db.py** โ validator: completeness, uniqueness, guessability, balance. | |
| ## Deep dives | |
| - **[How question generation works](./docs/QUESTION_GENERATION.md)** โ the engine | |
| picks the attribute, the model only phrases it, and questions are pre-generated | |
| and cached at boot for instant gameplay. | |
| - **[How Teach / discovery works](./docs/TEACH_MODE.md)** โ how the Oracle learns a | |
| new item (player answers + Llama + Wikipedia + the existing DB) and explains why a | |
| wrong answer threw it off. | |
| ## Run locally | |
| ```bash | |
| pip install -r requirements.txt | |
| python app.py # plays immediately, offline (built-in question phrasing) | |
| ``` | |
| For natural, model-written questions (runs the 3B model in-process via llama.cpp โ | |
| the GGUF downloads once from the Hub): | |
| ```bash | |
| ORACLE_QUESTION_LLM=1 python app.py | |
| ``` | |
| (Alternatively, run a local `llama-server` and set `ORACLE_LLAMA_URL`.) | |
| ## Test & validate | |
| ```bash | |
| pytest -q # 20 offline tests | |
| python check_db.py # validate the attribute database | |
| ``` | |
| ## Deploy (Hugging Face Spaces ยท Gradio) | |
| Push these files to a Space under `build-small-hackathon`; the Gradio SDK runs | |
| `app.py` on port 7860. The game works offline out of the box. To persist items | |
| taught during play and to run the Llama model on the Space (for the **Llama | |
| Champion** badge), see **[DEPLOY.md](./DEPLOY.md)** โ in short: mount a Storage | |
| Bucket at `/data`, set `ORACLE_DATA_DIR=/data`, and set `ORACLE_QUESTION_LLM=1`. | |
| ## Config (env vars) | |
| | Var | Default | Purpose | | |
| |-----|---------|---------| | |
| | `ORACLE_QUESTION_LLM` | `1` | `1` = model-written questions via llama.cpp; `0` = built-in phrasing (no model) | | |
| | `ORACLE_REVEAL_LLM` | `0` | `1` = model writes the pre-guess reveal line; `0` = instant templated line | | |
| | `ORACLE_DISCOVERY_WEB` | `1` | `1` = allow Wikipedia grounding when teaching a new item | | |
| | `ORACLE_DATA_DIR` | _(bundled `data/`)_ | point at a mounted HF Storage Bucket (e.g. `/data`) to persist learned items; the model also caches in `<dir>/models/` | | |
| | `ORACLE_MODEL_DIR` | _(`<ORACLE_DATA_DIR>/models`)_ | override where the GGUF model is downloaded/cached | | |
| | `ORACLE_LLAMA_REPO` / `ORACLE_LLAMA_FILE` | Llama-3.2-3B GGUF | swap the model (e.g. the 1B for more speed) | | |
| | `ORACLE_LLAMA_URL` | `http://localhost:8080/v1/chat/completions` | external llama-server endpoint (HTTP fallback) | | |
| | `ORACLE_LLAMA_THREADS` | _(cores available, capped at 4)_ | llama.cpp threads โ keep small on CPU Spaces | | |
| | `ORACLE_LLM_TIMEOUT` | `25` | seconds before a slow generation gives up and uses built-in phrasing | | |
| | `ORACLE_MAX_QUESTIONS` | `20` | force a guess after this many questions | | |
| --- | |
| **MIT** | |