DEV Community

Cover image for Crumb AI: A Sales Forecaster for Anton's Café
Linuka Arambawela
Linuka Arambawela

Posted on

Crumb AI: A Sales Forecaster for Anton's Café

Hacktoberfest Weekend Challenge: Build for a Friend Submission 🤝

This is a submission for the Hacktoberfest Weekend Challenge: Build for a Friend.

What I Built

Every 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.

So I built Crumb AI, a local-first sales forecasting assistant. Anton uploads a sales CSV and asks practical questions in plain language:

  • "How many croissants should I bake on Saturday?"
  • "Which items sold best this month?"
  • "Did anything unusual happen to sales in June?"

Crumb 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.

The sales data and the AI inference never leave Anton's computer.

Demo

Crumb AI runs locally at http://127.0.0.1:8000.

ollama pull gemma3:4b
pip install -r requirements.txt
uvicorn crumb.app:app --host 127.0.0.1 --port 8000
Enter fullscreen mode Exit fullscreen mode

Then upload data/sample_bakery_sales.csv (synthetic) and explore the forecast chart, anomaly markers, and chat panel.

Demo video: Watch on Youtube

Crumb AI forecast chart for one item with the uncertainty band, anomaly markers, and chat panel

Code

Repository: Crumb AI on GitHub

Built with Python, FastAPI, pandas, TabPFN, Ollama, Gemma, vanilla JavaScript, and a locally vendored copy of Chart.js.

How I Built It

Crumb AI has two open-source AI components with clearly separated jobs:

  1. TabPFN runs locally as the forecasting model. It predicts item-level sales from calendar and history features and returns a median plus a lower and upper range.
  2. Gemma, running locally through Ollama, is the conversational layer. It picks which tool should answer a question, then phrases the result in friendly language.

The 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.

CSV upload
    -> validation and date cleaning
    -> deterministic sales tools
    -> local TabPFN forecast (or labelled baseline fallback)
    -> Gemma routes the question and explains the result
    -> numeric post-check
Enter fullscreen mode Exit fullscreen mode

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.

Items with fewer than 60 usable daily rows use a clearly labelled moving-average fallback instead of pretending to have a model.

Results

I 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.

Item Rows TabPFN MAE Naive MAE Moving-average MAE TabPFN WAPE
Cinnamon Roll 273 1.4 1.9 1.7 5.1%
Croissant 273 2.3 3.0 2.8 4.6%
Espresso 273 2.8 4.1 3.2 4.1%
Pain au chocolat 273 1.6 2.0 2.0 4.9%
Sourdough Loaf 273 0.4 0.5 0.6 3.0%
Overall 1365 1.7 2.3 2.1 4.3%

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.

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.

To reproduce these numbers, run python scripts/run_backtest.py data/sample_bakery_sales.csv from the repository root.

Why Does Open Innovation Matter?

Anton'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.

Running open models locally made three things possible that a closed API wouldn't:

  • Privacy: After the one-time model downloads, TabPFN and Gemma work with no sales data sent to any hosted AI service.
  • Control: The tool schemas, prompts, number verification, and fallback behavior are all visible and changeable. When an answer looked wrong, I could trace exactly why.
  • Cost and flexibility: There is no per-question bill, and swapping the chat model is a one-line config change to another Ollama model.

Open 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.

What I Learned

Separating 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.

Local 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.

Limitations

  • The included CSV is synthetic. A real shop needs enough consistent history for useful forecasts.
  • Items with fewer than 60 usable daily rows use a labelled baseline fallback.
  • Forecasts are estimates, not guarantees, which is why Crumb AI always shows the range and backtest error.
  • Gemma's wording can occasionally be clumsy, although its numbers are checked against tool output.
  • The app holds one uploaded dataset in memory and is built for a single local user.

Prize Categories

  • Best Use of TabPFN: TabPFN is the local forecasting engine behind Crumb AI's item-level predictions, uncertainty ranges, and backtests.
  • Best Use of Gemma: Gemma is the local natural-language interface that routes questions to deterministic tools and explains their verified results.

Built For

This was built for Anton. After trying it, he said:

"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."

Top comments (0)