You remember the moment. VagueMemory remembers the evidence.
An Android app that searches your gallery by what you remember, not by filename. Type "find that screenshot where my friend sent me Python code" and it finds it.
This zip contains the Flutter source only. The android/ folder is generated
on your machine so it matches your exact Flutter version.
cd find_it
flutter create . # generates android/ (keeps all existing files)
dart run tool/patch_android.dart # adds permissions, sets minSdk 24
flutter pub get
flutter run # phone plugged in, USB debugging onFull step-by-step guide: see SETUP.md.
USER MEMORY QUERY
↓
AI QUERY INTERPRETER (OpenRouter, structured JSON)
↓
STRUCTURED SEARCH INTENT (SearchQuery)
↓
LOCAL SEARCH ENGINE (OCR + metadata + deterministic scoring)
↓
TOP 3–5 CANDIDATES
↓
AI VISION RERANKING (only the candidates, never the gallery)
↓
FINAL RESULTS + EXPLANATION
The differentiator: the LLM never touches the whole gallery. It converts a vague memory into a search plan. The phone does retrieval locally. Only the strongest 4 candidates are ever sent to a vision model.
| File | Role |
|---|---|
lib/models.dart |
IndexedImage, SearchQuery, SearchResult, AiRankingResult |
lib/store.dart |
AIConfig, persistence, AI event log |
lib/indexing.dart |
MediaStore access, ML Kit OCR, screenshot heuristics, demo data |
lib/local_search.dart |
Local query parser + deterministic scoring engine |
lib/ai.dart |
Prompt templates, OpenRouter client, interpreter, vision reranker, Memory Lens, pipeline |
lib/screens.dart |
Onboarding, Home, Indexing, Results, Detail, AI Console, Settings |
lib/main.dart |
Entry point + Material 3 dark theme |
Every AI path has a real local equivalent. If OpenRouter times out, returns
invalid JSON, is rate-limited, or there is no network, the app logs the failure,
shows AI unavailable — using local search instead, and keeps working.
Local scoring:
| Signal | Points |
|---|---|
| Exact OCR keyword | +5 |
| Partial OCR keyword | +3 |
| Exact phrase | +5 |
| Filename match | +2 |
| Screenshot match | +3 |
| Date match | +3 |
| Time match | +3 |
| Concept / synonym | +2 |
Scores are normalised to 0–1 and sorted descending.
- Get a key at https://openrouter.ai/keys.
- Open the app → Settings → paste the key → Save → Test connection. The key is stored on-device; you can use your own OpenRouter key (or any OpenAI-compatible endpoint + key) and it is never shared.
Or bake it in at build time:
flutter build apk --release --dart-define=OPENROUTER_API_KEY=sk-or-v1-xxxxDefault model: google/gemma-4-31b-it:free (free tier, vision-capable).
Model IDs on OpenRouter change often — if you get an error, pick a current
vision model from https://openrouter.ai/models and paste the ID into Settings.
Free models are rate-limited (roughly 20 requests/minute).
Never commit a real key. .env is gitignored; .env.example is the template.
All prompts live in lib/ai.dart → PromptTemplates. Nothing is scattered
through the UI. The interpreter prompt establishes role, goal, hard rules
(never invent, use null when unknown, preserve explicit terms, separate literal
OCR terms from semantic concepts), and a strict JSON schema. The model is asked
to produce a search plan, never an answer. Every field is validated before it
becomes a Dart object; any parse failure drops to the local parser.
flutter analyze
flutter test
flutter build apk --debug
flutter build apk --releaseRelease output: build/app/outputs/flutter-apk/app-release.apk
Install on a physical device:
flutter devices
flutter install
# or
adb install -r build/app/outputs/flutter-apk/app-release.apkCompetition device: iQOO Android phone. No emulator-only APIs are used.
- Open VagueMemory, show the home screen and the AI status badge.
- Type: Find that screenshot where my friend sent me Python code.
- Watch the live pipeline: understanding → searching → candidates → vision.
- Show the hero result and the
% MEMORY MATCHbadge. - Open Why this matched.
- Tap Ask AI about this → Summarize this image.
- Open the AI Console and explain:
"The LLM doesn't search my gallery. It converts my vague memory into a structured search plan. The phone performs local retrieval, then vision AI verifies only the strongest candidates."
- Turn off AI mode in Settings (or airplane mode), search again — results still work, with the local-fallback notice.
Tap Demo mode on Home to load five bundled scenarios (Python screenshot,
10 AM timetable, restaurant menu, linked list notes, OS notes). Drop matching
images into assets/demo/ — see assets/demo/README.txt. Real device indexing
remains the primary flow.
Your image index stays on your device. VagueMemory only sends selected information to the AI service when AI features are enabled: your typed memory, and up to 4 candidate thumbnails with their extracted text.
- OCR is Latin-script only.
- Indexing the full gallery can take a few minutes on large libraries. You can cap it in Settings (full gallery, 300, or 1000 images) — it defaults to all.
- Vision reranking depends on the chosen model actually supporting images.
- Free OpenRouter models are rate-limited; a fresh search may fall back to local.
- No splash screen or transition animations — reliability was prioritised.