Skip to content

feat(embedding): opt-in on-device neural embedder (MediaPipe USE) - #328

Merged
SMK1705 merged 1 commit into
mainfrom
feat/mediapipe-neural-embedder-impl
Jul 28, 2026
Merged

feat(embedding): opt-in on-device neural embedder (MediaPipe USE)#328
SMK1705 merged 1 commit into
mainfrom
feat/mediapipe-neural-embedder-impl

Conversation

@SMK1705

@SMK1705 SMK1705 commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Phase 2, on the versioned vector space from #327. Adds a real neural embedder behind
the Embedder seam so semantic search/dedup can match on MEANING, not just words —
"plumber" finds a "kitchen tap" note with zero shared tokens, which HashingEmbedder
structurally can't.

- MediaPipeEmbedder: Universal Sentence Encoder via MediaPipe tasks-text (id use-v1,
  100-dim, L2-normalised so cosine is a dot product). Lazy-loads the downloaded ~6 MB
  model from a direct ByteBuffer; a missing/broken model makes embed() a no-op (null),
  which every caller already treats as "skip", so it degrades to lexical rather than
  crashing.
- RoutingEmbedder (now the bound Embedder): neural when the user enabled it AND the
  model is present, else the always-available lexical embedder. id/dimension/searchFloor
  and embed() all resolve to the SAME delegate, and there is deliberately NO cross-space
  fallback in embed() — a hashing vector tagged with the neural id would poison the very
  invariant #327 added, so when neural isn't ready embed() returns null and backfill
  re-indexes once it loads. Search keeps working meanwhile via the lexical substring
  match. refresh() re-resolves after a toggle or a completed download.
- Settings: a "Semantic Search" section — download (~6 MB, egress-logged via
  ModelDownloader), an opt-in toggle, remove, and an honest offline explanation. Toggling
  or downloading calls refresh() + backfill() so notes re-index into the active space.
- SettingsManager.useNeuralEmbedder (default off) + the search floor now travels per
  embedder (neural 0.55 vs lexical 0.35).

Off by default and self-healing: nothing changes until the user opts in, and if the
neural path ever fails, routing falls back to exactly today's lexical behaviour.

Tests: RoutingEmbedderTest covers default-lexical, enabled-but-absent → lexical,
enabled+present → neural with its own id/dim/floor, refresh-after-toggle, and the
no-cross-space-fallback invariant (neural-active-but-not-ready returns null, never a
mislabelled hashing vector). Full suite green.

Not yet done (device-gated, tracked on #316): the on-device run proving the model
downloads + loads + embeds on hardware, and the recall benchmark vs the lexical baseline
that would gate ever turning it on by default. Cost noted: tasks-text adds ~31 MB of
native runtime to the APK.
@SMK1705
SMK1705 merged commit d7f0f78 into main Jul 28, 2026
1 check passed
@SMK1705
SMK1705 deleted the feat/mediapipe-neural-embedder-impl branch July 28, 2026 04:54
SMK1705 added a commit that referenced this pull request Jul 28, 2026
…arger APK

CI's packageDebug hit java.lang.OutOfMemoryError at the 2 GB daemon heap now that the
APK is ~199 MB — the MediaPipe tasks-text native runtime added in #328 pushed packaging
over the edge (#328 passed narrowly; the next build tipped it). GitHub runners have ~7 GB,
so 4 GB leaves comfortable headroom.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant