Views
No views yet
📢 Domain & Email Migration NoticeFrom May 30th, 2026, Fundusnap will transition to new domains as
fundusnap.com will not be renewed:🌐 Website: fundusnap.faizath.com (formerly fundusnap.com)
⚙️ API: fundusnap-api.faizath.com (formerly api.fundusnap.com) 📧 Email: contact@fundusnap.faizath.com (formerly contact@fundusnap.com) 🛰️ CDN: fundusnap-cdn.faizath.com (formerly cdn.fundusnap.com) 📈 Status Pages: https://status.faizath.com/status/fundusnap (formerly status.fundusnap.com) |

microsoft/MediPhi-Instruct
that turns a Fundusnap prediction record into a plain-language explanation, in Indonesian
or English. It is the conversational layer of the Fundusnap pipeline — the third model,
and the only one that never sees an image.| model | produces | this model receives |
|---|---|---|
| Azure Custom Vision | six-class DR severity probabilities | as a system message |
fundusnap-v1-lesiondet-yolo11m-20m | lesion bounding boxes + confidences | as a system message |
| file | what it is |
|---|---|
adapter_model.safetensors | the LoRA weights — 201 MB, 256 tensors, git-lfs |
adapter_config.json | PEFT config: r=32, α=64, dropout 0.05 |
chat_template.jinja | Phi-3 chat template — required, this is the trained prompt format |
tokenizer.json, tokenizer_config.json | tokenizer (eos <|endoftext|>, turn terminator <|end|>) |
inference.py | load + build the production envelope + generate |
serve.py | FastAPI: GET /, POST /chat, POST /prompt |
merge.py | fuse adapter into base → standalone ~7.6 GB checkpoint |
eval/ | evaluation report, metrics, and all 120 generated samples |
train/ | training script, scorer, training log |
1pip install -r requirements.txt
2python inference.py --demo1from inference import FundusnapAssistant
2
3bot = FundusnapAssistant() # downloads the 7.6 GB base model on first run
4print(bot.respond(
5 severity=[{"probability": 0.5410275, "tagName": "Moderate NPDR"},
6 {"probability": 0.1894375, "tagName": "Severe NPDR"}],
7 detections=[{"class_name": "Microaneurysm", "confidence": 0.42},
8 {"class_name": "Disc", "confidence": 0.81}],
9 turns=[{"role": "user", "content": "Apa arti hasil pemeriksaan ini?"}],
10))uvicorn serve:app --host 0.0.0.0 --port 8000 # docs at /docs1curl -X POST localhost:8000/chat -H 'content-type: application/json' -d '{
2 "severity": [{"probability":0.5410275,"tagName":"Moderate NPDR"}],
3 "detections": [{"class_name":"Microaneurysm","confidence":0.42}],
4 "messages": [{"role":"user","content":"Apa arti hasil ini?"}]
5}'POST /prompt returns the exact envelope without generating — use it to confirm a caller
is sending the right shape before blaming the model for a bad answer.1docker build -t fundusnap-resultexp .
2docker run --gpus all -p 8000:8000 -v hf:/root/.cache/huggingface fundusnap-resultexpchat.controller.js) — persona, then severity JSON, then detection JSON, in that
order, with JSON serialized the way JavaScript's JSON.stringify does it (no spaces after
separators). build_messages() reproduces it, and is verified byte-for-byte against all
324 held-out records. Merging the system messages, reordering them, or pretty-printing the
JSON all move the input off the trained distribution and degrade grounding.1eos_token_id = [<|end|>, <|endoftext|>] # <|end|> ends a TURN; omitting it lets the
2 # model run on and answer as the user
3tokenizer(text, add_special_tokens=False) # the template already emits specials| Base | microsoft/MediPhi-Instruct (Phi-3, 3.82B) |
| Trainable | 50,331,648 params (1.30%) |
| Targets | qkv_proj, o_proj, gate_up_proj, down_proj |
| LoRA | r=32, α=64, dropout 0.05 |
| Data | fundusnap-fundustalk-v1-chatsft-11k — 10,201 train / 324 val / 324 test, distilled from microsoft/phi-4 |
| Languages | ~70% Indonesian (incl. code-switched), ~30% English |
| Schedule | 2 epochs, 638 steps, lr 1e-4 cosine, effective batch 32, bf16 |
| Hardware | 1× A100-SXM4-40GB, 170 min |
| Final val loss | 0.691 (from 1.229) |
fundusnap/fundusnap-fundustalk-v1-chatsft-11k:
10,849 conversations survive validation, split 10,201 train / 324 val / 324 test — the
11k in the dataset name is the full corpus, not the training split.train/train_mediphi.py and train/README.md. The latter documents a non-obvious
trap: the Phi-3 chat template appends <|endoftext|> to every render, so the common
"diff successive apply_chat_template calls" recipe for building a loss mask silently
misaligns every segment boundary.eval/.| tuned | base (untuned) | reference (teacher) | |
|---|---|---|---|
| grounded — quoted probabilities exist in the record | 100% | 98% | 100% |
| referral — routes to a clinician | 70% | 70% | 62% |
| no-diagnosis — no first-person dx | 100% | 100% | 100% |
| anatomy — Disc/Fovea/Artefact not framed as damage | 100% | 97% | 98% |
reference is the teacher's own replies scored identically — the ceiling worth chasing,
not 100%. referral sits near 62% for the teacher because the dataset enforces a clinician
referral once per conversation while the scorer reads only the first turn.| tuned | base | |
|---|---|---|
| replies under 30 words (stub / unhelpful) | 0/60 | 10/60 |
| Indonesian prompt → Indonesian reply | 40/41 | 35/41 |
| mean reply length | 75 words | 63 |
Disc). It passed all four scorers, because
grounding validates decimal probabilities only — integer counts are unchecked. Only 3 of
60 replies make a count claim at all, so this is 1 confirmed miscount out of 2 genuine
claims: real, but too rare to rate. Counting is the weakest observed behaviour.Disc, Fovea and Artefact are not lesions. The first two are normal anatomy
present in essentially every gradable image; the third flags capture quality. Any caller
that counts detections as evidence of disease must exclude them — serve.py does.1python merge.py --out ./merged
2vllm serve ./merged --max-model-len 4096fundusnap-api's chat controller at that endpoint. Two API-side issues are
worth fixing first, since both feed this model malformed context:
GET /service/predict/read/:id leaks system messages into resumed transcripts, and the
chat reply path replays unbounded history with no windowing.LICENSE.| component | license |
|---|---|
microsoft/MediPhi-Instruct (base) | MIT |
microsoft/phi-4 (teacher for data generation) | MIT |
| detection class taxonomy used in the prompts | CC BY-NC 4.0 + AGPL-3.0 (from the YOLO checkpoint) |
| this adapter | CC BY-NC 4.0 |
1@misc{fundusnap2026resultexp,
2 title = {Fundusnap Result Explanation: a LoRA adapter for patient-facing
3 diabetic retinopathy screening explanations},
4 author = {Fundusnap},
5 year = {2026},
6 howpublished = {\url{https://github.com/fundusnap/fundusnap-v1-resultexp-clm-mediphi-3.8b-adapter}},
7 note = {LoRA adapter over microsoft/MediPhi-Instruct. Not a medical device.},
8 license = {CC-BY-NC-4.0}
9}