Spaces:
Sleeping
Sleeping
Codex commited on
Commit Β·
e9342cc
1
Parent(s): 85e7beb
Add field notes (lessons learned) to README and FIELD_NOTES.md
Browse files- FIELD_NOTES.md +75 -0
- README.md +28 -0
FIELD_NOTES.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Field Notes β Building Tabras
|
| 2 |
+
|
| 3 |
+
What I learned building a small-model card duel for the Build Small hackathon.
|
| 4 |
+
|
| 5 |
+
## 1. ZeroGPU is a GPU-sharing mechanism, not a hosted GPU provider
|
| 6 |
+
|
| 7 |
+
This was my biggest wall, and I hit it late. I built Tabras assuming a Hugging Face
|
| 8 |
+
ZeroGPU Space would behave like a hosted GPU I could just run my models on. It does
|
| 9 |
+
not. ZeroGPU *time-slices* a shared GPU and only grants it inside `@spaces.GPU`
|
| 10 |
+
functions β and it ships args/returns between processes by pickling them. That
|
| 11 |
+
breaks the moment you try to hand it a `trust_remote_code` model (MiniCPM) or a
|
| 12 |
+
diffusers pipeline: they aren't picklable, and low-level CUDA init isn't allowed in
|
| 13 |
+
the main process at all. I burned real time learning this through a wall of
|
| 14 |
+
`PicklingError` and "CUDA init reached" tracebacks.
|
| 15 |
+
|
| 16 |
+
The last-minute fix was to **re-architect**: make the Space a thin HTTP client and
|
| 17 |
+
move every model onto **Modal** GPU endpoints (MiniCPM for cards, Nemotron for the
|
| 18 |
+
boss, SDXL-Turbo for art), each autoscaled on its own dedicated GPU. The Space just
|
| 19 |
+
POSTs prompts and renders the results. Lesson: understand the *execution model* of
|
| 20 |
+
your compute before you design around it, not after.
|
| 21 |
+
|
| 22 |
+
Crucially, Tabras still runs **fully local / off-grid** β a `MODE` switch flips
|
| 23 |
+
between LOCAL (in-process Transformers/Diffusers, or a local `llama.cpp` server for
|
| 24 |
+
MiniCPM) and MODAL (the hosted endpoints). The README has the local instructions.
|
| 25 |
+
Small and local was always the point, and keeping that path alive mattered to me.
|
| 26 |
+
|
| 27 |
+
## 2. Small models: surprisingly powerful, with sharp edges
|
| 28 |
+
|
| 29 |
+
- **Image models punch way above their size.** SDXL-Turbo, in ~4 denoising steps,
|
| 30 |
+
produced genuinely striking card art and theme backgrounds. The visual identity of
|
| 31 |
+
the whole game is carried by a tiny, fast diffusion model.
|
| 32 |
+
- **Nemotron was the standout for agentic / tool-calling work.** Giving it the public
|
| 33 |
+
board state and a constrained JSON action schema, it reliably reads the situation
|
| 34 |
+
and returns valid, sensible plays. A 4B model running a competent opponent was the
|
| 35 |
+
part I expected to be flaky and wasn't.
|
| 36 |
+
- **The text model owns meaning, not structure.** MiniCPM writes evocative names and
|
| 37 |
+
flavor, but it leans on lazy patterns ("Fire Card"), leaks prompt vocabulary, and
|
| 38 |
+
truncates its own JSON. The single highest-leverage fix was reordering the requested
|
| 39 |
+
JSON so `effects` and `name` come *first* β they survive a token cutoff that used to
|
| 40 |
+
silently collapse a whole card to a fallback. Design around what the model is bad at.
|
| 41 |
+
|
| 42 |
+
## 3. Perceived latency beats raw latency
|
| 43 |
+
|
| 44 |
+
Small/local inference isn't instant, and the variance (some packs fast, some slow)
|
| 45 |
+
looked worse than a consistent wait. The fixes that made the demo feel *good* were
|
| 46 |
+
mostly UX, not compute:
|
| 47 |
+
|
| 48 |
+
- A **minimum loading window** on each draft transition, so every pick shows the same
|
| 49 |
+
deliberate "forging" beat β that uniform pause hides the slow ones behind the same
|
| 50 |
+
animation the fast ones use.
|
| 51 |
+
- **Prefetching every branch** of the draft during idle time (the reveal/rules screens,
|
| 52 |
+
and while you read the current pack), so whichever card you pick, its next pack is
|
| 53 |
+
already generating.
|
| 54 |
+
- **Pre-baking static art** for the fixed backbone cards once and bundling it, so they
|
| 55 |
+
never spend a live generation or shimmer.
|
| 56 |
+
|
| 57 |
+
A consistent 2s always beats an unpredictable 0β8s.
|
| 58 |
+
|
| 59 |
+
## 4. Card-game design is hard β and a ton of fun
|
| 60 |
+
|
| 61 |
+
Balancing a generative card game is genuinely difficult. The core principle that made
|
| 62 |
+
it tractable: **the LLM owns meaning, the engine owns math.** The model picks *which*
|
| 63 |
+
effects a card has and writes its identity; deterministic Python prices every number
|
| 64 |
+
against a point budget, so cards are balanced-by-construction no matter what the model
|
| 65 |
+
invents. The draft is deck-aware β it reads the build you're assembling and shapes
|
| 66 |
+
packs toward it, sometimes dangling a tempting off-archetype card. Getting that loop to
|
| 67 |
+
feel fair *and* surprising was the most fun part of the whole project.
|
| 68 |
+
|
| 69 |
+
## 5. Ambition under a deadline
|
| 70 |
+
|
| 71 |
+
This was an ambitious build for the time window β three small models, a custom UI, a
|
| 72 |
+
last-minute compute re-architecture, and a real game loop. The thing that saved me was
|
| 73 |
+
treating the **demo video as the deliverable** and optimizing the local recording
|
| 74 |
+
surface hard, rather than betting everything on a flawless live Space. I'm proud of how
|
| 75 |
+
much of it came together, and I had a lot of fun doing it.
|
README.md
CHANGED
|
@@ -54,6 +54,34 @@ Demo video: https://youtu.be/qHuk9XjaFWU
|
|
| 54 |
|
| 55 |
Social post: https://x.com/yewzoid/status/2066647997740691678?s=20
|
| 56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
## What Makes It AI-Native
|
| 58 |
|
| 59 |
- **MiniCPM authors draft cards.** It proposes card concepts, names, flavor text, and effect shapes for the current deck.
|
|
|
|
| 54 |
|
| 55 |
Social post: https://x.com/yewzoid/status/2066647997740691678?s=20
|
| 56 |
|
| 57 |
+
## Field Notes β What I Learned
|
| 58 |
+
|
| 59 |
+
Full write-up in [FIELD_NOTES.md](FIELD_NOTES.md). The short version:
|
| 60 |
+
|
| 61 |
+
- **ZeroGPU is a GPU-*sharing* mechanism, not a hosted GPU provider.** I found this out
|
| 62 |
+
late. ZeroGPU time-slices a shared GPU inside `@spaces.GPU` calls and pickles
|
| 63 |
+
args/returns between processes β which breaks on `trust_remote_code` models and
|
| 64 |
+
diffusers pipelines (unpicklable), and forbids CUDA init in the main process. I had to
|
| 65 |
+
**re-architect at the last minute**: make the Space a thin HTTP client and move every
|
| 66 |
+
model onto **Modal** GPU endpoints. It still runs **fully local / off-grid** through a
|
| 67 |
+
`MODE` switch (in-process Transformers/Diffusers, or a local `llama.cpp` server for
|
| 68 |
+
MiniCPM) β small-and-local was always the point.
|
| 69 |
+
- **Small models are surprisingly capable, with sharp edges.** SDXL-Turbo makes genuinely
|
| 70 |
+
striking art in ~4 steps; Nemotron was impressive at agentic, tool-calling boss play
|
| 71 |
+
from a constrained JSON action schema. MiniCPM owns *meaning* (names, flavor) but not
|
| 72 |
+
*structure* β the biggest fix was reordering the requested JSON so `effects`/`name`
|
| 73 |
+
come first and survive a token cutoff.
|
| 74 |
+
- **Perceived latency beats raw latency.** A minimum loading window per draft pick (a
|
| 75 |
+
uniform "forging" beat) hides the slow packs behind the same animation as the fast
|
| 76 |
+
ones; prefetching every branch during idle screens makes picks feel instant; and
|
| 77 |
+
pre-baking the fixed backbone-card art means it never shimmers.
|
| 78 |
+
- **Generative card-game design is hard and a ton of fun.** The principle that made it
|
| 79 |
+
tractable: *the LLM owns meaning, the engine owns math* β the model invents the card,
|
| 80 |
+
deterministic code prices every number, so cards are balanced by construction.
|
| 81 |
+
- **Ambitious for the deadline, and I'm happy with it.** Three small models, a custom UI,
|
| 82 |
+
a compute re-architecture, and a real game loop. Treating the demo video as the
|
| 83 |
+
deliverable β and optimizing the local recording surface β is what made it land.
|
| 84 |
+
|
| 85 |
## What Makes It AI-Native
|
| 86 |
|
| 87 |
- **MiniCPM authors draft cards.** It proposes card concepts, names, flavor text, and effect shapes for the current deck.
|