DEV Community

Utkarsh Singh
Utkarsh Singh

Posted on

The $0-per-report feature: a patient-friendly radiology explainer that runs on the clinic's own laptop

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.

What I Built

PlainRay turns radiology reports into plain-language patient explainers — on the clinic's own machine, with an open-weight model, for zero rupees per report.

It reads a report and produces, in one pass:

  • a plain-language summary of what the exam showed (under 150 words, roughly 8th-grade reading level),
  • a glossary of every term and abbreviation it used, translated to everyday words ("consolidation → cloudy areas in the lung"),
  • questions the patient can ask their doctor, and
  • a calm urgency note — only if the report itself flags something, in the report's own wording. The model is not allowed to invent findings, diagnose, or reassure.

Who would pay for this: the kind of clinic that runs two radiologists, one front desk, and a stack of reports whose first word is usually "IMPRESSION:". Behind that desk sits a person who answers "what does effusion mean?" eleven times a week. Every hosted "AI patient portal" wants a per-report meter and a copy of the report on someone else's server. PlainRay flips it: flat license, zero usage cost, nothing ever leaves the machine.

The side-hustle math that makes it a real product instead of a demo: the clinic already owns the laptop, and the model runs on an RTX 4060. So the unit economics are: model = free forever, electricity = what the lamp costs, product = one flat fee per workstation. There is no other version of this offer where the cost driver of the whole business is $0 — that's only possible because the model is an open-weight one you can download.

Demo

Three synthetic reports are in the repo (samples/sample_reports.json — no real PHI is ever committed). Here is the actual flow, captured locally while the machine was offline for everything else:

PlainRay explaining a chest X-ray report - summary, reading grade, glossary and questions

The second frame is the mammography screening sample — the star of the show, because screening reports are the least readable of all (see it here).

No login, no cloud, no network. Unplug the ethernet cable and it keeps working — that is the demo. Run it yourself:

git clone https://github.com/4-thkind/plainray
ollama pull gemma3:4b          # one time, ~3 GB
python -m plainray serve       # http://127.0.0.1:7860
python -m plainray explain --file report.txt   # JSON on stdout
python -m plainray selftest    # prompts + validator + readability, no model needed
Enter fullscreen mode Exit fullscreen mode

Code

Everything lives in the 4-thkind/plainray repo:

  • plainray/llm.py — a minimal local Ollama client in stdlib urllib, ~40 lines. No pip, no SDK.
  • plainray/prompts.py — the patient-safety prompt, readable in plain text by whoever deploys it.
  • plainray/app.py — CLI (explain / serve / selftest) and a validator that enforces the JSON shape: caps list lengths, dedupes glossary terms, refuses an empty summary.
  • plainray/readability.py — a Flesch-Kincaid grader in stdlib. Numbers, not promises.
  • static/index.html — the UI; the server binds to 127.0.0.1 on purpose.

How I Built It

  • Model: Gemma 3 4B (open weights), served locally by Ollama on an RTX 4060 Laptop (8 GB VRAM). One-time ~3 GB pull; after that it runs offline. Generation takes ~30 s per report on this laptop.
  • The prompt is the product. Anti-diagnosis, no invented findings, glossary limited to terms that actually appear in the report, calm-not-reassuring urgency wording. That last rule came from watching Gemma happily soften "consolidation" into "all is well" — which is exactly wrong for a patient explainer.
  • Structured output enforced in code, not trusted from the model. _validate() rebuilds the JSON from scratch. A 4B model will slip a made-up term into the glossary now and then ("mets", when the report never said it) — the design answer is to show the glossary to a human for one click of review, not to pretend the model is a pharmacist.
  • Measured readability. The Flesch-Kincaid grade runs on the raw report and on the generated explainer, and both numbers show in the UI:
Sample report Raw grade PlainRay explainer
Chest X-ray, consolidation 10.4 7.6
Knee MRI, meniscus tear 10.0 7.6
Mammography screening 12.3 6.7
  • Zero dependencies: everything is stdlib (urllib, http.server). Clinic IT never touches a package manager.
  • Swapping the model is one variable (PLAINRAY_MODEL): gemma3:12b on a bigger box, or the medical-tuned sibling medgemma — same code, same prompt.

Why Does Open Innovation Matter?

  1. PHI is the whole story. A radiology report is protected health information. A closed, hosted model requires sending that text to someone else's server before any feature can run — which is why most clinics simply don't have this feature. With Gemma running locally, that sentence disappears from the compliance conversation: the data physically cannot leave the laptop.
  2. It fixes the cost basis of the product. A center reading ~120 reports a day on a hosted API pays a meter every month, forever, for a feature most clinics treat as nice-to-have. Local open weights move that cost to approximately zero — which is what makes the pitch survive an accountant.
  3. Unplugged beats pretty. The internet does go down in small clinics. PlainRay doesn't care; the server is on the same machine.
  4. Customize for a niche. A general-purpose closed API handles "ACR 2" the way a search engine does. Locally, a clinic can tune the prompt to their own reporting style, and the weights are there if they ever want to go further.
  5. Fair for a two-doctor clinic. No usage meter, no lock-in, no per-seat squeeze. The feature that is free for a solo practitioner is the same for a hospital.

My Agent Session

This project was built in one working session with an AI coding agent — I set the problem, the constraints ("small, specific, useful, and it must actually run on this laptop") and reviewed everything it produced. The short form of that session — constraints discovered first, the decisions worth keeping, and what was hard — is checked into the repo as AGENT_NOTES.md. No DevRelay install for this build; the notes file is the session, honestly summarized.

My First User

Honest version: my first real user test hasn't happened yet. During this challenge window I built the product and ran it end-to-end on my own machine against three synthetic reports; nobody's real report went through it, because the right first user is a clinic that will trade one afternoon for a demo, and setting that up properly takes longer than the window. Rather than report a fake ten, here is the actual next step: one call to my cousin's ortho clinic, a printed example made from samples/, and two questions I'll log verbatim in the repo — would you pay, and how much per year?

If you're a radiologist or run a small clinic and want to be user #1: the demo runs from a fresh clone in under two minutes, and my contact is on my profile.

Prize Categories

Best Use of Gemma — Gemma 3 4B runs entirely on-device through Ollama, powers the whole product, and the architecture keeps gemma3:12b / medgemma as one-variable drop-ins.

(Solo build — no teammates.)

Top comments (0)