FredinVázquez commited on
Commit
bad5d84
·
1 Parent(s): c795fe5

add strategy plan

Browse files
Strategy/arquitectura.html ADDED
@@ -0,0 +1,668 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="es">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Cocina Conmigo — Plan visual del proyecto</title>
7
+ <style>
8
+ :root {
9
+ --bg: #f5ecd9;
10
+ --card: #fffbf0;
11
+ --ink: #2b2018;
12
+ --accent: #a85c2a; /* terracotta */
13
+ --accent-soft: #f6dccc;
14
+ --accent2: #6b4a2a;
15
+ --gold: #c9962b;
16
+ --green: #3f7a3a;
17
+ --green-soft: #dbe9d8;
18
+ --red: #b03a2e;
19
+ --red-soft: #f4d6d2;
20
+ --gray: #8a7e6f;
21
+ --line: #d8c9ad;
22
+ }
23
+ * { box-sizing: border-box; }
24
+ body {
25
+ font-family: 'Inter', -apple-system, sans-serif;
26
+ background: var(--bg);
27
+ color: var(--ink);
28
+ margin: 0;
29
+ padding: 32px 16px 80px;
30
+ line-height: 1.55;
31
+ }
32
+ .wrap { max-width: 1240px; margin: 0 auto; }
33
+
34
+ h1 { font-family: 'Lora', Georgia, serif; font-size: 46px; margin: 0 0 4px;
35
+ letter-spacing: -0.5px; font-weight: 700; }
36
+ h1 em { color: var(--accent); font-style: italic; }
37
+ .subtitle { color: var(--accent2); font-style: italic; margin-bottom: 28px; font-size: 17px; }
38
+
39
+ h2 {
40
+ margin-top: 56px; border-top: 1px dashed var(--line); padding-top: 24px;
41
+ font-size: 26px; font-family: 'Lora', Georgia, serif; letter-spacing: 0.3px;
42
+ }
43
+ h2 .num {
44
+ color: var(--accent); font-family: ui-monospace, monospace;
45
+ font-size: 20px; margin-right: 10px;
46
+ }
47
+ h3 { font-size: 18px; margin-top: 28px; color: var(--accent2); font-family: 'Lora', Georgia, serif; }
48
+
49
+ /* Hero */
50
+ .hero {
51
+ background: var(--card); border: 2px solid var(--ink); border-radius: 14px;
52
+ padding: 30px 32px; display: grid; grid-template-columns: 1fr; gap: 18px;
53
+ }
54
+ @media(min-width: 760px){ .hero { grid-template-columns: 2fr 1fr; align-items: center; } }
55
+ .hero h2 { border:0; margin:0 0 6px; padding:0; font-size: 22px; }
56
+ .hero .quote {
57
+ font-style: italic; font-size: 17px; color: var(--accent2);
58
+ border-left: 3px solid var(--accent); padding-left: 14px; margin: 6px 0 0;
59
+ }
60
+ .hero .target {
61
+ background: #fff3cf; border-radius: 12px; padding: 14px 16px;
62
+ font-size: 13px; border: 1px solid var(--line); line-height: 1.55;
63
+ }
64
+ .hero .target strong { color: var(--accent); }
65
+
66
+ /* Pills */
67
+ .pill {
68
+ display: inline-block; padding: 2px 9px; border-radius: 12px;
69
+ color: white; font-size: 12px; margin: 2px 4px 2px 0; font-family: ui-monospace, monospace;
70
+ }
71
+ .pill.user { background: var(--gray); }
72
+ .pill.gradio { background: var(--accent); }
73
+ .pill.hf { background: var(--gold); }
74
+ .pill.modal { background: var(--green); }
75
+ .pill.flux { background: #111; }
76
+ .pill.openbmb { background: #075e54; }
77
+ .pill.cohere { background: #5e3aa3; }
78
+ .pill.openai { background: #2c5e8a; }
79
+ .pill.llama { background: #6a3d8a; }
80
+
81
+ /* Phone/recipe card mockup */
82
+ .phone-row {
83
+ display: grid; grid-template-columns: 1fr; gap: 18px; margin-top: 16px;
84
+ }
85
+ @media(min-width: 760px){ .phone-row { grid-template-columns: repeat(4, 1fr); } }
86
+ .phone {
87
+ background: #111; border-radius: 24px; padding: 8px;
88
+ box-shadow: 0 8px 22px rgba(0,0,0,0.18);
89
+ }
90
+ .phone .screen {
91
+ background: #fffbf0; border-radius: 18px; overflow: hidden;
92
+ height: 380px; display: flex; flex-direction: column;
93
+ }
94
+ .phone .topbar {
95
+ background: var(--accent); color: white; padding: 10px 14px;
96
+ font-size: 13px; font-family: 'Lora', serif;
97
+ }
98
+ .phone .body { padding: 12px; flex: 1; overflow-y: auto; font-size: 12px; }
99
+ .phone .body .illu {
100
+ width: 100%; aspect-ratio: 4/3; border-radius: 8px;
101
+ background: linear-gradient(135deg, #ffd28b 0%, #c97a3e 100%);
102
+ display: flex; align-items: center; justify-content: center;
103
+ font-size: 48px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); margin-bottom: 8px;
104
+ }
105
+ .phone .body p { margin: 6px 0; line-height: 1.5; }
106
+ .phone .body .voice {
107
+ background: var(--green-soft); border-radius: 6px; padding: 6px 10px;
108
+ margin-top: 8px; font-size: 11px; color: var(--green);
109
+ }
110
+ .phone .body .tip {
111
+ background: var(--red-soft); border-radius: 6px; padding: 6px 10px;
112
+ margin-top: 6px; font-size: 11px; color: var(--red);
113
+ }
114
+ .scenario-label {
115
+ text-align: center; font-size: 13px; color: var(--accent2);
116
+ margin-top: 8px; font-style: italic;
117
+ }
118
+
119
+ /* SVG */
120
+ svg { width: 100%; height: auto; display: block; }
121
+ .node-box { fill: var(--card); stroke: var(--ink); stroke-width: 1.5; }
122
+ .node-text { font-family: 'Inter', sans-serif; font-size: 14px; fill: var(--ink); }
123
+ .node-title { font-weight: 700; font-size: 15px; }
124
+ .node-sub { font-size: 11px; fill: var(--accent2); font-style: italic; }
125
+ .arrow { stroke: var(--ink); stroke-width: 1.8; fill: none; }
126
+ .arrow-label { font-size: 11px; fill: var(--accent2); font-family: ui-monospace, monospace; }
127
+ .dashed { stroke-dasharray: 6 4; }
128
+ .arrow-loop { stroke: var(--accent); stroke-width: 2.2; fill: none; }
129
+
130
+ /* Cards */
131
+ .grid-2 { display: grid; grid-template-columns: 1fr; gap: 18px; margin-top: 16px; }
132
+ @media(min-width: 880px){ .grid-2 { grid-template-columns: 1fr 1fr; } }
133
+ .grid-3 { display: grid; grid-template-columns: 1fr; gap: 14px; margin-top: 14px; }
134
+ @media(min-width: 760px){ .grid-3 { grid-template-columns: repeat(3, 1fr); } }
135
+
136
+ .card {
137
+ background: var(--card); border: 1px solid var(--line);
138
+ border-radius: 10px; padding: 18px 20px;
139
+ }
140
+ .card.pick { border: 2px solid var(--accent); }
141
+ .pick-tag {
142
+ display: inline-block; background: var(--accent); color: white;
143
+ font-family: ui-monospace, monospace; font-size: 11px;
144
+ padding: 1px 7px; border-radius: 10px; margin-bottom: 6px;
145
+ }
146
+
147
+ table {
148
+ width: 100%; border-collapse: collapse; background: var(--card);
149
+ border: 1px solid var(--line); margin-top: 14px; font-size: 14px;
150
+ }
151
+ th, td { padding: 8px 10px; text-align: left; border-bottom: 1px solid var(--line); vertical-align: top; }
152
+ th { background: #efe4cb; font-size: 13px; letter-spacing: 0.5px; text-transform: uppercase; }
153
+ code {
154
+ background: #efe4cb; border-radius: 3px; padding: 1px 5px; font-size: 13px;
155
+ }
156
+
157
+ /* Forbidden zone */
158
+ .forbidden {
159
+ background: var(--red-soft); border: 1px solid var(--red);
160
+ border-radius: 8px; padding: 14px 18px; margin-top: 14px;
161
+ }
162
+ .forbidden strong { color: var(--red); }
163
+ .forbidden ul {
164
+ columns: 2; column-gap: 28px; margin: 8px 0 0; padding-left: 18px; font-size: 14px;
165
+ }
166
+
167
+ /* Timeline */
168
+ .timeline { position: relative; padding-left: 36px; margin-top: 20px; }
169
+ .timeline::before {
170
+ content: ""; position: absolute; left: 12px; top: 6px; bottom: 6px;
171
+ width: 3px; background: var(--accent); border-radius: 2px;
172
+ }
173
+ .day {
174
+ position: relative; margin-bottom: 14px; background: var(--card);
175
+ border: 1px solid var(--line); border-radius: 8px; padding: 12px 16px;
176
+ }
177
+ .day::before {
178
+ content: ""; position: absolute; left: -29px; top: 16px;
179
+ width: 13px; height: 13px; background: var(--accent);
180
+ border: 2px solid var(--card); border-radius: 50%;
181
+ }
182
+ .day .lbl {
183
+ display: inline-block; background: var(--accent); color: white;
184
+ font-family: ui-monospace, monospace; font-size: 11px;
185
+ padding: 1px 7px; border-radius: 10px; margin-right: 8px;
186
+ }
187
+ .day strong { font-size: 15px; }
188
+ .day .what { font-size: 13px; color: var(--accent2); margin-top: 2px; }
189
+
190
+ /* Award rows */
191
+ .award-row {
192
+ display: flex; justify-content: space-between;
193
+ padding: 8px 12px; border-bottom: 1px solid var(--line); font-size: 14px;
194
+ }
195
+ .award-row:last-child { border-bottom: 0; }
196
+ .prob {
197
+ font-family: ui-monospace, monospace; font-size: 12px;
198
+ padding: 1px 8px; border-radius: 10px; color: white;
199
+ }
200
+ .prob-h { background: #2e7d32; }
201
+ .prob-m { background: #ef9c2c; }
202
+ .prob-l { background: #b03a2e; }
203
+
204
+ /* Badges grid */
205
+ .badges-grid {
206
+ display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
207
+ gap: 12px; margin-top: 14px;
208
+ }
209
+ .badge-card {
210
+ background: var(--card); border: 1px solid var(--line);
211
+ border-radius: 8px; padding: 12px 14px;
212
+ }
213
+ .badge-card.skip { opacity: 0.45; border-style: dashed; }
214
+ .badge-card .tag {
215
+ display: inline-block; background: var(--accent); color: white;
216
+ font-family: ui-monospace, monospace; font-size: 11px;
217
+ padding: 1px 7px; border-radius: 10px; margin-bottom: 6px;
218
+ }
219
+ .badge-card.skip .tag { background: var(--gray); }
220
+ .badge-card strong { font-size: 14px; }
221
+ .badge-card p { font-size: 13px; color: var(--accent2); margin: 4px 0 0; }
222
+
223
+ .footnote {
224
+ margin-top: 30px; padding: 14px 18px;
225
+ border-left: 4px solid var(--accent);
226
+ background: var(--card); font-size: 14px; border-radius: 4px;
227
+ }
228
+ </style>
229
+ <link rel="preconnect" href="https://fonts.googleapis.com">
230
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
231
+ <link href="https://fonts.googleapis.com/css2?family=Lora:wght@400;600;700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
232
+ </head>
233
+ <body>
234
+ <div class="wrap">
235
+
236
+ <h1><em>Cocina Conmigo</em></h1>
237
+ <div class="subtitle">Sous-chef multimodal con visión, voz y Flux.2 — para cocinar con tu mamá sin tener las manos libres</div>
238
+
239
+ <div class="hero">
240
+ <div>
241
+ <h2>La idea en una frase</h2>
242
+ <p>Tu mamá toma foto del refri, la app le propone qué cocinar, le <strong>muestra cómo se debe ver cada paso</strong> con Flux.2, y la <strong>narra por voz</strong> mientras ella cocina con las manos llenas.</p>
243
+ <p class="quote">"Mi mamá me pidió que le enseñara a hacer ramen. Le construí un sous-chef que vive en su tablet."</p>
244
+ <div style="margin-top: 14px;">
245
+ <span class="pill flux">Flux.2 Klein 9B</span>
246
+ <span class="pill openbmb">MiniCPM-V + voice</span>
247
+ <span class="pill cohere">Cohere voice</span>
248
+ <span class="pill gradio">Gradio Workflows</span>
249
+ <span class="pill modal">Modal-powered</span>
250
+ <span class="pill llama">llama.cpp</span>
251
+ </div>
252
+ </div>
253
+ <div class="target">
254
+ <strong>Track:</strong> Backyard AI<br/>
255
+ <strong>Persona:</strong> tu mamá / pareja / vecino<br/>
256
+ <strong>Idioma:</strong> español-mexicano<br/>
257
+ <strong>Total params:</strong> ~17B (≤ 32B ✓)<br/>
258
+ <strong>Cocina:</strong> mexicana tradicional<br/>
259
+ <strong>Storyline:</strong> "Para que mi mamá deje de googlear"
260
+ </div>
261
+ </div>
262
+
263
+
264
+ <h2><span class="num">01</span>Por qué esta idea, y no las anteriores</h2>
265
+ <table>
266
+ <thead><tr><th>Iteración</th><th>Idea</th><th>Por qué se descartó</th></tr></thead>
267
+ <tbody>
268
+ <tr><td>v1</td><td>Abuelita (parent phone helper)</td><td>En la lista pre-cocinada de OpenBMB → 5-15 equipos lo harán</td></tr>
269
+ <tr><td>v2</td><td>Cuentacuentos (voice storyteller)</td><td>También en la lista pre-cocinada de OpenBMB</td></tr>
270
+ <tr style="background:#fff3cf;"><td><strong>v3 (ésta)</strong></td><td><strong>Cocina Conmigo</strong></td><td>Refinamiento de tu idea #1 · NO está en ninguna lista pre-cocinada · usa Flux.2 + Workflows + voces · diaria + universal</td></tr>
271
+ </tbody>
272
+ </table>
273
+
274
+ <div class="forbidden">
275
+ <strong>⛔ Las 12 ideas en zona prohibida (clúster OpenBMB):</strong>
276
+ <ul>
277
+ <li>parent phone helper</li>
278
+ <li>receipt / bill explainer</li>
279
+ <li>shop menu / repair manual</li>
280
+ <li>offline personal assistant / voice companion</li>
281
+ <li>voice storyteller</li>
282
+ <li>visual mystery box</li>
283
+ <li>AI museum (≈ tu idea #4)</li>
284
+ <li>doodle creature</li>
285
+ <li>dream postcard gen</li>
286
+ <li>omni-modal adventure</li>
287
+ <li>tiny local NPC / character agent</li>
288
+ <li>cortes de cabello (tu idea #3, ya saturada)</li>
289
+ </ul>
290
+ </div>
291
+
292
+
293
+ <h2><span class="num">02</span>Las 4 historias del demo</h2>
294
+ <div class="phone-row">
295
+
296
+ <div>
297
+ <div class="phone"><div class="screen">
298
+ <div class="topbar">📸 Tengo esto en el refri</div>
299
+ <div class="body">
300
+ <div class="illu">🍅🌶🐔🧅</div>
301
+ <p><strong>Veo:</strong> pollo, jitomate, cebolla, cilantro, tortillas, queso.</p>
302
+ <p style="background:#fff3cf;border-radius:6px;padding:6px 10px;">
303
+ <strong>3 opciones:</strong><br/>
304
+ 🌮 Tinga · 🌯 Enchiladas · 🧀 Quesadillas
305
+ </p>
306
+ <div class="voice">🔊 "¿Qué traes ganas?"</div>
307
+ </div>
308
+ </div></div>
309
+ <div class="scenario-label">1. Visión + Planner</div>
310
+ </div>
311
+
312
+ <div>
313
+ <div class="phone"><div class="screen">
314
+ <div class="topbar">👩‍🍳 Paso 2 de 5</div>
315
+ <div class="body">
316
+ <div class="illu">🍳✨</div>
317
+ <p><strong>Acitrona la cebolla en aceite caliente.</strong></p>
318
+ <p style="font-size:11px;color:var(--gray);">⏱ 4 minutos · hasta que esté transparente</p>
319
+ <div class="voice">🔊 OpenBMB voice narra…</div>
320
+ </div>
321
+ </div></div>
322
+ <div class="scenario-label">2. Voz + imagen objetivo</div>
323
+ </div>
324
+
325
+ <div>
326
+ <div class="phone"><div class="screen">
327
+ <div class="topbar">📸 ¿Voy bien?</div>
328
+ <div class="body">
329
+ <div class="illu">🍳👀</div>
330
+ <p style="background:var(--green-soft);border-radius:6px;padding:6px 10px;color:var(--green);">
331
+ <strong>✅ Va perfecto.</strong> La cebolla ya se ve transparente.
332
+ </p>
333
+ <div class="tip">🔊 Cohere voice: "¡Súbele 1 minuto más, está bien!"</div>
334
+ </div>
335
+ </div></div>
336
+ <div class="scenario-label">3. Closed-loop visual</div>
337
+ </div>
338
+
339
+ <div>
340
+ <div class="phone"><div class="screen">
341
+ <div class="topbar">🔄 Replan</div>
342
+ <div class="body">
343
+ <p>Usuario: <em>"No tengo cilantro."</em></p>
344
+ <div class="illu" style="background: linear-gradient(135deg,#ffd28b,#a85c2a);">🌮</div>
345
+ <p>"No pasa nada. Le ponemos perejil o nada. Sigue siendo tinga."</p>
346
+ <div class="voice">🔊 Receta regenera · plato final actualizado</div>
347
+ </div>
348
+ </div></div>
349
+ <div class="scenario-label">4. Adaptación en vivo</div>
350
+ </div>
351
+
352
+ </div>
353
+
354
+
355
+ <h2><span class="num">03</span>Arquitectura — 5 agentes en un Gradio Workflow</h2>
356
+
357
+ <svg viewBox="0 0 1240 540" xmlns="http://www.w3.org/2000/svg">
358
+ <defs>
359
+ <marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
360
+ <path d="M0,0 L10,5 L0,10 z" fill="#2b2018"/>
361
+ </marker>
362
+ <marker id="aro" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
363
+ <path d="M0,0 L10,5 L0,10 z" fill="#a85c2a"/>
364
+ </marker>
365
+ </defs>
366
+
367
+ <!-- User input area -->
368
+ <rect x="20" y="40" width="200" height="240" rx="10" fill="#fff3cf" stroke="#d8c9ad" stroke-dasharray="4 3"/>
369
+ <text x="40" y="62" class="node-text node-title" fill="#6b4a2a">USUARIO (cocina)</text>
370
+
371
+ <rect class="node-box" x="40" y="80" width="160" height="50" rx="6" fill="#ddd1bd"/>
372
+ <text x="120" y="102" class="node-text node-title" text-anchor="middle">📸 Foto del refri</text>
373
+ <text x="120" y="118" class="node-text node-sub" text-anchor="middle">trigger inicial</text>
374
+
375
+ <rect class="node-box" x="40" y="140" width="160" height="50" rx="6" fill="#ddd1bd"/>
376
+ <text x="120" y="162" class="node-text node-title" text-anchor="middle">🎙️ Pregunta voz</text>
377
+ <text x="120" y="178" class="node-text node-sub" text-anchor="middle">"¿voy bien?"</text>
378
+
379
+ <rect class="node-box" x="40" y="200" width="160" height="50" rx="6" fill="#ddd1bd"/>
380
+ <text x="120" y="222" class="node-text node-title" text-anchor="middle">📸 Foto progreso</text>
381
+ <text x="120" y="238" class="node-text node-sub" text-anchor="middle">closed-loop</text>
382
+
383
+ <!-- Output area -->
384
+ <rect x="20" y="320" width="200" height="180" rx="10" fill="#fff3cf" stroke="#d8c9ad" stroke-dasharray="4 3"/>
385
+ <text x="40" y="342" class="node-text node-title" fill="#6b4a2a">SALIDA</text>
386
+
387
+ <rect class="node-box" x="40" y="360" width="160" height="50" rx="6" fill="#dbe9d8"/>
388
+ <text x="120" y="382" class="node-text node-title" text-anchor="middle">🍽️ Plato final + receta</text>
389
+ <text x="120" y="398" class="node-text node-sub" text-anchor="middle">imagen + texto</text>
390
+
391
+ <rect class="node-box" x="40" y="420" width="160" height="50" rx="6" fill="#dbe9d8"/>
392
+ <text x="120" y="442" class="node-text node-title" text-anchor="middle">🔊 Voz por paso</text>
393
+ <text x="120" y="458" class="node-text node-sub" text-anchor="middle">narrador + tips</text>
394
+
395
+ <!-- Pipeline center -->
396
+ <rect x="260" y="40" width="700" height="460" rx="10" fill="#fffaf0" stroke="#d8c9ad" stroke-width="1.5"/>
397
+ <text x="610" y="62" class="node-text node-title" text-anchor="middle" fill="#6b4a2a">HF SPACE — Gradio Workflow (5 agentes)</text>
398
+
399
+ <!-- Vision (Mise en Place) -->
400
+ <rect class="node-box" x="280" y="90" width="200" height="80" rx="6" fill="#e6d5ed"/>
401
+ <text x="380" y="110" class="node-text node-title" text-anchor="middle">👁️ MISE EN PLACE</text>
402
+ <text x="380" y="126" class="node-text node-sub" text-anchor="middle">MiniCPM-V (Q4)</text>
403
+ <text x="380" y="142" class="node-text node-sub" text-anchor="middle">~2-4B</text>
404
+ <text x="380" y="160" class="node-text node-sub" text-anchor="middle">identifica ingredientes</text>
405
+
406
+ <!-- Recipe Planner -->
407
+ <rect class="node-box" x="510" y="90" width="200" height="80" rx="6" fill="#fbe4d3"/>
408
+ <text x="610" y="110" class="node-text node-title" text-anchor="middle">🧠 RECIPE PLANNER</text>
409
+ <text x="610" y="126" class="node-text node-sub" text-anchor="middle">MiniCPM-4 (LoRA mx)</text>
410
+ <text x="610" y="142" class="node-text node-sub" text-anchor="middle">~4B</text>
411
+ <text x="610" y="160" class="node-text node-sub" text-anchor="middle">arma receta JSON · replan</text>
412
+
413
+ <!-- Step Illustrator -->
414
+ <rect class="node-box" x="740" y="90" width="200" height="80" rx="6" fill="#f6dccc"/>
415
+ <text x="840" y="110" class="node-text node-title" text-anchor="middle">🎨 STEP ILLUSTRATOR</text>
416
+ <text x="840" y="126" class="node-text node-sub" text-anchor="middle">Flux.2 Klein 9B</text>
417
+ <text x="840" y="142" class="node-text node-sub" text-anchor="middle">en Modal GPU L4</text>
418
+ <text x="840" y="160" class="node-text node-sub" text-anchor="middle">imagen-objetivo por paso</text>
419
+
420
+ <!-- Sous-Chef Narrator -->
421
+ <rect class="node-box" x="510" y="200" width="200" height="70" rx="6" fill="#cfe0ee"/>
422
+ <text x="610" y="222" class="node-text node-title" text-anchor="middle">🔊 SOUS-CHEF NARRATOR</text>
423
+ <text x="610" y="238" class="node-text node-sub" text-anchor="middle">OpenBMB voice (~1B)</text>
424
+ <text x="610" y="254" class="node-text node-sub" text-anchor="middle">tono cálido</text>
425
+
426
+ <!-- Tip Giver -->
427
+ <rect class="node-box" x="740" y="200" width="200" height="70" rx="6" fill="#e9d6f5"/>
428
+ <text x="840" y="222" class="node-text node-title" text-anchor="middle">🎭 TIP GIVER</text>
429
+ <text x="840" y="238" class="node-text node-sub" text-anchor="middle">Cohere voice (~1B)</text>
430
+ <text x="840" y="254" class="node-text node-sub" text-anchor="middle">warnings · enérgico</text>
431
+
432
+ <!-- Progress Validator (closed loop) -->
433
+ <rect class="node-box" x="280" y="290" width="220" height="90" rx="6" fill="#dbe9d8" stroke="#3f7a3a" stroke-width="2"/>
434
+ <text x="390" y="312" class="node-text node-title" text-anchor="middle" fill="#3f7a3a">✅ PROGRESS VALIDATOR</text>
435
+ <text x="390" y="328" class="node-text node-sub" text-anchor="middle">MiniCPM-V (reuso)</text>
436
+ <text x="390" y="344" class="node-text node-sub" text-anchor="middle">compara foto usuario vs</text>
437
+ <text x="390" y="360" class="node-text node-sub" text-anchor="middle">imagen-objetivo</text>
438
+ <text x="390" y="376" class="node-text node-sub" text-anchor="middle">CLOSED LOOP 🔄</text>
439
+
440
+ <!-- STT -->
441
+ <rect class="node-box" x="280" y="200" width="200" height="70" rx="6" fill="#cfe0ee"/>
442
+ <text x="380" y="222" class="node-text node-title" text-anchor="middle">🎙️ STT (opcional)</text>
443
+ <text x="380" y="238" class="node-text node-sub" text-anchor="middle">Whisper-tiny (~40M)</text>
444
+ <text x="380" y="254" class="node-text node-sub" text-anchor="middle">"¿voy bien?" hands-free</text>
445
+
446
+ <!-- Recipe State -->
447
+ <rect class="node-box" x="510" y="290" width="430" height="90" rx="6" fill="#fff3cf"/>
448
+ <text x="725" y="312" class="node-text node-title" text-anchor="middle" fill="#8a6a18">📖 RECIPE STATE (dataclass)</text>
449
+ <text x="725" y="328" class="node-text node-sub" text-anchor="middle">name · final_dish_image · steps · current_step ·</text>
450
+ <text x="725" y="344" class="node-text node-sub" text-anchor="middle">missing_ingredients · substitutes · user_progress_photos</text>
451
+ <text x="725" y="362" class="node-text node-sub" text-anchor="middle">cada agente lee y escribe sobre este objeto</text>
452
+
453
+ <!-- Page assembler -->
454
+ <rect class="node-box" x="280" y="400" width="660" height="60" rx="6" fill="#f6dccc"/>
455
+ <text x="610" y="422" class="node-text node-title" text-anchor="middle">📖 RECIPE CARD ASSEMBLER</text>
456
+ <text x="610" y="438" class="node-text node-sub" text-anchor="middle">renderiza la tarjeta de receta + cards por paso + audio reproducible</text>
457
+
458
+ <!-- Modal box -->
459
+ <rect x="990" y="40" width="240" height="460" rx="10" fill="#dbe9d8" stroke="#3f7a3a" stroke-width="1.5"/>
460
+ <text x="1110" y="62" class="node-text node-title" text-anchor="middle" fill="#3f7a3a">MODAL</text>
461
+
462
+ <rect class="node-box" x="1010" y="90" width="200" height="80" rx="6" fill="#fff"/>
463
+ <text x="1110" y="112" class="node-text node-title" text-anchor="middle">Flux endpoint</text>
464
+ <text x="1110" y="128" class="node-text node-sub" text-anchor="middle">runtime · @app.cls L4</text>
465
+ <text x="1110" y="144" class="node-text node-sub" text-anchor="middle">scaledown 180s</text>
466
+ <text x="1110" y="160" class="node-text node-sub" text-anchor="middle">~1-3s/imagen</text>
467
+
468
+ <rect class="node-box" x="1010" y="190" width="200" height="80" rx="6" fill="#fff"/>
469
+ <text x="1110" y="212" class="node-text node-title" text-anchor="middle">Dataset cocina mx</text>
470
+ <text x="1110" y="228" class="node-text node-sub" text-anchor="middle">offline · 200 recetas</text>
471
+ <text x="1110" y="244" class="node-text node-sub" text-anchor="middle">Codex API genera</text>
472
+ <text x="1110" y="260" class="node-text node-sub" text-anchor="middle">~$5</text>
473
+
474
+ <rect class="node-box" x="1010" y="290" width="200" height="80" rx="6" fill="#fff"/>
475
+ <text x="1110" y="312" class="node-text node-title" text-anchor="middle">LoRA Planner</text>
476
+ <text x="1110" y="328" class="node-text node-sub" text-anchor="middle">offline · A10G ~30 min</text>
477
+ <text x="1110" y="344" class="node-text node-sub" text-anchor="middle">push GGUF a HF</text>
478
+ <text x="1110" y="360" class="node-text node-sub" text-anchor="middle">~$1</text>
479
+
480
+ <rect class="node-box" x="1010" y="390" width="200" height="80" rx="6" fill="#fff"/>
481
+ <text x="1110" y="412" class="node-text node-title" text-anchor="middle">Eval pipeline</text>
482
+ <text x="1110" y="428" class="node-text node-sub" text-anchor="middle">consistencia visual</text>
483
+ <text x="1110" y="444" class="node-text node-sub" text-anchor="middle">% ingredientes correctos</text>
484
+ <text x="1110" y="460" class="node-text node-sub" text-anchor="middle">~$1</text>
485
+
486
+ <!-- Arrows: input → vision -->
487
+ <path class="arrow" d="M200 105 L278 130" marker-end="url(#ar)"/>
488
+ <text x="200" y="100" class="arrow-label">refri</text>
489
+
490
+ <!-- input → STT -->
491
+ <path class="arrow" d="M200 165 L278 235" marker-end="url(#ar)"/>
492
+ <text x="205" y="200" class="arrow-label">audio</text>
493
+
494
+ <!-- input progress → validator -->
495
+ <path class="arrow arrow-loop" d="M200 225 L278 330" marker-end="url(#aro)"/>
496
+ <text x="200" y="270" class="arrow-label" style="fill:#a85c2a;">progreso</text>
497
+
498
+ <!-- Vision → Planner -->
499
+ <path class="arrow" d="M480 130 L508 130" marker-end="url(#ar)"/>
500
+ <text x="482" y="120" class="arrow-label">ingredientes</text>
501
+
502
+ <!-- Planner → Illustrator -->
503
+ <path class="arrow" d="M710 130 L738 130" marker-end="url(#ar)"/>
504
+ <text x="712" y="120" class="arrow-label">visual prompt</text>
505
+
506
+ <!-- Illustrator → Modal -->
507
+ <path class="arrow dashed" d="M940 130 L1008 130" marker-end="url(#ar)"/>
508
+ <text x="945" y="120" class="arrow-label">.remote()</text>
509
+
510
+ <!-- Planner → narrator -->
511
+ <path class="arrow" d="M610 170 L610 198" marker-end="url(#ar)"/>
512
+ <!-- Planner → tip giver -->
513
+ <path class="arrow" d="M710 145 C 760 170, 800 180, 800 198" marker-end="url(#ar)"/>
514
+
515
+ <!-- Validator → Planner (loop) -->
516
+ <path class="arrow arrow-loop" d="M390 290 C 390 240, 470 190, 510 145" marker-end="url(#aro)"/>
517
+ <text x="395" y="240" class="arrow-label" style="fill:#a85c2a;">verdict · feedback</text>
518
+
519
+ <!-- STT → Validator -->
520
+ <path class="arrow dashed" d="M380 270 L380 288" marker-end="url(#ar)"/>
521
+
522
+ <!-- Recipe state ↔ all agents -->
523
+ <path class="arrow dashed" d="M725 290 L725 270" marker-end="url(#ar)"/>
524
+ <path class="arrow dashed" d="M610 290 L610 270" marker-end="url(#ar)"/>
525
+
526
+ <!-- All → Assembler -->
527
+ <path class="arrow" d="M610 380 L610 398" marker-end="url(#ar)"/>
528
+
529
+ <!-- Assembler → output -->
530
+ <path class="arrow" d="M280 425 C 240 425, 220 410, 200 385" marker-end="url(#ar)"/>
531
+ <path class="arrow" d="M280 440 C 240 440, 220 445, 200 445" marker-end="url(#ar)"/>
532
+
533
+ <!-- Modal → Planner (LoRA pesos offline) -->
534
+ <path class="arrow dashed" d="M1010 330 C 870 330, 750 280, 710 165" marker-end="url(#ar)"/>
535
+ <text x="900" y="280" class="arrow-label">LoRA pesos</text>
536
+ </svg>
537
+ <p style="font-size: 13px; color: var(--accent2); margin-top: 10px;">
538
+ <strong>Flecha naranja</strong> = closed-loop visual (la innovación). El usuario toma foto del progreso, MiniCPM-V valida vs imagen-objetivo, el Planner ajusta o avanza. Ningún recipe app del mercado lo hace.
539
+ </p>
540
+
541
+
542
+ <h2><span class="num">04</span>El truco innovador: closed-loop visual cocinero</h2>
543
+ <div class="grid-3">
544
+ <div class="card">
545
+ <h3>1. Imagen-objetivo por paso</h3>
546
+ <p style="font-size:13px;">Flux.2 genera "así debe verse el sartén/plato/olla en el paso N". No es texto, no es stock photo: es generación context-aware del estado deseado.</p>
547
+ </div>
548
+ <div class="card">
549
+ <h3>2. Validación con foto del usuario</h3>
550
+ <p style="font-size:13px;">El usuario sube foto de cómo va. MiniCPM-V compara contra la imagen-objetivo y devuelve verdict: <code>go</code> · <code>wait</code> · <code>fix</code>.</p>
551
+ </div>
552
+ <div class="card">
553
+ <h3>3. Replan adaptativo</h3>
554
+ <p style="font-size:13px;">"No tengo cilantro." → Planner regenera receta + Flux regenera imagen final. El plan no es estático, evoluciona con el estado real.</p>
555
+ </div>
556
+ </div>
557
+ <p style="margin-top:14px; font-size:14px;">
558
+ <strong>Esta es la sección destacada del README</strong> y el blog post de Field Notes badge: <em>"How visual closed-loop cooking guidance works."</em>
559
+ </p>
560
+
561
+
562
+ <h2><span class="num">05</span>Badges objetivo (5/6)</h2>
563
+ <div class="badges-grid">
564
+ <div class="badge-card"><span class="tag">LLAMA.CPP</span><br/><strong>Llama Champion</strong><p>Vision + Planner via <code>llama-cpp-python</code> con GGUF Q4.</p></div>
565
+ <div class="badge-card"><span class="tag">FINE-TUNED</span><br/><strong>Well-Tuned</strong><p>LoRA en cocina mexicana · publicado en HF.</p></div>
566
+ <div class="badge-card"><span class="tag">CUSTOM UI</span><br/><strong>Off-Brand</strong><p>UI tarjeta de receta · serif · paleta cálida · modo cocina XL.</p></div>
567
+ <div class="badge-card"><span class="tag">OPEN TRACE</span><br/><strong>Sharing is Caring</strong><p>Dataset 150 recetas mx + traces + recetas generadas al Hub.</p></div>
568
+ <div class="badge-card"><span class="tag">TENTATIVE</span><br/><strong>Field Notes</strong><p>Blog: "Le construí un sous-chef a mi mamá".</p></div>
569
+ <div class="badge-card skip"><span class="tag">LOCAL-FIRST</span><br/><strong>Off the Grid</strong><p>Sacrificado: Flux.2 corre en Modal por calidad.</p></div>
570
+ </div>
571
+
572
+
573
+ <h2><span class="num">06</span>Premios objetivo</h2>
574
+ <div class="card">
575
+ <div class="award-row"><span><strong>Backyard AI Track</strong> · $1K–$4K</span><span class="prob prob-h">ALTA</span></div>
576
+ <div class="award-row"><span><strong>Modal Awards</strong> · $3K–$10K credits</span><span class="prob prob-h">ALTA</span></div>
577
+ <div class="award-row"><span><strong>OpenBMB Award</strong> · $1K–$2.5K</span><span class="prob prob-h">ALTA</span></div>
578
+ <div class="award-row"><span><strong>Best Demo</strong> · $1K</span><span class="prob prob-h">ALTA</span></div>
579
+ <div class="award-row"><span><strong>Community Choice</strong> · $2K</span><span class="prob prob-h">ALTA</span></div>
580
+ <div class="award-row"><span><strong>Best Agent</strong> · $1K</span><span class="prob prob-h">ALTA — closed-loop multi-agente real</span></div>
581
+ <div class="award-row"><span><strong>Bonus Quest Champion</strong> · $2K</span><span class="prob prob-m">MEDIA-ALTA · 5/6 badges</span></div>
582
+ <div class="award-row"><span><strong>Off-Brand</strong> · $1.5K</span><span class="prob prob-m">MEDIA</span></div>
583
+ <div class="award-row"><span><strong>Tiny Titan</strong> · $1.5K</span><span class="prob prob-l">BAJA · Flux 9B saca del rango</span></div>
584
+ </div>
585
+ <p style="font-size: 14px; margin-top: 8px;"><strong>Cota razonable acumulada: $5K–$12K cash + $3K–$10K Modal credits.</strong></p>
586
+
587
+
588
+ <h2><span class="num">07</span>Timeline de 10 días</h2>
589
+ <div class="timeline">
590
+ <div class="day"><span class="lbl">D1</span><strong>Setup + Modal Flux endpoint</strong><div class="what">"Hola Flux": prompt → imagen de un platillo. Space vacío deployado.</div></div>
591
+ <div class="day"><span class="lbl">D2</span><strong>Vision: identificación de ingredientes</strong><div class="what">MiniCPM-V Q4 · prueba con 5 fotos reales del refri.</div></div>
592
+ <div class="day"><span class="lbl">D3</span><strong>Recipe Planner LLM</strong><div class="what">MiniCPM-4 · JSON estructurado · 3 opciones a partir de ingredientes.</div></div>
593
+ <div class="day"><span class="lbl">D4</span><strong>Step Illustrator (Flux + consistencia)</strong><div class="what">Imagen del plato final + 5 imágenes-objetivo por paso · i2i suave.</div></div>
594
+ <div class="day"><span class="lbl">D5</span><strong>Voz: narrador + tip-giver</strong><div class="what">OpenBMB voice + Cohere voice · audio pre-renderizado por paso.</div></div>
595
+ <div class="day"><span class="lbl">D6</span><strong>UI Off-Brand: recipe card</strong><div class="what">gr.Blocks + CSS serif tierra · modo cocina XL hands-free.</div></div>
596
+ <div class="day"><span class="lbl">D7</span><strong>Gradio Workflows showcase</strong><div class="what">Pipeline reescrita como Workflow visible · pestaña separada.</div></div>
597
+ <div class="day"><span class="lbl">D8</span><strong>Fine-tune del Planner en cocina mx</strong><div class="what">200 recetas sintéticas · LoRA · GGUF · push HF.</div></div>
598
+ <div class="day"><span class="lbl">D9</span><strong>STT + Progress Validator + eval</strong><div class="what">Whisper · closed-loop activo · Sharing is Caring badge.</div></div>
599
+ <div class="day"><span class="lbl">D10</span><strong>Demo + README + blog + submit</strong><div class="what">Mamá real cocinando · 60-90s · subtítulos EN · Field Notes blog.</div></div>
600
+ </div>
601
+
602
+
603
+ <h2><span class="num">08</span>Plan B (corte de scope)</h2>
604
+ <table>
605
+ <thead><tr><th>#</th><th>Cortar</th><th>Pierdes</th><th>Conservas</th></tr></thead>
606
+ <tbody>
607
+ <tr><td>1</td><td>STT (preguntas voz)</td><td>comodidad demo</td><td>texto + foto</td></tr>
608
+ <tr><td>2</td><td>2da voz (Cohere tip-giver)</td><td>1 sponsor voice</td><td>narrador único</td></tr>
609
+ <tr><td>3</td><td>Progress Validator (closed-loop)</td><td><strong>Best Agent</strong> + innovación principal</td><td>demo lineal</td></tr>
610
+ <tr><td>4</td><td>Fine-tune del Planner</td><td><strong>Well-Tuned</strong></td><td>resto badges</td></tr>
611
+ <tr><td>5</td><td>Gradio Workflows showcase</td><td>diferenciador "fresh"</td><td>pipeline Python</td></tr>
612
+ <tr><td>6</td><td>UI super-custom</td><td><strong>Off-Brand</strong></td><td>UI default</td></tr>
613
+ <tr style="background:#fff3cf;"><td>—</td><td><strong>NUNCA</strong></td><td colspan="2">Vision + Planner + Illustrator + Narrator + UI mínima + video con persona real cocinando</td></tr>
614
+ </tbody>
615
+ </table>
616
+
617
+
618
+ <h2><span class="num">09</span>Riesgos clave</h2>
619
+ <table>
620
+ <thead><tr><th>Riesgo</th><th>Mitigación</th></tr></thead>
621
+ <tbody>
622
+ <tr><td>Flux.2 Klein no tiene API/pesos públicos cuando lo necesitas</td><td>Plan B: Flux.1-schnell o SDXL-Lightning. Pierdes posicionamiento sponsor pero idea sobrevive.</td></tr>
623
+ <tr><td>MiniCPM-V no identifica ingredientes mexicanos (chile poblano, nopales)</td><td>Few-shot en prompt; eventualmente fine-tune ligero del visión sobre 50 fotos etiquetadas</td></tr>
624
+ <tr><td>Flux.2 genera comida poco apetitosa</td><td>Itera prompts ("recipe magazine, warm light, top-down"); usa imagen final como ref para los pasos</td></tr>
625
+ <tr><td>Progress Validator da false positives</td><td>Conservador: solo dice "vas bien" si similitud es alta; default es "sigue" sin juicio fuerte</td></tr>
626
+ <tr><td>Latencia receta &gt; 30s</td><td>Streaming progresivo; paraleliza Flux + TTS</td></tr>
627
+ <tr><td>Modal cold start ~30-60s en Flux</td><td>Pre-warm 30s antes de filmar · <code>keep_warm=1</code> el día del demo</td></tr>
628
+ <tr><td>Persona del demo se quema/cocina mal</td><td>Practica la receta una vez antes · 2-3 candidatos de receta listos</td></tr>
629
+ <tr><td>Otro equipo presenta "recipe app con AI"</td><td>Diferéncialo con: closed-loop visual + español + cocina mx + dataset publicado + persona real</td></tr>
630
+ </tbody>
631
+ </table>
632
+
633
+
634
+ <h2><span class="num">10</span>Cómo gastar los créditos</h2>
635
+ <div class="grid-2">
636
+ <div class="card">
637
+ <h3>Modal · $250</h3>
638
+ <table>
639
+ <tr><td>Flux dev (días 1-9)</td><td>$5-15</td></tr>
640
+ <tr><td>Dataset cocina mx</td><td>$3-8</td></tr>
641
+ <tr><td>LoRA + sweeps</td><td>$4-5</td></tr>
642
+ <tr><td>Eval</td><td>$1</td></tr>
643
+ <tr><td>Inferencia grading jueces</td><td>$10-25</td></tr>
644
+ <tr><th>Subtotal</th><th>$25-65</th></tr>
645
+ <tr><th>+ Buffer</th><th>$30</th></tr>
646
+ <tr><th>Proyectado</th><th><strong>~$55-95 / $250</strong></th></tr>
647
+ </table>
648
+ </div>
649
+ <div class="card">
650
+ <h3>OpenAI Codex · $100</h3>
651
+ <table>
652
+ <tr><td>Codex CLI pair-programmer</td><td>$20-40</td></tr>
653
+ <tr><td>200 recetas mx sintéticas</td><td>$10-25</td></tr>
654
+ <tr><td>Prompts Flux por paso</td><td>$5-10</td></tr>
655
+ <tr><td>Reserva</td><td>$30</td></tr>
656
+ <tr><th>Proyectado</th><th><strong>~$65-105 / $100</strong></th></tr>
657
+ </table>
658
+ </div>
659
+ </div>
660
+
661
+
662
+ <div class="footnote">
663
+ <strong>Mantra del proyecto:</strong> "Una mamá cocinando frente a la cámara. Un platillo que se ve apetitoso. Una voz que la acompaña sin juzgar. Un paso a la vez."
664
+ </div>
665
+
666
+ </div>
667
+ </body>
668
+ </html>
Strategy/estrategia.md ADDED
@@ -0,0 +1,496 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Estrategia detallada — "Cocina Conmigo"
2
+
3
+ > Documento de ejecución. Lee primero `plan.md` para el "qué" y el "por qué".
4
+ > Este archivo es el "cómo": modelo mental, multi-agente, timeline, gasto de créditos, riesgos, snippets.
5
+
6
+ ---
7
+
8
+ ## 1. Modelo mental: la "receta" como objeto de estado
9
+
10
+ La app no es un chatbot. Es una **máquina de estado** alrededor de un objeto `Recipe` que evoluciona en el tiempo. Ese estado se actualiza en cada turno.
11
+
12
+ ```python
13
+ @dataclass
14
+ class Recipe:
15
+ name: str # "Tinga de Pollo"
16
+ final_dish_image: bytes # imagen Flux del plato final
17
+ available_ingredients: list[str] # lo que la cámara vio en el refri
18
+ missing_ingredients: list[str] # lo que falta + sus sustitutos
19
+ steps: list[Step] # 5-7 pasos
20
+ current_step: int # qué paso vamos haciendo
21
+ user_progress_photos: list[bytes] # fotos que el usuario tomó
22
+
23
+ @dataclass
24
+ class Step:
25
+ n: int
26
+ instruction_text: str # "Pica la cebolla en cubos chicos"
27
+ visual_target: bytes # imagen Flux: "así debe verse el sartén"
28
+ duration_estimate: str # "4 minutos"
29
+ audio_narration: bytes # narración pre-renderizada
30
+ tip: str | None # "no la quemes"
31
+ tip_audio: bytes | None # voz Cohere
32
+ ```
33
+
34
+ Ventajas de pensarlo así:
35
+ - Cada nodo del Workflow toma `Recipe` y devuelve `Recipe` modificada. Composable y observable.
36
+ - El "replan" (no tengo cilantro) es una sola función `recipe.replan(missing="cilantro") → Recipe`.
37
+ - El "validador" toma `Recipe` + `progress_photo` y devuelve `feedback`.
38
+
39
+ ---
40
+
41
+ ## 2. Los 5 agentes (multi-agente real, no simulado)
42
+
43
+ | Agente | Responsabilidad | Trigger | Output |
44
+ |---|---|---|---|
45
+ | **Mise en Place** | Identificar ingredientes en foto del refri | foto del refri | `available_ingredients` |
46
+ | **Recipe Planner** | Proponer 3 recetas factibles · armar la elegida | usuario elige idea | `Recipe` con steps |
47
+ | **Step Illustrator** | Generar imagen-objetivo de cada paso + plato final | nueva receta | `Step.visual_target` para cada paso |
48
+ | **Sous-Chef Narrator** | Narrar instrucciones por voz | paso activo | `Step.audio_narration` |
49
+ | **Progress Validator** | Comparar foto del usuario vs imagen-objetivo | usuario sube foto mid-cooking | `feedback` (texto + voz tip) |
50
+
51
+ Esto es un **sistema multi-agente real**: cada agente tiene su propia función, su propio modelo, y se comunican por estado compartido (`Recipe`). No es un agente único con tools — es 5 agentes en pipeline + closed-loop.
52
+
53
+ > **Best Agent badge candidate.** Documenta esto en el README con un diagrama explícito.
54
+
55
+ ---
56
+
57
+ ## 3. El truco innovador: closed-loop visual
58
+
59
+ ```
60
+ ┌─────────────────────────────────────┐
61
+ │ │
62
+ ▼ │
63
+ [Step Illustrator]──▶ visual_target ──▶ [Usuario cocina]
64
+
65
+
66
+ 📸 progress_photo
67
+
68
+
69
+ [Progress Validator]
70
+ (MiniCPM-V)
71
+
72
+ ┌───────────────────┤
73
+ │ │
74
+ ✅ va bien ❌ ajustar
75
+ │ │
76
+ siguiente paso [Recipe Planner]
77
+ replan/tip
78
+
79
+ └──────▶ vuelta al loop
80
+ ```
81
+
82
+ Esta es **la innovación técnica** del proyecto. La mayoría de "recipe apps" son listas estáticas. Cocina Conmigo:
83
+
84
+ 1. Genera *visualmente* cómo debe verse cada paso (no solo texto).
85
+ 2. Acepta foto del usuario y la *compara* con el objetivo.
86
+ 3. Adapta el plan en vivo si algo no va.
87
+
88
+ Sección dedicada en el README: *"How visual closed-loop cooking guidance works"*. Es también el blog post de Field Notes.
89
+
90
+ ---
91
+
92
+ ## 4. Cronograma — 10 días
93
+
94
+ > ~50-70 horas de trabajo + 1 humano + Codex CLI como pair.
95
+
96
+ ### Día 1 — Setup + Modal Flux endpoint
97
+ - `pip install gradio modal openai huggingface-hub diffusers llama-cpp-python`
98
+ - `modal setup` y deploya el endpoint Flux que devuelve imagen dada un prompt.
99
+ - Crea Space vacío en HF, push inicial.
100
+ - **Entregable:** Space que muestra una imagen Flux dado un texto.
101
+
102
+ ### Día 2 — Vision: identificación de ingredientes
103
+ - Carga MiniCPM-V Q4 GGUF en local.
104
+ - Función: `identify_ingredients(fridge_photo) → list[str]`.
105
+ - Prueba con 5 fotos de refri reales (el tuyo, el de tu mamá).
106
+ - **Entregable:** dada foto del refri, devuelve lista correcta de 80%+ ingredientes visibles.
107
+
108
+ ### Día 3 — Recipe Planner LLM
109
+ - Carga MiniCPM-4 Q4 GGUF.
110
+ - Prompt template estructurado que devuelve JSON:
111
+ ```json
112
+ {
113
+ "name": "Tinga de Pollo",
114
+ "options": [{"name": "...", "why": "..."}, ...],
115
+ "steps": [{"n": 1, "instruction": "...", "duration": "...", "visual": "..."}],
116
+ "missing": ["cilantro"],
117
+ "substitutes": {"cilantro": ["perejil", "nada"]}
118
+ }
119
+ ```
120
+ - Conecta Vision + Planner: foto refri → 3 opciones de receta.
121
+ - **Entregable:** dada foto + selección, devuelve receta completa estructurada.
122
+
123
+ ### Día 4 — Step Illustrator (Flux.2 con consistencia)
124
+ - Para cada `Step.visual` del JSON, llama Flux.2 endpoint con prompt:
125
+ > *"Top-down view of a kitchen pan with [step.visual]. Mexican cooking style. Warm lighting. Natural ingredients. Photorealistic, recipe magazine style."*
126
+ - Para mantener estilo entre pasos: usa la imagen del paso anterior como `ref` con `strength=0.6` (más relajado que cuentos, porque el contenido cambia mucho).
127
+ - Genera también imagen del plato final (sin `ref`).
128
+ - **Entregable:** receta de 5 pasos cada uno con imagen-objetivo + foto del plato final.
129
+
130
+ ### Día 5 — Voz: narrador + tip-giver
131
+ - **OpenBMB voice** para `Step.audio_narration`: instrucciones tono cálido y claro.
132
+ - **Cohere Labs voice** para `Step.tip_audio`: tono más enérgico ("¡cuidado!").
133
+ - Genera audio de los 5 pasos por adelantado (no en streaming, evita cold starts molestos).
134
+ - **Entregable:** receta completa con narración audible.
135
+
136
+ ### Día 6 — UI Off-Brand: tarjeta de receta
137
+ - `gr.Blocks` + CSS custom.
138
+ - Layout: hero con imagen del plato final + título grande, abajo carrusel de pasos cada uno con `imagen objetivo + texto + botón "ya"`, modo cocina hands-free con texto enorme.
139
+ - Estilo: serif elegante (`Lora`), paleta cálida tierra/dorado.
140
+ - **Entregable:** Space que parece tarjeta de revista de cocina, no Gradio.
141
+
142
+ ### Día 7 — Gradio Workflows showcase
143
+ - Reescribe pipeline como **Gradio Workflow** con nodos visibles.
144
+ - Nodos: `📸 Fridge → 👁️ Vision → 🧠 Planner → 🎨 Illustrator → 🔊 Narrator → 📖 Recipe Card`.
145
+ - Para `Progress Validator`, agrega rama: `📸 Progress Photo → 👁️ Validator → 💬 Feedback`.
146
+ - Pestaña separada en el Space que muestra el grafo del Workflow corriendo en vivo.
147
+ - **Entregable:** Workflow visualmente impresionante en pantalla. Diferenciador para jueces de Gradio.
148
+
149
+ ### Día 8 — Fine-tune del Planner en cocina mexicana
150
+ - **Dataset sintético en Modal:** Codex API genera 200 recetas mexicanas en formato JSON estructurado (tinga, mole, chiles rellenos, sopes, pozole, etc.). Filtras manualmente las 150 mejores.
151
+ - **LoRA en Modal A10G:** ~30-60 min de fine-tune sobre MiniCPM-4 4B.
152
+ - **GGUF + push HF:** convierte a Q4_K_M, sube a HF Hub.
153
+ - Reemplaza el Planner por la versión fine-tuneada.
154
+ - **Entregable:** modelo `tu-usuario/cocinaconmigo-4b-mx-Q4_K_M-gguf` publicado.
155
+
156
+ ### Día 9 — STT + Progress Validator + eval
157
+ - `faster-whisper tiny` en español: usuario pregunta hands-free.
158
+ - Implementa **Progress Validator**: foto del usuario → MiniCPM-V compara contra `Step.visual_target` → genera feedback.
159
+ - Eval: 10 recetas generadas, mide:
160
+ - % ingredientes correctamente identificados.
161
+ - % pasos con imagen-objetivo coherente.
162
+ - Calidad subjetiva de validación (5 fotos de progreso).
163
+ - Sube traces al Hub (badge **Sharing is Caring**).
164
+ - **Entregable:** app completa con voz IN, validador, traces publicados.
165
+
166
+ ### Día 10 — Demo video + README + blog + submit
167
+ - **Filma a una persona real cocinando** una receta sugerida por la app, de principio a fin.
168
+ - 60-90 segundos: foto del refri → 3 opciones → elige → cocina con voz → toma foto mid-cooking → app valida → plato final → la persona come.
169
+ - README: badges declarados, diagrama, link al video, sección "How closed-loop visual cooking guidance works".
170
+ - Blog post (badge **Field Notes**): "Le construí un sous-chef a mi mamá".
171
+ - Submit + post social.
172
+
173
+ ---
174
+
175
+ ## 5. Decisiones técnicas explícitas
176
+
177
+ ### 5.1 Por qué Modal en runtime (rompiendo Off the Grid)
178
+ Igual que en planes anteriores: Flux.2 9B en CPU del Space free es inviable (GB de RAM y minutos por imagen). Modal-powered es la elección obligada cuando el centro de la app es generación visual.
179
+
180
+ ### 5.2 Por qué cocina mexicana específicamente
181
+ - Dataset acotado pero rico. Cubrible en 200 recetas.
182
+ - Diferenciador cultural automático.
183
+ - Se alinea con el público "para mi mamá" (si tu mamá es latina).
184
+ - Si los jueces son mexicanos en Discord/Slack, +1.
185
+
186
+ ### 5.3 Por qué visual_target con Flux.2 en lugar de imagen stock
187
+ - Stock photos tienen sesgo americano/europeo. Flux genera estilo mexicano si lo prompteas.
188
+ - Stock no se ajusta al ingrediente exacto que tienes (Flux sí).
189
+ - Esto es lo que hace única la app — es el wow factor.
190
+
191
+ ### 5.4 Por qué pre-renderizar audio en lugar de streaming
192
+ - Latencia: streaming TTS tarda y se ve mal en demo.
193
+ - Cocina es secuencial: sabes los 5 pasos cuando empieza la receta. Pre-render todo en paralelo.
194
+ - Si el usuario hace replan, regeneras solo los pasos afectados.
195
+
196
+ ### 5.5 LoRA y no full fine-tune
197
+ Mismo argumento de planes anteriores: 150-200 ejemplos = LoRA r=16 es suficiente. ~30 min A10G ≈ $1.
198
+
199
+ ### 5.6 Cómo gastar los $250 de Modal
200
+ | Concepto | Estimado |
201
+ |---|---|
202
+ | Inferencia Flux.2 dev (días 1-9, ~5h GPU L4) | $5-15 |
203
+ | Generación dataset sintético cocina mexicana (~2h) | $3-8 |
204
+ | LoRA fine-tune + sweeps (~3h A10G) | $4-5 |
205
+ | Eval pipeline | $1 |
206
+ | Inferencia durante grading de jueces (~10h) | $10-25 |
207
+ | **Subtotal** | **$25-65** |
208
+ | Buffer | $30 |
209
+ | **Total proyectado** | **~$55-95 / $250** |
210
+
211
+ ### 5.7 Cómo gastar los $100 de OpenAI Codex
212
+ - Codex CLI durante 10 días como pair-programmer: $20-40.
213
+ - Generación de 200 recetas mexicanas estructuradas (Día 8): $10-25.
214
+ - Generación de prompts de Flux para los pasos (Día 4): $5-10.
215
+ - Reserva: $30.
216
+
217
+ ---
218
+
219
+ ## 6. Riesgos y mitigaciones
220
+
221
+ | Riesgo | Impacto | Mitigación |
222
+ |---|---|---|
223
+ | Flux.2 Klein no tiene API/pesos públicos cuando lo necesitas | Bloquea idea | Plan B: Flux.1-schnell o SDXL-Lightning. Pierdes tag sponsor pero idea sobrevive. |
224
+ | MiniCPM-V no identifica ingredientes mexicanos (chile poblano, chayote, nopales) | Recipe Planner falla | Agrega few-shot examples al prompt; eventualmente fine-tune del visión sobre 50 fotos etiquetadas |
225
+ | Flux.2 genera comida poco apetitosa/uncanny | Mata el demo | Itera prompts (style="recipe magazine, warm light, top-down"); usa imagen de plato final como ref para los pasos |
226
+ | Latencia: receta completa tarda más de 30s en generarse | Demo aburrido | Streaming progresivo (muestra opción + plato final primero, pasos después); paraleliza Flux + TTS |
227
+ | Modal cold start ~30-60s en Flux | Primera demo lenta | Pre-warm 30s antes de filmar; `keep_warm=1` el día del demo |
228
+ | Validador de progreso da false positives ("vas bien" cuando no) | Confunde al usuario | Conservador: solo dice "vas bien" si la similitud es muy alta; default es "sigue" sin juicio fuerte |
229
+ | TTS español sin acento mexicano | Suena raro | Si OpenBMB no tiene es-MX, usa Cohere o Kokoro con voz neutra; pre-graba para video |
230
+ | Usuario del demo cocina mal/se quema | Mata el video | Practica la receta una vez antes de filmar; ten 2-3 candidatos de receta listos |
231
+ | Otro equipo presenta "recipe app con AI" | Compite por premios | Diferénciate con: closed-loop visual + español + cocina mexicana específica + dataset publicado + persona real cocinando + Workflow visible |
232
+ | Workflows de Gradio inestable (lanzado ayer) | Rompe app | Versión sin Workflows como backup. Workflows es decoración. |
233
+
234
+ ---
235
+
236
+ ## 7. Plan B — corte de scope
237
+
238
+ Si en Día 7 ves que no llegas, recorta features en este orden:
239
+
240
+ | # | Cortar | Pierdes | Conservas |
241
+ |---|---|---|---|
242
+ | 1 | STT (preguntas hands-free por voz) | comodidad demo | input por texto + foto |
243
+ | 2 | 2da voz (Cohere tip-giver) | un sponsor de voz | narrador único |
244
+ | 3 | Progress Validator (closed-loop) | **Best Agent badge** + innovación principal | demo lineal sin loop |
245
+ | 4 | Fine-tune del Planner | **Well-Tuned badge** | base model + prompting |
246
+ | 5 | Gradio Workflows showcase | diferenciador "fresh" | pipeline Python |
247
+ | 6 | UI super-custom | **Off-Brand badge** | UI default |
248
+
249
+ **NUNCA cortar:**
250
+ - Vision + Planner + Step Illustrator + Narrator + UI mínima + video con persona real cocinando.
251
+
252
+ Eso solo ya entra fuerte a Backyard AI track.
253
+
254
+ ---
255
+
256
+ ## 8. Métricas de éxito (auto-evaluación pre-submit)
257
+
258
+ Antes de mandar:
259
+
260
+ - [ ] Una persona real cocinó una receta entera con la app y se la comió.
261
+ - [ ] El video tiene una cara humana y un plato terminado en al menos 30s de los 90s.
262
+ - [ ] La app identifica correctamente ≥4 de 5 ingredientes en una foto típica de refri.
263
+ - [ ] Las imágenes de Flux para los pasos se ven *apetitosas* (test: si las muestras a alguien sin contexto, dice "se ve rico").
264
+ - [ ] Una receta completa se genera en menos de 30s (texto + 5 imágenes + audio).
265
+ - [ ] El Progress Validator funciona en al menos 5 de 10 fotos de progreso reales.
266
+ - [ ] El README tiene un diagrama y la sección "How closed-loop cooking works".
267
+ - [ ] Hay 3 recetas pre-renderizadas listas para que jueces las vean sin esperar.
268
+ - [ ] Total params declarado y verificado ≤ 32B.
269
+ - [ ] Sin secrets hardcoded.
270
+
271
+ Si fallas más de 2, no submitas; arregla.
272
+
273
+ ---
274
+
275
+ ## 9. Lo que NO debes hacer
276
+
277
+ - **No** intentes generar video del platillo. Imagen estática se ve mejor que video AI mediocre.
278
+ - **No** hagas más de 7 pasos por receta. Atención del juez = 60-90s.
279
+ - **No** soportes 100 recetas. Soporta 20 recetas mexicanas excelentes y di "más recetas pronto".
280
+ - **No** subas fotos del refri real con productos identificables (marcas, info personal). Borra labels.
281
+ - **No** persigas Off the Grid. Decisión ya tomada.
282
+ - **No** dejes el video de demo para el último día sin practicar la receta antes.
283
+ - **No** publiques tokens en el repo.
284
+ - **No** generes recetas con ingredientes raros que la mayoría no tenga (cocina accesible > cocina chef).
285
+
286
+ ---
287
+
288
+ ## 10. Pitch del README (esqueleto)
289
+
290
+ ```markdown
291
+ # Cocina Conmigo
292
+ > A visual sous-chef that sees what's in your fridge,
293
+ > shows you what each step should look like, and walks you through it
294
+ > with voice — hands-free.
295
+
296
+ [60-second demo video embed: tu mamá cocinando tinga]
297
+
298
+ ## Why it shouldn't exist (but does)
299
+ Every recipe app is a list of steps. Cocina Conmigo is a closed-loop assistant:
300
+ it generates the *target image* of each cooking step with Flux.2, listens
301
+ when you ask "¿voy bien?", and adapts when you say "no tengo cilantro."
302
+
303
+ ## Tech
304
+ - 👁️ MiniCPM-V — sees your fridge + validates your progress
305
+ - 🧠 MiniCPM-4 4B (LoRA fine-tuned on Mexican cuisine) — recipe planner
306
+ - 🎨 Flux.2 Klein 9B (Modal endpoint) — generates target images per step
307
+ - 🔊 OpenBMB voice — sous-chef narrator
308
+ - 🎭 Cohere voice — tip-giver second voice
309
+ - 🎙️ Whisper-tiny — voice input
310
+ - ⚙️ Gradio Workflows — visible pipeline of nodes
311
+
312
+ Total params: ~17B (≤ 32B ✓)
313
+
314
+ ## Badges
315
+ ✓ Llama Champion · ✓ Well-Tuned · ✓ Off-Brand · ✓ Sharing is Caring · ✓ Field Notes
316
+
317
+ ## Built for
318
+ My mom. She makes great mole. She can never remember tinga.
319
+
320
+ ## Try it
321
+ [HF Space link]
322
+ ```
323
+
324
+ ---
325
+
326
+ ## 11. Apéndice: snippets clave
327
+
328
+ ### 11.1 Mise en Place agent (vision)
329
+ ```python
330
+ def identify_ingredients(image: PIL.Image) -> list[str]:
331
+ prompt = """Veo esta foto de un refrigerador o despensa.
332
+ Lista TODOS los ingredientes que se ven, en español, en JSON:
333
+ {"ingredients": ["pollo", "cebolla", "cilantro", ...]}
334
+ Solo ingredientes alimentarios, no contenedores."""
335
+ out = mini_cpm_v.create_chat_completion(messages=[
336
+ {"role": "user", "content": [
337
+ {"type": "image_url", "image_url": pil_to_data_url(image)},
338
+ {"type": "text", "text": prompt}
339
+ ]}
340
+ ])
341
+ return json.loads(out["choices"][0]["message"]["content"])["ingredients"]
342
+ ```
343
+
344
+ ### 11.2 Recipe Planner agent (LLM)
345
+ ```python
346
+ SYS = """Eres un chef mexicano. Generas recetas a partir de ingredientes
347
+ disponibles. Prefiere cocina mexicana tradicional, accesible.
348
+
349
+ Salida JSON estricta:
350
+ {
351
+ "name": "...",
352
+ "options": [{"name": "...", "why": "..."}],
353
+ "steps": [
354
+ {"n": 1, "instruction": "...", "duration": "4 min",
355
+ "visual": "english visual description for image gen",
356
+ "tip": "optional warning or tip"}
357
+ ],
358
+ "missing": ["cilantro"],
359
+ "substitutes": {"cilantro": ["perejil", "nada"]},
360
+ "final_dish_visual": "english visual description of the final plated dish"
361
+ }
362
+ """
363
+
364
+ def plan_recipe(ingredients, choice=None):
365
+ msgs = [{"role": "system", "content": SYS}]
366
+ msgs.append({"role": "user", "content":
367
+ f"Tengo: {', '.join(ingredients)}.\n"
368
+ + (f"Quiero hacer: {choice}." if choice else "Propón 3 opciones.")})
369
+ raw = llm.create_chat_completion(messages=msgs, temperature=0.7)
370
+ return json.loads(raw["choices"][0]["message"]["content"])
371
+ ```
372
+
373
+ ### 11.3 Step Illustrator (Flux endpoint)
374
+ ```python
375
+ import modal
376
+ app = modal.App("cocina-flux")
377
+ image = modal.Image.debian_slim().pip_install("torch","diffusers","transformers","accelerate","Pillow")
378
+
379
+ @app.cls(image=image, gpu="L4", scaledown_window=180, keep_warm=0)
380
+ class FluxKlein:
381
+ @modal.enter()
382
+ def load(self):
383
+ from diffusers import FluxPipeline
384
+ self.pipe = FluxPipeline.from_pretrained("black-forest-labs/FLUX.2-klein",
385
+ torch_dtype="bfloat16").to("cuda")
386
+
387
+ @modal.method()
388
+ def render_step(self, visual: str, ref_png: bytes | None = None) -> bytes:
389
+ from PIL import Image; import io
390
+ prompt = (f"Top-down photo of a kitchen pan or plate showing {visual}. "
391
+ f"Mexican home cooking, warm natural lighting, recipe magazine "
392
+ f"style, photorealistic, appetizing.")
393
+ if ref_png:
394
+ ref = Image.open(io.BytesIO(ref_png)).convert("RGB")
395
+ img = self.pipe(prompt=prompt, image=ref, strength=0.6,
396
+ num_inference_steps=4).images[0]
397
+ else:
398
+ img = self.pipe(prompt=prompt, num_inference_steps=4).images[0]
399
+ buf = io.BytesIO(); img.save(buf, "PNG"); return buf.getvalue()
400
+ ```
401
+
402
+ ### 11.4 Progress Validator (closed-loop)
403
+ ```python
404
+ def validate_progress(target_image: PIL.Image, user_photo: PIL.Image,
405
+ step_instruction: str) -> dict:
406
+ prompt = f"""Compara estas dos fotos de cocina.
407
+ Foto 1 (objetivo): cómo debe verse después del paso "{step_instruction}".
408
+ Foto 2 (usuario): cómo va el usuario.
409
+
410
+ Responde en JSON:
411
+ {{"verdict": "go|wait|fix", "feedback_es": "...", "tip": "..." | null}}
412
+ - "go": va bien, siguiente paso
413
+ - "wait": le falta tiempo
414
+ - "fix": algo se ve mal, sugiere ajuste
415
+ """
416
+ out = mini_cpm_v.create_chat_completion(messages=[
417
+ {"role": "user", "content": [
418
+ {"type": "image_url", "image_url": pil_to_data_url(target_image)},
419
+ {"type": "image_url", "image_url": pil_to_data_url(user_photo)},
420
+ {"type": "text", "text": prompt}
421
+ ]}
422
+ ])
423
+ return json.loads(out["choices"][0]["message"]["content"])
424
+ ```
425
+
426
+ ### 11.5 UI Off-Brand (recipe card)
427
+ ```python
428
+ import gradio as gr
429
+
430
+ CSS = """
431
+ @import url('https://fonts.googleapis.com/css2?family=Lora:wght@400;700&family=Inter:wght@400;600&display=swap');
432
+ .gradio-container {background: #f5ecd9 !important; font-family: 'Inter', sans-serif !important;}
433
+ .recipe-hero {background: #fffbf0; border-radius: 14px; padding: 28px;
434
+ box-shadow: 0 8px 24px rgba(0,0,0,0.12); border: 1px solid #d8c9ad;}
435
+ .recipe-hero h1 {font-family: 'Lora', serif !important; font-size: 36px !important;
436
+ margin: 0 0 6px !important; color: #6b4a2a !important;}
437
+ .step-card {background: #fffbf0; border-left: 4px solid #a85c2a;
438
+ border-radius: 8px; padding: 18px 22px; margin: 12px 0;}
439
+ .step-card h3 {font-family: 'Lora', serif !important; margin: 0 !important;}
440
+ .step-card p {font-size: 17px !important; line-height: 1.6;}
441
+ button.primary {background: #a85c2a !important; font-family: 'Inter', sans-serif !important;
442
+ font-weight: 600 !important; font-size: 16px !important; padding: 14px 22px !important;}
443
+ """
444
+
445
+ with gr.Blocks(css=CSS, title="Cocina Conmigo") as demo:
446
+ gr.Markdown("# 👩‍🍳 Cocina Conmigo")
447
+ fridge = gr.Image(label="📸 Foto de tu refri o despensa", type="pil")
448
+ btn = gr.Button("¿Qué cocino?", variant="primary")
449
+ with gr.Column(elem_classes=["recipe-hero"]):
450
+ title = gr.Markdown()
451
+ final_img = gr.Image(show_label=False)
452
+ steps_box = gr.Column()
453
+ progress = gr.Image(label="📸 Tómame foto de tu progreso", type="pil")
454
+ feedback = gr.Markdown()
455
+ # callbacks omitidos
456
+ ```
457
+
458
+ ### 11.6 LoRA fine-tune del Planner en Modal
459
+ ```python
460
+ @app.function(image=image_train, gpu="A10G", timeout=60*60*2,
461
+ volumes={"/cache": modal.Volume.from_name("hf-cache", create_if_missing=True)})
462
+ def train_planner():
463
+ import os; os.environ["HF_HOME"] = "/cache"
464
+ from transformers import AutoModelForCausalLM, AutoTokenizer
465
+ from peft import LoraConfig, get_peft_model
466
+ from trl import SFTTrainer, SFTConfig
467
+ from datasets import load_dataset
468
+
469
+ base = "openbmb/MiniCPM-4-Base"
470
+ tok = AutoTokenizer.from_pretrained(base, trust_remote_code=True)
471
+ model = AutoModelForCausalLM.from_pretrained(base, trust_remote_code=True,
472
+ device_map="cuda", torch_dtype="bfloat16")
473
+ model = get_peft_model(model, LoraConfig(r=16, lora_alpha=32,
474
+ target_modules="all-linear"))
475
+ ds = load_dataset("tu-usuario/recetas-mexicanas-sft", split="train")
476
+ SFTTrainer(model=model, tokenizer=tok, train_dataset=ds,
477
+ args=SFTConfig(output_dir="/cache/out", num_train_epochs=2,
478
+ per_device_train_batch_size=4, learning_rate=2e-4,
479
+ push_to_hub=True,
480
+ hub_model_id="tu-usuario/cocinaconmigo-4b-mx")
481
+ ).train()
482
+ ```
483
+
484
+ ---
485
+
486
+ ## 12. Lectura recomendada antes del Día 1
487
+
488
+ - `Context/guia-tecnologias.md` (sección 3 Modal, sección 4 llama.cpp).
489
+ - HF Black Forest Labs: <https://huggingface.co/black-forest-labs> — confirma versión Flux.2 Klein.
490
+ - HF MiniCPM-V: <https://huggingface.co/openbmb> — versión vision con GGUF.
491
+ - Modal stable-diffusion example: <https://github.com/modal-labs/modal-examples/tree/main/06_gpu_and_ml/stable_diffusion>.
492
+ - Diffusers img2img: <https://huggingface.co/docs/diffusers/using-diffusers/img2img>.
493
+ - Gradio Workflows: <https://www.gradio.app/guides> (busca el guide más reciente).
494
+ - Cohere Labs voice: confirma con sponsor el modelo exacto disponible.
495
+
496
+ > Cocina con tu mamá una vez antes de empezar a programar. Te va a aclarar más sobre qué necesita tu app que cualquier brainstorm. Suerte.
Strategy/plan.md ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Plan ganador — "Cocina Conmigo"
2
+
3
+ > Un sous-chef multimodal que ve lo que tienes en el refri, te dice qué cocinar, te muestra cómo debe verse cada paso con Flux.2, y te narra todo por voz mientras cocinas con las manos llenas.
4
+ >
5
+ > Hackathon "Small models / Big adventures" — junio 2026.
6
+
7
+ ---
8
+
9
+ ## TL;DR
10
+
11
+ **Idea elegida:** **Cocina Conmigo** — un copiloto de cocina hands-free que combina visión, razonamiento, generación de imagen en tiempo real, y voz, para acompañarte de principio a fin: desde *"¿qué cocino con esto?"* hasta *"¿voy bien?"*.
12
+
13
+ **Por qué esta y no otra:** es la única idea que **(1) está fuera de las 11 ideas pre-cocinadas por OpenBMB**, **(2) usa Flux.2 + voces + Workflows como núcleo**, y **(3) tiene utilidad real, diaria y universal**. Nadie cocina como hobby; todos cocinan por necesidad.
14
+
15
+ ---
16
+
17
+ ## Por qué cambió el plan respecto a iteraciones anteriores
18
+
19
+ | Iteración | Idea | Por qué se descartó |
20
+ |---|---|---|
21
+ | v1 | Abuelita (parent phone helper) | **Está en la lista pre-cocinada de OpenBMB para Backyard AI.** 5-15 equipos van a hacer la misma cosa. |
22
+ | v2 | Cuentacuentos (storyteller ilustrado) | **Está en la lista pre-cocinada de OpenBMB para Thousand Token Wood ("voice storyteller").** Mismo problema de saturación. |
23
+ | v3 (ésta) | **Cocina Conmigo** | Refinamiento de **tu propia idea #1**, ahora viable de verdad gracias a Flux.2. **No está en ninguna lista pre-cocinada.** |
24
+
25
+ La regla estratégica: **usar los modelos de los sponsors, no copiar sus templates de proyecto.**
26
+
27
+ ---
28
+
29
+ ## Las 12 ideas en zona prohibida (clúster OpenBMB)
30
+
31
+ | Backyard AI | Thousand Token Wood |
32
+ |---|---|
33
+ | Parent phone helper | Voice storyteller |
34
+ | Receipt / bill explainer | Visual mystery box |
35
+ | Shop menu / repair manual | AI museum |
36
+ | Offline personal assistant / voice companion | Doodle creature |
37
+ | | Dream postcard gen |
38
+ | | Omni-modal adventure |
39
+ | | Tiny local NPC / character agent |
40
+
41
+ Y de tus 5 ideas originales, también caen:
42
+ - #3 cortes de cabello (tú mismo dijiste "ya está muy trabajado")
43
+ - #4 museum Q&A (choca con "AI museum")
44
+
45
+ **Quedan vivas, fuera de zona prohibida:**
46
+ - #1 Recetas (→ **Cocina Conmigo**, esta propuesta)
47
+ - #2 Detector de intenciones (no usa Flux.2, demo aburrida)
48
+ - #5 Outfits con armario (alternativa B, ver final del documento)
49
+
50
+ ---
51
+
52
+ ## El producto en una frase
53
+
54
+ > *"Mi mamá me pidió que le enseñara a hacer ramen. Le construí un sous-chef que vive en su tablet."*
55
+
56
+ ---
57
+
58
+ ## Las 4 historias del demo
59
+
60
+ ### 1. *"Tengo esto en el refri"*
61
+ ```
62
+ 👩 Mamá toma foto del refri abierto.
63
+ 🤖 [MiniCPM-V] "Veo: pollo, cebolla, jitomate, cilantro, tortillas, queso."
64
+ 🤖 [LLM] "Te puedo proponer: tinga de pollo, enchiladas, o quesadillas. ¿Qué traes ganas?"
65
+ 👩 "Tinga."
66
+ 🤖 [Flux.2] genera foto del platillo final, hermosa, mexicana.
67
+ 🤖 "Perfecto. Te tomará 35 minutos. ¿Empezamos?"
68
+ ```
69
+
70
+ ### 2. *"Cocina paso a paso"* (hands-free)
71
+ ```
72
+ 🤖 [Flux.2] muestra: olla con cebolla acitronándose
73
+ 🤖 [Voz OpenBMB] "Pica la cebolla en cubitos chicos y ponla en aceite caliente."
74
+ 👩 (cocinando, manos sucias)
75
+ 👩 "¿Cuánto tiempo?"
76
+ 🤖 [Voz] "Hasta que se vea transparente. Como 4 minutos."
77
+ ```
78
+
79
+ ### 3. *"¿Voy bien?"* (visión en loop)
80
+ ```
81
+ 👩 (toma foto del sartén con cebolla)
82
+ 🤖 [MiniCPM-V] compara contra imagen objetivo.
83
+ 🤖 [Voz Cohere — el "tip-giver"] "Le falta un poquito. Súbele 1 minuto más, está bien."
84
+ ```
85
+
86
+ ### 4. *"No tengo cilantro"* (replan adaptativo)
87
+ ```
88
+ 👩 "No tengo cilantro."
89
+ 🤖 [LLM] re-planea sobre la marcha.
90
+ 🤖 [Voz] "No pasa nada. Le ponemos perejil o nada. Sigue siendo tinga."
91
+ 🤖 [Flux.2] regenera la foto del plato final, ahora sin cilantro.
92
+ ```
93
+
94
+ Las 4 historias usan los **mismos 5 modelos**. Una sola pipeline.
95
+
96
+ ---
97
+
98
+ ## Por qué este plan **gana** este hackathon
99
+
100
+ ### 1. "Build for someone you actually know" → Backyard AI track
101
+ La descripción literal del track dice: *"Solve a real problem for someone you actually know. Pick a person — a neighbor, a parent, a small-business owner..."*. Tu mamá. Tu hermana. Tu hermano que vive solo. **Todos** cocinan. Pocas apps de hackathon van a tener un usuario tan cercano y tan recurrente.
102
+
103
+ ### 2. Aprovecha **todos** los assets sponsor sin copiar templates
104
+ | Asset | Cómo se usa |
105
+ |---|---|
106
+ | **Flux.2 Klein 9B** (sponsor) | Genera la imagen-objetivo del platillo + "esto debes ver" en cada paso · i2i para ajustes |
107
+ | **MiniCPM-V** (OpenBMB) | Visión: identifica ingredientes + valida progreso ("¿voy bien?") |
108
+ | **MiniCPM razonamiento** (OpenBMB) | Recipe Planner: arma receta + replan adaptativo |
109
+ | **OpenBMB voice / TTS** | Voz principal del sous-chef (cálida, paciente) |
110
+ | **Cohere Labs voice** (sponsor) | Segunda voz: tips, advertencias ("¡cuidado, se quema!") |
111
+ | **Whisper-tiny** | STT: preguntas hands-free mientras cocinas |
112
+ | **Gradio Workflows** | UI de nodos visible: Vision → Planner → Illustrator → Narrator → Validator |
113
+ | **Modal $250** | Hostea Flux.2 en GPU + dataset sintético + LoRA fine-tune |
114
+ | **OpenAI Codex $100** | Pair-programmer y generador de dataset de recetas |
115
+
116
+ Todos los sponsors tocados. Cero ideas copiadas.
117
+
118
+ ### 3. **Innovación técnica concreta**: el bucle visual cerrado
119
+ La mayoría de "recipe apps" del mundo son listas de pasos. Cocina Conmigo introduce un **closed-loop visual**:
120
+
121
+ ```
122
+ [Flux.2 muestra paso ideal] ──▶ [Usuario cocina]
123
+ ▲ │
124
+ │ ▼
125
+ [LLM ajusta plan] ◀── [MiniCPM-V valida foto del usuario]
126
+ ```
127
+
128
+ Esto es un agente real, no un wrapper. Best Agent badge en juego.
129
+
130
+ ### 4. Demo apetitoso = video viral
131
+ Persona real cocinando + voz cálida + ilustraciones live + "¡me quedó igual!" + plato final que se come frente a la cámara. Best Demo + Community Choice por inercia. **Nadie va a recordar la submission #14 de "voice storyteller"; van a recordar el video donde tu mamá hace tinga con AI.**
132
+
133
+ ### 5. Diferenciación cultural sostenible
134
+ - **Español-mexicano-first** — diferenciador en hackathon US-céntrico.
135
+ - **Cocina mexicana** como dataset de fine-tune — territorio que pocos van a tocar.
136
+ - "Para mi mamá" como historia: emocional + universal.
137
+
138
+ ---
139
+
140
+ ## Arquitectura (resumen — ver `arquitectura.html`)
141
+
142
+ 5 nodos en un Gradio Workflow visible:
143
+
144
+ ```
145
+ [📸/🎙️ Input] ──▶ [👁️ Vision MiniCPM-V] ──▶ [🧠 Recipe Planner] ──▶ [🎨 Step Illustrator Flux.2]
146
+
147
+
148
+ [🔊 Sous-Chef Narrator OpenBMB] + [🎭 Tip-Giver Cohere]
149
+
150
+
151
+ [✅ Progress Validator] ──▶ loop al usuario
152
+ ```
153
+
154
+ | Nodo | Modelo | Tamaño | Rol |
155
+ |---|---|---|---|
156
+ | Vision In | MiniCPM-V 2.6 / 4 (Q4 GGUF) | ~2-4B | Identifica ingredientes + valida progreso |
157
+ | Planner | MiniCPM-4 4B (LoRA en cocina mexicana) | ~4B | Genera receta JSON estructurado · replan |
158
+ | Illustrator | Flux.2 Klein 9B (Modal GPU) | 9B | Imagen final + paso-a-paso, i2i para consistencia |
159
+ | Narrator | OpenBMB voice / Kokoro | ~1B | Voz principal: instrucciones |
160
+ | Tip-Giver | Cohere Labs voice | ~1B | Segunda voz: warnings, encouragement |
161
+ | STT (opcional) | Whisper-tiny | ~40M | "¿voy bien?" "¿cuánto tiempo?" |
162
+
163
+ **Total: ~17B parámetros** (cap 32B ✓)
164
+
165
+ **Donde corre:**
166
+ - Vision, Planner, voces, STT → CPU del HF Space (llama.cpp + bindings ligeros)
167
+ - **Flux.2 → endpoint Modal con GPU L4** (no aguanta CPU del Space)
168
+
169
+ > Mismo tradeoff que los planes anteriores: **rompemos Off the Grid** intencionalmente para preservar calidad de imagen y latencia. A cambio calificamos para Modal Awards.
170
+
171
+ ---
172
+
173
+ ## Badges objetivo (5/6)
174
+
175
+ | Badge | Cómo |
176
+ |---|---|
177
+ | ✓ **Llama Champion** | Vision + Planner via `llama-cpp-python` con GGUF Q4 |
178
+ | ✓ **Well-Tuned** | LoRA del Planner en dataset de cocina mexicana, publicado en HF |
179
+ | ✓ **Off-Brand** | UI estilo "tarjeta de receta" + modo cocina hands-free, no parece Gradio default |
180
+ | ✓ **Sharing is Caring** | Dataset de recetas mexicanas + agent traces + recetas generadas, todo al Hub |
181
+ | ✓ **Field Notes** | Blog: "Le construí un sous-chef a mi mamá" |
182
+ | ✗ **Off the Grid** | Sacrificio consciente: Flux.2 corre en Modal |
183
+
184
+ 5 badges + Modal-powered fuerte = competitivo para **Bonus Quest Champion ($2K)**.
185
+
186
+ ---
187
+
188
+ ## Premios objetivo (proyección)
189
+
190
+ | Premio | Probabilidad | Por qué |
191
+ |---|---|---|
192
+ | **Backyard AI Track** ($1K–$4K) | **Alta** | Idea es texto literal del track. Demo emocional. |
193
+ | **Modal Awards** ($3K–$10K credits) | **Alta** | Flux en Modal en runtime + entrenamiento offline. Modal-powered de manual. |
194
+ | **OpenBMB Award** ($1K–$2.5K) | **Alta** | Usa modelos OpenBMB en 3 roles (vision, planner, voice) sin copiar template |
195
+ | **Best Demo** ($1K) | **Alta** | Persona cocinando + comida final + voz = video apetitoso |
196
+ | **Community Choice** ($2K) | **Alta** | Apela a memoria emocional universal (tu mamá cocinando) |
197
+ | **Bonus Quest Champion** ($2K) | Media-alta | 5/6 badges es competitivo |
198
+ | **Best Agent** ($1K) | Media-alta | Closed-loop multi-agent real (5 agentes) |
199
+ | **Off-Brand** ($1.5K) | Media | UI tarjeta-de-receta tiene buenas chances |
200
+ | **Tiny Titan** ($1.5K) | Baja | Flux.2 9B nos saca del rango ≤4B |
201
+
202
+ **Cota razonable acumulada:** $5K–$12K cash + $3K–$10K Modal credits.
203
+
204
+ ---
205
+
206
+ ## Las 3 condiciones que pone Idea.md
207
+
208
+ | Condición | Cómo se cumple |
209
+ |---|---|
210
+ | **Innovador** | Closed-loop visual (Flux genera ideal → usuario cocina → vision valida → planner ajusta) — no existe en apps de receta |
211
+ | **Fresco** | Combina Flux.2 (nuevo) + Workflows (lanzado ayer) + voces multi-sponsor + cocina hands-free. Ninguna submission tendrá esa combinación. |
212
+ | **Útil** | Cocinar es diario, universal, recurrente. La app reemplaza Google + YouTube + adivinar. |
213
+
214
+ ---
215
+
216
+ ## Decisiones que tienes que tomar tú
217
+
218
+ | Decisión | Recomendación |
219
+ |---|---|
220
+ | ¿Cocina Conmigo o Mi Espejo (outfits)? | **Cocina.** Menor riesgo técnico (Flux generando platos > generando personas reales con ropa). Más universal. |
221
+ | ¿Cocina mexicana o cocina general? | **Mexicana.** Diferenciador + fine-tune en dataset acotado y rico. |
222
+ | ¿Persona real para el demo? | **Sí, no negociable.** Tu mamá, tu pareja, tu vecina. Que coma frente a la cámara al final. |
223
+ | ¿Empiezas con texto o con voz/foto? | **Empieza con foto del refri + texto.** Voz se agrega en Día 7-9. |
224
+ | ¿Cuántos pasos por receta? | 5-7 pasos. Más es muy largo para el demo, menos no es una receta. |
225
+
226
+ ---
227
+
228
+ ## Plan B — alternativa "Mi Espejo"
229
+
230
+ Si por cualquier razón Cocina Conmigo no avanza (ej. Flux.2 genera platillos feos consistentemente), pivota a **"Mi Espejo"** (refinamiento de tu idea #5):
231
+
232
+ - 📸 Subes foto tuya + fotos del armario.
233
+ - 🧠 Stylist LLM combina outfits según ocasión + tendencia.
234
+ - 🎨 **Flux.2 i2i te genera vistiendo cada combinación.**
235
+ - 🔊 Voz comenta el look.
236
+
237
+ Mismas badges, mismo track (Backyard), pero más alto wow visual y más alto riesgo (uncanny valley con personas reales). **Es plan B**, no plan A.
238
+
239
+ ---
240
+
241
+ ## Siguiente paso
242
+
243
+ Lee **`estrategia.md`** (timeline 10 días, gasto Modal/Codex, riesgos+mitigaciones, snippets) y **`arquitectura.html`** (diagrama del sistema + las 4 historias del demo + Workflow visual). Luego abre Codex CLI y haz el "hola mundo" del Día 1: un endpoint Modal que devuelve una imagen Flux.2 de un platillo dado un nombre de receta.
244
+
245
+ > *"Cocinar es la última cosa que la IA debería poder ayudarte a hacer bien. Y por eso es la mejor cosa que puedes ganar haciendo."*
Strategy/plan_implementacion.md ADDED
@@ -0,0 +1,674 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Implementation Plan — "Cook With Me"
2
+
3
+ > Step-by-step implementation guide for developers building the multimodal cooking sous-chef Gradio app for Hugging Face Spaces.
4
+ >
5
+ > **Hackathon:** Small models / Big adventures — June 2026
6
+ > **Read first:** `plan.md` (the *what* and *why*) and `estrategia.md` (the *how* at a strategic level). This document is the *how* at a tactical level — turn this into code.
7
+
8
+ ---
9
+
10
+ ## 0. Locked decisions (do not re-discuss)
11
+
12
+ | Decision | Value | Reason |
13
+ |---|---|---|
14
+ | UI framework | **Gradio** | Hackathon requirement |
15
+ | Hosting | **Hugging Face Space** | Hackathon requirement |
16
+ | Inference runtime (text + vision) | **llama.cpp** via `llama-cpp-python` | Runs inside the Space CPU, no external APIs needed for now. Future: migrate to Modal |
17
+ | Image generation | **FLUX.2 Klein 9B** (`black-forest-labs/FLUX.2-klein-9B`) | Sponsor model; runs in the Space if a GPU Space is rented (or via `enable_model_cpu_offload()` as fallback). Plan to migrate this specific component to Modal post-hackathon |
18
+ | Recipe planner / reasoning | **`openbmb/MiniCPM-V-4`** (GGUF) | Provided requirement |
19
+ | Vision (ingredient ID + progress validator) | **`openbmb/MiniCPM-V-4.6`** (GGUF) | Provided requirement |
20
+ | Text-to-speech | **OpenBMB VoxCPM2** | Provided requirement |
21
+ | Recipe dataset | **`thedevastator/better-recipes-for-a-better-life`** (Kaggle) — international cuisine | Provided requirement; not limited to Mexican food |
22
+ | App language | **English only** | Provided requirement |
23
+ | Final output | **Recipe + step images + voice + nutritional values** | Provided requirement |
24
+ | External API calls at runtime | **None** | "llama.cpp inside the Space" mandate |
25
+
26
+ ---
27
+
28
+ ## 1. Architecture (final, English-only, llama.cpp-first)
29
+
30
+ ```
31
+ ┌──────────────────────────────────────┐
32
+ │ Hugging Face Space (Gradio) │
33
+ │ (CPU + optional GPU upgrade) │
34
+ ├──────────────────────────────────────┤
35
+ 📸 Fridge photo ─────▶│ [Vision Agent] │
36
+ │ MiniCPM-V-4.6 GGUF (llama.cpp) │
37
+ │ → list[ingredient] │
38
+ │ │ │
39
+ │ ▼ │
40
+ 🥘 User picks dish ───▶│ [Recipe Planner] │
41
+ │ MiniCPM-V-4 GGUF (llama.cpp) │
42
+ │ + retrieval over Kaggle dataset │
43
+ │ → Recipe JSON (steps, nutrition) │
44
+ │ │ │
45
+ │ ▼ │
46
+ │ [Step Illustrator] │
47
+ │ FLUX.2 Klein 9B (diffusers) │
48
+ │ → PNG per step + final dish │
49
+ │ │ │
50
+ │ ▼ │
51
+ │ [Narrator] │
52
+ │ VoxCPM2 → MP3 per step │
53
+ │ │ │
54
+ │ ▼ │
55
+ 📸 Progress photo ────▶│ [Progress Validator] │
56
+ │ MiniCPM-V-4.6 (vision compare) │
57
+ │ → "go / wait / fix" + tip │
58
+ └──────────────────────────────────────┘
59
+ ```
60
+
61
+ **Total parameter count (≤ 32B requirement):**
62
+ - MiniCPM-V-4 (reasoning) ≈ 4B
63
+ - MiniCPM-V-4.6 (vision) ≈ 4.6B
64
+ - FLUX.2 Klein ≈ 9B
65
+ - VoxCPM2 ≈ 1B (estimate)
66
+ - **Total ≈ 18.6B ✓**
67
+
68
+ ---
69
+
70
+ ## 2. Repository layout
71
+
72
+ ```
73
+ cook-with-me/
74
+ ├── app.py # Gradio entrypoint (Space looks for this)
75
+ ├── requirements.txt
76
+ ├── packages.txt # apt packages (ffmpeg, libsndfile1)
77
+ ├── README.md # Space card (HF requires YAML frontmatter)
78
+ ├── .gitignore
79
+ ├── src/
80
+ │ ├── __init__.py
81
+ │ ├── config.py # paths, model IDs, constants
82
+ │ ├── models/
83
+ │ │ ├── __init__.py
84
+ │ │ ├── vision.py # MiniCPM-V-4.6 wrapper (llama-cpp)
85
+ │ │ ├── planner.py # MiniCPM-V-4 wrapper (llama-cpp)
86
+ │ │ ├── illustrator.py # FLUX.2 Klein wrapper (diffusers)
87
+ │ │ ├── narrator.py # VoxCPM2 wrapper
88
+ │ │ └── loader.py # lazy singletons + GGUF download
89
+ │ ├── agents/
90
+ │ │ ├── mise_en_place.py # ingredient identification
91
+ │ │ ├── recipe_planner.py # builds Recipe object
92
+ │ │ ├── step_illustrator.py # per-step image gen
93
+ │ │ ├── narrator.py # per-step TTS
94
+ │ │ └── progress_validator.py
95
+ │ ├── data/
96
+ │ │ ├── recipe_index.py # loads Kaggle dataset, builds retrieval
97
+ │ │ └── nutrition.py # USDA-style nutrition computation
98
+ │ ├── pipeline.py # Recipe state machine, orchestration
99
+ │ ├── prompts/
100
+ │ │ ├── vision_prompt.txt
101
+ │ │ ├── planner_system.txt
102
+ │ │ └── validator_prompt.txt
103
+ │ └── ui/
104
+ │ ├── theme.py # custom CSS (Off-Brand badge)
105
+ │ └── components.py # reusable Gradio Blocks pieces
106
+ ├── scripts/
107
+ │ ├── download_models.py # pre-warms GGUF + Flux weights at build time
108
+ │ ├── build_recipe_index.py # caches Kaggle dataset locally
109
+ │ └── smoke_test.py # end-to-end validation before push
110
+ └── assets/
111
+ ├── sample_fridge_1.jpg
112
+ └── sample_progress_1.jpg
113
+ ```
114
+
115
+ ---
116
+
117
+ ## 3. Phase-by-phase plan (10 days)
118
+
119
+ > Each phase has: **goal**, **tasks**, **deliverable**, **verification check**. Do not move to the next phase if verification fails.
120
+
121
+ ---
122
+
123
+ ### Phase 0 — Day 0 (½ day): Account + tooling setup
124
+
125
+ **Goal:** every credential and CLI is ready before writing code.
126
+
127
+ **Tasks**
128
+ 1. Create or confirm Hugging Face account; generate a **write token** (Settings → Access Tokens). Store as `HF_TOKEN` env var locally.
129
+ 2. Install Hugging Face CLI: `pip install -U huggingface_hub` then `huggingface-cli login`.
130
+ 3. Install Kaggle CLI: `pip install kaggle`. Place `kaggle.json` (Account → API → Create New Token) in `~/.kaggle/kaggle.json` with `chmod 600`.
131
+ 4. Install OpenAI Codex CLI (pair-programmer) and verify your $100 credit is active.
132
+ 5. Install local Python 3.11 venv: `python -m venv .venv && source .venv/bin/activate`.
133
+ 6. Create the repo locally: `git init cook-with-me && cd cook-with-me`.
134
+ 7. Create an empty Hugging Face Space: huggingface.co → New Space → SDK = **Gradio**, Hardware = **CPU basic** (upgrade later if you need GPU for FLUX). Clone it and copy your repo skeleton into it.
135
+ 8. Verify model availability: open in a browser and confirm pages exist:
136
+ - `huggingface.co/openbmb/MiniCPM-V-4`
137
+ - `huggingface.co/openbmb/MiniCPM-V-4-6`
138
+ - `huggingface.co/openbmb/VoxCPM2` (or whatever the exact repo name is — search "VoxCPM" on HF)
139
+ - `huggingface.co/black-forest-labs/FLUX.2-klein-9B`
140
+
141
+ **Deliverable:** empty Space deployed showing "Hello World" Gradio.
142
+
143
+ **Verify:** `https://huggingface.co/spaces/<you>/cook-with-me` loads.
144
+
145
+ ---
146
+
147
+ ### Phase 1 — Day 1: Project skeleton + recipe dataset ingestion
148
+
149
+ **Goal:** the Kaggle dataset is downloaded, parsed, and cached as a local artifact ready for retrieval.
150
+
151
+ **Tasks**
152
+ 1. Write `requirements.txt` (initial version — packages will be added as phases progress):
153
+ ```text
154
+ gradio>=4.44
155
+ huggingface_hub>=0.24
156
+ llama-cpp-python>=0.3.2
157
+ numpy
158
+ pandas
159
+ Pillow
160
+ pydantic>=2
161
+ sentence-transformers
162
+ ```
163
+ 2. Write `packages.txt`:
164
+ ```text
165
+ ffmpeg
166
+ libsndfile1
167
+ ```
168
+ 3. Write `scripts/build_recipe_index.py`:
169
+ - Use `kagglehub.load_dataset(KaggleDatasetAdapter.PANDAS, "thedevastator/better-recipes-for-a-better-life", file_path)` — discover `file_path` by listing the dataset files first via `kagglehub.dataset_download`.
170
+ - Normalize columns: `name`, `ingredients` (list[str]), `instructions` (list[str]), `cuisine` (str if present, else "international"), `prep_time`, `servings`.
171
+ - Drop rows missing critical fields. Lowercase + strip ingredient strings.
172
+ - Save to `data/recipes.parquet` (~5–50MB depending on dataset size).
173
+ - Build sentence embeddings of the recipe **name + first 3 ingredients** using `sentence-transformers/all-MiniLM-L6-v2` and save to `data/recipes_emb.npy`.
174
+ - This script runs **once locally**; commit the parquet + npy files to the repo (or to a private HF Dataset, then download in `app.py`). If files exceed 100MB, push to a HF Dataset repo: `<you>/cook-with-me-recipes`.
175
+ 4. Write `src/data/recipe_index.py`:
176
+ - `class RecipeIndex` with `.search(ingredients: list[str], top_k=5) -> list[RecipeRow]`.
177
+ - Build a query string from ingredients, embed it, cosine-similarity against the cached embeddings, return top-k.
178
+
179
+ **Deliverable:** `python -c "from src.data.recipe_index import RecipeIndex; r=RecipeIndex(); print(r.search(['chicken','onion','tomato']))"` prints 5 sensible recipes.
180
+
181
+ **Verify:** at least 3 of the top-5 results contain ≥2 of the input ingredients.
182
+
183
+ ---
184
+
185
+ ### Phase 2 — Day 2: Vision agent (Mise en Place) — MiniCPM-V-4.6 via llama.cpp
186
+
187
+ **Goal:** given a fridge photo, return a clean list of English ingredient names.
188
+
189
+ **Background:** llama.cpp supports multimodal models through a vision projector (`mmproj-*.gguf`) plus the language model GGUF. MiniCPM-V family ships both files on the Hub.
190
+
191
+ **Tasks**
192
+ 1. Find the GGUF release of MiniCPM-V-4.6. Search HF for `MiniCPM-V-4_6-gguf` or `openbmb/MiniCPM-V-4_6-gguf`. You need **two** files:
193
+ - `Model-Q4_K_M.gguf` (or similar quant)
194
+ - `mmproj-model-f16.gguf` (the vision projector)
195
+ 2. Write `src/models/loader.py`:
196
+ ```python
197
+ from huggingface_hub import hf_hub_download
198
+ from llama_cpp import Llama
199
+ from llama_cpp.llama_chat_format import MiniCPMv26ChatHandler # or matching handler
200
+
201
+ _vision = None
202
+
203
+ def get_vision_model():
204
+ global _vision
205
+ if _vision is None:
206
+ model_path = hf_hub_download(
207
+ repo_id="openbmb/MiniCPM-V-4_6-gguf", # confirm exact repo
208
+ filename="Model-Q4_K_M.gguf",
209
+ )
210
+ mmproj_path = hf_hub_download(
211
+ repo_id="openbmb/MiniCPM-V-4_6-gguf",
212
+ filename="mmproj-model-f16.gguf",
213
+ )
214
+ handler = MiniCPMv26ChatHandler(clip_model_path=mmproj_path)
215
+ _vision = Llama(
216
+ model_path=model_path,
217
+ chat_handler=handler,
218
+ n_ctx=4096,
219
+ n_threads=4,
220
+ verbose=False,
221
+ )
222
+ return _vision
223
+ ```
224
+ 3. Write `src/agents/mise_en_place.py`:
225
+ ```python
226
+ import base64, io, json
227
+ from PIL import Image
228
+ from src.models.loader import get_vision_model
229
+
230
+ PROMPT = (
231
+ "You are an ingredient detector. Look at the fridge/pantry photo and "
232
+ "list every edible ingredient you can identify. Return strict JSON: "
233
+ '{"ingredients": ["chicken", "onion", "tomato", ...]} '
234
+ "Lowercase, English, no brand names, no containers."
235
+ )
236
+
237
+ def _img_to_data_url(img: Image.Image) -> str:
238
+ buf = io.BytesIO(); img.save(buf, "JPEG", quality=85)
239
+ b64 = base64.b64encode(buf.getvalue()).decode()
240
+ return f"data:image/jpeg;base64,{b64}"
241
+
242
+ def identify_ingredients(image: Image.Image) -> list[str]:
243
+ llm = get_vision_model()
244
+ out = llm.create_chat_completion(messages=[
245
+ {"role": "user", "content": [
246
+ {"type": "image_url", "image_url": {"url": _img_to_data_url(image)}},
247
+ {"type": "text", "text": PROMPT},
248
+ ]}
249
+ ], temperature=0.2, response_format={"type": "json_object"})
250
+ data = json.loads(out["choices"][0]["message"]["content"])
251
+ return [s.lower().strip() for s in data["ingredients"]]
252
+ ```
253
+ 4. Test locally with 5 sample fridge photos.
254
+
255
+ **Deliverable:** the function returns a non-empty English list with ≥80% precision on a clean fridge photo.
256
+
257
+ **Verify:** stash these 5 results in `tests/vision_smoke.json` for regression checks.
258
+
259
+ ---
260
+
261
+ ### Phase 3 — Day 3: Recipe Planner — MiniCPM-V-4 via llama.cpp + retrieval
262
+
263
+ **Goal:** given a list of ingredients (and optionally a chosen dish), return a fully structured `Recipe` JSON including steps, durations, visual descriptions, and nutritional values.
264
+
265
+ **Tasks**
266
+ 1. Find or convert MiniCPM-V-4 to GGUF. Likely repo: `openbmb/MiniCPM-V-4-gguf` or community quants. Pick `Q4_K_M`.
267
+ 2. Add to `src/models/loader.py` a `get_planner_model()` (same pattern as vision but without `chat_handler`).
268
+ 3. Write `src/agents/recipe_planner.py`:
269
+ - **Step A — propose:** call planner with `Tengo: [ingredients]. Propose 3 dish options that fit. Reply JSON.`
270
+ - **Step B — retrieve:** for the chosen dish name, call `RecipeIndex.search(...)` and pick the closest match. Use it as a *grounded reference*.
271
+ - **Step C — restructure:** prompt the planner with both the user's available ingredients and the retrieved reference recipe, asking it to output the canonical `Recipe` JSON schema below. The retrieval grounds the model and prevents hallucinated steps.
272
+ - **Step D — nutrition:** from the recipe ingredients, compute approximate nutritional values per serving. See Phase 3.5.
273
+ 4. Define the canonical schema in `src/pipeline.py` using Pydantic:
274
+ ```python
275
+ from pydantic import BaseModel
276
+ from typing import Optional
277
+
278
+ class Step(BaseModel):
279
+ n: int
280
+ instruction: str # English, imperative
281
+ duration: str # "4 minutes"
282
+ visual: str # English visual description for FLUX prompt
283
+ tip: Optional[str] = None
284
+
285
+ class Nutrition(BaseModel):
286
+ calories: int # per serving
287
+ protein_g: float
288
+ carbs_g: float
289
+ fat_g: float
290
+ fiber_g: float
291
+
292
+ class Recipe(BaseModel):
293
+ name: str
294
+ cuisine: str
295
+ servings: int
296
+ total_time_minutes: int
297
+ options: list[dict] # only populated on "propose" call
298
+ ingredients_have: list[str]
299
+ ingredients_missing: list[str]
300
+ substitutes: dict[str, list[str]]
301
+ steps: list[Step]
302
+ final_dish_visual: str
303
+ nutrition_per_serving: Nutrition
304
+ ```
305
+ 5. Write the system prompt (`src/prompts/planner_system.txt`):
306
+ - Persona: international chef
307
+ - Hard rule: output JSON only, matching schema
308
+ - Hard rule: prefer dishes feasible with available ingredients
309
+ - Hard rule: 5–7 steps, each ≤ 25 words, each with a concrete `visual` field for image generation
310
+ - Hard rule: include `nutrition_per_serving` (model is allowed to estimate; you'll override with `data/nutrition.py` for accuracy)
311
+ 6. Use `response_format={"type": "json_object"}` in the chat completion call. Set `temperature=0.7, top_p=0.95, enable_thinking=True` for the propose step (creative); `temperature=0.4` for the structured-output step (deterministic).
312
+
313
+ **Deliverable:** for `["chicken","onion","tomato","tortilla","cheese"]` and chosen dish "chicken tinga", the function returns a valid `Recipe` Pydantic object with 5–7 steps.
314
+
315
+ **Verify:** the JSON parses, each step has all required fields, and total inference time on Space CPU < 60 seconds.
316
+
317
+ ---
318
+
319
+ ### Phase 3.5 — Day 3 (afternoon): Nutritional values
320
+
321
+ **Goal:** the recipe ends with reliable per-serving nutrition (not hallucinated by the LLM).
322
+
323
+ **Approach:** small, embedded reference table beats LLM math.
324
+
325
+ **Tasks**
326
+ 1. Bundle `data/nutrition_table.csv` — a 200-row CSV mapping common English ingredient names to per-100g macros (kcal, protein, carbs, fat, fiber). Source: USDA FoodData Central CSV download (free, public domain). Trim columns; commit to repo.
327
+ 2. Write `src/data/nutrition.py`:
328
+ - `parse_quantity(line: str) -> (grams, ingredient_name)` — handle "2 cups flour", "200 g chicken", "1 tbsp olive oil". Use a small regex + a unit-to-grams table (cup=240, tbsp=15, tsp=5, oz=28.35).
329
+ - `compute_nutrition(ingredient_lines: list[str], servings: int) -> Nutrition` — sum per-100g values weighted by grams, divide by servings.
330
+ - If a line cannot be parsed, skip it and log; don't crash.
331
+ 3. After the planner returns a recipe, **overwrite** `recipe.nutrition_per_serving` with the computed value. Keep the LLM's value only as a fallback when the parser yields zero.
332
+
333
+ **Deliverable:** for a known recipe (e.g., spaghetti with tomato sauce, 4 servings), computed calories per serving is within ±25% of online references.
334
+
335
+ ---
336
+
337
+ ### Phase 4 — Day 4: Step Illustrator — FLUX.2 Klein 9B
338
+
339
+ **Goal:** generate an appetizing image for the final dish + one image per step.
340
+
341
+ **Constraint:** FLUX.2 Klein on CPU is impractical; on a free Space CPU it would take ~10 minutes per image. Two paths:
342
+ - **Path A (recommended for the hackathon):** upgrade the Space to a GPU instance (T4 or A10G — paid, but $20 HF credits cover it for a week of development). Code stays unchanged.
343
+ - **Path B (fallback):** run FLUX in `enable_model_cpu_offload()` mode with `num_inference_steps=4` and accept ~3 min/image — only feasible for pre-rendered demo recipes, not live runs.
344
+
345
+ **Tasks**
346
+ 1. Add to `requirements.txt`:
347
+ ```text
348
+ diffusers>=0.31
349
+ transformers>=4.45
350
+ accelerate
351
+ torch
352
+ safetensors
353
+ ```
354
+ 2. Write `src/models/illustrator.py`:
355
+ ```python
356
+ import torch
357
+ from diffusers import Flux2KleinPipeline
358
+
359
+ _pipe = None
360
+
361
+ def get_flux():
362
+ global _pipe
363
+ if _pipe is None:
364
+ dtype = torch.bfloat16
365
+ _pipe = Flux2KleinPipeline.from_pretrained(
366
+ "black-forest-labs/FLUX.2-klein-9B",
367
+ torch_dtype=dtype,
368
+ )
369
+ _pipe.enable_model_cpu_offload()
370
+ return _pipe
371
+
372
+ def render(prompt: str, seed: int = 0) -> "PIL.Image.Image":
373
+ pipe = get_flux()
374
+ device = "cuda" if torch.cuda.is_available() else "cpu"
375
+ img = pipe(
376
+ prompt=prompt,
377
+ height=1024, width=1024,
378
+ guidance_scale=1.0,
379
+ num_inference_steps=4,
380
+ generator=torch.Generator(device=device).manual_seed(seed),
381
+ ).images[0]
382
+ return img
383
+ ```
384
+ 3. Write `src/agents/step_illustrator.py`:
385
+ - For each `Step.visual`, build a prompt like:
386
+ > `f"Top-down photo of a kitchen pan or plate showing {visual}. {cuisine} home cooking, warm natural lighting, recipe magazine style, photorealistic, appetizing."`
387
+ - Generate the **final dish image first**, then the per-step images, all in **one Python loop** (no parallelism — FLUX holds the GPU).
388
+ - Cache results on disk keyed by `hash(prompt)` to avoid re-renders on re-runs.
389
+ - Emit Gradio progress updates so the UI doesn't appear frozen.
390
+ 4. **Critical tuning:** keep `num_inference_steps=4` (Klein is distilled). Higher counts blow latency and offer minimal quality gain at this scale.
391
+
392
+ **Deliverable:** for a 5-step recipe, all 6 images (final + 5 steps) render in:
393
+ - < 1 minute on T4 GPU Space
394
+ - < 8 minutes on CPU offload (acceptable only for pre-cached demos)
395
+
396
+ **Verify:** show the 6 images to an unprompted human; ≥4 should be described as "appetizing".
397
+
398
+ ---
399
+
400
+ ### Phase 5 — Day 5: Narrator — VoxCPM2
401
+
402
+ **Goal:** every step's instruction is rendered to an MP3 in a warm, clear English voice.
403
+
404
+ **Tasks**
405
+ 1. Confirm the exact VoxCPM2 repo name on HF (`openbmb/VoxCPM2` or similar). Read its README for the inference snippet — TTS APIs vary widely between models.
406
+ 2. Add to `requirements.txt`: `soundfile`, `torchaudio`, `numpy`. If VoxCPM2 ships GGUF, use it via `llama-cpp-python` audio extension (if available); otherwise load via `transformers` directly.
407
+ 3. Write `src/models/narrator.py`:
408
+ ```python
409
+ _tts = None
410
+
411
+ def get_tts():
412
+ global _tts
413
+ if _tts is None:
414
+ # placeholder — replace with the exact VoxCPM2 loading code from its README
415
+ from transformers import AutoModel, AutoProcessor
416
+ _tts = ... # load on CPU; VoxCPM2 is small (~1B)
417
+ return _tts
418
+
419
+ def synthesize(text: str, voice: str = "warm_female_en") -> bytes:
420
+ """Returns MP3 bytes."""
421
+ tts = get_tts()
422
+ wav = tts.generate(text, voice=voice) # API depends on VoxCPM2
423
+ # encode wav -> mp3 with soundfile + ffmpeg-python or pydub
424
+ return mp3_bytes
425
+ ```
426
+ 4. Write `src/agents/narrator.py`:
427
+ - For each step, synthesize `step.instruction`. If `step.tip` is set, synthesize a separate "tip" clip.
428
+ - Save MP3 files in a per-recipe temp directory; return file paths to Gradio.
429
+ 5. Pre-render all step audio when the recipe is finalized — never stream per-step in the demo (too much UI lag).
430
+
431
+ **Deliverable:** clicking "Play" on step 1 in the UI plays clear English narration.
432
+
433
+ **Verify:** on a 5-step recipe, total TTS rendering time < 30 seconds on CPU.
434
+
435
+ ---
436
+
437
+ ### Phase 6 — Day 6: Gradio UI (Off-Brand)
438
+
439
+ **Goal:** the Space looks like a recipe magazine, not stock Gradio.
440
+
441
+ **Tasks**
442
+ 1. Write `src/ui/theme.py`:
443
+ ```python
444
+ import gradio as gr
445
+
446
+ theme = gr.themes.Soft(
447
+ primary_hue="orange",
448
+ neutral_hue="stone",
449
+ font=[gr.themes.GoogleFont("Inter"), "sans-serif"],
450
+ font_mono=[gr.themes.GoogleFont("JetBrains Mono"), "monospace"],
451
+ )
452
+
453
+ CSS = """
454
+ .gradio-container { background: #f5ecd9 !important; }
455
+ .recipe-hero { background:#fffbf0; border-radius:14px; padding:28px; }
456
+ .recipe-hero h1 { font-family:'Lora',serif!important; font-size:36px!important; color:#6b4a2a!important; }
457
+ .step-card { background:#fffbf0; border-left:4px solid #a85c2a; border-radius:8px; padding:18px 22px; margin:12px 0; }
458
+ .nutri-grid { display:grid; grid-template-columns:repeat(5,1fr); gap:12px; margin-top:24px; }
459
+ .nutri-cell { background:#fffbf0; border:1px solid #d8c9ad; border-radius:10px; padding:12px; text-align:center; }
460
+ """
461
+ ```
462
+ 2. Write `app.py` with three tabs:
463
+ - **Tab 1 — Cook**: fridge photo input → ingredient chips → 3 dish options → selected recipe card with hero image, steps (image + text + audio play button each), nutrition grid at the bottom.
464
+ - **Tab 2 — Check Progress**: upload a progress photo + select active step → validator returns badge (`go/wait/fix`) + tip + audio.
465
+ - **Tab 3 — About / Tech**: README-style explanation, badges, model list.
466
+ 3. Use `gr.Blocks` with `gr.State` to hold the current `Recipe` Pydantic object across UI events. Serialize to/from `dict` since Pydantic objects don't survive Gradio state by default — wrap in `state.value = recipe.model_dump()`.
467
+ 4. Wire callbacks:
468
+ - `btn_propose.click(fn=on_propose, inputs=[fridge_photo], outputs=[ingredient_chips, dish_options, state])`
469
+ - `dish_options.select(fn=on_pick_dish, inputs=[state, picked_dish], outputs=[recipe_card, hero_img, steps_column, nutrition_grid, state])`
470
+ - `progress_image.upload(fn=on_validate, inputs=[state, current_step_idx, progress_image], outputs=[verdict_md, tip_audio])`
471
+
472
+ **Deliverable:** end-to-end run from a sample fridge photo to a fully rendered recipe card with audio and nutrition. No Gradio default look anywhere.
473
+
474
+ ---
475
+
476
+ ### Phase 7 — Day 7: Progress Validator (closed loop)
477
+
478
+ **Goal:** user uploads a progress photo, app says "go / wait / fix" with a voiced tip.
479
+
480
+ **Tasks**
481
+ 1. Write `src/agents/progress_validator.py`:
482
+ ```python
483
+ PROMPT = """Compare these two cooking photos.
484
+ Photo 1 (target): how it should look after the step "{instruction}".
485
+ Photo 2 (user's pan/plate): the user's current progress.
486
+ Reply strict JSON: {"verdict": "go|wait|fix", "feedback": "...", "tip": "..."}
487
+ - "go": looks right, move to next step
488
+ - "wait": needs more time, do not change anything yet
489
+ - "fix": something is off; suggest a concrete adjustment in one sentence
490
+ """
491
+ def validate(target_img, user_img, step_instruction): ...
492
+ ```
493
+ 2. Use the same vision model singleton as Phase 2 — both calls share weights.
494
+ 3. Render the verdict as a colored badge (green/amber/red) and play the tip via VoxCPM2.
495
+
496
+ **Deliverable:** running the validator on 5 real progress photos returns the correct verdict on ≥3.
497
+
498
+ ---
499
+
500
+ ### Phase 8 — Day 8: Fine-tune the Planner on the Kaggle dataset (Well-Tuned badge)
501
+
502
+ > **Important caveat:** The user instruction says "for now keep inference on llama.cpp inside HF Space, future migration to Modal." Fine-tuning still **requires GPU**, so training itself happens on Modal (one-shot, offline) or on a rented Colab/Lambda GPU. Inference of the resulting model stays on llama.cpp inside the Space (as GGUF). This does **not** violate the runtime constraint — only the build pipeline touches a GPU.
503
+
504
+ **Goal:** publish a fine-tuned Planner GGUF to the Hub and load it from the Space.
505
+
506
+ **Tasks**
507
+ 1. **Build SFT dataset** (`scripts/build_sft_dataset.py`):
508
+ - Load Kaggle `better-recipes` dataset.
509
+ - For each recipe, build a `(prompt, completion)` pair where `prompt` is `"Available ingredients: X, Y, Z. Propose recipe."` and `completion` is the full canonical `Recipe` JSON.
510
+ - Generate ~1000 pairs, push to `<you>/cook-with-me-sft` HF Dataset.
511
+ 2. **LoRA training** (`scripts/train_planner.py` — to be run on a GPU machine, not the Space):
512
+ ```python
513
+ # peft + trl SFTTrainer, base = openbmb/MiniCPM-V-4
514
+ # r=16, alpha=32, lr=2e-4, epochs=2, batch=4
515
+ # push_to_hub=True, hub_model_id="<you>/cook-with-me-planner-4b"
516
+ ```
517
+ 3. **Convert to GGUF** (Day 8 evening):
518
+ - Use `llama.cpp/convert_hf_to_gguf.py` then `quantize` to `Q4_K_M`.
519
+ - Push GGUF to `<you>/cook-with-me-planner-4b-gguf`.
520
+ 4. Update `src/models/loader.py` to point at your GGUF instead of the base model.
521
+
522
+ **Deliverable:** the Space loads your fine-tuned Planner GGUF and produces JSON recipes that are noticeably better-formatted than the base model on a held-out test set.
523
+
524
+ ---
525
+
526
+ ### Phase 9 — Day 9: End-to-end test, performance pass, pre-warm cache
527
+
528
+ **Goal:** the Space loads in <60s and a full recipe (text + 5 images + 5 audios + nutrition) renders in <2 minutes on the chosen hardware.
529
+
530
+ **Tasks**
531
+ 1. Write `scripts/smoke_test.py` that runs the full pipeline on 3 sample fridge photos and asserts:
532
+ - Each ingredient list is non-empty
533
+ - Each recipe has 5–7 steps
534
+ - Each step has a non-empty image and audio path
535
+ - Nutrition has all 5 macros set
536
+ 2. Implement **on-disk caching** for FLUX outputs (key = SHA256 of prompt) so re-runs of the same recipe are instant. Save to `~/.cache/cook-with-me/flux/`.
537
+ 3. Pre-render and commit **3 fully-prepared demo recipes** (chicken tinga, pasta carbonara, chicken tikka) so judges see results in <5s on first click.
538
+ 4. Add error handling at every UI boundary: a model failure should display a friendly message, not a stack trace.
539
+ 5. Add a "Loading models..." progress bar on first request — first cold start can take 90s.
540
+
541
+ **Deliverable:** smoke test passes on the live Space.
542
+
543
+ ---
544
+
545
+ ### Phase 10 — Day 10: README, demo video, social post, submit
546
+
547
+ **Tasks**
548
+ 1. Write `README.md` with the required HF Space frontmatter:
549
+ ```yaml
550
+ ---
551
+ title: Cook With Me
552
+ emoji: 🍲
553
+ colorFrom: orange
554
+ colorTo: yellow
555
+ sdk: gradio
556
+ sdk_version: 4.44.0
557
+ app_file: app.py
558
+ pinned: false
559
+ license: apache-2.0
560
+ ---
561
+ ```
562
+ Followed by:
563
+ - One-paragraph pitch
564
+ - 60-second demo video embed
565
+ - Architecture diagram (export from `arquitectura.html` as PNG)
566
+ - Section: "How closed-loop visual cooking guidance works"
567
+ - Models used (with HF links + total parameter count)
568
+ - Badges declared
569
+ - Build / run instructions
570
+ 2. Record a 60–90 second demo video: real person cooks a recipe end-to-end with the app guiding via voice, ending with the cooked plate on camera.
571
+ 3. Write the Field Notes blog post: one of the engineering surprises (e.g., "FLUX.2 step images at 4 steps look better than 8 — here's why" or "Closed-loop validation needs the same vision model on both sides").
572
+ 4. Social post on X / LinkedIn with the demo video.
573
+ 5. Submit on the hackathon platform.
574
+
575
+ ---
576
+
577
+ ## 4. Tools usage matrix (when to reach for what)
578
+
579
+ | Phase | Primary tools | Why |
580
+ |---|---|---|
581
+ | 0 — setup | HF CLI, Kaggle CLI, OpenAI Codex CLI | one-shot config |
582
+ | 1 — data | `kagglehub`, `pandas`, `sentence-transformers` | offline dataset prep |
583
+ | 2 — vision | `llama-cpp-python` + `MiniCPMv26ChatHandler` | runs inside Space, badge: Llama Champion |
584
+ | 3 — planner | `llama-cpp-python` + retrieval over local parquet | grounded JSON output |
585
+ | 3.5 — nutrition | local CSV + regex parser | reliable, no LLM math |
586
+ | 4 — illustrator | `diffusers` + `Flux2KleinPipeline` | sponsor model showcase |
587
+ | 5 — narrator | VoxCPM2 via `transformers` (or its native API) | local TTS |
588
+ | 6 — UI | `gradio` + custom CSS theme | Off-Brand badge |
589
+ | 7 — validator | same vision singleton as phase 2 | closed-loop innovation, Best Agent |
590
+ | 8 — fine-tune | `peft`, `trl`, `llama.cpp` convert/quantize, on a GPU machine | Well-Tuned badge |
591
+ | 9 — test/cache | `pytest`, `hashlib`, on-disk FLUX cache | demo reliability |
592
+ | 10 — submit | HF Spaces, video tool, social | shipping |
593
+
594
+ ---
595
+
596
+ ## 5. Performance budget on the HF Space
597
+
598
+ | Operation | Target latency | Hardware needed |
599
+ |---|---|---|
600
+ | Vision: ingredient ID | < 8 s | CPU 4-thread |
601
+ | Planner: propose 3 dishes | < 12 s | CPU 4-thread |
602
+ | Planner: build full recipe JSON | < 20 s | CPU 4-thread |
603
+ | Nutrition computation | < 0.1 s | CPU |
604
+ | FLUX: 1 image (4 steps) | < 12 s on T4 / < 90 s on CPU offload | GPU strongly recommended |
605
+ | FLUX: 6 images (final + 5 steps) | < 80 s on T4 | GPU |
606
+ | VoxCPM2: 1 step narration | < 5 s | CPU |
607
+ | Validator: 1 progress check | < 8 s | CPU |
608
+ | **Full recipe end-to-end** | **< 2 min on T4 Space** | — |
609
+
610
+ **Hardware decision:** rent a T4 Space (~$0.40/hr) for the demo week. The $20 HF credits cover ~50 hours.
611
+
612
+ ---
613
+
614
+ ## 6. Risks and mitigations (delta from `estrategia.md`)
615
+
616
+ | Risk | Mitigation |
617
+ |---|---|
618
+ | MiniCPM-V-4 has no public GGUF | Convert yourself with `llama.cpp/convert_hf_to_gguf.py`. Allow a half-day buffer in Phase 2. |
619
+ | llama-cpp-python's MiniCPM-V chat handler version mismatch | Pin `llama-cpp-python==0.3.2` minimum; test the handler import on Day 2. If it fails, fall back to MiniCPM-V-2.6 GGUF (well-supported) for vision and document the swap. |
620
+ | FLUX.2 Klein 9B too slow on free CPU Space | Upgrade to a paid GPU Space (~$10 for the demo week). Document this in the README so judges expect it. |
621
+ | VoxCPM2 docs sparse | Drop to Kokoro-82M or Piper TTS as a backup. Lose the OpenBMB voice angle but keep the audio. |
622
+ | Kaggle dataset has format quirks (HTML in instructions, missing fields) | The Phase 1 normalization step handles this; budget 2 hours. |
623
+ | Nutrition CSV missing exotic ingredients | Skip-and-log strategy already designed; demo-day recipes use common ingredients only. |
624
+ | Total params >32B if VoxCPM2 turns out to be 7B | Check size in Phase 0; if too large, drop to a smaller TTS. |
625
+
626
+ ---
627
+
628
+ ## 7. "Day-1 hello world" checklist
629
+
630
+ Before writing any agent code, get this minimal end-to-end loop working — it proves your stack:
631
+
632
+ 1. ☐ Empty Gradio Space deployed, shows "Hello"
633
+ 2. ☐ `huggingface-cli login` works locally
634
+ 3. ☐ `kaggle datasets download thedevastator/better-recipes-for-a-better-life` succeeds
635
+ 4. ☐ `from llama_cpp import Llama` runs in your venv
636
+ 5. ☐ Download one tiny GGUF (e.g., TinyLlama Q4) and call it from a Gradio textbox round-trip
637
+ 6. ☐ Push the round-trip to the Space; confirm it answers in the cloud
638
+
639
+ **Only after all 6 are checked, start Phase 1.**
640
+
641
+ ---
642
+
643
+ ## 8. Where this plan differs from `estrategia.md` (deltas to communicate)
644
+
645
+ | Topic | `estrategia.md` (Spanish, Mexican-cuisine focus) | This document (current requirements) |
646
+ |---|---|---|
647
+ | Language | Spanish-first | **English only** |
648
+ | Cuisine | Mexican | **International** (Kaggle dataset) |
649
+ | Voice models | OpenBMB voice + Cohere Labs | **VoxCPM2** only (single voice) |
650
+ | Vision model | MiniCPM-V 2.6 / 4 | **MiniCPM-V-4.6** |
651
+ | Reasoning model | MiniCPM-4 4B | **MiniCPM-V-4** |
652
+ | FLUX runtime | Modal endpoint | **Inside Space (llama.cpp principle)**; Modal kept as a future migration target only |
653
+ | External APIs at runtime | Allowed (Modal, OpenAI optional) | **None** — full local inference inside Space |
654
+ | Nutritional info | Not specified | **Required** at end of recipe |
655
+ | Fine-tune dataset | 200 synthetic Mexican recipes | **Kaggle better-recipes (international)** |
656
+
657
+ If anything in `plan.md` or `estrategia.md` conflicts with this document, **this document wins** — it reflects the latest user requirements.
658
+
659
+ ---
660
+
661
+ ## 9. Definition of done
662
+
663
+ The implementation is complete when **all** of these are true:
664
+
665
+ - [ ] Public HF Space `https://huggingface.co/spaces/<you>/cook-with-me` loads
666
+ - [ ] App is fully in English
667
+ - [ ] Fridge photo → ingredient list → 3 dish options → full recipe with images, audio, and nutrition works end-to-end
668
+ - [ ] Progress validator returns sensible verdicts on 3+ test photos
669
+ - [ ] All inference (vision, planner, TTS) runs through llama.cpp / local diffusers — **no external API calls at runtime**
670
+ - [ ] Total parameters declared in README ≤ 32B
671
+ - [ ] Fine-tuned Planner GGUF published to HF Hub (Well-Tuned badge)
672
+ - [ ] Demo video (60–90s) recorded with a real person cooking
673
+ - [ ] Field Notes blog post published
674
+ - [ ] Submitted on the hackathon platform before deadline