<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mark Martin</title>
    <description>The latest articles on DEV Community by Mark Martin (@storycrafter).</description>
    <link>https://dev.to/storycrafter</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4199417%2Ff8ef6824-d49b-4b6b-b771-7ec940f3f4c1.png</url>
      <title>DEV Community: Mark Martin</title>
      <link>https://dev.to/storycrafter</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/storycrafter"/>
    <language>en</language>
    <item>
      <title>Fridge-to-Table: an offline kitchen agent that plans dinner, schedules the thaw, and reads the plan aloud</title>
      <dc:creator>Mark Martin</dc:creator>
      <pubDate>Mon, 26 Oct 2026 06:51:33 +0000</pubDate>
      <link>https://dev.to/storycrafter/fridge-to-table-an-offline-kitchen-agent-that-plans-dinner-schedules-the-thaw-and-reads-the-plan-m97</link>
      <guid>https://dev.to/storycrafter/fridge-to-table-an-offline-kitchen-agent-that-plans-dinner-schedules-the-thaw-and-reads-the-plan-m97</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge Week 3&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Fridge-to-Table: an offline kitchen agent that plans dinner, schedules the thaw, and reads the plan aloud
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Demo video:&lt;/strong&gt; all shots were recorded today; the edited cut lands in this same post within 24 hours (the entry is being submitted inside the entry window — this line will become the video link).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://gitlab.com/storycrafter/fridge-to-table" rel="noopener noreferrer"&gt;https://gitlab.com/storycrafter/fridge-to-table&lt;/a&gt; (public, MIT)&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;My wife, being the self-appointed cook in the house (it was an uncontested appointment), often struggles with all the duties of being a mother, teacher, wife, home keeper, volunteer, coach, counselor, and all around nurse. Our family has a few food allergies to contend with and fasting rules during certain seasons of the year. Keeping track of what frozen meats to thaw and prepare, what meals to serve that follow the fasting rule (when the rule is in effect), and what pre-packaged foods might cause discomfort or illness because of their ingredients has previously been a chore. Planning what to prepare and when, and tracking what's in the fridge, freezer, or pantry has been an open source of stress for years.&lt;/p&gt;

&lt;p&gt;Fridge-to-Table is a small agent that closes that loop so my wife never thinks about it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Plans&lt;/strong&gt; the week's dinners against a real constraint set — a liturgical fasting calendar (which days permit what), a household allergy matrix encoded as hard constraints the planner cannot override, and the actual contents of the freezer and pantry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schedules the thaw.&lt;/strong&gt; It works backward from each meal: meat needs 36+ hours of refrigerator thawing, so if Thursday is shepherd's pie, Tuesday at 6 AM you get one Telegram message: "Pull 2 lb ground beef from the freezer today."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reads the plan aloud.&lt;/strong&gt; A spoken briefing of today's defrost list and tonight's dinner, generated on-device, for the kitchen counter.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The whole thing runs on a home server: open-weight models served by llama.cpp, inventory in SQLite. No cloud AI account is involved at runtime. If the internet is down, dinner still gets planned.&lt;/p&gt;

&lt;p&gt;The laziness quotient: the family's entire involvement is answering one question a week ("anything new in the freezer?"). The agent does the calendar math, the constraint checking, the thaw scheduling, and the reminding.&lt;/p&gt;

&lt;h2&gt;
  
  
  The kiosk: a UNO Q board that boots into a talking console
&lt;/h2&gt;

&lt;p&gt;The briefing got a body. &lt;code&gt;kiosk/&lt;/code&gt; in the repo turns an &lt;strong&gt;Arduino UNO Q (4 GB, ~$79)&lt;/strong&gt; into a headless kitchen appliance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local LLM on the board&lt;/strong&gt; — Qwen3.5-0.8B Q8_0 GGUF (~880 MB) behind llama.cpp, warm replies in 4–6 s, ~2.5 GB of RAM still free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A wall tablet as the interface&lt;/strong&gt; — a one-file Flask app with big buttons (dinner, lights, timer, joke), typed input, and browser voice input, served over HTTPS with a self-signed cert (browsers only grant microphone access in a secure context — a detail that cost us an evening).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rolling conversation memory&lt;/strong&gt; — follow-ups like &lt;em&gt;"what about a dessert that goes with that?"&lt;/em&gt; work within the session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boot-to-talking&lt;/strong&gt; — the bootstrap leaves the board headless with linger on, and systemd units bring up the model server, the web app, and a 30-second Bluetooth reconnect/keep-warm timer. Power cord in → talking console in ~2.5 minutes. No login, ever.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Bluetooth part taught us the most: the stock desktop session quietly registers the A2DP profile first, so every audio stack you actually run gets &lt;code&gt;UUID already registered&lt;/code&gt; and the speaker stays silent — the fix is documented in &lt;code&gt;kiosk/RUNBOOK.md&lt;/code&gt; so nobody debugs it twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;The public repo (created in-window, 2026-10-21): &lt;a href="https://gitlab.com/storycrafter/fridge-to-table" rel="noopener noreferrer"&gt;https://gitlab.com/storycrafter/fridge-to-table&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;planner/&lt;/code&gt; — constraint-checked meal plan generation (fasting calendar + allergy matrix as data files, not prompts)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;thaw/&lt;/code&gt; — backward scheduling from meal date → pull date, with the 36-hour rule as a config constant&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;extraction/&lt;/code&gt; — decisive-first few-shot protein extraction, keyword-fallback union (recall-biased)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;voice/&lt;/code&gt; — TTS briefing generation: ElevenLabs &lt;code&gt;eleven_multilingual_v2&lt;/code&gt; as the demo voice, on-device piper as the offline fallback (&lt;code&gt;FTT_TTS_ENGINE=piper&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;inventory/&lt;/code&gt; — SQLite inventory service with YAML interop&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;run.py&lt;/code&gt; / &lt;code&gt;weekly.py&lt;/code&gt; — one-day and full-week runners&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;benchmarks/&lt;/code&gt; — honest extraction trap suite: Gemma 3 4B vs the production Qwen3-4B baseline, real results committed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;schemas/&lt;/code&gt; — &lt;strong&gt;the constraint schema as a reusable artifact&lt;/strong&gt;: JSON Schemas for both constraint files, a dependency-free &lt;code&gt;validate.py&lt;/code&gt;, and a fork guide — point it at a kosher, halal, or lent-calendar kitchen&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kiosk/&lt;/code&gt; — the UNO Q kitchen console: bootstrap, tablet web app, systemd units, board runbook&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data/sample/&lt;/code&gt; — &lt;strong&gt;synthetic&lt;/strong&gt; freezer inventory and meal plan (the real household data stays private)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The stack is open end to end:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inference:&lt;/strong&gt; Gemma 3 4B IT (&lt;code&gt;gemma-3-4b-it&lt;/code&gt;, Q4_K_M GGUF) behind llama.cpp's OpenAI-compatible server on &lt;code&gt;127.0.0.1&lt;/code&gt;. Quantized to Q4 so it fits the existing home-server hardware. The benchmark suite ran this head-to-head against the Qwen3-4B GGUF that serves the household's production bot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The core trick — deterministic first, LLM second.&lt;/strong&gt; The thaw schedule is plain date arithmetic; no model involved. The model is used only where judgment is needed: extracting which ingredients in a planned meal are freezer proteins, and proposing meals that satisfy the day's constraints. When the model is unsure, a keyword fallback runs and the two answer sets are unioned. A frozen block of beef is a worse failure than a slightly clumsy message, so the design biases toward recall.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The constraint set is data, not prompts.&lt;/strong&gt; The fasting calendar and allergy matrix live in versioned YAML. The planner treats them as hard filters; the LLM proposes within the filtered space and can never "argue" an allergen into a plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extraction:&lt;/strong&gt; decisive-first few-shot protein extraction with a keyword-fallback union, so recall is preserved when the model hedges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Voice:&lt;/strong&gt; the briefing is ElevenLabs (&lt;code&gt;eleven_multilingual_v2&lt;/code&gt;) with on-device piper as the offline fallback — pull the WAN cable and the plan still gets read aloud.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Three reasons this specific job needs open:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The data can't leave the house.&lt;/strong&gt; A household food system contains medical-adjacent information — allergen lists, eating patterns, who's home which nights. That belongs on a machine the family owns, behind their own firewall, not in a third-party inference log. Open weights make the privacy real.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It has to work when everything else doesn't.&lt;/strong&gt; The freezer is most relevant exactly when a storm takes the internet out. The planning core — constraints, proposals, extraction, thaw math — is fully local and fails none of its duties with the WAN cable pulled; even the voice briefing degrades gracefully to the on-device piper engine. Open weights are what make that degradation a design property instead of a prayer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The marginal cost of a reminder must be zero.&lt;/strong&gt; An agent that nudges you 200 times a year cannot have a per-call price, or you'll stress about the costs. Running a 4B model quantized on existing hardware makes the nudges essentially free — the difference between a system the family keeps and one they turn off.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And because the harness, models, and scheduler are all open, anyone can fork this and point it at their own constraint set — a different fasting calendar, different allergies, a kosher or halal kitchen. The rules are data files. The constraint schema is a standalone artifact in the repo: JSON Schemas for both files, a dependency-free validator, and a fork guide.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lazy Payoff
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Measured during the build (honest numbers, not estimates):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Extraction accuracy, head-to-head trap suite&lt;/strong&gt; (7 adversarial cases: dairy-only day, hidden shrimp in curry, multi-protein cookout, fish fry, tofu false-positive bait, eggs-excluded day, PB&amp;amp;J): Gemma 3 4B &lt;strong&gt;6/7&lt;/strong&gt;, production Qwen3-4B &lt;strong&gt;7/7&lt;/strong&gt; — and Gemma's one miss is a &lt;em&gt;rule violation&lt;/em&gt; (listed eggs on an eggs-excluded day). Recorded honestly rather than tuned away; see &lt;code&gt;benchmarks/benchmarks.md&lt;/code&gt; in the repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency:&lt;/strong&gt; median 0.379s (Gemma) / 0.371s (Qwen) per extraction call after warmup; a full one-day plan-to-message run completes in seconds on the home server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; the planning core is $0.00 per reminder — a quantized 4B model on hardware the family already owns. The ElevenLabs briefing is 370 characters; a daily briefing consumes ~9% of a Creator account's 131k-char monthly quota (measured live via the API), and the on-device piper engine covers any no-budget fallback at literally $0.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test coverage:&lt;/strong&gt; 119 unit tests, zero model calls (LLM client is injectable), so the deterministic core is verifiable offline.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma — SHIPPED.&lt;/strong&gt; Gemma 3 4B IT Q4_K_M is the planner/extractor, benchmarked head-to-head against the production Qwen3-4B baseline with committed results (&lt;code&gt;benchmarks/&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of ElevenLabs — SHIPPED.&lt;/strong&gt; The briefing voice is ElevenLabs (&lt;code&gt;eleven_multilingual_v2&lt;/code&gt;), with the on-device piper engine as the offline fallback — pull the WAN cable and the plan still gets read aloud.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Uno Q — SHIPPED, implemented and verified end-to-end.&lt;/strong&gt; The UNO Q is the whole appliance: the model (Qwen3.5-0.8B Q8_0, ~880 MB behind llama.cpp), the Flask web server for the tablet, and the voice stack. Concretely shipped: headless bootstrap with linger (power cord in → talking console in ~2.5 min, no login), HTTPS tablet UI with buttons/typed/browser-voice input, rolling conversation memory, preloaded dinner context reproducible across reboots, ElevenLabs-primary voice with automatic per-request Piper fallback, and a 30 s BT reconnect + keep-warm timer. Self-contained except the wireless link. Repo: &lt;code&gt;kiosk/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Tinker — NOT entered this week (attempted, honest no).&lt;/strong&gt; Built a veto-distillation dataset (294 verified-clean examples from our own hard filters) and ran three Qwen3.5-4B LoRA SFT runs via Tinker; no eval improvement over the already-at-ceiling base model (suspected LoRA-not-applied-at-sampling). Deferred to Week 4 with a diagnosis plan — see &lt;code&gt;tinker_train/README.md&lt;/code&gt; in the repo. The post stands on the three shipped categories.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>python</category>
    </item>
  </channel>
</rss>
