This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle
What I Built
Letter Decoder β a local-first, open-source app that reads your official letters and explains them in plain language.
Tax notices. Fines. Insurance rejections. Bank letters. Landlord warnings. Drop one in as a photo, PDF, screenshot, or pasted text, and get back an answer card:
- β A one-line verdict ("you owe X by Y β act soon")
- β³ A deadline countdown with a
.icscalendar export - π΄ A severity rating
- π Three concrete next steps
- π A document checklist
- βοΈ An editable draft reply
Every date and amount links back to the exact line of the original letter it came from. If the model can't verify a claim against the extracted facts, it says so β no confident hallucinations.
Who would pay for this? Anyone who has ever frozen looking at a government envelope, hired a solicitor at Β£150/hour just to be told "this is a routine payment reminder", or simply had a letter in their second language. The realistic addressable market is anyone who receives official correspondence β which is everyone.
The letter never leaves your device. No accounts, no cloud, no telemetry. That's the product differentiator and the privacy guarantee rolled into one.
Demo
| URL | Notes | |
|---|---|---|
| Frontend | https://letter-decoder.vercel.app | React 19 app, deployed on Vercel |
| Public API | https://letter-decoder-api-qvxs.onrender.com | FastAPI β runs in ENGINE_MODE=mock, replays 14 canonical examples through the full SSE pipeline with realistic delays |
For real decoding with a local model, clone the repo and run with Ollama (see below). The public endpoint intentionally uses mock mode so no real letters are ever uploaded to a server.
Code
RAMBO-LC
/
letter-decoder
Local-first, open-source app that explains official letters in plain language with open-weight models on your own machine. No cloud, no telemetry, no accounts.
Letter Decoder
"A patient friend read it for me, and now I know what to do."
A local-first, open-source app that explains official letters in plain language.
Tax notices, fines, insurance rejections, bank letters, landlord notices β drop one in as a photo, PDF, screenshot, or pasted text, and get back an answer card: a one-line verdict, a deadline countdown, severity, three concrete next steps, a document checklist, and an editable draft reply. Every date and amount links back to the exact line of the original letter it came from.
No cloud calls. No telemetry. No accounts. The letter never leaves your device.
Live Demo
| URL | What it is | |
|---|---|---|
| Frontend | https://letter-decoder.vercel.app | React app (Vercel) |
| API | https://letter-decoder-api-qvxs.onrender.com | FastAPI engine (Render) |
The public API runs
ENGINE_MODE=mock, replaying canonical examples through the same endpoints and SSE event streams with realistic delays. For real decoding with an open-weight model, run it locallyβ¦
Quick start with a real model:
# Terminal 1 β start the inference server
ollama serve
ollama pull qwen2.5:7b
# Terminal 2 β start the app
git clone https://github.com/RAMBO-LC/letter-decoder.git
cd letter-decoder
uv sync --all-extras
uv run python -m api.main
Open http://127.0.0.1:8000 β everything runs fully offline from here.
How I Built It
The core design principle is: code finds facts, the model explains them.
The Pipeline
[Input] β Preprocess β Extract Text β Deterministic Facts β Classify β Notice Pack β LLM Explain β Verify β Answer
-
Preprocess (
engine/preprocess.py) β OpenCV deskew and denoise for photos/scans. -
Extract text (
engine/extract_text.py) β native PDF text extraction, falling back to Tesseract OCR. -
Deterministic extraction (
engine/deterministic_extract.py) β regex +dateparserpull every date, amount, and reference number from the raw text. The LLM never sees a blank slate: facts are pre-extracted before it is invoked. -
Classify (
engine/classifier.py) β keyword and embedding matching identifies the notice type and loads the right notice pack (a YAML file per country/type innotices/). -
LLM Explain (
engine/llm_constrained.py) β Ollama runs locally; the default model is Qwen 2.5 7B (fully open weights). JSON-constrained output means the model can only produce the schema the verifier expects. -
Verify (
engine/verifier.py) β every factual claim in the LLM output is checked against the deterministically extracted facts. A fact the model invented that wasn't in the source text fails verification β you get a clean error, not a confident lie.
Open-Source AI Stack
| Layer | Tool | License |
|---|---|---|
| Local inference | Ollama | MIT |
| Default model | Qwen 2.5 7B | Apache 2.0 |
| OCR | Tesseract | Apache 2.0 |
| Image processing | OpenCV | Apache 2.0 |
| API framework | FastAPI | MIT |
| Frontend | React 19 | MIT |
Every layer is open source. You can swap qwen2.5:7b for Gemma, Llama, Mistral, or a fine-tuned checkpoint by changing one environment variable.
Contributing is Data, Not Code
Most contributions to this project are YAML files, not Python PRs. Adding support for a new country is:
# notices/fr/tax_reminder.yaml
name: "Avis de rappel fiscal"
keywords: ["TrΓ©sor public", "DGFIP", "avis d'imposition"]
severity_default: act_soon
mock_simple: "This is a French tax reminder."
That's it. CI validates it against the JSON Schema automatically.
Why Does Open Innovation Matter?
A letter contains income, legal status, financial history, and sometimes your immigration situation. Explaining it with a closed API means uploading it.
There is no enterprise tier, privacy toggle, or data-processing agreement that un-does the fact that your letter travelled to someone else's data center. With an open local model:
-
The letter structurally never leaves the machine. This isn't a policy β it's enforced:
tests/test_local_only.pyfails the CI build if any code path attempts an outbound network call. - Zero marginal cost. A family that gets ten letters a year pays nothing extra after the initial setup.
- Auditability. The verifier that proves "the model never invents a deadline" is code you can read, fork, and run yourself. You don't have to trust a vendor's marketing copy.
- Swappability. Communities can fine-tune a small model on their country's letter formats β a GDPR-compliant, locally-hosted specialist β without any API negotiation.
Open weights made this project's privacy guarantee credible. A closed API could promise the same guarantee; open weights prove it.
My Agent Session
Prize Categories
- Hacktoberfest Open-Source AI Challenge β Week 2: Side Hustle
Top comments (0)