LFED / docs /PLAN.md
Kasualdad's picture
Initial commit: Kasualdad LFED — Phases 0-8 complete
17674c2
|
Raw
History Blame Contribute Delete
7.18 kB
# Full Plan — Kasualdad LFED
> Reference: produced 2026-06-06 in session 1, used to drive [`HANDOFF.md`](./HANDOFF.md)
---
## Why this project
A school-district admin (principal, superintendent, dept. head) types a natural-language question → the app returns the answer as a table + chart, with the SQL shown for transparency. All inference is local (llama.cpp, GGUF). No data leaves the machine. Model is a small open LLM fine-tuned on text-to-SQL via Modal. Runs on a free Hugging Face Space.
**Target**: win the HF "Build Small" Hackathon (Chapter One: Backyard AI) + produce a credible demo slice for clients/employers.
---
## 9-Day Schedule (Locked)
| Day | Date | Phase | Work | Output |
|---|---|---|---|---|
| 1 | Jun 6 | 0 | Bootstrap (3.12 venv, packages.txt, pinned requirements) | Working 3.12 env |
| 1 | Jun 6 | 1 | Model sanity check (Qwen2.5-Coder-7B Q4_K_M) | Locked model name |
| 1 | Jun 6 | 2 | Refactor: `data_engine.py` / `model_inference.py` / `prompts.py` / `app.py` | Modular codebase |
| 2 | Jun 7 | 3 | Robustness: column validation, read-only wrap + LIMIT, timeout, JSON parser, streaming, per-request DuckDB conn | Bulletproof pipeline |
| 2 | Jun 7 | 4 | Data: realistic seed (5 schools × 4 years, populated chronically-absent flag) | Demoable queries |
| 3 | Jun 8 | 5 | UI polish (Linear/Vercel + teal + Off-Brand criteria) | Polished Space |
| 3 | Jun 8 | 6 | Tests (execution_guard, data_engine, model_inference) | Green pytest |
| 3 | Jun 8 | 7a | Synthetic NL→SQL pairs (2–3k) | `train.jsonl` |
| 3→4 | Jun 8–9 | 7b | Modal Unsloth QLoRA (A10G, ~3–6 hrs, **overnight**) | Fine-tuned adapter |
| 4 | Jun 9 | 7c | Merge → GGUF Q4_K_M → push to HF → swap `REPO_ID` | Custom model live |
| 4–5 | Jun 9–10 | 8 | README (frontmatter, mermaid, badges, design doc, demo GIF) | Submission-ready README |
| 5–6 | Jun 10–11 | 9 | Deploy to Space, cold-start verify, end-to-end smoke test | Live demo URL |
| 7–8 | Jun 12–13 | 10 | Buffer: prompt iteration, UX hardening, retrain if needed | Polish |
| 9 | Jun 14 | 11 | Final verification + submit | Submitted |
**Critical path**: Phase 7 (fine-tuning). Starting it on Day 3 gives a full day of compute buffer if a re-train is needed.
---
## Phase Details
### Phase 0 — Bootstrap ✅ DONE
- Project copied to `~/projects/build-small-hackathon/Kasualdad_LFED/`
- venv at Python 3.12.8
- `requirements.txt`: gradio==6.16.0, duckdb==1.5.3, llama-cpp-python==0.3.26, huggingface_hub==1.18.0
- `packages.txt`: cmake, build-essential, libopenblas-dev
- All imports verified
### Phase 1 — Model sanity check 🟡 IN PROGRESS
- ✅ Identified candidate GGUFs (mradermacher quantizer for both)
- ✅ Downloaded Qwen2.5-Coder-7B-Instruct.Q4_K_M.gguf to `/tmp/lfed-models/qwen/` (4.4 GB)
- ❌ Run inference on 10 hand-crafted prompts to confirm SQL quality
- ⏸️ Llama-3.1-8B-Instruct Q4_K_M — only download if Qwen fails
**Sanity-check command** is in [`HANDOFF.md`](../docs/HANDOFF.md) under "Phase 1: Sanity-Check Resume".
### Phase 2 — Refactor
- Extract `data_engine.py` (DuckDB, schema introspection, `execute_safe()`)
- Extract `model_inference.py` (Llama lifecycle, `generate_sql()`, streaming)
- Extract `prompts.py` (system prompt + few-shot examples + schema docstring)
- Slim `app.py` to UI + controller
### Phase 3 — Robustness
- Parse the JSON `{sql, explanation}` envelope (fall back to ```sql``` block)
- Validate column names exist in schema (AST parse, not regex)
- Reject any non-SELECT; wrap as `SELECT * FROM (<user_query>) LIMIT 1000`
- Per-query timeout via `duckdb.set_query_timeout`
- Thread safety: per-request DuckDB connection (cheap, in-memory)
- Streaming: yield tokens to Gradio's `stream=True` callback
- Error UX: surface clean messages in UI
### Phase 4 — Data
- 5 schools, 4 school years (`2021-2022` … `2024-2025`), 12 grade levels
- Attendance with `is_chronically_absent` populated ~15% true
- Document schema in README
### Phase 5 — UI polish (Off-Brand badge)
- Custom CSS injected via `gr.Blocks(css=...)` on top of Gradio default
- Inter (UI) + JetBrains Mono (code)
- Single accent color (#14b8a6 teal), neutral grays
- Single column, max-width 960px
- Smooth state changes (120ms ease-out), 200ms result reveal
- Honors `prefers-reduced-motion`
- Example-query chips: 4–6 one-click starters
- Mobile/responsive sanity
### Phase 6 — Tests
- `test_execution_guard.py`: malicious inputs, malformed JSON, missing columns, multi-statement
- `test_data_engine.py`: schema introspection, timeout, empty result
- `test_model_inference.py`: mock the LLM, verify prompt assembly and JSON parsing
### Phase 7 — Modal fine-tuning (Well-Tuned badge)
- `generate_synthetic.py`: 2–3k (NL question, SQL) pairs from seed schema
- `train.py`: Unsloth + QLoRA, ~3 epochs, A10G on Modal (free credits)
- `export_gguf.py`: merge LoRA → GGUF Q4_K_M → push to new HF repo
- `app.py` swaps `REPO_ID` to fine-tuned model
- Verify accuracy improvement on a held-out prompt set
**Modal config**:
```python
import modal
app = modal.App("kasualdad-lfed-train")
# image: unsloth + transformers + trl + bitsandbytes
# volume: /data for training pairs
# secret: HF_TOKEN (for Phase 7c push)
@app.function(gpu="A10G", timeout=4*3600, secrets=[modal.Secret.from_name("huggingface")])
def train(...):
...
```
**HF repo name (TBD username)**: `<hf-username>/lfed-qwen2.5-coder-7b-sql-gguf`
### Phase 8 — README
- Frontmatter: `tags: text-to-sql, education, local-first, llama-cpp, duckdb`
- Mermaid architecture diagram
- Badge checklist with links
- 30-second demo GIF
- "How to run locally" section
### Phase 9 — Deploy & verify
- `python app.py` boots, sample queries return correct results
- Push to HF Space, watch cold start (model download, GGUF cache)
- Submit to hackathon with Space URL + HF model repo + brief write-up
---
## Out of Scope (defer or skip)
- ❌ Real Parquet/CSV data lake loader — seed tables only
- ❌ Per-request data file upload — fixed schema
- ❌ User accounts / auth — single-user demo
- ❌ Chart auto-generation — basic dataframe display only
- ❌ Detailed observability/logging beyond stderr
---
## Risks & Mitigations
| Risk | Mitigation |
|---|---|
| `gradio==6.16.0` may not exist on Spaces | Verified on PyPI; if Space rejects, downgrade to 5.x |
| Modal cold start eats the A10G budget | Cache image; warm-up job; free credits = no budget pressure |
| Synthetic data quality is too low → bad fine-tune | Hand-validate 50 pairs before training; iterate prompt template if model can't generalize |
| HF Space cold start > 5 min (8B model download) | Q4_K_M (~4.4 GB) fits well; add startup log |
| llama-cpp-python wheel not available for Py 3.12 | `packages.txt` includes build tools; pin a known-good version |
| User provides wrong HF username | Repo name is a one-line swap on Day 4 |
---
## Outstanding Dependencies (chronological)
| When | Need | Status |
|---|---|---|
| Day 1 (now) | — | ✅ Unblocked |
| Day 3 | Modal secret `huggingface` (HF token) | ⏳ Pending |
| Day 4 | HF username (user said `kasualdad` was wrong) | ⏳ Pending |