{"slug": "crumb-ai-a-sales-forecaster-for-anton-s-cafe", "title": "Crumb AI: A Sales Forecaster for Anton's Café", "summary": "A developer built Crumb AI, a local-first sales forecasting assistant for a friend's café that combines a local TabPFN forecasting model with Gemma via Ollama to answer plain-language questions about sales data. The system keeps all data and inference on the user's machine, and a numeric post-check rejects any LLM-generated figure that doesn't match the underlying Python tool output, falling back to a deterministic template. In backtests on a synthetic 1,365-row bakery dataset, TabPFN achieved an overall MAE of 1.7 units versus 2.3 for a naive baseline and 2.1 for a moving average.", "body_md": "*This is a submission for the [Hacktoberfest Weekend Challenge: Build for a Friend](https://dev.to/challenges/hacktoberfest-weekend-2026-10-01).*\n\nEvery evening, my friend Anton has to guess how many croissants to bake for tomorrow. Too few and the shelf is empty by mid-morning. Too many and they go to waste. Anton helps run a small cafe and has months of sales history sitting in a spreadsheet, but no time to turn it into a forecast.\n\nSo I built **Crumb AI**, a local-first sales forecasting assistant. Anton uploads a sales CSV and asks practical questions in plain language:\n\nCrumb AI answers with a forecast and uncertainty range, while the dashboard reports backtest error when available so Anton can see how much to trust the number. It also flags unusual days: possible closures, spikes, and slow periods.\n\nThe sales data and the AI inference never leave Anton's computer.\n\nCrumb AI runs locally at `http://127.0.0.1:8000`.\n\n```\nollama pull gemma3:4b\npip install -r requirements.txt\nuvicorn crumb.app:app --host 127.0.0.1 --port 8000\n```\n\nThen upload `data/sample_bakery_sales.csv` (synthetic) and explore the forecast chart, anomaly markers, and chat panel.\n\n**Demo video:** [Watch on Youtube](https://youtu.be/oKSu4OnTYBQ)\n\n**Repository:** [Crumb AI on GitHub](https://github.com/LinukaAr/crumb-ai)\n\nBuilt with Python, FastAPI, pandas, TabPFN, Ollama, Gemma, vanilla JavaScript, and a locally vendored copy of Chart.js.\n\nCrumb AI has two open-source AI components with clearly separated jobs:\n\nThe key design decision was giving the LLM less authority, not more. Gemma never computes or invents a sales number. Python tools (pandas, baselines, anomaly detection, TabPFN) produce every figure, and then Crumb AI checks each number in Gemma's answer against the tool output. If a number doesn't match, it regenerates once and then falls back to a deterministic template.\n\n``` php\nCSV upload\n    -> validation and date cleaning\n    -> deterministic sales tools\n    -> local TabPFN forecast (or labelled baseline fallback)\n    -> Gemma routes the question and explains the result\n    -> numeric post-check\n```\n\n**The check in action:** During development, I tested the safety check with a deliberately fabricated response: \"You have 99999 items available.\" The tool result did not contain that number, so Crumb rejected the response and used its deterministic template instead. This behavior is covered by a unit test, so the language model cannot quietly introduce an unsupported number into the answer.\n\nItems with fewer than 60 usable daily rows use a clearly labelled moving-average fallback instead of pretending to have a model.\n\nI backtested the synthetic sample on its final 28 days, forecasting 7 days at a time across four rolling folds. The comparison uses TabPFN, a same-weekday-last-week baseline, and a four-week same-weekday moving average.\n\n| Item | Rows | TabPFN MAE | Naive MAE | Moving-average MAE | TabPFN WAPE | \n|---|---|---|---|---|---|\n| Cinnamon Roll | 273 | 1.4 | 1.9 | 1.7 | 5.1% | \n| Croissant | 273 | 2.3 | 3.0 | 2.8 | 4.6% | \n| Espresso | 273 | 2.8 | 4.1 | 3.2 | 4.1% | \n| Pain au chocolat | 273 | 1.6 | 2.0 | 2.0 | 4.9% | \n| Sourdough Loaf | 273 | 0.4 | 0.5 | 0.6 | 3.0% | \n| **Overall** | **1365** | **1.7** | **2.3** | **2.1** | **4.3%** | \n\n**Data used:** This table comes from the included synthetic sample, not Anton's private sales data. It contains 1,365 rows covering five items from January 1 through September 30, 2026.\n\n**What I found:** TabPFN had the lowest MAE for every item in this sample, with an overall MAE of 1.7 units compared with 2.3 for the naive baseline and 2.1 for the moving average. This is an encouraging result, but it is not evidence that TabPFN will win on every real shop's data.\n\nTo reproduce these numbers, run `python scripts/run_backtest.py data/sample_bakery_sales.csv` from the repository root.\n\nAnton's sales figures are exactly the kind of data a small business may not want on someone else's server: daily revenue, product mix, and slow and busy patterns. For this workflow, keeping the CSV and the inference on the shop computer is a better fit than uploading it to a hosted assistant.\n\nRunning open models locally made three things possible that a closed API wouldn't:\n\nOpen source also made honesty easier. Crumb AI can show uncertainty, backtest error, and the difference between a TabPFN forecast and a simple fallback, instead of hiding them behind one polished number.\n\nSeparating routing, computation, phrasing, and verification made the assistant far easier to reason about than letting one model do everything. The LLM is good at understanding a question and explaining an answer, and it should not be trusted with inventory math.\n\nLocal AI also has practical setup costs. The first run needs model downloads and local authentication for gated TabPFN weights. After that, the whole workflow stays on the machine. I also learned that local models need deliberate caching: Crumb keeps fitted forecasts, prefetches item forecasts after upload, keeps Ollama warm between requests, and runs expensive work in the background so the dashboard stays responsive.\n\nThis was built for **Anton**. After trying it, he said:\n\n\"I like that I can upload our sales file and quickly see how many croissants to prepare without sending our business data anywhere. The forecast range is helpful because it shows me how much extra stock to keep ready.\"", "url": "https://wpnews.pro/news/crumb-ai-a-sales-forecaster-for-anton-s-cafe", "canonical_source": "https://dev.to/linuka_ar/crumb-ai-a-sales-forecaster-for-antons-cafe-3cji", "published_at": "2026-10-04 16:37:29+00:00", "updated_at": "2026-10-04 16:42:31.283821+00:00", "lang": "en", "topics": ["ai-tools", "machine-learning", "large-language-models", "ai-agents"], "entities": ["Crumb AI", "Anton's Café", "TabPFN", "Ollama", "Gemma", "FastAPI", "pandas", "GitHub"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/crumb-ai-a-sales-forecaster-for-anton-s-cafe", "markdown": "https://wpnews.pro/news/crumb-ai-a-sales-forecaster-for-anton-s-cafe.md", "text": "https://wpnews.pro/news/crumb-ai-a-sales-forecaster-for-anton-s-cafe.txt", "jsonld": "https://wpnews.pro/news/crumb-ai-a-sales-forecaster-for-anton-s-cafe.jsonld"}}