unstuck / README.md
art87able's picture
honest NVIDIA path (Nemotron-Mini-4B 5/5) + bake-off table + Modal runtime serving + 3-tier fallback + SFT dataset
9ff6699 verified
|
Raw
History Blame Contribute Delete
10.9 kB
---
title: Unstuck
emoji: 🧩
colorFrom: indigo
colorTo: purple
sdk: gradio
sdk_version: "6.17.3"
python_version: "3.12.12"
app_file: app.py
pinned: false
short_description: "Tiny timed steps that learn your time-blindness"
tags:
- build-small-hackathon
- backyard-ai
- track:backyard
- track:wood
- achievement:offbrand
- achievement:fieldnotes
- achievement:sharing
- achievement:offgrid
- achievement:llama
- achievement:welltuned
- sponsor:openai
- sponsor:openbmb
- sponsor:nvidia
- sponsor:modal
- tiny-titan
- tiny-model
- llama-cpp
- off-brand
- field-notes
- sharing-is-caring
- codex
- agent-trace
- zerogpu
- qwen
- adhd
- time-blindness
- task-breakdown
- calibration
- privacy
- small-models
---
# Unstuck
Unstuck turns one overwhelming task into tiny timed steps, then learns your personal time-blindness and recalibrates the estimates to you. Built for the HF Build Small Hackathon (Backyard AI track). Runs a ≀4B model (`Qwen/Qwen3-4B-Instruct-2507`).
**Your data stays yours.** Plans and calibration history live in your browser (localStorage), not on the server β€” nothing is shared between users, and only the task text you submit ever reaches the model. Export/Import gives you the full round-trip.
What it does beyond a breakdown bot: a built-in step timer (Start β†’ Done measures for you), per-category calibration learned from your actual times with a visible "Your patterns" history, progressive reveal (only the next step is live β€” no wall of steps), recursive "Still stuck?" re-breakdown, skip-without-polluting-the-data, plan persistence across reloads, and a markdown checklist export. One `generate()` seam serves eight interchangeable backends: ZeroGPU, HF serverless inference, Nebius Token Factory, a fully-offline local GGUF (`offgrid`), OpenBMB **MiniCPM** (`minicpm`) and NVIDIA **Nemotron** (`nemotron`), a **fine-tuned** Qwen2.5-0.5B we trained on Modal and published to the Hub (`finetuned`), and that fine-tune **served on a Modal web endpoint** (`modal`) β€” each picked by one env var with no app changes. ZeroGPU also falls back ZeroGPU β†’ HF serverless β†’ local fine-tune, so a plan always returns even if GPU quota is exhausted.
## Demo & submission
- πŸ“Ί **Demo video:** [watch the ~90-second demo](https://huggingface.co/spaces/build-small-hackathon/unstuck/resolve/main/unstuck-demo.mp4)
- πŸ“£ **Social post:** https://x.com/arty_able/status/2066306266843021603
- 🧾 **Agent trace (open trace):** https://huggingface.co/datasets/build-small-hackathon/unstuck-agent-trace
- πŸ“ **Field notes (build write-up):** [HF blog post](https://huggingface.co/blog/art87able/building-unstuck-small) Β· [source](https://github.com/art87able/unstuck/blob/main/docs/field-notes.md)
- πŸ’» **Source:** https://github.com/art87able/unstuck
**Model:** `Qwen/Qwen3-4B-Instruct-2507` (4B β€” within the Tiny Titan ≀4B bar). **Built small, in the open** with OpenAI Codex (Codex-attributed commits) and an honest deterministic calibration layer β€” no AI in the differentiator.
## Badges & bonus quests β€” the evidence
| Badge / track | Claim | Evidence |
|---|---|---|
| 🏑 **Backyard AI** (track) | A real anti-overwhelm tool for ADHD time-blindness | the whole app: tiny timed steps + honest per-category calibration |
| πŸͺΆ **Tiny Titan** (≀4B) | Runs on a genuinely tiny model | default model `Qwen/Qwen3-4B-Instruct-2507` (4 B) |
| πŸ”Œ **Off the Grid** (local-first) | No cloud APIs β€” runs on the model in front of you | `UNSTUCK_BACKEND=offgrid` β†’ local GGUF, zero network. **Verified:** [`docs/offgrid-proof.png`](docs/offgrid-proof.png) is a plan generated fully offline by the local model. See *Run fully offline* below. |
| πŸ¦™ **Llama Champion** (llama.cpp) | Model runs through the llama.cpp runtime | the `offgrid` backend drives `Qwen3-4B-…-Q4_K_M.gguf` via `llama-cpp-python` (same proof screenshot) |
| 🧾 **Sharing is Caring** (open trace) | Agent trace shared on the Hub | [unstuck-agent-trace dataset](https://huggingface.co/datasets/build-small-hackathon/unstuck-agent-trace) |
| 🎨 **Off-Brand** (custom UI) | A frontend that pushes past the default Gradio look | custom `gr.themes` theme + a Fraunces gradient wordmark, gradient buttons, layered/hover step cards, indigo focus rings, fully de-branded chrome (see `THEME`/`CSS` in `app.py`) |
| πŸ“ **Field Notes** | A published write-up of what we built and learned | [HF blog: *Building Unstuck small*](https://huggingface.co/blog/art87able/building-unstuck-small) (also in [`docs/field-notes.md`](https://github.com/art87able/unstuck/blob/main/docs/field-notes.md)) |
| πŸ€– **sponsor:openai** (Codex) | Codex-attributed commits in the repo | built with the OpenAI Codex CLI β€” commit trail + the [agent-trace dataset](https://huggingface.co/datasets/build-small-hackathon/unstuck-agent-trace) |
| 🟒 **sponsor:nvidia** (Nemotron) | The app runs on an NVIDIA Nemotron model | `nvidia/Nemotron-Mini-4B-Instruct` (4B, under the 32B cap) through the app's exact prompt + validator β€” **verified 5/5 schema-valid** breakdowns ([`scripts/finetune/modal_nemotron_test.py`](https://github.com/art87able/unstuck/blob/main/scripts/finetune/modal_nemotron_test.py)). The larger reasoning `Nemotron-3-Nano-30B` is also callable via Nebius/NIM, but its think-tokens are less schema-reliable β€” see the bake-off below |
| πŸ”΅ **sponsor:openbmb** (MiniCPM) | The app runs on an OpenBMB MiniCPM model | `UNSTUCK_BACKEND=minicpm` serves `openbmb/MiniCPM-V-4_5` over Nebius serverless via the same seam β€” live-verified valid breakdowns |
| ⚑ **sponsor:modal** (Modal) | Modal used to build **and serve** the app | the fine-tune was **LoRA-trained on a Modal A10G GPU** ([`modal_finetune.py`](https://github.com/art87able/unstuck/blob/main/scripts/finetune/modal_finetune.py)) **and is served on a Modal web endpoint** ([`modal_serve.py`](https://github.com/art87able/unstuck/blob/main/scripts/finetune/modal_serve.py)) reachable via `UNSTUCK_BACKEND=modal` β€” development *and* runtime |
| 🎯 **achievement:welltuned** (fine-tuned) | The app uses a fine-tuned model published on HF | [`art87able/unstuck-qwen2.5-0.5b-steps`](https://huggingface.co/art87able/unstuck-qwen2.5-0.5b-steps) β€” Qwen2.5-0.5B LoRA-tuned on 130 distilled Unstuck breakdowns ([dataset](https://huggingface.co/datasets/art87able/unstuck-sft-breakdowns)); `UNSTUCK_BACKEND=finetuned` runs the app on it, and it's the always-on local fallback if ZeroGPU quota runs dry |
| πŸ… **Bonus Quest Champion** | Most bonus criteria met | the twelve rows above, each genuinely earned |
## Backend bake-off β€” the seam, measured
Every small model is driven through Unstuck's *exact* breakdown contract (strict JSON schema + one repair retry) via the same `ModelAdapter` the app uses. Honest results ([`scripts/bakeoff.py`](https://github.com/art87able/unstuck/blob/main/scripts/bakeoff.py)):
| Model (serverless on Nebius) | Valid / 5 | Avg steps | Avg latency |
|---|---|---|---|
| Qwen3-30B-A3B (teacher) | 5/5 | 5.0 | 2.9s |
| OpenBMB MiniCPM-V-4.5 | 5/5 | 4.2 | **0.8s** |
| NVIDIA Nemotron-3-Nano-30B (reasoning) | 0/5 | β€” | 41.8s |
| NVIDIA Nemotron-Mini-4B-Instruct *(on Modal)* | **5/5** | 5.4 | β€” |
| **Unstuck fine-tune** (Qwen2.5-0.5B) *(on Modal)* | βœ“ valid | β€” | β€” |
The reasoning 30B Nemotron is the outlier β€” its think-tokens overrun the token budget and break the schema, which is exactly why we use the 4B non-reasoning **Nemotron-Mini** for the NVIDIA path.
## Run locally
```bash
pip install -r requirements.txt gradio
UNSTUCK_BACKEND=hf_inference HF_TOKEN=... python app.py
```
The default backend is `zerogpu`, which the Space uses. The `hf_inference` path is the lightweight local option.
### Run fully offline (`offgrid`)
No network, no cloud β€” a local quantised GGUF drives the same `generate()` seam (the honest basis for the `offgrid` badge):
```bash
pip install -r requirements.txt gradio llama-cpp-python
# drop a Qwen3-4B GGUF (e.g. Qwen3-4B-Instruct-2507-Q4_K_M.gguf) into ./models/
UNSTUCK_BACKEND=offgrid OFFGRID_GGUF_PATH=models/Qwen3-4B-Instruct-2507-Q4_K_M.gguf python app.py
```
`llama-cpp-python` is deliberately left out of `requirements.txt` (it would bloat the Space build) β€” install it only for offline use.
Verified locally β€” here it is generating a plan with **no network**, entirely on `Qwen3-4B-Instruct-2507-Q4_K_M.gguf` through llama.cpp:
![Unstuck running fully offline on a local GGUF via llama.cpp](docs/offgrid-proof.png)
Reproducible transcript (env β†’ local `.gguf` load β†’ model output, no network): [`docs/offgrid-proof.log`](https://github.com/art87able/unstuck/blob/main/docs/offgrid-proof.log).
### Run on MiniCPM or Nemotron (Nebius serverless)
The same `generate()` seam runs the app on OpenBMB MiniCPM or NVIDIA Nemotron β€” both small (<32B) models served by Nebius Token Factory, selected by one env var:
```bash
# OpenBMB MiniCPM (sponsor:openbmb)
UNSTUCK_BACKEND=minicpm NEBIUS_API_KEY=... python app.py
# NVIDIA Nemotron (sponsor:nvidia) β€” 30B-A3B reasoning model, callable but less schema-reliable
UNSTUCK_BACKEND=nemotron NEBIUS_API_KEY=... python app.py
```
MiniCPM-V is live-verified valid (5/5 in the bake-off). The Nebius Nemotron above is the **30B reasoning** model β€” callable, but the *verified-valid* Nemotron is the 4B `nvidia/Nemotron-Mini-4B-Instruct` (run on Modal, 5/5). Override `MINICPM_MODEL` / `NEMOTRON_MODEL` (or `*_BASE_URL` + `*_API_KEY`) to point at any other host β€” e.g. NVIDIA's own NIM endpoint at [build.nvidia.com](https://build.nvidia.com):
```bash
# Nemotron on NVIDIA's own NIM (build.nvidia.com)
UNSTUCK_BACKEND=nemotron \
NEMOTRON_BASE_URL=https://integrate.api.nvidia.com/v1 \
NEMOTRON_API_KEY=nvapi-... \
NEMOTRON_MODEL=nvidia/nemotron-3-nano-30b-a3b \
python app.py
```
### Run on our fine-tuned model (`finetuned`)
`UNSTUCK_BACKEND=finetuned` runs the app on [`art87able/unstuck-qwen2.5-0.5b-steps`](https://huggingface.co/art87able/unstuck-qwen2.5-0.5b-steps) β€” a Qwen2.5-0.5B-Instruct LoRA we **trained on a Modal A10G GPU** by distilling 130 schema-valid breakdowns out of the strong serverless model, then merged and published to the Hub:
```bash
pip install -r requirements.txt gradio torch transformers
UNSTUCK_BACKEND=finetuned python app.py # loads the published model, runs locally
```
The whole pipeline is in [`scripts/finetune/`](https://github.com/art87able/unstuck/tree/main/scripts/finetune): `gen_dataset.py` (distillation) β†’ `modal_finetune.py` (LoRA on Modal) β†’ `modal_verify.py` (reloads the published model and asserts a schema-valid breakdown).
Your history lives in your browser. Use the in-app **Export**/**Import** buttons to move it between devices.
Source: https://github.com/art87able/unstuck (Codex Track)