This is my submission for the Hacktoberfest Open-Source AI Challenge: Week 2, theme: Side Hustle.
The Problem
Small neighbourhood shops (kirana stores, tailors, tiffin kitchens, repair stalls) sell on credit all the time. "Ravi anna took 2 kg rice and oil, he'll pay Saturday." That sentence goes into a paper notebook, if it gets written down at all.
Then three things go wrong:
- The shopkeeper is busy with customers and forgets to write it
- Handwriting gets messy, and the notebook gets lost or damp
- At month end, nobody is sure who owes what
Existing apps need typing, English menus and a steady internet connection. The shopkeeper has wet hands, a queue, and a phone.
What I Built
Udhaar Voice lets the shopkeeper say the entry in their own language, in a short voice note, and the system does the rest:
- Transcribes the audio using Whisper, running locally
- Understands the sentence with a local Gemma model and pulls out: customer name, items, amount, and promised pay date
- Updates a ledger (one SQLite file) and keeps each customer's running balance
- Generates a reminder message for any customer overdue, ready to send in one tap
A voice note like "Ravi anna ku oru kilo arisi, 80 rupees, Saturday tharuvaar" becomes:
| Customer | Items | Amount | Due |
|---|---|---|---|
| Ravi | 1 kg rice | 80 | Saturday |
The shopkeeper can speak in their normal mix of languages. They never touch a form.
Who Would Pay For This
Shopkeepers who already lose money to forgotten credit. If the tool helps them recover even a few unpaid amounts a month, a small monthly fee pays for itself. That is a much easier sell than "digitise your business".
Why Open Innovation Matters Here
This idea only works because the pieces are open.
- It runs offline. Shop phones and cheap laptops often have poor connectivity. Whisper and Gemma run on the device, so the ledger works when the network doesn't.
- Customer data stays private. A credit ledger lists who owes whom and how much. That is sensitive community information. Nothing is uploaded anywhere.
- No per-minute bills. Hosted speech APIs charge by the minute of audio. A shop recording 50 notes a day would make a cheap product unprofitable. Local inference costs nothing per note.
- Language flexibility. Whisper handles many Indian languages and mixed-language speech reasonably well, and I can fine-tune later on local shop vocabulary such as item names and nicknames, which general tools often get wrong.
- No lock-in. If a model gets better or a provider changes its prices, I swap the model, not the product.
How It Works
Step 1: transcribe locally with faster-whisper.
from faster_whisper import WhisperModel stt = WhisperModel("small", compute_type="int8") # runs on a normal CPU def transcribe(audio_path): segments, info = stt.transcribe(audio_path, task="transcribe") return " ".join(s.text for s in segments).strip() Step 2: turn the sentence into structured data with a local Gemma model.
import json, ollama PROMPT = """You help a shopkeeper keep a credit ledger. From the sentence below, return ONLY JSON with keys: customer, items (list of strings), amount (number or null), due (text or null), type ("credit" if they took goods, "payment" if they paid). Sentence: {text}""" def parse(text, model="gemma3"): r = ollama.chat(model=model, messages=[{"role": "user", "content": PROMPT.format(text=text)}], format="json") return json.loads(r["message"]["content"]) Step 3: save to the ledger and keep a running balance.
import sqlite3 db = sqlite3.connect("ledger.db") db.execute("""CREATE TABLE IF NOT EXISTS entries( id INTEGER PRIMARY KEY, customer TEXT, items TEXT, amount REAL, due TEXT, type TEXT, created TEXT DEFAULT CURRENT_TIMESTAMP)""") def save(entry): if entry["amount"] is None or not entry["customer"]: return "needs_confirmation" # never guess money db.execute("INSERT INTO entries(customer, items, amount, due, type) VALUES (?,?,?,?,?)", (entry["customer"], ", ".join(entry["items"]), entry["amount"], entry["due"], entry["type"])) db.commit() return "saved" def balance(customer): cur = db.execute("""SELECT COALESCE(SUM(CASE WHEN type='credit' THEN amount ELSE -amount END),0) FROM entries WHERE customer = ?""", (customer,)) return cur.fetchone()[0] Design decisions
- Read it back before saving. The app shows "Ravi, 1 kg rice, 80, Saturday. Correct?" with one tap to confirm. Speech recognition makes mistakes, and money entries must be right.
- Missing amount means ask, not guess. The code above refuses to save an entry without a customer or an amount.
- One file, no server. The whole ledger is a single SQLite file the shopkeeper can back up by copying it.
Demo
- Code:
[GitHub repo link] - Demo (video or GIF showing a voice note turning into a ledger row):
[link]
My First User
[Edit honestly. For example: who tried it, what language they spoke, what the app got wrong, whether they would pay. If nobody has tried it yet, write: "I haven't had a user yet. This week I'll ask one local shopkeeper to test it and I'll update this post with what they say."]
What I Learned
- The hard part is not the AI, it is the trust. Reading the entry back before saving mattered more than any model choice.
- Names and local item words are the weakest point of speech recognition. This is where fine-tuning on real shop speech would help most.
- Designing for wet hands and a queue changed the whole product: one tap, no typing.
What's Next
- A simple phone-friendly interface with one big record button
- A weekly summary of "who owes what"
- Reminder messages written in the customer's language
- Fine-tuning on real shop voice notes (with permission)
Prize Category
Best Use of Gemma: a local Gemma model turns messy speech into structured ledger entries.
Thanks for reading! I'd love to hear from anyone who has built for small shops or offline-first users.
