Codex commited on
Commit
e9342cc
Β·
1 Parent(s): 85e7beb

Add field notes (lessons learned) to README and FIELD_NOTES.md

Browse files
Files changed (2) hide show
  1. FIELD_NOTES.md +75 -0
  2. 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.