feat: integrate Google semantic search and branded desktop launcher
Core CI / core (ubuntu-latest) (push) Failing after 3m27s
Core CI / core (macos-latest) (push) Canceled after 0s
Core CI / core (windows-latest) (push) Canceled after 0s
Core CI / desktop (ubuntu-latest) (push) Successful in 5m45s
Core CI / desktop (macos-latest) (push) Canceled after 0s
Core CI / desktop (windows-latest) (push) Canceled after 0s
Core CI / core (ubuntu-latest) (push) Failing after 3m27s
Core CI / core (macos-latest) (push) Canceled after 0s
Core CI / core (windows-latest) (push) Canceled after 0s
Core CI / desktop (ubuntu-latest) (push) Successful in 5m45s
Core CI / desktop (macos-latest) (push) Canceled after 0s
Core CI / desktop (windows-latest) (push) Canceled after 0s
This commit is contained in:
1 parent
a90df15503
commit
caf6a2f82e
80 files changed
+14971
-837
No files matched your search
+158
-134
@@ -1,155 +1,179 @@
|
||||
# CPU semantic inference proof
|
||||
# Local semantic search
|
||||
|
||||
This milestone runs genuine neural embeddings locally. It does not yet connect
|
||||
semantic vectors to the SQLite search index or desktop interface. Production
|
||||
retrieval quality, large-corpus performance, and Linux/macOS runtime packaging
|
||||
remain unverified.
|
||||
Searchi uses **Google EmbeddingGemma 2**, with the pinned Q8 GGUF text encoder
|
||||
and llama.cpp revision used by DigUp. Production has no MiniLM, simulated-vector,
|
||||
or remote embedding fallback. The rejected ONNX experiment is retained only
|
||||
under the opt-in minilm-proof feature and tools/semantic-proof/HISTORICAL.md.
|
||||
|
||||
## Model and runtime decision
|
||||
## Model and runtime
|
||||
|
||||
The first working baseline is the quantized ONNX export of
|
||||
`sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2`, published by
|
||||
`Xenova`. The upstream model card describes 384-dimensional text embeddings,
|
||||
attention-mask mean pooling, a 128-token maximum, and Apache-2.0 licensing.
|
||||
It covers multiple languages. This is a text-only baseline; it does not claim
|
||||
EmbeddingGemma 2's multimodal features or compatibility.
|
||||
- Model: google/embeddinggemma-2, **768-dimensional** output.
|
||||
- GGUF publisher: ggml-org/embeddinggemma-2-GGUF.
|
||||
- Revision: bfcd298762cc34d0357ece5ebdd31791a3a374d8.
|
||||
- llama.cpp: b11461, commit 4d756bc72bf00a4aacf410ae15a2d315f3db400d,
|
||||
with DigUp's no-logits memory patch.
|
||||
- Query prompt: task: search result | query: TEXT.
|
||||
- Document prompt: title: TITLE_OR_none | text: TEXT.
|
||||
- Noncausal encoder, mean pooling, finite L2-normalized vectors.
|
||||
- Query/document token budgets: 512/2048.
|
||||
|
||||
Sources reviewed on 2026-10-12:
|
||||
The persistent native helper uses bounded NDJSON over stdin/stdout. It has no
|
||||
HTTP server or download code. Rust verifies the text model's length and SHA256
|
||||
before starting it. Setup provisions assets; search never downloads them.
|
||||
|
||||
- [Original model card and pooling architecture](https://huggingface.co/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2)
|
||||
- [ONNX export publisher](https://huggingface.co/Xenova/paraphrase-multilingual-MiniLM-L12-v2)
|
||||
- [ONNX Runtime Python session API](https://onnxruntime.ai/docs/api/python/api_summary.html)
|
||||
- [Rust ort dynamic-library initialization](https://docs.rs/ort/2.0.0-rc.13/ort/environment/fn.init_from.html)
|
||||
- [Rust ort session configuration](https://docs.rs/ort/2.0.0-rc.13/ort/session/builder/struct.SessionBuilder.html)
|
||||
Production device mode is auto, with available compiled acceleration and CPU
|
||||
fallback. Model/context initialization and returned inference/nonfinite-output
|
||||
failures retry on CPU once. A lost GPU worker can be restarted as CPU for one
|
||||
retry of that request. WorkerInfo and responses report the actual device.
|
||||
**Windows VM tests remain CPU only**, via SEARCHI_FORCE_CPU=1 and Device::Cpu.
|
||||
GPU execution/failure recovery has not been exercised here.
|
||||
|
||||
Inference explicitly selects `CPUExecutionProvider`. Python uses the CPU
|
||||
`onnxruntime` wheel, never `onnxruntime-gpu`. The native Rust crate disables ort
|
||||
default features: there are no binary downloads, GPU provider features, or model
|
||||
HTTP client features. It loads an explicitly supplied local runtime library and
|
||||
disables runtime telemetry. Tokenization is sequential; ONNX sessions use
|
||||
sequential graph execution with bounded intra-op threads and one inter-op thread.
|
||||
|
||||
## Reproduce on Windows
|
||||
|
||||
Run these commands from the repository root. Provisioning commands intentionally
|
||||
use the network; the inference command does not provision anything.
|
||||
|
||||
```powershell
|
||||
python -m venv tools/semantic-proof/.venv
|
||||
& tools/semantic-proof/.venv/Scripts/python.exe -m pip install --only-binary=:all: -r tools/semantic-proof/requirements.txt
|
||||
& tools/semantic-proof/.venv/Scripts/python.exe tools/semantic-proof/download.py
|
||||
& tools/semantic-proof/.venv/Scripts/python.exe tools/semantic-proof/proof.py --threads 1 --output tools/semantic-proof/windows-cpu-result.json
|
||||
```
|
||||
|
||||
On Linux/macOS, use `.venv/bin/python` instead of `.venv/Scripts/python.exe`.
|
||||
These instructions have only been executed on Windows. Wheel availability and
|
||||
native library distribution must be checked on other platforms.
|
||||
|
||||
`download.py` downloads exactly two revision-pinned artifacts, streams them to a
|
||||
temporary file, rejects oversized downloads, checks byte length and SHA256, then
|
||||
atomically replaces the local asset. Assets occupy approximately 136 MB in
|
||||
decimal units and are ignored by Git. Inference validates both files again and
|
||||
fails clearly when either is missing or damaged. It never contacts the model
|
||||
hub. The Python proof sets offline environment flags and rejects Python socket
|
||||
connect, name-resolution, and bind audit events throughout import and inference.
|
||||
This guard checks Python socket activity; it is not an operating-system firewall
|
||||
against arbitrary native libraries.
|
||||
|
||||
The proof intentionally exits nonzero if any fixture query misses top-1. Its
|
||||
JSON report is written before the quality gate, preserving failures for review.
|
||||
|
||||
## Pinned artifacts and licensing
|
||||
|
||||
Revision: `2c4055b12046f11709e9df2c122e59ffbdc2f900`.
|
||||
The manifest in `tools/semantic-proof/model-manifest.json` records the same
|
||||
model contract and hashes used by the native adapter.
|
||||
The Windows worker links a static MSVC CRT and static llama libraries, without
|
||||
an external OpenMP runtime. Production CMake supports cpu, auto, metal, vulkan,
|
||||
and cuda; CPU builds force GPU flags off. Runtime/provider parity and packaging
|
||||
on other operating systems remain independent release checks.
|
||||
|
||||
| Artifact | Bytes | SHA256 |
|
||||
| --- | ---: | --- |
|
||||
| `onnx/model_quantized.onnx` | 118,308,126 | `66fc00f5f29afcaff34092e1bdd20008ca3918265a82fb9695a551e510cc4ebc` |
|
||||
| `tokenizer.json` | 17,082,913 | `b60b6b43406a48bf3638526314f3d232d97058bc93472ff2de930d43686fa441` |
|
||||
| embeddinggemma-2-Q8_0.gguf | 309,855,456 | 2188ac1deca4b77dffefd603c2776a9d76d9d74ec01841392982ebb840b09135 |
|
||||
| mmproj-embeddinggemma-2-Q8_0.gguf | 554,821,024 | c4a8a52691ecef40618438928bdf9e68379b854e24166f292592353db0aab64f |
|
||||
|
||||
The base model card specifies Apache-2.0. The export card identifies that base
|
||||
model but does not display its own license declaration. Before redistributing
|
||||
the ONNX export in installers, confirm export licensing and preserve the base
|
||||
license, attribution, and applicable notices. Current code explicitly downloads
|
||||
user-local assets and does not ship model weights or runtime DLLs in Git.
|
||||
ONNX Runtime uses MIT licensing; ort uses MIT or Apache-2.0 and tokenizers uses
|
||||
Apache-2.0. Release packaging must retain third-party notices and validate
|
||||
platform-specific runtime hashes independently; a model hash does not attest
|
||||
to the runtime library.
|
||||
Setup downloads both pinned files. **The current worker implements text**;
|
||||
image/audio projection loading and image/audio/video embeddings remain required
|
||||
work. Downloaded media weights do not establish functioning multimodal search.
|
||||
|
||||
The tested Windows ONNX Runtime 1.31.0 DLL is 19,288,888 bytes with SHA256
|
||||
`90497e3d406c09f4d59d364c696a2f93dc02f6ef13741b3b89eec95bdb4a3806`.
|
||||
The adapter currently trusts the explicitly supplied runtime path; it validates
|
||||
model hashes, not runtime-library hashes. A release distribution must add a
|
||||
platform runtime manifest before shipping. `requirements-lock-windows.txt`
|
||||
captures the exact Python environment used for this proof.
|
||||
Weights use Apache-2.0; llama.cpp and DigUp use MIT; USearch and NumKong use
|
||||
Apache-2.0. Retain notices with distributions. Five DigUp text reference vectors
|
||||
are copied under MIT in crates/searchi-semantic/tests/fixtures with the upstream
|
||||
license and source commit alongside them.
|
||||
|
||||
## Measured Windows VM result
|
||||
Primary sources:
|
||||
|
||||
`tools/semantic-proof/windows-cpu-result.json` contains the full measurement and
|
||||
rankings. The run used Windows 11 AMD64, Python 3.14.8, ONNX Runtime 1.31.0,
|
||||
four visible logical CPUs, and **one inference thread with only the CPU provider**.
|
||||
- [Google developer guide](https://developers.googleblog.com/embeddinggemma-2-the-developer-guide/)
|
||||
- [Model documentation](https://huggingface.co/docs/transformers/model_doc/embedding_gemma2)
|
||||
- [Pinned GGUF publisher](https://huggingface.co/ggml-org/embeddinggemma-2-GGUF/tree/bfcd298762cc34d0357ece5ebdd31791a3a374d8)
|
||||
- [Pinned DigUp reference](https://github.com/ARahim3/DigUp/tree/6faa29e4c315a3a591de1b41bd78751bdf39588d)
|
||||
- [USearch HNSW and filtered retrieval](https://github.com/unum-cloud/USearch)
|
||||
|
||||
## Shared API and bounds
|
||||
|
||||
GemmaEmbedder owns actual tokenization, document/query prompts, and inference.
|
||||
HybridSearch is the shared CLI/desktop service:
|
||||
|
||||
~~~rust
|
||||
HybridSearch::open(db, model_dir, worker_executable, threads)
|
||||
HybridSearch::embedding_status(db) // No model/runtime load or network
|
||||
service.warmup() // Real query inference + ANN cache
|
||||
service.index_embeddings()
|
||||
service.search(query, limit, &SearchOptions)
|
||||
service.search_semantic(query, limit, &SearchOptions) // Diagnostics
|
||||
service.worker_info() // Actual model/runtime/device/threads
|
||||
~~~
|
||||
|
||||
Threads are bounded to 1–8; VM tests use one. Document batches contain at most
|
||||
eight windows. Request/response budgets are 2/4 MiB, startup has a 120-second
|
||||
deadline, and each request has a 180-second deadline. Malformed or interrupted
|
||||
IPC and invalid embeddings fail explicitly.
|
||||
|
||||
The service makes 64 KiB UTF-8 slices with 512-byte overlap before genuine model
|
||||
token windows. It preserves global byte ranges and page/paragraph/line sources,
|
||||
handles 2 MiB passages, caps each passage at 2048 windows, and bounds titles to
|
||||
512 UTF-8 bytes. Generation fingerprints include this chunk policy as well as
|
||||
model/runtime/prompts/token budgets/normalization.
|
||||
|
||||
Keep a prewarmed query service and a separate background builder. Readiness
|
||||
requires actual inference, rather than merely finding verified model files.
|
||||
|
||||
## Persistence, ANN, and ranking
|
||||
|
||||
Semantic storage has an independent schema version within the shared SQLite
|
||||
database. Chunks store generation, passage/path identity, original-source hash,
|
||||
location, byte range, snippet, and vector. HNSW uses USearch 2.26.4, cosine/F32,
|
||||
16 connections, construction expansion 128, and search expansion 64. NumKong
|
||||
dispatches supported CPU SIMD kernels with portable fallback. The Cargo config
|
||||
contains the Windows NK_DYNAMIC override required for static NumKong linkage.
|
||||
|
||||
Staging vectors and a real HNSW graph are persisted with an ANN blob SHA256.
|
||||
Activation is atomic. Failed rebuilds preserve the active generation; the next
|
||||
rebuild reclaims abandoned staging under a cross-process lock. Search uses a
|
||||
consistent read snapshot.
|
||||
|
||||
Metadata filters and source hashes establish eligible keys before ANN top-k.
|
||||
The cache is keyed by generation, corpus revision, and normalized filters.
|
||||
Persistent dirty-path triggers invalidate affected files; unchanged warm
|
||||
queries hash zero passages. Large change batches or expired history rebuild
|
||||
eligibility. Default-filter prewarm includes that initial work.
|
||||
|
||||
Fusion uses reciprocal-rank fusion with k = 60. Exact filenames and literal
|
||||
identifiers retain priority. Ordinary partial filenames receive lexical
|
||||
weight 1.15 and can be outranked by keyword/semantic consensus. File results
|
||||
retain source snippets and deterministic path tie breaks.
|
||||
|
||||
Current caps are 100,000 chunks and a 512 MiB serialized ANN blob per generation.
|
||||
Full rebuilds still regenerate embeddings and the graph. Incremental reuse/jobs
|
||||
and efficient ANN updates remain production work. One million filename records
|
||||
does not imply one million semantic chunks under these defaults.
|
||||
|
||||
## Executed Windows CPU evidence
|
||||
|
||||
Actual Google CPU vectors match five pinned DigUp text references at cosine
|
||||
**0.99979–0.99994**, exceeding the 0.999 gate. These cover English, Bengali,
|
||||
Arabic, a short document, and a document beyond the sliding attention window.
|
||||
Actual tests also cover English/German retrieval, persisted vectors, changed/
|
||||
deleted-source suppression, verified long windows, and a tail **after 64 KiB**.
|
||||
|
||||
The immutable 20-document/24-query seed corpus produced:
|
||||
|
||||
| Lane | Top 1 | Recall at 3 |
|
||||
| --- | ---: | ---: |
|
||||
| Lexical | 5/24 | 5/24 |
|
||||
| Google semantic | 24/24 | 24/24 |
|
||||
| Hybrid HNSW | 24/24 | 24/24 |
|
||||
|
||||
With one CPU thread, fresh model/context startup took approximately **7 seconds
|
||||
in the background**, actual query/ANN prewarm **128 ms**, and first foreground
|
||||
natural-language search **99 ms** with the correct supplier document first.
|
||||
Warm seed-corpus hybrid p50/p95 was **92/107 ms**. Model downloads are excluded.
|
||||
|
||||
A separate release-profile benchmark used 100,000 synthetic normalized 768-dimensional
|
||||
vectors and 20 queries against exact nearest neighbors:
|
||||
|
||||
| Metric | Measurement |
|
||||
| --- | ---: |
|
||||
| Fixture documents / queries | 8 / 6 |
|
||||
| Timed warm query runs | 60 |
|
||||
| Artifact verification plus runtime/model load | approximately 3.4 seconds |
|
||||
| Corpus batch embedding | approximately 145 ms |
|
||||
| Warm query p50 / p95 | approximately 3.1 / 11.9 ms |
|
||||
| Top-1 accuracy | 5/6 (83.3%) |
|
||||
| Recall at 3 | 6/6 (100%) |
|
||||
| Recall at 10 vs exact | 98.5% |
|
||||
| HNSW query p95 | 1.37 ms |
|
||||
| Warm service retrieval p95 | 6.40 ms |
|
||||
| Warm passages rehashed | 0 |
|
||||
| Initial eligibility work | 1.61 seconds |
|
||||
| Graph construction/serialization | 57.95 seconds |
|
||||
| Reported graph memory | 554 MB |
|
||||
| Dispatched CPU kernel | skylake |
|
||||
|
||||
The corpus includes English and German documents, same-language semantic
|
||||
queries, and cross-language queries. A question about a postponed product
|
||||
release ranks the supplier contract above the expected meeting notes. That
|
||||
failure remains in the fixture and blocks the strict top-1 quality gate.
|
||||
This small demonstration supports the feasibility of CPU inference; it does
|
||||
not establish large-corpus latency, robust multilingual quality, or production
|
||||
readiness. Quantization and batch shape can affect similarity scores.
|
||||
This is **index-only performance**, not neural quality across 100,000 documents.
|
||||
The seed corpus and synthetic vectors do not certify production relevance.
|
||||
Representative corpora, cold/selective-filter latency, concurrent indexing,
|
||||
resource profiles, GPU parity/fallback, multimodal retrieval, and platform
|
||||
releases remain validation gates.
|
||||
|
||||
## Native integration contract
|
||||
## Reproduce
|
||||
|
||||
`searchi-semantic` exposes:
|
||||
Run from the repository root. Provisioning uses the network explicitly:
|
||||
|
||||
```rust
|
||||
CpuEmbedder::load(model_dir, runtime_library, threads) -> Result<CpuEmbedder>
|
||||
CpuEmbedder::encode(&mut self, texts: &[&str]) -> Result<Vec<Vec<f32>>>
|
||||
DIMENSIONS // 384
|
||||
MODEL_FINGERPRINT // revision + quantization + pooling + normalization + token cap
|
||||
```
|
||||
~~~powershell
|
||||
python tools/embeddinggemma2/download.py
|
||||
python tools/embeddinggemma2/build_runtime.py --backend cpu
|
||||
$env:SEARCHI_FORCE_CPU = '1'
|
||||
$env:SEARCHI_GEMMA_MODEL_DIR = (Resolve-Path .searchi/models/embeddinggemma2).Path
|
||||
$env:SEARCHI_GEMMA_WORKER = (Resolve-Path .searchi/runtime/cpu/Release/searchi-gemma-worker.exe).Path
|
||||
cargo test -p searchi-semantic --test gemma_cpu -- --ignored --nocapture
|
||||
cargo test -p searchi-service --test google_cpu -- --ignored --nocapture
|
||||
cargo test -p searchi-service --lib google_cpu_preserves -- --ignored --nocapture
|
||||
cargo run -p searchi-service --example evaluate -- .searchi/models/embeddinggemma2 .searchi/runtime/cpu/Release/searchi-gemma-worker.exe .searchi/google-relevance-ann.json
|
||||
$env:SEARCHI_ANN_REPORT = (Join-Path (Get-Location) '.searchi/ann-100k.json')
|
||||
cargo test --release -p searchi-service ann_100k -- --ignored --nocapture
|
||||
~~~
|
||||
|
||||
Inputs are bounded to eight texts per batch, 64 KiB UTF-8 per text, and 128
|
||||
tokens. Outputs are finite, normalized `f32` vectors. The runtime library is
|
||||
process-global; adapter initialization rejects attempts to switch libraries or
|
||||
silently accept an environment initialized outside the adapter. Supply a known,
|
||||
trusted runtime before calling any other ort APIs in the process.
|
||||
|
||||
Default unit tests need no model or runtime. The real-artifact test is opt-in:
|
||||
|
||||
```powershell
|
||||
$env:SEARCHI_MODEL_DIR = (Resolve-Path tools/semantic-proof/model).Path
|
||||
$env:SEARCHI_ORT_LIBRARY = (Resolve-Path tools/semantic-proof/.venv/Lib/site-packages/onnxruntime/capi/onnxruntime.dll).Path
|
||||
cargo test -p searchi-semantic --test cpu_model -- --ignored --nocapture
|
||||
```
|
||||
|
||||
The smoke test checks actual local inference, vector normalization, English and
|
||||
German retrieval, recall at three, and the measured five-of-six top-1 baseline.
|
||||
It does not replace the strict production quality gate.
|
||||
|
||||
The native real-artifact smoke was executed successfully on the Windows VM,
|
||||
using that CPU-only DLL. It reproduced five-of-six top-1 and six-of-six recall
|
||||
at three. Three default unit tests passed; targeted Clippy passed with warnings
|
||||
denied. Linux and macOS native checks have not been executed.
|
||||
|
||||
The next integration should read bounded `SearchIndex::passage_batch` rows,
|
||||
split long passages into overlapping tokenizer windows, preserve source page or
|
||||
paragraph locations, and persist each vector with a content fingerprint and
|
||||
`MODEL_FINGERPRINT`. Passage IDs are not immutable content identities: changes
|
||||
must invalidate stale vectors. Queries should combine lexical and semantic
|
||||
retrieval while preserving exact filename priority. Model upgrades build a
|
||||
separate vector generation before an atomic swap. Cancellation, persistent
|
||||
embedding jobs, ANN retrieval, representative quality corpora, and signed
|
||||
cross-platform runtime packaging are still required.
|
||||
The ignored ANN test deliberately requires release mode. Default tests require
|
||||
no model/network and cover protocol bounds, invalid vectors, UTF-8 coverage,
|
||||
fusion, lifecycle, checksums, pre-top-k filtering, and dirty-path caching.
|
||||
Linux/macOS and GPU checks are not inferred from this Windows CPU run.
|
||||
Reference in new issue
Block a user