README.md
357a0c1ClinIQ — eICR → FHIR R4 on the edge with Gemma 4
A SwiftUI iPhone app that turns a clinician's dictation, paste, or eICR XML into a FHIR R4 Bundle ready for public-health surveillance — all on-device, no PHI off the phone.
Submission for the Gemma 4 Good Hackathon (Kaggle × Google DeepMind), targeting the Health & Sciences category and the Unsloth ($10K) prize track. Deadline 2026-05-18 23:59 UTC.
| Cases list with offline banner | On-device longitudinal diff across three eCRs for the same patient | HF Spaces hosted demo — paste eICR, get FHIR R4 Bundle |
| Cases list, offline mode | Longitudinal diff (on-device) | Hosted demo on HF Spaces |
Headline numbers
F1 = 1.000 sustained across 80 deterministic reps (combined-45 ×20 + combined-54 ×30 + combined-64 ×30, 0 FPs ×50 reps)F1 = 0.997 on the 64-case adversarial bench (recall = 1.000)F1 = 1.000 on 7 / 7 external CDC eICR test vectors (360 / 360 codes recovered)HL7 FHIR validator (Java IG): structural pass on every emitted BundleF1 = 1.000 on Jetson Orin NX 8 GB k8s pod (edge deployment)0 parse errors over 81 runs of the agent path (grammar stability)Unsloth track delta (val-compact, 200 cases held out from training, Mac M-series Q3_K_M for like-for-like edge-deployment numbers):
| base Q3_K_M | v62 (ships) | v63 ⚠ | v63b ⚠ | |
|---|---|---|---|---|
| Micro-F1 | 0.337 | 0.837 | 0.548 | 0.610 |
| Micro-precision | 0.469 | 0.837 | 0.393 | 0.474 |
| Micro-recall | 0.263 | 0.837 | 0.902 | 0.856 |
| JSON-validity | 100% | 92% | 92% | 80% |
| Latency p50 (Mac) | 6.6 s | 3.08 s | 2.79 s | 2.81 s |
| LoRA tensors | — | 490 | 410 ⚠ | 786 |
| Train wall-clock (T4) | — | 1h 04m | 3h 04m | 3h 04m |
v62 is the shipped Unsloth-track LoRA. v63 and v63b are documented
diagnostic work that found a silent toolchain regression: post-2026-04-30
releases of unsloth/peft drop LoRA k_proj + v_proj weights on Gemma 4's
20 global-attention layers (15–34) when transformers > 5.5.0 is
installed. The bug hides at full precision (Kaggle PyTorch F1 = 0.9989)
and only surfaces under Q3_K_M quantization (Mac F1 = 0.548). v63b's
explicit layers_to_transform=list(range(35)) cleared the coverage
gate (35/35 wrapping confirmed) but only recovered F1 to 0.610 — the
remaining gap is the 786-tensor adapter wrapping the SigLIP vision
tower in addition to the decoder. Ship gate (F1 ≥ 0.85) not met; v62
stays. Full diagnosis in tools/autoresearch/v63-experiment/EXPERIMENT.md
and tools/autoresearch/v63b-experiment/EXPERIMENT.md; final
submission narrative at tools/autoresearch/hackathon-submission-2026-05-18.md.
Try it
| Surface | URL | What you'll see |
|---|---|---|
| Hosted demo (HF Spaces) | https://huggingface.co/spaces/kcirtapfromspace/cliniq-eicr-fhir | Paste an eICR narrative, watch the 3-tier pipeline emit a FHIR Bundle on ZeroGPU H200 |
| iOS app | apps/mobile/ios-app/ClinIQ/ | SwiftUI app, builds on iPhone17ProDemo simulator (UDID CADA1806-F64D-4B02-B983-B75F197D1EF3). Gemma 4 E2B Q3_K_M GGUF is side-loaded into the simulator's Documents/ on first demo run (see DEMO_SCRIPT.md step 0); the app's loader probes Documents/ → tmp/ → Bundle.main so seeded weights take precedence. |
| 60-second demo script | apps/mobile/ios-app/DEMO_SCRIPT.md | Read-along narration timed to a 7-beat sim walkthrough |
How the pipeline works
Three tiers, escalated by latency:
| Tier | What | Median latency | LLM? |
|---|---|---|---|
| 1. Deterministic | Regex over inline (SNOMED 12345) markers + CDA <code code="…" /> attrs + curated alias lookup with NegEx scope | ~5 ms | No |
| 2. RAG fast-path | Curated CDC NNDSS + WHO IDSR DB (~60 entries) with NegEx applied to the matched phrase | ~80 ms | No |
| 3. Gemma 4 agent | Native function calling, 6-turn cap, GBNF-locked tool-call grammar | 5–35 s | Yes |
Most cases never invoke the model. On the 54-case bench: 24 short-circuit at Tier 1, 14 at Tier 2, 16 reach the agent.
The Cand D gate decides when the deterministic + lookup path is
trustworthy enough to bypass the agent entirely. See
apps/mobile/convert/agent_pipeline.py and the c20 ledger in
tools/autoresearch/c20-llm-tuning-2026-04-25.md.
What's in the repo
apps/mobile/convert/ Python pipeline (regex / RAG / agent / bundle / score)apps/mobile/ios-app/ SwiftUI iOS appspaces/ HF Spaces hosted demo (Gradio + ZeroGPU)kaggle-training/ Kaggle T4 training kernel (compact LoRA)tools/autoresearch/ Submission narrative + experiment ledgerstools/autoresearch/v62-submission/ Unsloth-track artifacts (model card, notebook plan)infra/ Talos k8s overlays for Jetson clusterdata/eicr-samples/ CDC eICR test vectorsscripts/test_cases*.jsonl Bench cases (combined-27, adv4-7, longitudinal)Read in this order (judges, 5-min path)
- This README — orientation
tools/autoresearch/hackathon-submission-2026-05-18.md— final judge-facing one-pager (Health & Sciences + Unsloth)tools/autoresearch/v62-submission/MODEL_CARD.md— Unsloth-track model card (v62 shipped, v63 follow-up section appended)tools/autoresearch/v63-experiment/EXPERIMENT.md— v63 run record (max_seq=1024 retrain, F1=0.9989)- HF Space — see it run
15-min path: add tools/autoresearch/c20-llm-tuning-2026-04-25.md (full
ledger, 80-rep variance tables, every ablation) and apps/mobile/convert/build/c45_sustained_*.json (the 20 sustained-load reps).
Reproduce
# Python venv with bench harness depspython -m venv scripts/.venv && source scripts/.venv/bin/activatepip install -e apps/mobile/convert# Fetch the base Gemma 4 E2B Q3_K_M GGUF (2.4 GB):huggingface-cli download unsloth/gemma-4-E2B-it-GGUF \ gemma-4-E2B-it-Q3_K_M.gguf --local-dir models/# Run the canonical bench against the local llama-server:llama-server --model models/gemma-4-E2B-it-Q3_K_M.gguf \ --port 8090 --jinja --ctx-size 32768 \ --parallel 4 --n-gpu-layers 99 --threads 8 &scripts/.venv/bin/python apps/mobile/convert/agent_pipeline.py \ --cases scripts/test_cases.jsonl \ scripts/test_cases_adversarial{,2,3,4,5,6,7}.jsonl \ --out-json apps/mobile/convert/build/repro_combined64.json \ --endpoint http://127.0.0.1:8090Expect F1 = 0.997 on combined-64. Single sustained rep takes ~6 minutes.
To bench the v62 Unsloth fine-tune:
llama-server --model models/gemma-4-E2B-it-Q3_K_M.gguf \ --lora models/cliniq-gemma4-e2b-v62-lora.gguf \ --port 8091 --jinja --reasoning-format none --reasoning off \ --ctx-size 8192 --n-gpu-layers 99 --threads 8 &scripts/.venv/bin/python apps/mobile/convert/bench_v62_singleshot.py \ --max-tokens 2048 --compare \ --out apps/mobile/convert/build/v62_val_compact_bench.jsonWhat's novel
- Three-tier extraction with a learned gate — most cases land on regex or RAG. The agent only fires when both lower tiers are uncertain or multi-axis-incomplete. F1 = 1.000 with median ~5 ms, not seconds.
- Autoresearch loop — six
bench → bug-find → fix → re-benchcycles grew the bench from 27 → 64 cases, surfacing 13 precision bugs, fixing 8 in Python and Swift. The model never moved. - External credibility — every Bundle structurally validates against HL7's official Java IG validator. 7/7 perfect on the official CDC eICR test vectors.
- Unsloth-distilled single-shot path —
unsloth/gemma-4-E2B-itv62 LoRA trained on a free Kaggle T4 in 1h 4m turns the base model from F1 = 0.34 into F1 = 0.84 on Mac Q3_K_M with 0.84 precision, 9% faster per inference than the v63 follow-up (which has a known Mac-quantization regression being addressed in v63b). The fine-tune isn't replacing the agent (which already hits 1.000) — it's the speed tier for known-format inputs.
Background
The canonical electronic Case Report processing path is cloud-only: NLP extraction (e.g., AWS Comprehend Medical) → ontology mapping (e.g., IMO Precision Normalize) → identity resolution (e.g., AWS Glue FindMatches) → jurisdiction-specific rules engine. ClinIQ is the on-device extraction tier of that pipeline — what an LMIC clinic would run when there's no AWS, no internet, no Verato, and no AIMS.
The longitudinal "what's new" view (see apps/mobile/ios-app/ClinIQ/ClinIQ/Views/Cases/PatientTimelineView.swift) is the edge-side analogue
of the flat-CSV diff between case versions for the same patient that
production eCR pipelines emit.
License + acknowledgements
Apache 2.0 (model adapter follows base model's Gemma TOU).
Built with: Gemma 4 E2B (Google DeepMind), Unsloth, llama.cpp, HuggingFace Transformers + PEFT + TRL, SwiftUI, Talos Linux, HL7 FHIR Validator, fhir.resources (Python).
Thanks to the Gemma team for the open weights.