Skip to content

feat(semantic): support real transformer embedding models via transformers.js (ONNX) - #329

Open
limpidwei wants to merge 1 commit into
shaqmughal:mainfrom
limpidwei:feat/transformer-embedder
Open

limpidwei wants to merge 1 commit into
shaqmughal:mainfrom
limpidwei:feat/transformer-embedder

Conversation

@limpidwei

Copy link
Copy Markdown

Problem

Model2Vec static embeddings are context-free: token ids are gathered from a fixed matrix and mean-pooled. For CJK vaults this degrades to near bag-of-words — Chinese semantics are highly compositional, and the 256-dim PCA static models cannot separate concepts.

Measured on a real 1,244-note Chinese personal vault (probe query 中间人撮合模式 触达差 信任差):

The static multilingual model from #326 is fine as a lightweight default, but CJK users need a contextualized model — and the loader is hardwired to the Model2Vec format, so there is currently no way to use one.

What this PR does

Adds an async transformer runtime next to the untouched sync Model2Vec path:

  • Format detection (looksLikeTransformerModel): a model dir containing onnx/ + config.json (HuggingFace layout, e.g. a local clone of Xenova/bge-small-zh-v1.5) loads via @huggingface/transformers; anything else stays Model2Vec → fully backward compatible.
  • AsyncEmbedder seam (semantic/transformer-embedder.ts): mirrors the sync Embedder interface with embed()/embedBatch() returning Promises. Mean pooling + L2 normalize, dtype: q8, allowRemoteModels = false — the zero-network guarantee and out-of-band fetch contract are preserved exactly.
  • Semantic supports both runtimes: batched per-note embedding on the build path (one ONNX session per note instead of per chunk); a warmup embed at load pins dim before SemanticStore construction.
  • searchAsync dispatched for the search tool: lexical and Model2Vec paths stay synchronous and byte-identical; hybrid routing logic is unchanged (only the query embedding awaits); MaxSim rerank passes through for runtimes without token vectors (by design, documented).
  • README + changeset included; new tests cover format detection, the type guard, and the async search path (620 passed / 1 skipped locally).

Live verification (same vault, after swap)

Same probe query, Xenova/bge-small-zh-v1.5 (22.9 MB quantized ONNX, CPU-only):

Notes for review

  • @huggingface/transformers is added as a regular dependency but loaded via dynamic import() — Model2Vec-only installs never pay the ONNX Runtime load cost.
  • embedQuery() (sync) now throws a descriptive error when the runtime is async; the dispatched path always uses embedQueryAsync.
  • Follow-up idea (separate PR, not included): frontmatter-aware result filtering (e.g. default-excluding status: superseded notes) — happy to discuss shape first.

🤖 Generated with Claude Code

…rmers.js

Model2Vec static embeddings are context-free; for CJK vaults their
concept-level discrimination is near bag-of-words (measured: semantic
top-6 all irrelevant for a real Chinese concept query, while lexical
ranked the correct note first). This adds an async transformer runtime
alongside the untouched sync Model2Vec path:

- Format detection: a model dir with onnx/ + config.json loads through
  @huggingface/transformers (mean pooling, L2-normalize, dtype q8);
  anything else stays Model2Vec (fully backward compatible).
- New AsyncEmbedder seam; Semantic supports both runtimes (batched
  per-note embedding on the build path, warmup pins dim at boot).
- searchAsync dispatched for the search tool; lexical + Model2Vec stay
  synchronous; MaxSim rerank passes through without token vectors.
- Zero-network guarantee preserved: allowRemoteModels=false, models are
  fetched out-of-band like `seekstone fetch-model`.

Verified live against a 1,244-note Chinese vault
(Xenova/bge-small-zh-v1.5): the same probe query now ranks the correct
note shaqmughal#1 (0.613) and its summary page shaqmughal#2, top-6 all relevant; index
build 1,916 chunks in 166s (cached afterwards), query ~140ms.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@codacy-production

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 2 high

Alerts:
⚠ 2 issues (≤ 0 issues of at least minor severity)

Results:
2 new issues

Category Results
Security 2 high

View in Codacy

🟢 Metrics 66 complexity

Metric Results
Complexity 66

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

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