DEV Community

Cover image for "red label adha kg 2 box": an offline WhatsApp order reader for Indian shops that asks before it guesses
Bipul Chamoli
Bipul Chamoli

Posted on AI-assisted

"red label adha kg 2 box": an offline WhatsApp order reader for Indian shops that asks before it guesses

Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle Submission 💸

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

ParchiMatch review screen: WhatsApp order lines with green checked rows and amber questions

TL;DR: Small Indian shops get orders on WhatsApp in a mix of Hindi, English and shorthand. ParchiMatch Lite turns each line into a checked billing row, on the shop's own 8 GB laptop with the Wi-Fi off. When it isn't sure, it asks one short question instead of guessing. On 80 locked test orders it filled in 0 wrong rows, and on messages my friends typed by hand, also 0.

What I Built

This is built for small Indian stores. Their orders come in on WhatsApp, and one message can mix Hindi, English and abbreviations:

Namaste bhaiya
kit kat 10/- darjan 4
red label adha kg 2 box
12 pack lais peela 10 wala
teen petti marrie 10 rs
Kal subah tak bhej dena
Enter fullscreen mode Exit fullscreen mode

Someone has to retype every line into billing software like Tally, Busy or Marg. Petti is a carton, darjan a dozen, adha kg half a kilo, lais peela the yellow Lay's. And if a line is matched to the wrong product, that's expensive: the wrong stock gets packed, billed and put on the van.

So I tried to build something that needs minimal input from a person, but asks for their full attention exactly where a mistake would cost money. It reads each line, matches it to the shop's own catalogue, and sorts it into one of three colours:

🟩 Green Matched and checked: teen petti marrie 10 rs → 3 cartons of Marie ₹10 (180 packets)
🟧 Amber Not sure, so it asks one short question with its best guess filled in: marie 30 wala 20 jodi → What is "jodi"? packet or carton?
⬜ Grey Greetings and notes, ignored: Kal subah tak bhej dena

Then it exports a CSV for the billing software. It also learns the shop's own words: confirm "lais peela" once with Remember ticked, and next time it goes straight through.

The rule behind everything: a wrong row must never be filled in on its own. When unsure, ask. A shop owner forgives a question. They don't forgive a wrong delivery.

Who would pay: small FMCG wholesalers and kirana suppliers who get dozens of WhatsApp orders a day and pay someone to retype them. It runs on the laptop they already have, works without internet, and costs nothing per order.

Pricing (an untested guess): around ₹299 a month per shop. I haven't put this price in front of a real shop yet; that's the first thing I'll test.

Demo

The whole video is recorded with Wi-Fi off, on a MacBook Air M2 with 8 GB of RAM. Even the project site in the last minute is served from the laptop. What you'll see:

  • A real-looking order, matched in about 8 seconds: 6 lines green, 5 amber, 2 notes.
  • The model's guess can be wrong, and that's why it asks: for "lais peela" (the yellow Lay's) it suggests Magic Masala, but yellow is Classic Salted. One tap to pick the right one, and tick Remember.
  • It learns: after export it says "Learned 3 new names", and the same order comes back with 8 green lines instead of 6, with the corrected products.
  • Four tricky lines, and what the app does with each:
I typed What happened Why
aashirvaad atta 10 kg 1,500 bag 🟩 1,500 bags An early version read this as 1 bag and accepted it (see What I got wrong)
maggi 1,5 pkt 🟧 What does "1,5" mean? An unclear number is never guessed
red label adha kg 2 box 🟧 Is it Brooke Bond Red Label (250 g)? The model guessed 250 g; adha kg means 500 g. The code saw two sizes fit and asked
पारले जी 5 वाला 2 पेटी 🟩 2 cartons Parle-G ₹5 (288 packets) Hindi in Devanagari works too

Project site: https://bipul724.github.io/parchimatch/

Code

ParchiMatch Lite

CI

Messy wholesale orders → clean billing rows, on the shop's own computer.

A small FMCG wholesaler gets orders like these on WhatsApp:

Namaste bhaiya
parle g 5/- 2 petti
tata namak 1 kg 5 ctn, lays hara 10 rs 3 box
पारले जी 5 वाला 2 पेटी
fortune oil 2
Kal subah tak bhej dena

Someone retypes every line into Tally, Busy or Marg. ParchiMatch Lite reads each line, matches it to the shop's own catalogue, and either fills in a checked row or asks one short question. It never guesses a number, and it never auto-accepts a row it can't justify. It runs fully offline with an open-weight model on an 8 GB laptop.

🟩 Green Matched and checked: parle g 5/- 2 petti → 2 cartons Parle-G ₹5 (288 packets)
🟧 Amber One question, best guess pre-filled: marie 30 wala 20 jodi → What is
…

Zero npm dependencies; Node 24 runs the TypeScript directly. To try it: npm run app -- --open.

How I Built It

The model only picks the product. My code reads every number.

Small models are good at "which product did they mean?" and unreliable at "is petti a packet or a carton?". So the work is split:

  1. Code reads every number: quantities, unit words (petti, ctn, darjan, bori…), prices (5/-, 10 rs), pack sizes (1 kg, 500ml), Hindi number words (teen, paanch).
  2. Fuzzy text matching handles most lines instantly, with no model at all.
  3. Only when fuzzy matching can't decide does the local model see the line plus the 8 closest products. It answers in strict JSON, at temperature 0, and can only pick one of those 8.
  4. 16 safety rules in code check the pick: does the price match? The pack size? Is the product sold in that unit? Any failed check turns the row amber, and the question is written by the code, not the model.

Choosing the model: I tested five

On my 8 GB MacBook I tried five open-weight models in that "decider" role, on 320 synthetic practice orders:

Model Auto-accepted Wrong rows No typing needed Right suggestion on amber rows Typical / slowest
Gemma 4 E2B (chosen) 88.7% 0 96.1% 65.2% 3.1 s / 9.2 s
Qwen3.5 2B 88.7% 0 94.6% 52.2% 2.5 s / 3.3 s
Gemma 3 4B 88.7% 0 95.6% 60.9% 5.3 s / 8.4 s
Qwen3.5 4B 88.2% 0 95.6% 62.5% 5.2 s / 8.4 s
Gemma 4 E4B – 0 – – 5.6 s / 37 s

I picked Gemma 4 E2B because when it isn't sure, its suggestion is right far more often, so the shopkeeper taps Confirm instead of typing.

Then I tested it once, on orders I had locked away

Before tuning anything, I set aside 80 test orders and locked them; CI fails if they change. Each method ran on them exactly once:

Method Handled with no review Wrong rows auto-accepted No typing needed
Exact match 54.9% 0 54.9%
Fuzzy match 84.3% 0 84.3%
Model on every line 84.3% 0 92.2%
Hybrid (the app) 88.2% 0 92.2%

The model was needed on only 31 of the 80 lines, about 1.5 s each.

Watching the model be wrong, with Sentry

I traced an evaluation run in Sentry, one trace per order line, with steps for fuzzy matching, the model call and the safety rules. My favourite trace is the Red Label line.

Adha kg means half a kilo, so the 500 g pack. The model answered the 250 g pack, with status "ok", confident and wrong:

Sentry: the model's JSON answer for

The safety rules didn't trust it. Both pack sizes fit the order's words equally well, so the line went to a human:

Sentry trace: the safety-rules step for the Red Label line returns status needs_review because the 500 g and 250 g packs fit the order equally well

The model suggests; the code decides. The trace also shows every model call going to http://127.0.0.1:11434: the laptop itself, not a cloud. Across all 320 practice orders: 113 model calls, 640 tool calls, 58K tokens, $0.

Sentry Agents view: 113 LLM calls, 640 tool calls, 58K tokens, total cost $0

Tracing lives only in the evaluation tool, runs only on synthetic data, and only when switched on. The shop app itself sends nothing anywhere.

CI keeps me honest

Every push runs 210 tests on GitHub Actions, checks that the locked test orders haven't changed by a single byte, and posts the no-model baselines to the run page.

GitHub Actions run: tests,

What I Got Wrong

  • My first design asked the model for everything: product, quantity and unit. During development it was the worst design I measured, and on the final test it only tied plain fuzzy matching. That's why the model now picks only the product.
  • "1,500 bags" became 1 bag, and went green. The comma split aashirvaad atta 10 kg 1,500 bag into two lines. A wrong row, filled in on its own: the one thing the app must never do. Now Indian and Western digit groups (1,500, 1,00,000) are read as one number, and anything unclear like 1,5 gets a question.
  • "Remember" could learn the wrong size. "tata tea premium" was amber only because the order didn't say which size. Learning that name would have auto-accepted one size forever. Now only names the catalogue doesn't know can be learned, and a learned name means the product, never one pack size.
  • The model won near-ties for a silly reason. lays 10 wala 2 ctn names no flavour, but the model's pick, Classic Salted, led by 0.074, only because its alias was short. Now a model pick is accepted only if the order's own words put it ahead by at least 0.08, the same margin fuzzy matching needs. That was the last wrong row in development.
  • I tried a bigger model. Gemma 4 E4B needs about 6 GB; on 8 GB, only 2.5 GB fit on the GPU and the slowest line took 37 seconds. On a shop laptop, RAM is the limit, not disk.
  • I nearly trusted my friends' first replies. They were written by ChatGPT, not typed by hand. If I hadn't noticed that all four had the same shape, my "real-world" test would have been one more synthetic test (the full story is under My First User).

Why Does Open Innovation Matter?

  • The orders are the business. Customer names, what they buy, at what price: a shop won't paste that into someone else's cloud. With an open-weight model, nothing leaves the laptop. The app only listens on 127.0.0.1.
  • It costs ₹0 per order. No API bill growing with every WhatsApp message. That's what makes small-shop pricing possible.
  • It works when the internet doesn't. Market internet is unreliable; this runs fully offline, as the video shows.
  • I could test and choose. Because the weights are open and run locally, I could put five models through the same orders and pick the one that fits an 8 GB laptop.

My First User

Honestly, I don't have a paying user yet. No shopkeeper has used ParchiMatch. So I did the next best thing: I asked friends to type a restock order the way they'd really send it to a supplier.

The first replies were suspiciously perfect. Four messages, each exactly 17 lines, every item in the same order as my list, one a light rewording of another. My friends had asked ChatGPT. 😄 I threw them away and asked again, with six items and "please type it yourself."

Three real, messy messages came back, like this one:

bhai kal subh tk ye dal dena parle g 5wla 2 petti tata nmk 1 bori fotune tel pouch 1l 3 petti amul btar 100g 20 pis dairy milk 20rs 2 dabba or lifeboy 10 sabun 1 ptti bil sth bhej dena
Enter fullscreen mode Exit fullscreen mode

I labelled the 11 order pieces, locked them, and scored them once:

  • 0 wrong rows filled in, and 80% needed no typing.
  • One strict miss: fortune oil 1 ltr ke 1 carton went through without a question, though I had labelled it as worth asking (pouch or bottle?). Its row matched my label exactly, but by my own strict rule it counts, so I report it.
  • The app asked instead of guessing on every hard piece. For items joined with aur, though, its suggestion names only one product, so a busy operator could miss the second.

It's 11 pieces from 3 people: a signal, not proof.

What It Can't Do (Yet)

  • Items joined by aur, or sent with no separator, aren't split. The app asks about them, but its suggestion names only one product.
  • "Pouch" isn't a unit word yet. And Tata namak fits Tata Tea about as well as Tata Salt, so it asks.
  • Tested on synthetic shop data plus 11 real pieces, not on a real shop's catalogue yet.
  • It exports a CSV; it doesn't write into Tally or Busy directly.
  • Tested only on a MacBook Air M2 with 8 GB. Windows and Linux are untested.

The next step is one real shop and one week of real orders.

Prize Categories

  • Best Use of Gemma: Gemma 4 E2B (gemma4:e2b-it-qat via Ollama) is the model in the app, chosen by testing five models and confirmed once on locked test orders.
  • Best Use of Sentry Agent Tracing: the evaluation tool sends invoke_agent, chat and execute_tool spans for every order line. The traces above show a confidently wrong model answer being caught by the safety rules.
  • Best Use of GitHub Copilot: entered through the GitHub Actions route. CI runs the tests, the locked-test guard and the baselines on every push.

If you run a shop, or know someone who does: clone it, load your own catalogue as a CSV, and paste in yesterday's WhatsApp orders. It never needs the internet, and it will ask before it guesses.

Top comments (0)