Skip to content

Embeddings

Every fact is embedded into a vector for semantic search, clustering, dedup, and discovery. Embeddings run locally and in-process (ONNX Runtime via onnxruntime_go + daulet/tokenizers) — there is no external embedding API.

PropertyValue
Default idembeddinggemma (KNOMIT_EMBED_MODEL / [embeddings] model)
Dimensions768
Max tokens2048
ONNX inputs / outputsinput_ids, attention_masksentence_embedding
Poolingnone — the export emits an already pooled + normalized sentence_embedding
Query templatetask: search result | query: {content}
Doc templatetitle: {title} | text: {content}

Source (Hugging Face onnx-community/embeddinggemma-300m-ONNX): model_fp16.onnx (+ .onnx_data weights) and tokenizer.json. A legacy nomic-v1.5 model remains in the registry for historical comparison.

Model files are cached under KNOMIT_HOME/models/<id>/ (e.g. ~/.knomit/models/embeddinggemma/). Pre-download without booting the server:

Terminal window
knomit warm-models # configured model
knomit warm-models --model embeddinggemma

warm-models only fetches files — it neither boots the app nor initialises ONNX Runtime, so it runs in a build stage without the ORT shared library present.

The cache is skip-if-present: a file that lands corrupt is never re-fetched, and surfaces as a cryptic ONNX failure on every subsequent boot. So each download is verified before it is renamed into place, and written to a temp file in the destination directory and Sync()ed first, so the rename is atomic and the file carries no unwritten tail blocks.

CheckApplies when
SHA-256 against a pinned digest (ModelSHA256 / DataSHA256 / TokenizerSHA256)The registry pins one for that file
Byte count against the response’s Content-LengthThe server declares one — catches a 200 whose body is cut short, which io.Copy reports as success

A response with neither is accepted; there is nothing left to compare against. The registry entries ship unpinned, because their URLs point at mutable Hugging Face main refs that a pin would break on every upstream republish.

Embeddings are mandatory, so the model download runs before the server listens. A connection that accepts but never delivers data therefore used to hang boot indefinitely. A watchdog samples a progress counter every 5s and aborts the transfer after 90 seconds with no bytes received.

BoundValue
Dial30s
TLS handshake30s
Response headers60s
No-progress stall90s
Whole requestnone — by design

There is deliberately no whole-request timeout: the model is hundreds of megabytes, and a legitimate download over a slow link must be allowed to run long. The stall watchdog is what a whole-request timeout is usually reaching for — it distinguishes “slow” from “dead”.

EnsureModel and NewEmbedder take a context.Context that bounds these downloads; cancelling it aborts a fetch in progress, and knomit warm-models passes the command context. EmbedQuery and EmbedDocument also take a context, but only as a pre-flight checkpoint — the ONNX Run call cannot be interrupted, so cancelling after that check does not stop an inference already underway.

The ONNX Runtime shared library is located at runtime via ONNXRUNTIME_SHARED_LIBRARY (or onnx_lib_path); it is fetched into dist/<platform>/lib/ at build time by fetchlibs.

Cosine-similarity distributions differ sharply between models — EmbeddingGemma runs much cooler than nomic. So all six retrieval thresholds are per-model fields on the model descriptor, not global constants:

ThresholdEmbeddingGemmaUsed for
Dedup0.82near-duplicate detection in review prune
ReflectNovelty0.69reject near-duplicate methodologies (KNOMIT_REFLECT_NOVELTY_THRESHOLD overrides)
SimilarTo0.18”related facts”
SearchFloor0.05recall floor for min_similarity=0
RerankHigh0.43rerank band (high)
RerankLow0.10rerank band (low)

These are derived empirically by the build-only calibrate tool, which measures a model’s geometry against a real corpus. When swapping embedding models, re-calibrate — do not reuse another model’s thresholds.