# MedOS Offline-Lite **Status:** design only. Implementation lands when a contributor picks it up. This document describes a deliberately small, deliberately limited offline mode for MedOS. It is **not** a promise of a full offline medical AI. The deterministic safety floor is what works offline; an LLM is only added on top of it as a small-model best-effort. Read this together with `SAFETY.md` at the repo root. ## Why "Lite" Two honest constraints set the scope: 1. **Llama 3.3 70B does not run on a phone or a budget laptop.** Promising "offline medical AI" with a 70B model is misleading. We do not. 2. **Safety must hold offline.** The deterministic red-flag rules in `9-HuggingFace-Global/lib/safety/red-flags.ts` and the regional emergency numbers in `config/locales/*.medical.json` are pure code + pure data. Both already work offline. That is the foundation. Offline-Lite therefore ships: ```text Always available offline: - Deterministic red-flag triage (R0..R5) - Regional emergency numbers, crisis lines, poison-control numbers - Locale disclaimers + clinician-referral phrasing - (Historically lib/providers/cached-faq.ts held canned FAQ entries here; it was removed in b83db95 because its keyword matcher surfaced unrelated answers — e.g. "my child has fever" returned malaria info. Offline guidance in this tier is now limited to the deterministic triage + emergency-number cards above, which are always safe to show without an LLM.) Best-effort offline (optional, behind a flag): - Phi-3 mini / Gemma 2B / TinyLlama for non-clinical guidance only - Strictly capped at R0/R1 outputs - Strict post-filter ``` ## What the user sees A clearly-marked **Offline Lite** banner appears whenever the app detects no connectivity (or the user explicitly enables it): > **You are using Offline Lite.** I can show emergency numbers, run a basic safety check on your symptoms, and answer common questions from a small, on-device model. I cannot do full medical chat without a connection. **For anything urgent, call your local emergency number.** The banner is sticky, dismissible per session but never per app, and the local emergency number from the active locale pack is shown next to it. ## Architecture ```text User input ↓ Pre-LLM safety engine (always; pure code, works offline) ↓ - R5 → emergency template, end. (NO LLM CALL.) - R4 → urgent template + local emergency number - R3 → urgent care suggestion + clinician phrasing - R0/R1 → small on-device model OR cached FAQ, then post-filter - R2 → cached FAQ + clinician referral ↓ Post-LLM safety filter (always; pure code, works offline) ↓ Final answer + disclaimer + emergency number when required ``` ## Small-model selection | Model | Approx. size | Plausible host | License | |---|---|---|---| | Phi-3 mini (3.8B) | ~2.4 GB quantised | Browser via WebGPU / phone via ONNX | Permissive | | Gemma 2B-IT | ~1.5 GB quantised | Same | Open weights, terms apply | | TinyLlama 1.1B | ~700 MB quantised | Lightest option | Permissive | The model **must** be: - Routed through the existing `chatWithFallback` path (so the safety sandwich still applies). - Pinned to **R0/R1 only**. Anything routed to R2+ uses cached responses or a clinician-referral template, **not** the small model. - Wrapped in the **same** post-filter as the cloud path. There are no shortcuts for "the small model is on-device, so it's fine." It isn't. - Distributed via the standard PWA caching / app-bundle path. No third-party CDN that can later silently change weights. ## Implementation outline A contributor implementing this would touch: 1. `9-HuggingFace-Global/lib/providers/offline-lite.ts` — new provider that wraps a WebGPU / ONNX / GGUF runtime. Returns the same `ProviderResponse` shape so `chatWithFallback` can plug it in. 2. `9-HuggingFace-Global/lib/providers/index.ts` — add Offline-Lite as the **last** fallback step, after cached FAQ. 3. `9-HuggingFace-Global/lib/safety/safety-engine.ts` — when the input resolves to R2 or higher under Offline-Lite, route to a template / cached response, not the small model. Add an explicit `offline: true` audit field. 4. `web/components/OfflineBanner.tsx` (or equivalent in the live app) — the always-visible banner described above. 5. PWA / service-worker config — pre-cache `config/locales/*.medical.json` at install time. They are tiny and safety-critical. ## What Offline-Lite explicitly does NOT do - Full medical chat. - Diagnosis (it never does, online or offline). - Dose recommendations. - "You're fine, no need to see a doctor." The post-filter blocks this on every path. - Pretending to be the same product as online MedOS. The banner is loud about the limits. ## Testing The same `tests/safety/run_golden.ts` set runs in offline mode by mocking the provider chain to return only Offline-Lite results. The R5 / R4 floor must hold; the post-filter rules apply identically. A few extra cases tagged `offline_lite` may be added if specific failure modes are discovered (e.g., a small model hallucinating a dose). ## Roadmap - **Step 1.** Pre-cache the medical packs in the service worker. (Self-contained, no model.) - **Step 2.** Add a clear Offline banner that surfaces emergency numbers regardless of model availability. - **Step 3.** Add Offline-Lite small-model provider behind a feature flag (`MEDOS_OFFLINE_LITE_ENABLED=true`). Keep the flag off by default while validation runs. - **Step 4.** Run the full golden set in offline mode; require the same R5/R4 sensitivity targets as the online path before flipping the flag on. Until Step 4 passes, Offline-Lite is documented but not enabled. The deterministic safety floor + emergency numbers + cached FAQ already give users a useful, honest offline experience.