Add dev scripts + simplify CLAUDE.md
Browse files- CLAUDE.md +33 -52
- scripts/check_imports.sh +5 -0
- scripts/check_sdk.sh +12 -0
- scripts/dev.sh +9 -0
- scripts/run.sh +14 -0
- scripts/syntax.sh +12 -0
- scripts/test.sh +6 -0
CLAUDE.md
CHANGED
|
@@ -4,39 +4,20 @@
|
|
| 4 |
Kids mental math app. Correct answers earn AI-generated cute animal images (FLUX.1-schnell).
|
| 5 |
Stack: Python Β· Gradio Β· diffusers Β· HuggingFace Spaces (ZeroGPU).
|
| 6 |
|
| 7 |
-
## Mandatory dev loop
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
print(f'β
sdk_version {sdk} matches local gradio')
|
| 22 |
-
" 2>&1
|
| 23 |
-
4. RUN β kill previous instance, start fresh:
|
| 24 |
-
pkill -f "python3 app.py"; ~/miniforge3/bin/python3 app.py > /tmp/numzoo.log 2>&1 &
|
| 25 |
-
sleep 10 && cat /tmp/numzoo.log # must show no errors
|
| 26 |
-
5. TEST β run Puppeteer (arm64 Node):
|
| 27 |
-
arch -arm64 /bin/zsh -c "source ~/.nvm/nvm.sh && nvm use 20 && cd /Users/uplab/projects/puppeteer && node test_lovemath.mjs"
|
| 28 |
-
β take screenshots, read them, verify visually
|
| 29 |
-
6. FIX β if any step fails, fix and restart from step 1
|
| 30 |
-
7. PUSH β only when all steps pass:
|
| 31 |
-
git add -A && git commit -m "..." && git push origin main
|
| 32 |
-
```
|
| 33 |
-
|
| 34 |
-
**Version pinning rules:**
|
| 35 |
-
- **Never put `gradio` in `requirements.txt`** β HF Spaces installs it separately using `sdk_version` from README.md frontmatter. Conflicting versions cause build failure.
|
| 36 |
-
- **Always keep `sdk_version` in README.md in sync with local gradio** β run the SDK check above before every push.
|
| 37 |
-
- To upgrade gradio: update `sdk_version` in README.md, reinstall locally (`pip install gradio==x.y.z`), re-run full loop.
|
| 38 |
-
|
| 39 |
-
**Never push broken code.** If Puppeteer tests fail, do not proceed to push.
|
| 40 |
|
| 41 |
## Key files
|
| 42 |
|
|
@@ -45,8 +26,9 @@ Stack: Python Β· Gradio Β· diffusers Β· HuggingFace Spaces (ZeroGPU).
|
|
| 45 |
| `app.py` | Gradio UI, game logic, panel visibility, event wiring |
|
| 46 |
| `image_generator.py` | FLUX.1-schnell pipeline, emojiβtext prompt builder |
|
| 47 |
| `math_engine.py` | Question generation, level names & thresholds |
|
| 48 |
-
| `requirements.txt` | Python dependencies
|
| 49 |
-
| `README.md` | HF Spaces config (frontmatter) + user docs |
|
|
|
|
| 50 |
|
| 51 |
## Architecture
|
| 52 |
|
|
@@ -54,36 +36,35 @@ Stack: Python Β· Gradio Β· diffusers Β· HuggingFace Spaces (ZeroGPU).
|
|
| 54 |
|
| 55 |
**Game state dict keys:**
|
| 56 |
`name`, `level`, `score`, `streak`, `correct_since_reward`, `level_correct`,
|
| 57 |
-
`question`, `answer`, `selected_animals`, `selected_places`,
|
| 58 |
-
`future` (ThreadPoolExecutor Future), `awaiting_reward`
|
| 59 |
|
| 60 |
**Reward trigger:** every `REWARD_EVERY = 3` correct answers.
|
| 61 |
-
Image
|
| 62 |
|
| 63 |
## Gradio rules
|
| 64 |
-
- Use `gr.update(visible=True/False)` β never return bare
|
| 65 |
- Theme and CSS go in `demo.launch(css=..., theme=...)`, NOT in `gr.Blocks()`
|
| 66 |
-
- `gr.Group` has no `.change()` event
|
| 67 |
-
-
|
|
|
|
| 68 |
|
| 69 |
## Python runtime
|
| 70 |
- **Local:** `~/miniforge3/bin/python3` (arm64, Python 3.13, torch 2.12, MPS)
|
| 71 |
-
- **HF Spaces:** Python 3.13, ZeroGPU (A100
|
| 72 |
-
-
|
| 73 |
|
| 74 |
## Image generation
|
| 75 |
-
- Model: `black-forest-labs/FLUX.1-schnell` (gated β
|
| 76 |
-
- Local
|
| 77 |
-
- Always wrap
|
| 78 |
- `IS_HF_SPACE = os.environ.get("SPACE_ID") is not None` controls ZeroGPU decorator
|
| 79 |
|
| 80 |
## HuggingFace deployment
|
| 81 |
- Space: `https://huggingface.co/spaces/Goumsss/numzoo`
|
| 82 |
-
-
|
| 83 |
-
-
|
| 84 |
-
- ZeroGPU requires HF Pro or hackathon grant (set in Space Settings β Hardware)
|
| 85 |
|
| 86 |
## UI/UX principles
|
| 87 |
-
-
|
| 88 |
-
-
|
| 89 |
-
- Never crash visibly β all handlers wrapped in try/except with safe
|
|
|
|
| 4 |
Kids mental math app. Correct answers earn AI-generated cute animal images (FLUX.1-schnell).
|
| 5 |
Stack: Python Β· Gradio Β· diffusers Β· HuggingFace Spaces (ZeroGPU).
|
| 6 |
|
| 7 |
+
## Mandatory dev loop
|
| 8 |
+
Run these scripts in order before every push. Fix failures before proceeding.
|
| 9 |
+
|
| 10 |
+
| Step | Command | Must pass |
|
| 11 |
+
|------|---------|-----------|
|
| 12 |
+
| Syntax | `bash scripts/syntax.sh` | No syntax errors |
|
| 13 |
+
| Imports | `bash scripts/check_imports.sh` | No import errors |
|
| 14 |
+
| SDK version | `bash scripts/check_sdk.sh` | README sdk_version == local gradio |
|
| 15 |
+
| Run | `bash scripts/run.sh` | App starts, no errors in logs |
|
| 16 |
+
| Test | `bash scripts/test.sh` | All Puppeteer checks pass, screenshots look correct |
|
| 17 |
+
|
| 18 |
+
Full loop shortcut: `bash scripts/dev.sh` (runs steps 1β4, then run test.sh manually).
|
| 19 |
+
|
| 20 |
+
**Never push if any step fails.**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
## Key files
|
| 23 |
|
|
|
|
| 26 |
| `app.py` | Gradio UI, game logic, panel visibility, event wiring |
|
| 27 |
| `image_generator.py` | FLUX.1-schnell pipeline, emojiβtext prompt builder |
|
| 28 |
| `math_engine.py` | Question generation, level names & thresholds |
|
| 29 |
+
| `requirements.txt` | Python dependencies β never include `gradio` here |
|
| 30 |
+
| `README.md` | HF Spaces config (frontmatter `sdk_version`) + user docs |
|
| 31 |
+
| `scripts/` | Dev loop automation scripts |
|
| 32 |
|
| 33 |
## Architecture
|
| 34 |
|
|
|
|
| 36 |
|
| 37 |
**Game state dict keys:**
|
| 38 |
`name`, `level`, `score`, `streak`, `correct_since_reward`, `level_correct`,
|
| 39 |
+
`question`, `answer`, `selected_animals`, `selected_places`, `generate_now`
|
|
|
|
| 40 |
|
| 41 |
**Reward trigger:** every `REWARD_EVERY = 3` correct answers.
|
| 42 |
+
Image generated via `.then()` chaining on the Check button β synchronous, within Gradio request context (required for ZeroGPU).
|
| 43 |
|
| 44 |
## Gradio rules
|
| 45 |
+
- Use `gr.update(visible=True/False)` for visibility β never return bare booleans
|
| 46 |
- Theme and CSS go in `demo.launch(css=..., theme=...)`, NOT in `gr.Blocks()`
|
| 47 |
+
- `gr.Group` has no `.change()` event
|
| 48 |
+
- Never put `gradio` in `requirements.txt` β HF Spaces pins it via `sdk_version` in README
|
| 49 |
+
- Keep `sdk_version` in README in sync with local gradio (run `scripts/check_sdk.sh`)
|
| 50 |
|
| 51 |
## Python runtime
|
| 52 |
- **Local:** `~/miniforge3/bin/python3` (arm64, Python 3.13, torch 2.12, MPS)
|
| 53 |
+
- **HF Spaces:** Python 3.13, ZeroGPU (A100 via `@spaces.GPU`)
|
| 54 |
+
- Do not use system Python (`/usr/local/bin/python3`) β it's x64 Rosetta, incompatible
|
| 55 |
|
| 56 |
## Image generation
|
| 57 |
+
- Model: `black-forest-labs/FLUX.1-schnell` (gated β accept license on HF before first run)
|
| 58 |
+
- Local: ~5 min on MPS Β· HF ZeroGPU: ~5 sec
|
| 59 |
+
- Always wrap in try/except β return `(None, "")` on failure
|
| 60 |
- `IS_HF_SPACE = os.environ.get("SPACE_ID") is not None` controls ZeroGPU decorator
|
| 61 |
|
| 62 |
## HuggingFace deployment
|
| 63 |
- Space: `https://huggingface.co/spaces/Goumsss/numzoo`
|
| 64 |
+
- Push = redeploy. Rebuilds automatically.
|
| 65 |
+
- ZeroGPU: set in Space Settings β Hardware (requires HF Pro or hackathon grant)
|
|
|
|
| 66 |
|
| 67 |
## UI/UX principles
|
| 68 |
+
- English only, minimal text β words not sentences (kids with limited English)
|
| 69 |
+
- Emoji-heavy feedback (`β
Great! π₯π₯` not full sentences)
|
| 70 |
+
- Never crash visibly β all handlers wrapped in try/except with safe fallbacks
|
scripts/check_imports.sh
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
cd "$(dirname "$0")/.."
|
| 3 |
+
echo "Checking imports..."
|
| 4 |
+
~/miniforge3/bin/python3 -c "import app" 2>&1
|
| 5 |
+
if [ $? -eq 0 ]; then echo "β
imports OK"; else echo "β import failed"; exit 1; fi
|
scripts/check_sdk.sh
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
cd "$(dirname "$0")/.."
|
| 3 |
+
~/miniforge3/bin/python3 - <<'EOF'
|
| 4 |
+
import re, subprocess, sys
|
| 5 |
+
sdk = re.search(r'sdk_version: "(.+?)"', open('README.md').read()).group(1)
|
| 6 |
+
out = subprocess.check_output(['/Users/uplab/miniforge3/bin/pip','show','gradio']).decode()
|
| 7 |
+
ver = re.search(r'Version: (.+)', out).group(1)
|
| 8 |
+
if sdk != ver:
|
| 9 |
+
print(f'β MISMATCH: README sdk_version={sdk} but local gradio={ver}')
|
| 10 |
+
sys.exit(1)
|
| 11 |
+
print(f'β
sdk_version {sdk} matches local gradio')
|
| 12 |
+
EOF
|
scripts/dev.sh
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
set -e
|
| 3 |
+
DIR="$(dirname "$0")"
|
| 4 |
+
echo "=== 1/4 Syntax ===" && bash "$DIR/syntax.sh"
|
| 5 |
+
echo "=== 2/4 Imports ===" && bash "$DIR/check_imports.sh"
|
| 6 |
+
echo "=== 3/4 SDK ===" && bash "$DIR/check_sdk.sh"
|
| 7 |
+
echo "=== 4/4 Run ===" && bash "$DIR/run.sh"
|
| 8 |
+
echo ""
|
| 9 |
+
echo "App is running. Now run: bash scripts/test.sh"
|
scripts/run.sh
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
cd "$(dirname "$0")/.."
|
| 3 |
+
pkill -f "python3 app.py" 2>/dev/null; sleep 1
|
| 4 |
+
~/miniforge3/bin/python3 app.py > /tmp/numzoo.log 2>&1 &
|
| 5 |
+
echo "Starting app..."
|
| 6 |
+
sleep 10
|
| 7 |
+
PORT=$(lsof -i :7860 -i :7861 -i :7862 -i :7863 -i :7864 -i :7865 2>/dev/null | grep LISTEN | tail -1 | grep -oE '786[0-9]')
|
| 8 |
+
if [ -z "$PORT" ]; then
|
| 9 |
+
echo "β App failed to start. Logs:"
|
| 10 |
+
cat /tmp/numzoo.log
|
| 11 |
+
exit 1
|
| 12 |
+
fi
|
| 13 |
+
echo "β
App running on http://localhost:$PORT"
|
| 14 |
+
echo $PORT > /tmp/numzoo.port
|
scripts/syntax.sh
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
cd "$(dirname "$0")/.."
|
| 3 |
+
~/miniforge3/bin/python3 -c "
|
| 4 |
+
import ast, sys
|
| 5 |
+
for f in ['app.py','image_generator.py','math_engine.py']:
|
| 6 |
+
try:
|
| 7 |
+
ast.parse(open(f).read())
|
| 8 |
+
print(f'β
{f}')
|
| 9 |
+
except SyntaxError as e:
|
| 10 |
+
print(f'β {f}: {e}')
|
| 11 |
+
sys.exit(1)
|
| 12 |
+
"
|
scripts/test.sh
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
cd "$(dirname "$0")/.."
|
| 3 |
+
PORT=$(cat /tmp/numzoo.port 2>/dev/null || echo "7864")
|
| 4 |
+
# Update port in test file
|
| 5 |
+
sed -i '' "s|http://localhost:[0-9]*/|http://localhost:$PORT/|g" /Users/uplab/projects/puppeteer/test_lovemath.mjs
|
| 6 |
+
arch -arm64 /bin/zsh -c "source ~/.nvm/nvm.sh && nvm use 20 && cd /Users/uplab/projects/puppeteer && node test_lovemath.mjs"
|