This is a submission for the HF26 Challenge: Side Hustle
It's 1 PM and the phone won't stop ringing
If you run a small tiffin service in India, you know this day. It started raining at 12. One rider is new. Eight orders went out together. By 1 PM, customers are calling: "Bhaiya, khana kab aayega?"
The owner has months of delivery history in a notebook or a spreadsheet. Nobody has ever used it to see this coming. Swiggy has an ML team for that. A 40-order kitchen doesn't, and that owner isn't going to upload customer names, phone numbers and home addresses to a random SaaS to fix it.
So I built the version that runs on the laptop they already have.
What I built
DabbaETA is a local-first dispatch assistant for small delivery businesses: tiffin services, bakeries, local couriers.
- Upload past deliveries as a CSV (schema is validated and stored in a local SQLite file).
- TabPFN predicts the ETA and the late-delivery risk for every pending order, shown as HIGH / MODERATE / LOW badges.
- Gemma runs on Ollama on the same machine. Ask it "Aaj kaunsi orders late hongi?" and it answers. It also drafts polite delay messages in Hinglish, Hindi or English, ready to copy into WhatsApp.
- One click on Run dispatch starts a Temporal workflow: load orders → predict all → flag risky → explain bottlenecks → draft messages.
- Sentry spans wrap every Gemma and TabPFN call and record latency and token counts.
Code: https://github.com/Harsh63870/DabbaWala
Why open matters here
For this product, open is what makes the business possible at all.
-
Customer data stays put. Names, phones and addresses never reach an LLM API. Gemma runs locally. The only thing that leaves the machine is a table of numbers (distance, traffic, weather, prep time) sent to TabPFN. The predictor sits behind a
BasePredictorinterface that already supports both thetabpfn-clientAPI and the localtabpfnpackage, so going fully offline is a config change, not a rewrite. - No per-message bill. Every ETA question and every drafted message on a metered API costs money. On a tiffin owner's margin, that adds up quickly. Locally, the marginal cost is zero.
-
Swap the model in one line.
OLLAMA_MODELis an env var. When a better small Hindi-capable model ships, the owner upgrades without touching code. - No lock-in. There is no vendor whose pricing change can break a customer's daily workflow.
The benchmark (and what it does not show)
I didn't have a real tiffin dataset, so I generated 5,000 synthetic orders matching the schema of the Kaggle Food Delivery Times dataset: distance, traffic, weather, vehicle type, prep time, courier experience, evening rush and monsoon effects. Treat these numbers as a check that the pipeline works, not a claim about any real kitchen.
Full data (4,000 train / 1,000 test):
| Model | MAE (min) | RMSE | R² | Fit time |
|---|---|---|---|---|
| TabPFN | 3.60 | 6.05 | 0.937 | 0.39s |
| XGBoost | 3.31 | 5.22 | 0.953 | 0.11s |
| Linear regression | 6.27 | 10.15 | 0.822 | 0.003s |
With thousands of rows, XGBoost wins. I'm reporting that as is. Both tree-based approaches are far ahead of linear regression, which is expected, since delivery time depends on interactions like distance × traffic × weather.
A small owner doesn't have thousands of rows, though. They have a few weeks of history. That's why the benchmark script also runs a small-data experiment on 50, 100 and 200 training rows, and why the predictor is swappable: if XGBoost is better for your data size, switch to it in one line. The full results and chart are in the repo under docs/benchmark.json and docs/benchmark.png.
How it works
flowchart TD
A[Owner uploads CSV] --> B[(SQLite, local)]
B --> C[TabPFN: ETA + late risk]
subgraph T[Temporal DispatchWorkflow, exponential-backoff retries]
D[load_orders] --> E[predict_all]
E --> F[flag_risky]
F --> G[explain: Gemma]
G --> H[draft_messages: Gemma]
end
C --> D
H --> I[WhatsApp drafts in Hinglish / Hindi / English]
J[Chat: Aaj kaunsi orders late hongi?] --> K{Gemma agent, JSON tool routing}
K --> L[predict_eta]
K --> M[list_risky_orders]
K --> N[draft_message]
L --> C
M --> C
O[FastAPI + dashboard] -.-> T
O -.-> K
S[Sentry spans: tabpfn.predict, llm.gemma] -.-> C
S -.-> G
S -.-> H
A Gemma agent that doesn't depend on native tool-calling. Small local models are unreliable at it, so Gemma is prompted to emit {"tool": ..., "args": ...} and I parse it with regex extraction plus a retry loop. Three tools: predict_eta, list_risky_orders, draft_message. Heuristic routing catches Hinglish queries like "Aaj kaunsi orders late hongi?" even when the model's JSON is malformed.
Temporal for the morning run. A dispatch run chains a prediction step with several LLM calls, and any of them can fail halfway. Each step is an activity with exponential-backoff retries, so one flaky call doesn't mean redoing the whole run. If Temporal isn't reachable, the app falls back to an in-process run so the demo still works on any laptop.
Sentry for visibility. Custom spans around every Gemma and TabPFN call record latency_ms, tokens_in and tokens_out, which makes it clear where time goes when a dispatch run feels slow.
Tests. 8 pytest tests cover order-schema validation, the predictor interface, agent JSON extraction, Hindi-query routing, message drafting, the workflow happy path and the API endpoints.
Who would pay, and how much
My hypothesis, not yet validated: a tiffin or bakery owner will pay a small monthly fee, or a one-time setup fee, for "tell me which orders will be late before the customers call, and write the messages for me." The pitch is saved phone calls and fewer angry customers, not "AI". Because inference is local, my cost per customer is close to zero, so even a very small price leaves margin. A closed-API version would have to charge more just to cover token costs.
First user
I haven't landed a real paying user yet, and I'd rather say that than invent one. Everything above runs on synthetic data, so the first thing I'll do next is run it on a real owner's CSV and see where it breaks.
Limits
- The demo dataset is synthetic, so real-world accuracy will differ.
- On large data XGBoost beats TabPFN here, as the table shows.
- TabPFN's free API key is non-commercial. Selling this requires a commercial licence from Prior Labs, or running the local
tabpfnpackage under its own terms. - Gemma is fine for short drafts but not perfect at Hindi grammar, so owners should skim a message before sending it.
Prize categories
- Best Use of TabPFN: TabPFN is the prediction core (ETA and late-risk), benchmarked against XGBoost and linear regression, inside a wider agent that turns predictions into actions.
- Best Use of Gemma: Gemma runs locally via Ollama and powers the chat agent, JSON tool routing and Hinglish / Hindi / English message drafting.
-
Best Use of Temporal: the morning dispatch is a five-activity
DispatchWorkflowwith exponential-backoff retries. -
Best Use of Sentry Agent Tracing: custom
llm.gemmaandtabpfn.predictspans record latency and token counts.
What's next
Run it on a real owner's data, add rider-level features, build a one-tap WhatsApp send flow, and retrain automatically as new deliveries get logged.

Top comments (0)