Inspiration
I grew up watching small shop owners in Cameroon, especially in Bamenda, Douala, and Yaounde, struggle with the same problem: keeping track of who owes what, how much stock is left, and what to order. They either do it in their heads, on paper that gets lost, or they pay for cloud software that stops working when the internet drops. Most of these shops run on 8 GB laptops or less. I wanted to build something that actually works offline, on real hardware, without pretending the model knows things it does not.
What it does
DukaBind is an offline shop assistant. Staff ask questions like "Can I give Marie-Claire three crates on credit?" in English, French, or Swahili. The system runs an allowlisted SQLite query against a local ledger, does the arithmetic, and returns a grounded answer with the math shown. If a required field like "credit_limit" is missing, it refuses and names the field. It never invents a balance. The local LLM (Qwen2.5-1.5B Q4_K_M) may polish the wording, but the binder message is the only source of financial figures.
How we built it
The core is a Python binder that maps natural language to a small set of allowlisted SQL queries (credit, supplier balance, stock). Language detection picks the right intent regex and word-quantity table (English "three" = 3, French "trois" = 3, Swahili "tatu" = 3). The binder returns a structured result, and the CLI renders it. For narration, an optional llama.cpp server runs the Qwen GGUF locally and receives a JSON citation of the queried rows, with a rule that forbids amounts absent from that citation. The whole stack (server + binder) runs under 2 GB RSS on an Intel i7-8650U with no GPU.
Challenges we ran into
The biggest challenge was the thermal side. This laptop is a 2018-era i7 that throttles hard under sustained load. I spent days tuning thread count and context window to find a config that stays below 85 degrees Celsius during a 10-minute soak. The initial pass at THREADS=2/CTX=1024 did not reproduce on a re-run (peak jumped from 84 to 89 degrees C), which forced me to be honest in the documentation rather than fudge the numbers. Getting the Swahili quantities right was another hurdle. The 1.5B model does not narrate Swahili reliably, so I made Swahili binder-only, no model polish. That was the right call.
Accomplishments that we're proud of
The fail-closed design. Every NULL field produces a refusal with the field name. No balance is ever invented. The held-out evaluation (37 prompts across two shop ledgers, including cross-shop leak tests and adversarial jailbreaks) scores 100%. The ledger-flip proof works: change one row, ask the same question, get a different answer. That is binding, not recall. The whole thing runs on an 8 GB laptop with no internet, no GPU, and under 2 GB of RAM.
What we learned
That "offline" is not just a deployment claim; it is an engineering constraint. Every design decision had to pass the question: does this work with zero network? That ruled out cloud-hosted models, external APIs, and even telemetry. I also learned that thermal behavior on older laptops is not reproducible across days and that honesty in benchmarking matters more than looking good on paper.
What's next for DukaBind
The French and Swahili tracks are working now. The next steps are owner-facing PIN writes (the staff currently only reads the ledger), a simple HTMX staff UI for shops that want a visual interface, and testing on actual 8 GB laptops in Douala market conditions. Longer term, this could become a general-purpose offline binder for any small business ledger.
Log in or sign up for Devpost to join the conversation.