{"slug": "letter-decoder-your-ai-friend-for-scary-official-letters-runs-100-offline", "title": "Letter Decoder: Your AI Friend for Scary Official Letters (Runs 100% Offline)", "summary": "A developer built Letter Decoder, an open-source, local-first app that explains official letters such as tax notices, fines and insurance rejections in plain language, producing an answer card with a verdict, deadline countdown, next steps and a draft reply. The app runs fully offline with Ollama and the Qwen2.5 7B model, using a pipeline where deterministic regex and dateparser extraction finds dates, amounts and reference numbers while the LLM only explains them, and a verifier checks every factual claim against the extracted facts to prevent hallucinations. A public demo API runs in mock mode replaying 14 canonical examples so no real letters are uploaded.", "body_md": "*This is a submission for the [Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle](https://dev.to/challenges/hacktoberfest-week2-2026-10-12)*\n\n**[Letter Decoder](https://letter-decoder.vercel.app)** — a local-first, open-source app that reads your official letters and explains them in plain language.\n\nTax 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**:\n\n`.ics` calendar export\nEvery 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.\n\n**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.\n\n**The letter never leaves your device.** No accounts, no cloud, no telemetry. That's the product differentiator and the privacy guarantee rolled into one.\n\n|  | URL | Notes | \n|---|---|---|\n| **Frontend** | [https://letter-decoder.vercel.app](https://letter-decoder.vercel.app) | React 19 app, deployed on Vercel | \n| **Public API** | [https://letter-decoder-api-qvxs.onrender.com](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 | \n\nFor 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.\n\n\"A patient friend read it for me, and now I know what to do.\"\n\n**A local-first, open-source app that explains official letters in plain language.**\n\nTax notices, fines, insurance rejections, bank letters, landlord notices — drop one in as a\nphoto, PDF, screenshot, or pasted text, and get back an **answer card**: a one-line verdict, a\ndeadline countdown, severity, three concrete next steps, a document checklist, and an editable\ndraft reply. Every date and amount links back to the exact line of the original letter it came\nfrom.\n\nNo cloud calls. No telemetry. No accounts. **The letter never leaves your device.**\n\n| URL | What it is |  | \n|---|---|---|\n| **Frontend** | [https://letter-decoder.vercel.app](https://letter-decoder.vercel.app) | React app (Vercel) | \n| **API** | [https://letter-decoder-api-qvxs.onrender.com](https://letter-decoder-api-qvxs.onrender.com) | FastAPI engine (Render) | \n\nThe public API runs `ENGINE_MODE=mock`, replaying canonical examples through the same\nendpoints and SSE event streams with realistic delays. For real decoding with an open-weight\nmodel, run it locally…\n\nQuick start with a real model:\n\n```\n# Terminal 1 — start the inference server\nollama serve\nollama pull qwen2.5:7b\n\n# Terminal 2 — start the app\ngit clone https://github.com/RAMBO-LC/letter-decoder.git\ncd letter-decoder\nuv sync --all-extras\nuv run python -m api.main\n```\n\nOpen `http://127.0.0.1:8000` — everything runs fully offline from here.\n\nThe core design principle is: **code finds facts, the model explains them.**\n\n```\n[Input] → Preprocess → Extract Text → Deterministic Facts → Classify → Notice Pack → LLM Explain → Verify → Answer\n```\n\n`engine/preprocess.py`) — OpenCV deskew and denoise for photos/scans.` engine/extract_text.py`) — native PDF text extraction, falling back to Tesseract OCR.`engine/deterministic_extract.py`) — regex + `dateparser` pull every date, amount, and reference number from the raw text. The LLM `engine/classifier.py`) — keyword and embedding matching identifies the notice type and loads the right `notices/`).` engine/llm_constrained.py`) — `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.\n| Layer | Tool | License | \n|---|---|---|\n| Local inference | [Ollama](https://ollama.ai) | MIT | \n| Default model | [Qwen 2.5 7B](https://huggingface.co/Qwen/Qwen2.5-7B-Instruct) | Apache 2.0 | \n| OCR | [Tesseract](https://github.com/tesseract-ocr/tesseract) | Apache 2.0 | \n| Image processing | [OpenCV](https://opencv.org) | Apache 2.0 | \n| API framework | [FastAPI](https://fastapi.tiangolo.com) | MIT | \n| Frontend | [React 19](https://react.dev) | MIT | \n\nEvery layer is open source. You can swap `qwen2.5:7b` for Gemma, Llama, Mistral, or a fine-tuned checkpoint by changing one environment variable.\n\nMost contributions to this project are YAML files, not Python PRs. Adding support for a new country is:\n\n```\n# notices/fr/tax_reminder.yaml\nname: \"Avis de rappel fiscal\"\nkeywords: [\"Trésor public\", \"DGFIP\", \"avis d'imposition\"]\nseverity_default: act_soon\nmock_simple: \"This is a French tax reminder.\"\n```\n\nThat's it. CI validates it against the JSON Schema automatically.\n\nA letter contains income, legal status, financial history, and sometimes your immigration situation. Explaining it with a closed API means uploading it.\n\nThere 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:\n\n`tests/test_local_only.py` fails the CI build if Open weights made this project's privacy guarantee credible. A closed API could promise the same guarantee; open weights prove it.", "url": "https://wpnews.pro/news/letter-decoder-your-ai-friend-for-scary-official-letters-runs-100-offline", "canonical_source": "https://dev.to/rambolc/letter-decoder-your-ai-friend-for-scary-official-letters-runs-100-offline-2fji", "published_at": "2026-10-18 18:11:47+00:00", "updated_at": "2026-10-18 18:17:21.406929+00:00", "lang": "en", "topics": ["ai-tools", "large-language-models", "ai-agents", "developer-tools"], "entities": ["Letter Decoder", "Ollama", "Qwen2.5", "FastAPI", "React", "Vercel", "Render", "Tesseract"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/letter-decoder-your-ai-friend-for-scary-official-letters-runs-100-offline", "markdown": "https://wpnews.pro/news/letter-decoder-your-ai-friend-for-scary-official-letters-runs-100-offline.md", "text": "https://wpnews.pro/news/letter-decoder-your-ai-friend-for-scary-official-letters-runs-100-offline.txt", "jsonld": "https://wpnews.pro/news/letter-decoder-your-ai-friend-for-scary-official-letters-runs-100-offline.jsonld"}}