FredinVázquez commited on
Commit ·
bad5d84
1
Parent(s): c795fe5
add strategy plan
Browse files- Strategy/arquitectura.html +668 -0
- Strategy/estrategia.md +496 -0
- Strategy/plan.md +245 -0
- Strategy/plan_implementacion.md +674 -0
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 > 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
|