A privacy-first, offline-capable medical AI assistant for students and clinicians.
Runs entirely on-device using llama.cpp — no accounts, no API keys, no cloud.
AcornMed is a medical study companion that puts a capable LLM directly on your Android device. Designed for medical students, residents, and clinicians who need reliable, citeable information without compromising patient data or requiring an internet connection.
Current release:
v1.2.0— see Releases for signed APKs.
| Problem | AcornMed Solution |
|---|---|
| 🏥 Patient data privacy | 100% on-device — nothing leaves your phone |
| 💰 API costs / rate limits | Zero API keys — runs locally via llama.cpp |
| 📶 No internet in hospitals | Fully offline core functionality |
| 📚 Scattered resources | Unified RAG across 6 medical subjects + your PDFs |
| 🔍 Outdated knowledge | Optional live web search (PubMed, Wikipedia, Web) |
| 📸 Image-only materials | On-device OCR (ML Kit, no cloud) |
Recommended: arm64-v8a (21.8 MB) — for modern phones
All ABIs: Universal (45.8 MB) — if unsure or older/32-bit device
- Engine: llama.cpp via
llama_flutter_android(ARM64 + x86_64) - Models: Any GGUF (tested with small instruction-tuned models like Qwen2.5-0.5B-Instruct-Q4_K_M, Llama-3.2-1B, Phi-3-mini)
- Context: up to 4K tokens (configurable)
- GPU offload where the device supports it, with safe CPU fallback
- No internet required for core chat
A toggle in Settings → Faster responses tunes the on-device engine for lower latency:
- Trims the context window (4096 → 2048 tokens) and max reply length (1024 → 768 tokens)
- Reduces retrieved-knowledge injection so the model starts answering sooner
- Persisted and re-applied automatically at every app start
- Measured ~15% faster time-to-first-token on-device, with decode throughput essentially unchanged
- PubMed — Latest biomedical literature via NCBI E-utilities
- Wikipedia — General medical concepts via MediaWiki API
- DuckDuckGo — General web via HTML scrape (fallback)
- Auto-detect freshness queries ("latest guidelines", "2024 treatment")
- Manual toggle in chat input bar
- Smart intercept: Model emits
[SEARCH: query]→ app searches → re-prompts with results - Citations: Numbered source chips with tappable URLs
- Formats: PDF, DOCX, TXT, Markdown
- Extraction: Syncfusion PDF + custom DOCX (ZIP/XML) parser
- Indexing: TF-IDF + cosine similarity, per-document chunks
- Per-chat attachment: Select docs from library → searched first
- Management: Documents screen (add/delete/reindex)
- Source: Camera or gallery
- Engine: Google ML Kit Text Recognition (bundled model, no Play Services)
- Flow: Pick → OCR → preview extracted text → send with query
- Honest about limits: "I can't see images" for pure visual questions
Downloading a model in-app is now production-safe:
- Integrity verification — the downloaded file's byte length is checked against the expected size, and (when provided) a SHA-256 checksum (
crypto) is verified - Correct resume — distinguishes HTTP
206(append) from200(restart) so partial downloads are never double-counted - Retries transient network failures up to 3 times with backoff
- No silent corruption — a truncated/checksum-mismatched file is discarded and retried instead of being loaded by the engine (which previously crashed on a bad model)
- Friendly, actionable error messages if a download ultimately fails
- Real SVG logo → crisp at any resolution
- Adaptive launcher icons (foreground on sage
#8D9771) - Native splash (Android 8–11 + Android 12+)
- Dark / Light theme (sage/cream palette)
- Riverpod state management
- Hive local storage (conversations, documents, settings)
| Chat | Model Setup | Navigation Drawer | Image OCR |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
| Onboarding | Documents | First Launch | Splash Screen |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
lib/
├── core/
│ ├── theme/ # AppTheme (light/dark), AppColors
│ ├── utils/ # friendly_error, performance_mode
│ └── widgets/ # AppLogo (SVG + fallback)
├── data/
│ ├── models/ # ChatMessage, Conversation, DocumentItem, WebSource (Hive adapters)
│ └── services/
│ ├── ai_engine.dart # llama.cpp wrapper (load/chat/stop)
│ ├── model_manager.dart # Model discovery, download + integrity verification
│ ├── storage_service.dart # Hive boxes (conversations, documents)
│ ├── knowledge_service.dart# TF-IDF RAG (bundled + user docs)
│ ├── document_extractor.dart # PDF/DOCX/TXT text extraction
│ ├── document_service.dart # Import, delete, reindex
│ ├── ocr_service.dart # ML Kit text recognition
│ └── web_search_service.dart # PubMed/Wiki/DDG search
├── features/
│ ├── home/ # ChatView, HistoryDrawer
│ ├── documents/ # DocumentsScreen (library UI)
│ ├── settings/ # SettingsScreen (faster mode, theme, stats)
│ ├── model_setup/ # ModelSetupScreen (download / GGUF picker)
│ ├── onboarding/ # OnboardingScreen
│ └── splash/ # SplashScreen
├── providers/
│ └── chat_providers.dart # ChatController (Riverpod Notifier)
└── main.dart
User Query
│
├─▶ Attached Doc IDs ──▶ KnowledgeService.retrieve(docIds) ──▶
│ │
├─▶ Web Search (toggle/auto) ──▶ WebSearchService.search() ──▶
│ │
└─▶ OCR Text (if image) ─────────────────────────────────▶
▼
buildContext() → System Prompt
│
▼
llama.cpp Stream
│
▼
[SEARCH:] intercept?
│
┌────────────────┴────────────────┐
▼ ▼
Yes (re-search) No (finalize)
│ │
▼ ▼
_generate() again Save + UI
- Flutter 3.24+ (Dart 3.5+)
- Android SDK 34, NDK 26+
- Java 17 (for Gradle)
- Device/emulator with Android 8.0+ (API 26)
Download the APK from the Download section above (or from Releases):
- Transfer to your phone and open it (allow install from unknown sources if prompted).
- On first launch, follow onboarding, then Set up model (download or import a GGUF).
# Clone
git clone https://github.com/SparshMishra09/AcornMed.git
cd AcornMed
# Get dependencies
flutter pub get
# (Optional) Generate Hive adapters if models change
dart run build_runner build --delete-conflicting-outputs
# Debug build
flutter run --debug
# Release APK (universal, all ABIs)
flutter build apk --release
# Output: build/app/outputs/flutter-apk/app-release.apk
# Release APK (arm64-v8a only — smaller, recommended for phones)
flutter build apk --release --target-platform android-arm64 --split-per-abiYou have two options:
1. In-app download (recommended)
- Open the app → Set up model → pick a model from the catalog → Download.
- Downloads are verified for completeness (size + optional SHA-256) and resumed automatically; a corrupt download is retried rather than loaded.
2. Import your own GGUF
- Download a GGUF model (e.g., from Hugging Face):
Qwen2.5-0.5B-Instruct-Q4_K_M.gguf(~0.5 GB, fast on phones)Llama-3.2-1B-Instruct-Q4_K_M.gguf(~0.8 GB)Phi-3-mini-4k-instruct-q4.gguf(~2.3 GB)
- Transfer to device (Downloads, Documents, or any folder).
- Open app → Set up model → Import GGUF → select the file.
- The app loads the model (first load ~10–30s depending on device).
Tip: Place a model in
Android/data/com.acornmed.acorn_med/files/for quick access.
All models are text-only — none natively process images. When you attach an image, the app runs on-device OCR (ML Kit) to extract text, then sends that text to the model.
| Model | Size | Quality | Tool Use | Vision | Notes |
|---|---|---|---|---|---|
| SmolLM2 1.7B | 1.0 GB | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | Text only | Tiny and fast — best for older/low-RAM phones |
| Qwen2.5 1.5B | 0.9 GB | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | Text only | Light and fast, comfortable on most phones |
| Llama 3.2 3B | 1.9 GB | ⭐⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐⭐⭐ | Text only | Meta's compact model, well-rounded for study |
| Qwen2.5 3B | 1.8 GB | ⭐⭐⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐⭐⭐⭐ | Text only | Best balance — recommended for most devices |
| Gemma 3 4B | 2.4 GB | ⭐⭐⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐⭐⭐ | Text only | Strong factual recall, good for definitions |
| Phi-4 mini 3.8B | 2.4 GB | ⭐⭐⭐⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐⭐⭐⭐⭐ | Text only | Excellent reasoning, step-by-step explanations |
| Qwen2.5 7B | 4.6 GB | ⭐⭐⭐⭐⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐⭐⭐⭐ | Text only | Smartest option — needs ~5 GB RAM |
Image support: The app extracts text from images via OCR and sends it to the model. For images with no readable text (e.g., photos, diagrams), the model will honestly tell you it cannot see the image directly.
- Type a medical question → send
- Web toggle (🌐 chip): force live search
- Auto-search: detected for freshness terms ("latest", "2024", "new guideline")
- Citations: tap numbered chips to open source URLs
- Settings → Faster responses: enable for lower time-to-first-token on slower devices. Disable for longer, more thorough answers.
- Tap 📎 Docs chip in input bar
- Select from library or tap Add → Documents screen → upload
- Selected docs show badge count on chip
- Query → RAG searches your docs first, then bundled knowledge
- Drawer → Documents (or from attach sheet)
- Add: PDF, DOCX, TXT, MD (multi-select)
- Delete: swipe or tap delete icon
- Reindex: automatic on app start, or pull-to-refresh in Documents screen
- Tap 🖼️ Image chip → Camera or Gallery
- App runs on-device OCR (shows progress)
- Preview extracted text (editable)
- Send → text included as context for the model
- Faster responses: trade context length for speed
- Theme: Light / Dark / System
- Knowledge stats: bundled chunks, your document chunks
- Privacy: all local, no telemetry
| Parameter | Range | Default | Notes |
|---|---|---|---|
| Context length | 512–4096 | 4096 | Higher = more memory |
| Max reply tokens | 256–1024 | 1024 | Lowered to 768 in Faster mode |
| GPU layers | Auto | Device-dependent | CPU-only fallback if unstable |
Six subjects in assets/knowledge/:
anatomy.mdphysiology.mdpharmacology.mdpathology.mdbiochemistry.mdmicrobiology.md
Add/edit markdown files → rebuild app → auto-indexed on first launch.
static const sage = Color(0xFF8D9771); // Primary (logo match)
static const sageDark = Color(0xFF6B7A55);
static const sagePale = Color(0xFFE8ECE3);
static const cream = Color(0xFFFDFBF5); // Surface
static const coffee = Color(0xFF3D342C); // Text primary| Package | Purpose | Version |
|---|---|---|
flutter_riverpod |
State management | ^2.6.1 |
hive + hive_flutter |
Local NoSQL storage | ^2.2.3 / ^1.1.0 |
llama_flutter_android |
llama.cpp bindings | ^0.2.6 |
crypto |
SHA-256 download verification | ^3.0.3 |
http |
Model download / web search | ^1.4.0 |
syncfusion_flutter_pdf |
PDF text extraction | ^33.2.13 |
archive |
DOCX (ZIP) parsing | ^4.1.0 |
google_mlkit_text_recognition |
On-device OCR | ^0.16.0 |
image_picker |
Camera/gallery | ^1.2.3 |
flutter_svg |
SVG logo rendering | ^2.3.0 |
url_launcher |
Open citation URLs | ^6.3.2 |
file_picker |
Document selection | ^10.3.3 |
uuid |
Unique IDs | ^4.5.1 |
intl |
Date formatting | ^0.20.2 |
shared_preferences |
Onboarding / prefs | ^2.5.3 |
flutter_markdown_plus |
Render model responses | ^1.0.12 |
path_provider |
App directories | ^2.1.5 |
- No network calls unless you enable web search or download a model
- No accounts, no telemetry, no analytics
- Model weights stored in app-private directory
- Documents copied to app-private storage, indexed locally
- Conversations stored locally via Hive
- OCR runs entirely on-device (bundled ML Kit model)
- Download integrity verified locally (size + SHA-256) before a model is trusted
| Issue | Cause | Fix |
|---|---|---|
| "Model not found" | GGUF not in expected location | Settings → Model → pick file |
| "Could not load model" | Insufficient RAM / wrong arch | Use smaller quant (Q3/Q4), close apps |
| Download fails / "didn't finish completely" | Flaky network | App auto-retries; tap Download again |
| Web search returns 0 results | Network / API changes | Check connection, retry |
| Document shows "no readable text" | Scanned PDF / encrypted | OCR the PDF first, or use text-based PDF |
| OCR fails | Image too blurry / no text | Retake photo, ensure good lighting |
| Theme not applying | System theme override | Settings → Theme → explicit Light/Dark |
Run with verbose logging:
flutter run --verbose 2>&1 | grep -E "\[KnowledgeService\]|\[WebSearch\]|\[DocumentService\]"Key log prefixes (debug builds only — stripped in release):
[KnowledgeService]— RAG indexing, retrieval, context building[WebSearch]— PubMed/Wiki/DDG queries, status codes, result counts[DocumentService]— Import, extraction, reindex progress[ChatController]— Search intercept, generation flow
- In-app model download with integrity verification + resume
- Faster responses mode (lower latency on-device)
- Cross-platform: iOS, Desktop (Windows/macOS/Linux)
- Model quantization UI: Download/convert models in-app
- Citation export: Copy conversation with references
- Voice input: STT for hands-free queries
- Anki export: Generate flashcards from answers
- Multi-modal: Image understanding (when local VLMs mature)
- Plugin system: Custom knowledge packs
- Fork → create feature branch
- Follow existing code style (run
flutter analyze) - Add tests for new functionality
- Update README if user-facing changes
- Open PR with clear description
flutter analyze— must passdart format .— before commit- Conventional commits (
feat:,fix:,docs:,refactor:)
MIT License — see LICENSE for details.
Medical Disclaimer: AcornMed is an educational tool. It is not a substitute for professional medical advice, diagnosis, or treatment. Always consult qualified clinicians for patient care decisions.
- llama.cpp by Georgi Gerganov — making local LLMs practical
- Google ML Kit — on-device OCR without cloud
- Syncfusion — PDF text extraction (free community license)
- NCBI / Wikipedia / DuckDuckGo — free public APIs
- Flutter team — excellent framework
- Medical students who tested early builds and gave feedback
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Made with ❤️ for medical students everywhere.
Stay curious. Stay offline. Stay accurate.







