Replace relative paths (trace/, finetune/, FIELD_NOTES.md, README.md)
with absolute HF Space repo URLs (/tree/main/ for dirs, /blob/main/ for
files) — relative links in HF Space READMEs resolve to app routes, not
the repo file browser.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Final hackathon submission: README, Field Notes, UI overhaul, fine-tuned model
La nena se confundía con la pantalla overlay de canciones/cuentos y
terminaba cerrando la app para salir. Ahora Sofía reproduce la canción
o el cuento directo en su pantalla apenas termina de hablar, y el botón
de micrófono se transforma en un control de "terminar" (mood "playing":
ícono de stop, halo dorado pulsante) — un solo toque corta el audio y
vuelve a la charla.
- Elimina overlay-canciones/overlay-cuentos y songs.js/stories.js (la
grilla de exploración queda para una sección de padres más adelante)
- sofia.js: nuevas playSong()/playStory()/stopMedia() y mood "playing"
- engine.py: Sofía anuncia la canción/cuento sin pedir que toquen nada
- app.py: LUMI_SHARE=1 habilita link público temporal (*.gradio.live)
Add real song playback, precache stories, improve LLM fallback
- content/songs/: 6 curated mp3s (tracked via Git LFS), content/songs.json
now points to them and is loaded by ContentLibrary (loader.py).
- activities.json: drop the old text-only song_* placeholders, now that
"song" intent plays real audio (mirrors the "story" pattern: the LLM
only announces, never sings/invents lyrics).
- engine.py: dedicated <nota> + no-LLM fallback for the "song" intent;
FALLBACK_REPLY now points users at the real cause (ZeroGPU limit)
instead of a vague "se me trabó la lengua"; log each failed
_generate attempt for easier diagnosis without burning extra quota.
- frontend: songs.js plays/pauses the suggested song (auto-play on
overlay open, tap to pause), wired up in app.js.
- voice/tts.py + app.py: precache all content/stories.json audio at
startup, so "Escuchar cuento" never hits a cold ~25s synthesis wait.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Stop tracking CLAUDE.md/PLAN.md in the Space repo
Internal Claude Code notes shouldn't be part of the deployed Space's
file listing; keep them locally (same pattern already used for
HACKATHON.md/AGENTS.md).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add panel parental, fix app.py docstring, model card, and open trace
- finetune/build_dataset.py: genera dataset.jsonl (196 ejemplos: saludos,
charla/persona, actividades, sofia_color, cuentos, refusals) a partir
del SYSTEM_PROMPT real de llm/engine.py.
- finetune/train_modal.py: QLoRA (r=16, alpha=32, q/k/v/o_proj, 4-bit nf4)
sobre Qwen2.5-7B-Instruct en Modal/A10G.
- finetune/merge_lora.py: mergea el adapter con la base y publica el
modelo en HF.
- finetune/smoke_test_modal.py: smoke test de 5 turnos sobre el modelo
publicado (saludo, <contenido> verbatim, redirecciones).
Adapter entrenado y modelo mergeado publicado en
build-small-hackathon/sofia-qwen2.5-7b; MODEL_ID en llm/engine.py
(commit anterior) ya apunta a ese repo.
Add Sofia color-change, Cuentos frontend section, Ollama local backend
- content/colors.json + content/loader.py: paleta curada para "Sofía,
cambiate a <color>"; llm/engine.py detecta la intención sofia_color y
presenta el <contenido> curado, frontend/static/sofia.js aplica la
paleta a las variables CSS de Sofía.
- frontend: nueva sección "Cuentos" (frontend/static/stories.js,
content/stories.json, index.html, style.css) junto con ajustes en
activities.js, app.js, songs.js.
- llm/engine.py: bloques <nota> para saludo y presentación de cuentos,
backend alternativo vía Ollama (LUMI_LLM_BACKEND=ollama) para correr
local sin GPU grande, documentado en CLAUDE.md/requirements.txt.
- voice/tts.py: silencio inicial en el audio para que parlantes
Bluetooth no se coman las primeras sílabas.
- parental/store.py: quita líneas de fase/nombre de memory_block.
Remove internal hackathon notes and dead prototypes from the repo
Untrack HACKATHON.md, AGENTS.md, demo.py and bench.py (kept locally,
now gitignored): HACKATHON.md/AGENTS.md were internal hackathon
strategy/duplicate-agent docs, and demo.py/bench.py were pre-pivot
prototypes (ollama/edge-tts/Piper) no longer used by app.py. Keeps the
public Space repo focused on README.md, PLAN.md, CLAUDE.md and the
actual app code.
Rework frontend UI, add Actividades and Canciones sections
- Rebuild frontend (Sofia design): 3-tab app (Hablar/Actividades/Canciones)
with mood-based character animation, served via new /static and /content
static mounts.
- Actividades: "¿Qué forma es esta?" shape-recognition game, answerable by
touch or voice, content sourced from content/shapes.json.
- Canciones: song grid driven by content/songs.json, gracefully marks
tracks "Próximamente" until their mp3s are added to content/songs/.
- Tighten safety/guard.py to use word-boundary matching against
safety/blocklist.txt (avoids false positives like "arma" in "farmacia"),
and expand blocklist with plural/feminine variants.
_generate now declares a dynamic @spaces.GPU duration (15s for the
1-token warmup, 25s for normal turns) instead of the 60s default,
which HF docs say improves queue priority on the shared fleet.
Also documents the real cause behind "always FALLBACK_REPLY": ZeroGPU
GPU-time quota is billed per visitor (2/5/40 min for anon/free/PRO,
resetting 24h after first use), not per Space. Once a visitor's quota
is gone, every @spaces.GPU call from them fails with the same "No CUDA
GPUs are available" traceback as the import-order bug, but it's a
quota wall, not a code issue.
Simplify startup flow: drop scripted greeting, start from child's first turn
The session_start/awaiting_mood choreography always opened with a canned
"Hoy podemos hacer X, Y o Z" line (awkward Spanish for most activity
titles), and the child's first reply was discarded in favor of repeating
that same menu, which felt like the conversation restarting.
Remove the whole pre-scripted flow: the app now stays idle until the
child presses the mic, and the first utterance goes straight through
engine.respond() like any other turn.
Clean up stale GGUF/llama.cpp references in PLAN.md and finetune/README.md
Repo tree no longer lists models/model.gguf; finetune export now
targets a merged safetensors checkpoint pointed to via MODEL_ID in
llm/engine.py, matching the transformers+ZeroGPU runtime.
The transformers+ZeroGPU pivot is now confirmed working end-to-end on
the live Space (zero-a10g): real LLM replies in ~6-12s vs. 34-180s on
cpu-basic with llama.cpp. Document the two gotchas that blocked it
(spaces-before-torch import order, Kokoro device="cpu") and the
retry/fallback resilience, so future sessions don't regress these.
Update HACKATHON.md merit table/conflicts to reflect Mérito 1 verified.
ZeroGPU's RuntimeError "No CUDA GPUs are available" / "CUDA must not be
initialized in the main process" is commonly caused by torch getting
imported (and CUDA touched) before the `spaces` package patches torch's
CUDA init for its IPC handoff. Our engine.py imported torch before
spaces, and app.py never imported spaces directly. Make `import spaces`
the first import in app.py and reorder engine.py accordingly.
Also broaden _generate_with_retry's except clause to Exception: the
GPU-allocation failure crosses the ZeroGPU worker IPC boundary and may
not surface as a plain RuntimeError on the caller side.
Add retry + curated-content fallback for ZeroGPU allocation failures
ZeroGPU can return RuntimeError "No CUDA GPUs are available" from
worker_init when the shared fleet is saturated (common during a
hackathon with many concurrent Spaces). _generate_with_retry retries
briefly, and respond() falls back to the curated activity script (or a
warm canned line for free chat) instead of surfacing a 500 to the kid.
Fix ZeroGPU startup crash: force Kokoro TTS onto CPU
KPipeline(device=None) auto-detects torch.cuda.is_available(), which is
True under ZeroGPU even at module load time. It then moves the Kokoro
model to CUDA, but tts.warmup() runs outside any @spaces.GPU call, so
ZeroGPU's torch patching raises "Expected all tensors to be on the same
device, ... cuda:0 (ZeroGPU) and cpu". TTS doesn't need GPU; pin it to CPU.
cpu-basic gave 34-41s per LLM response even after the n_threads fix
-- unusable for the target audience (~3yo). The org's HF billing
doesn't support paid dedicated hardware, but ZeroGPU IS available
(confirmed via the hardware API, no billing error).
llama-cpp-python uses raw ctypes CUDA bindings (libcudart/libcublas),
not torch.cuda, so ZeroGPU's allocator can't grant it GPU access.
Rewrote llm/engine.py to load Qwen2.5-7B-Instruct via transformers
(module-level model/tokenizer, bf16, .to("cuda")) with a module-level
_generate() decorated with @spaces.GPU (must be module-level + take
only picklable args, since ZeroGPU calls run via IPC to a GPU-attached
subprocess -- a bound method closing over self.store's sqlite
connection would not survive that).
Drops Mérito 4 "Llama Champion" (documented in HACKATHON.md/PLAN.md/
CLAUDE.md as a conscious tradeoff); keeps Mérito 1 "Off the Grid"
since the model still runs in the Space with no external inference
API. Business logic (intent detecti…
Revert "Switch llama-cpp-python to CUDA wheel for T4-small upgrade"
CPU tier (cpu-basic) gave 34-41s per LLM response even after the
n_threads fix -- too slow for the demo. Moving to T4-small GPU
hardware per PLAN.md's credit strategy (HF $20, sleep-on-idle).
n_gpu_layers defaults to -1 (all layers on GPU), already the
default in llm/engine.py and not overridden by any Space variable.
Fix LLM perf on free CPU tier: pin n_threads=2, warmup at startup
Live Space testing showed every /chat call hitting the LLM took
90-180s (vs <1s locally on GPU). Without an explicit n_threads,
llama-cpp-python falls back to the host's CPU count, not the
cgroup quota of HF's cpu-basic tier (2 vCPU) -- massive thread
oversubscription. Also add engine.warmup() (mirrors tts.warmup())
so the first-call mmap/page-fault cost is paid at boot, not on
the child's first turn.
The Space build OOMKilled (exit 137) while compiling llama-cpp-python's
CMake/C++ build from the PyPI sdist. abetlen's CPU wheel index has a
prebuilt 0.3.28 wheel for cp312/linux_x86_64, skipping the compile step.
Pin Space to Python 3.12 (matches the env we validated locally)
- llm/engine.py, frontend/index.html: user-facing persona rename to Sofía
(with grammatical gender agreement in the system prompt)
- app.py: auto-download the GGUF from bartowski/Qwen2.5-7B-Instruct-GGUF
if models/model.gguf isn't present (Space doesn't version the 4.6GB file)
- requirements.txt: add huggingface_hub (used by the download fallback)
- README.md: add HF Space YAML frontmatter (Gradio SDK, app_file, license,
short description) and finish the Sofía rename in the landing page text
- LICENSE: add MIT license
- llm/engine.py: migrate from Ollama to llama-cpp-python (GGUF, chatml),
add real conversation history from ParentalStore, fix system prompt to
neutral Latin American Spanish
- voice/tts.py: remove edge-tts fallback, reuse a single Kokoro pipeline
instance, add warmup() to pay cold-load cost at startup not on first reply
- safety/blocklist.txt: expand from 3 to 44 terms
- requirements.txt, README.md: sync with actual stack