This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle
What I Built
Small sellers run on WhatsApp. A home baker, a tiffin service or a tailor gets orders like this:
C: bhaiya kal subah 20 samosa aur 1 dozen kachori chahiye
S: ho jayega, 20 samosa 10 rs each = 200, kachori 12 x 15 = 180. total 380
C: thik hai, paise delivery pe dungi. Neha, 9000000002
Then they copy it into a notebook, and a week later nobody remembers who paid the advance.
Order Ledger turns that chat into a structured order, shortened here:
{
"customer": "Neha", "phone": "9000000002",
"items": [
{ "name": "samosa", "qty": 20, "unit": "pcs" },
{ "name": "kachori", "qty": 12, "unit": "pcs" }
],
"delivery_date": "2026-10-14", "delivery_time": "morning",
"amount": 380, "paid": 0, "status": "confirmed"
}
The seller checks it on a review screen, saves it, and gets:
- a schedule grouped by day, with "today" and "overdue" markers
- pending payments, with the total owed and a button that copies a polite WhatsApp reminder
- an all orders table with a CSV export for the accountant
Who would pay? Anyone who takes orders in chat and tracks them by hand: home bakers, cloud kitchens, tailors, small shops. The pain is the retyping and the money that slips through. Customer names, numbers and prices are also private, which is why the whole thing runs on the seller's own laptop.
It handles English, Hinglish and Kannada in English letters, and it accepts exported WhatsApp chats as they are.
Demo
The demo shows: pasting a WhatsApp-style chat, reading the order, fixing a field on the review screen, the schedule and pending-payments tabs, and then Wi-Fi switched off with one more order processed.
Code
Order Ledger
Turn messy WhatsApp order chats into a clean order book. It runs entirely on your own computer with an open-weight model, so customer names, phone numbers and prices never leave your laptop.
Built for the Hacktoberfest Open-Source AI Challenge, Week 2 (Side Hustle).
Who it's for
Home bakers, tiffin and cloud-kitchen sellers, tailors and small shops who take orders over WhatsApp and keep track in a notebook. Their chats are messy: English mixed with Hindi or Kannada, "kal subah", "advance 500 diya", orders that change halfway through. You paste the chat, and the app turns it into a structured order.
What it does
- Reads a pasted WhatsApp chat (an exported chat, text copied from WhatsApp Web, or lines typed with
C:for the customer andS:for you) and extracts the customer, phone, items, quantities, delivery date and time, total, amount paid and status. - Shows the result onβ¦
MIT licensed. There is nothing to npm install:
ollama pull qwen2.5:7b
git clone https://github.com/Khushighosh/Order-Ledger.git
cd Order-Ledger
node server.mjs # then open http://localhost:3000
How I Built It
The open pieces
-
Runtime: Ollama, serving
qwen2.5:7b(4.7 GB) as the default andllama3.2:3bfor comparison -
App: Node.js with no packages (built-in
httpandfetch), one HTML file for the UI, and a JSON file for storage - Tests: a small evaluation harness I wrote for this
pasted chat βββΊ convertChat() WhatsApp export β "C:" / "S:" lines (in the browser)
βββΊ annotateDates() "kal" β "kal (2026-10-14)"
βββΊ Ollama (qwen2.5:7b, temperature 0, JSON-schema output)
βββΊ clean() validate phone, units, dates (server)
βββΊ review screen βββΊ orders.json βββΊ schedule / pending / CSV
1. Structured output, not "please reply in JSON"
Ollama accepts a JSON schema as format, so the model's reply always parses and uses only allowed values:
unit: { anyOf: [{ type: "string", enum: ["kg", "pcs", "litre", "box"] }, { type: "null" }] },
status: { type: "string", enum: ["confirmed", "tentative", "cancelled"] },
The schema guarantees the shape of the answer, not that it's right. That gap is why there's a review screen and a test harness.
2. Let code do code's job
Small models are unreliable at date arithmetic, so the date lookup happens in code and the model only reads the result:
export function annotateDates(text, cal) {
const rules = [
[/\bday after tomorrow\b|\bparso\b/gi, cal[2].iso],
[/\b(?<!after )tomorrow\b|\bkal\b|\bnale\b/gi, cal[1].iso],
[/\baaj\b|\btoday\b/gi, cal[0].iso],
];
let out = text;
for (const [re, iso] of rules) out = out.replace(re, (m) => `${m} (${iso})`);
return out;
}
// "nale 2 litre dosa hittu" β "nale (2026-10-14) 2 litre dosa hittu"
Phone numbers are cleaned in code too, because the model kept returning +91 90000 00009.
3. Measure everything
I wrote 18 synthetic WhatsApp-style chats (fake names and numbers) and hand-labelled the expected JSON for each. evaluate.mjs scores 9 fields per chat and breaks results down per field and per kind of chat (Hinglish, Kannada, same-day orders, "same as last time", and so on). With temperature: 0, re-running an unchanged prompt gave identical results, so every prompt change could be A/B tested.
| Test set | Model | Before the date fix | After the date fix |
|---|---|---|---|
| 12 chats (I tuned on these) | qwen2.5:7b | 11/12 orders correct Β· 99% of fields | 9/12 Β· 96% |
| 12 chats | llama3.2:3b | 1/12 Β· 82% | 2/12 Β· 84% |
| 6 fresh chats | qwen2.5:7b | 3/6 Β· 91% | 5/6 Β· 96% |
| 6 fresh chats | llama3.2:3b | 3/6 Β· 89% | 3/6 Β· 91% |
On a laptop the 7B model takes about 14 seconds per order once loaded (about 50 seconds for the first order while the model loads). The 3B takes about 7.
4. What went wrong (the useful part)
- One extra example made the prompt worse. Adding a fourth few-shot example dropped the 7B from 10/12 to 7/12 orders correct. With that example in the prompt, the 3B answered "same as last time" by inventing a pineapple cake order. I removed the example and the score came back.
-
A date bug hiding in my prompt. For "aaj" (today) the 7B answered
2026-10-20, and for "nale" (Kannada for tomorrow) it answered2026-10-17. The cause was that the date table in my prompt listed "Tuesday: 2026-10-20" (next week), and the test day was a Tuesday. The model read "today is Tuesday" and looked it up. I fixed it by labelling that row "next Tuesday" and moving the relative-date lookup into code. The fresh set went from 3/6 to 5/6. - Two of my labels were debatable. Is "2 meals" an item named "meal" in pcs, or "tiffin" in boxes? I left those as known misses instead of tuning them away.
- The 3B is fast and wrong. Only 42% of its items were right on the 12-chat set, so I didn't ship it.
An honest note on the numbers: I used both test sets to tune the prompt, and the date fix came after looking at the fresh set's failures. Treat these as indicative, not a benchmark. That's the reason the review screen exists.
Why Does Open Innovation Matter?
-
Privacy by default. A chat contains customer names, phone numbers and prices. Here it never leaves the laptop: the server only listens on
127.0.0.1, and orders live in a local file. - No per-order cost. The marginal cost of an order is the electricity on the seller's laptop, so a small seller can use it as much as they like.
-
I could test and choose. In an afternoon I ran two models through the same harness, saw that the 3B wasn't good enough, and kept the 7B. Swapping a model is one setting (
MODEL=...). With a closed API I couldn't have inspected the setup this way, and an API-side model update could silently change the results. -
Reproducible. The same model, prompt and
temperature: 0gave identical results on re-runs, which made the debugging above possible.
The trade-off, honestly: I didn't benchmark a closed API, so I'm not claiming open models are more accurate. A hosted frontier model is probably better at the tricky chats and faster per order. What open models gave me is privacy, a zero running cost and full control. For a seller who handles customers' private data on a laptop, that was the better deal.
My Agent Session
I built this in a long chat session with Claude as a pair programmer. It drafted the first versions of the prompt, server and UI, and I ran everything on my own laptop, pasted the failures back, and made the decisions: which idea to build, which model to keep, what to revert. The best moments came from the failures, like the 2026-10-20 date bug.
Prize Categories
- Overall winner (Side Hustle theme). Open-weight models (Qwen2.5 and Llama 3.2) running locally through Ollama are the core of the product, and the code is MIT-licensed on GitHub.
Top comments (0)