feat(embedding): opt-in on-device neural embedder (MediaPipe USE) - #328
Merged
Conversation
SMK1705
commented
Jul 28, 2026
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
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.