Spaces:
Running on Zero
Running on Zero
| # Plan: Gamified Productivity Buddy | |
| ## Context | |
| Building a hackathon entry for HF "Build Small" (Thousand Token Wood track, deadline June 15 2026 β 2 days). | |
| The app is a whimsical RPG-flavored productivity companion: a 3D character lives on the left, a local LLM buddy lives on the right, and your focus sessions earn XP, coins, and cosmetic gear. | |
| --- | |
| ## Tech Stack | |
| | Layer | Choice | Reason | | |
| |---|---|---| | |
| | App framework | Gradio `gr.Blocks` | Required by hackathon | | |
| | Hosting | HF Spaces (GPU T4) | Required; enables Off-the-Grid badge | | |
| | LLM | `Qwen/Qwen2.5-3B-Instruct` via π€ `transformers` | 3B = fast on T4, excellent tool-call support | | |
| | 3D avatar | Three.js + GLB/GLTF model | User confirmed; served from Gradio static | | |
| | Frontend glue | Embedded `<script>` in `gr.HTML` + hidden textbox bridge | JSβPython event passing | | |
| | Python env | `venv` | User requirement | | |
| **Bonus badges targeted**: Off the Grid (no cloud APIs), Off-Brand (custom frontend), Tiny Titan (β€4B model). | |
| --- | |
| ## Project Structure | |
| ``` | |
| 08/ | |
| βββ app.py # Gradio entry point | |
| βββ src/ | |
| β βββ llm.py # Model load, chat, tool-call parsing | |
| β βββ game_state.py # XP / coins / levels / inventory dataclass | |
| β βββ tools.py # Tool definitions + executor | |
| β βββ reminder.py # Idle detection thread | |
| βββ static/ | |
| β βββ js/ | |
| β β βββ avatar.js # Three.js scene, GLB loader, animations | |
| β β βββ timer.js # Pomodoro / free-flow countdown + JS events | |
| β β βββ bridge.js # Dispatches JS events β hidden Gradio textbox | |
| β βββ css/ | |
| β β βββ style.css # 2-column RPG dark theme | |
| β βββ models/ | |
| β βββ character.glb # Free CC0 character (see note below) | |
| βββ requirements.txt | |
| βββ .env.example | |
| βββ README.md | |
| ``` | |
| **GLB model source**: Use the free CC0 "Adventurer" from Kenney (kenney.nl/assets/adventurer) or any Mixamo Y-Bot export. Place the file at `static/models/character.glb`. Animations needed: `idle`, `walk`, `celebrate`. | |
| --- | |
| ## Layout (Gradio custom HTML) | |
| ``` | |
| ββββββββββββββββββββββββ¬βββββββββββββββββββββββ | |
| β 3D Avatar (Three.js)β Chat window β | |
| β [idle / celebrate] β (gr.Chatbot) β | |
| β β β | |
| β XP: 120 | β Lv.2 β ββ Inventory ββ β | |
| β π° Coins: 35 β (slides over chat β | |
| β β when [Bag] clicked)β | |
| β ββ Timer ββ β β | |
| β [π Pomodoro] β β | |
| β [β Free Flow] β β | |
| β [Start] [Stop] β β | |
| β β β | |
| β ββ To-Do ββ β β | |
| β [ ] Task 1 β β | |
| β [β] Task 2 β β | |
| β [+ Add task] β β | |
| ββββββββββββββββββββββββ΄βββββββββββββββββββββββ | |
| ``` | |
| The two columns are rendered inside a single `gr.HTML` component that bootstraps Three.js and the timer. Gradio components (`gr.Chatbot`, `gr.Textbox` input, hidden state bridges) sit in `gr.Row`/`gr.Column` alongside. | |
| --- | |
| ## Implementation Steps | |
| > Tracer-bullet order: each slice is a working vertical cut of the full stack. | |
| ### β 1. Bootstrap | |
| - `python -m venv .venv`, install deps, `requirements.txt` β **done** | |
| ### 2. Chat with local LLM | |
| Goal: bare-bones two-column Gradio app where you can talk to Qwen2.5-3B-Instruct. | |
| - `src/llm.py`: load model with `AutoModelForCausalLM` + `bfloat16` + `device_map="auto"`; `chat(messages) -> str` | |
| - `app.py`: minimal `gr.Blocks` with left placeholder column + right `gr.Chatbot` + `gr.Textbox` input | |
| - System prompt: whimsical RPG companion persona | |
| - Deliverable: type a message, get a reply | |
| ### 3. Timer | |
| Goal: functional Pomodoro/free-flow timer in the left column with configurable durations. | |
| - `static/js/timer.js`: countdown logic, work/break modes, start/stop/reset | |
| - `static/css/style.css`: dark RPG theme skeleton, two-column layout | |
| - Left column rendered via `gr.HTML`: timer display + mode selector (Pomodoro / Free Flow) + duration inputs + Start/Stop buttons | |
| - `static/js/bridge.js`: JS β Python bridge via hidden `gr.Textbox` | |
| - Python handler: receives `timer_complete` event, lays groundwork for XP (stub for now) | |
| - Deliverable: timer counts down, rings on completion | |
| ### 4. LLM β Timer tool calls | |
| Goal: LLM can start/stop the timer via natural language. | |
| - `src/game_state.py`: add `timer_state` dict to a minimal `GameState` dataclass | |
| - `src/tools.py`: define `start_timer` and `stop_timer` JSON schemas + executor | |
| - `src/llm.py`: extend `chat()` to pass tools, parse Qwen `<tool_call>` tags, execute, re-call model | |
| - Inject current timer state into system prompt each turn | |
| - Python β JS: after tool execution, return a JS command string to update timer UI | |
| - Deliverable: "start a 25 minute focus session" β timer starts in UI | |
| ### 5. Todo list | |
| Goal: functional todo list in the left column (below timer). | |
| - Extend `GameState`: add `todos: list[dict]` with `id`, `task`, `done` | |
| - `gr.HTML` todo section: renders current todos from state, checkbox to complete, delete button | |
| - Python handlers: `add_todo`, `complete_todo`, `remove_todo` update state + re-render HTML | |
| - Inject todo list into system prompt each turn | |
| - Deliverable: todos appear, can be checked off manually | |
| ### 6. LLM β Todo tool calls | |
| Goal: LLM can manage todos via natural language. | |
| - `src/tools.py`: add `add_todo`, `complete_todo`, `remove_todo` schemas + executors | |
| - Wire into `chat()` tool loop (same pattern as timer) | |
| - On `complete_todo`: award +10 XP (introduce XP to `GameState` here) | |
| - Deliverable: "add a todo: review PR" β todo appears; "mark it done" β checked off, XP ticks up | |
| ### β 7. 3D Avatar | |
| Goal: Three.js GLB character in the left column that reacts to app events. | |
| - `static/js/avatar.js`: Three.js scene, `GLTFLoader`, animation mixer | |
| - Animations mapped to events: `idle` (default), `working` (timer running), `celebrate` (timer complete / level up), `cheer` (todo complete) | |
| - `bridge.js`: extend Python β JS channel to accept `{cmd: "avatar", animation: "celebrate"}` | |
| - Hook avatar triggers into existing events: timer complete, todo complete, LLM tool responses | |
| - Deliverable: character idles, animates when timer runs, celebrates on completion | |
| --- | |
| ## Gamification Values | |
| | Event | XP | Coins | | |
| |---|---|---| | |
| | Complete a to-do | +10 | β | | |
| | Finish a Pomodoro | +25 | +5 | | |
| | Finish a free-flow session | +15 | +3 | | |
| | Level up | β | +20 bonus | | |
| Level thresholds: 0 / 100 / 250 / 500 / 900 / 1400 XP | |
| ### Sample Inventory Items | |
| | Item | Cost | Level Req | | |
| |---|---|---| | |
| | Red Hat | 10 coins | Lv.1 | | |
| | Blue Cape | 25 coins | Lv.2 | | |
| | Gold Crown | 50 coins | Lv.3 | | |
| | Dark Aura | 80 coins | Lv.4 | | |
| --- | |
| ## Verification | |
| 1. **Start app**: `python app.py` β opens at `localhost:7860` | |
| 2. **Timer**: Click Start Pomodoro β countdown runs in UI β on complete, XP/coins update, avatar plays celebrate animation | |
| 3. **LLM tool use**: Type "add a todo: finish the slides" β LLM calls `add_todo`, todo appears in left column | |
| 4. **Idle reminder**: Set idle threshold to 2 min for testing, wait β nudge appears in chat | |
| 5. **Inventory**: Click π β panel slides over chat β buy Red Hat β avatar updates | |
| 6. **Deploy**: Push to HF Spaces, confirm GPU Space, verify model loads and chat works | |