This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle
*Edits To Be Made in this post later
Who I built it for
Sharma Kirana is the shop next door. The owner already had a billing export, and he still ordered by feel.
A quiet week might mean nobody wants the item. It might also mean the shelf was empty. A festival week can look like a spike that will not come back. Packs arrive in fixed sizes, and a bad buy ties up cash a small counter does not have.
I built DukaanDost for that counter. Kam andaaza. Sahi stock. Behtar dukaan. Less guessing. The right stock. A better shop.
He used it. He read the forecast against his sales and the festivals coming up, and he restocked candles and oils, because the prediction put those items in demand.
The problem it solves for him
His bills already knew what sold. They did not tell him what to buy next, in which pack size, or whether a quiet stretch was a stockout. DukaanDost keeps that file on the shop computer and turns it into a quantity he can check before the next purchase.
What it does
DukaanDost takes a billing CSV and writes item-level demand, a reorder quantity, and a short explanation.
- Forecasts are built per product from lagged sales, rolling pace, and the calendar.
- Restock is lead-time demand plus safety stock, rounded to pack size.
- A stockout is not treated as proof that nobody wants the item.
- You can compare weeks. The app does not invent a festival uplift.
- Bad dates, blank products, and duplicate files are listed with row numbers.
- Explanations are English or Hinglish. If Gemma is offline, a template says so.
- If TabPFN cannot run, the screen names the baseline. It does not call that baseline TabPFN.
Forecasts are estimates, not a promise of tomorrow's sales. The public sample file uses the same shop name and is marked fictional, so a visitor does not confuse that demo with his bills. The candle and oil restock came from his own sales.
What's in it
| Area | What you can do |
|---|---|
| Sample | Open a labelled sample kirana that stays separate from your own shop |
| Import | Preview a CSV, map columns, and see row errors before anything is saved |
| Inventory | Update stock, with an audit row for receipts and adjustments |
| Forecast | One series per product, or a clear note when the history is too thin |
| Restock | Reorder point, pack size, slow-moving flag, stockout flag |
| Words | English or Hinglish. Gemma locally when it is up and the numbers still match |
| Export | Inventory, forecasts, recommendations, import quality, and evaluation as CSV |
| Privacy | SQLite on the machine that runs the app. A hosted copy says the data lives on that server |
Demo
DukaanDost
Kam andaaza. Sahi stock. Behtar dukaan.
DukaanDost turns a small shop's billing CSV into stock advice the owner can check. TabPFN forecasts demand from the history. Gemma explains the numbers in plain language when an API key is set. If either one is absent, the shop still gets a named baseline and a template that says so.
It is built for one real counter: Sharma Kirana, next door. He restocked candles and oils from the forecast.
Built by Aditya Kumar (@ADITYA-KUMAR-2358) for the DEV Hacktoberfest challenge, Side Hustle.
Agents and contributors: start with
docs/.
⭐ A star on the repo would genuinely make my day.
Code
The application code is MIT. TabPFN weights have their own license and are not covered by the MIT grant.
How I Built It
Open-source AI at the core: TabPFN reads the billing table, and Gemma writes the explanation. TabPFN can run on the shop computer. Gemma is a separate call, and only after you paste a key. Neither one is allowed to wear the other's name.
TabPFN comes in through the Apache-2.0 tabpfn package (9.1.0). The app asks for TabPFN v2. The package default, TabPFN 3.5, is non-commercial and can require a Prior Labs token, so that is not the setting this project ships. Weights download on the first real fit, not during tests.
Gemma is optional, through Google's OpenAI-compatible endpoint (gemma-4-26b-a4b-it by default). Leave LLM_API_KEY empty and the template is used. The reply is kept only when it still matches the calculated numbers.
Architecture
+--------------------------------------+
| BROWSER |
| React 19, Vite, TypeScript |
| Tailwind v4, GSAP |
+--------------------------------------+
|
| CSV, then JSON
v
+--------------------------------------+
| FastAPI |
| pandas, SQLAlchemy, Alembic, SQLite |
+--------------------------------------+
| |
v v
TabPFN v2 Gemma via Google
or a named or an English /
baseline Hinglish template
Local by default (or docker compose)
| Layer | Choice | Why |
|---|---|---|
| Frontend | React 19, Vite, TypeScript, Tailwind v4, GSAP | A shop page that stays readable, with a few deliberate motions |
| Backend | FastAPI, SQLAlchemy, Alembic, SQLite, pandas | One file on the shop computer. No account required |
| Forecast | TabPFN v2, then a named baseline | Tabular history is what a billing CSV already is |
| Words | Gemma 4 through the Google endpoint | Optional. Empty API key keeps the template |
| Honesty | Model id stored with the run | A fallback is never labelled TabPFN |
From a billing file to an order
Owner exports a billing CSV
|
v
Preview, map columns, list bad rows
|
v
Features: lags, rolling pace, calendar
|
v
TabPFN v2 ---- fails or missing ----> seasonal naive,
| Croston, or
| moving average
v
Reorder quantity, rounded to pack size
|
v
Gemma explains the numbers
| offline or the reply drifts
v
English or Hinglish template, and it says so
Things I had to get right
- Naming the model that actually ran. If TabPFN cannot run, the baseline keeps its own name.
- Keeping his shop separate from the sample. The demo file is fictional on purpose.
- Not inventing a festival uplift. He can compare weeks. The app does not add a festival percentage he cannot see.
- Not treating a stockout as "nobody wants this."
- Checking Gemma. If the sentence does not match the calculated numbers, the template is what he reads.
What was hard
- The weight license. TabPFN's code is open. The default 3.5 weights are a different deal. Shipping v2, and saying so in
.env.example, was the honest default. - Thin history. Some items do not have enough days for a fit. Those rows need a clear note, not a confident number.
- A quiet Gemma. With no API key, or when the free-tier limit is hit, the template has to be good enough to order from, and it has to admit Gemma was not used.
What I'd do next
A printed order list he can take to the wholesaler, and Hindi on the rest of the screen so the whole shop, not only the explanation, can be read in the language he uses at the counter.
Why Does Open Innovation Matter?
Sharma Kirana's bills are a private record. What sold last Tuesday should not have to leave the shop before he can decide what to buy.
-
Control. The forecast and the SQLite file can stay on the shop computer. Gemma is off until
LLM_API_KEYis set, and then only the calculated facts are sent, not the raw CSV. - Swappability. TabPFN can be turned off. Gemma's model name is a setting. The baseline and the template still answer.
-
Cost. Forecasts have no per-call bill. Gemma uses the free-tier limits in
.env(LLM_MAX_RPM,LLM_MAX_PER_DAY,SUBMIT_LIMIT). An empty key spends nothing. - Tunability. The rule that mattered most was faithfulness: show the number you calculated, and name the model that produced it.
With a closed forecast API, his CSV would leave the counter, and a failure could still come back wearing the vendor's name. With open weights, he can turn the model off and still read a baseline.
My Agent Session
The session is saved on DEV as a private draft: DukaanDost: forecasts for Sharma Kirana. Publish it from your DEV profile if you want the embed to be public.
My First User
He is the owner of Sharma Kirana, the shop next door. He used DukaanDost on his own sales. The forecast, read with those sales and the festivals coming up, put candles and oils in demand, and he restocked them.
He has not paid. I built it for his counter. The result so far is stock on the shelf, not a guess from memory.
Prize Categories
- Best Use of TabPFN: TabPFN forecasts item-level demand from his billing CSV. If the fit cannot run, the baseline keeps its own name.
- Best Use of Gemma: Gemma explains those numbers in English or Hinglish, and only when the words still match the math. No API key means the template.
Somewhere on his shelf are candles and oils he bought because the forecast said so. That is the whole reason it exists.
If you know a shop that still orders by feel, start with their last billing file. And if this is useful, please ⭐ star DukaanDost on GitHub. Thank you for reading. ❤️
Top comments (0)