From 70ed848b9d0b131a0a61e4a649c00d44ec2686c3 Mon Sep 17 00:00:00 2001 From: Ragnor Comerford Date: Mon, 15 Jun 2026 21:09:35 +0200 Subject: [PATCH] docs(embeddings): @embed model arg + same-space validation (RFC-012 Phase 3-4) Document the optional @embed model kwarg, the query-time same-space rejection, model-string strictness, and the loud schema-apply refusal on model change. Mark RFC-012 phases 1-4 implemented. --- docs/dev/rfc-012-embedding-provider-config.md | 4 ++++ docs/user/schema/index.md | 2 +- docs/user/search/embeddings.md | 14 ++++++++++++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/docs/dev/rfc-012-embedding-provider-config.md b/docs/dev/rfc-012-embedding-provider-config.md index 3c1da8c..de523c7 100644 --- a/docs/dev/rfc-012-embedding-provider-config.md +++ b/docs/dev/rfc-012-embedding-provider-config.md @@ -217,6 +217,10 @@ the design constraint; deferred to its own RFC/phase. | **5 — Cluster provider wiring** | un-reserve `providers.embedding`; `${NAME}` resolution | provider profile resolved from `cluster.yaml`; legacy `omnigraph.yaml` untouched | | later | ingest-time `@embed` (Shape C) | separate RFC | +**Status:** Phases 1–4 are implemented (`@embed("…", model="…")` is recorded in the schema IR and validated at +query time with a typed same-space error; an unrecorded `@embed` keeps working with no check). Phase 5 (cluster +`providers.embedding` wiring) and ingest-time `@embed` remain. + ## Invariants & deny-list check - **Invariant 9 (integrity failures are loud):** strengthened — query-time identity mismatch becomes a typed diff --git a/docs/user/schema/index.md b/docs/user/schema/index.md index 526f25f..105281c 100644 --- a/docs/user/schema/index.md +++ b/docs/user/schema/index.md @@ -45,7 +45,7 @@ Edge bodies only allow `@unique` and `@index`. - `@` or `@()` on any declaration or property. - Known annotations: - - `@embed("source_property")` on a Vector property — records which String property is the embedding source for query-time `nearest($v, "string")` auto-embedding. It is a catalog annotation; it does **not** populate the vector at ingest (supply vectors in load data, or pre-fill via the offline `omnigraph embed` pipeline). + - `@embed("source_property")` on a Vector property — records which String property is the embedding source for query-time `nearest($v, "string")` auto-embedding. It is a catalog annotation; it does **not** populate the vector at ingest (supply vectors in load data, or pre-fill via the offline `omnigraph embed` pipeline). An optional `model="…"` kwarg (`@embed("source_property", model="openai/text-embedding-3-large")`) records the embedding model so a `nearest()` query whose embedder uses a different model is rejected loudly; `model` is the only supported kwarg. See [search/embeddings.md](../search/embeddings.md). - `@description("…")`, `@instruction("…")` on query declarations (carried through to clients). - Custom annotations are accepted by the parser and surfaced in catalog metadata; unrecognized annotations don't fail compilation. diff --git a/docs/user/search/embeddings.md b/docs/user/search/embeddings.md index c31e25f..cd65587 100644 --- a/docs/user/search/embeddings.md +++ b/docs/user/search/embeddings.md @@ -45,10 +45,20 @@ The default zero-config path is OpenRouter: set `OPENROUTER_API_KEY` and run. Re ## `@embed` schema annotation -Mark a Vector property with `@embed("source_text_property")`. Today this is a **catalog annotation** consumed -by the query typechecker and linter: it records which String property is the embedding source and lets +Mark a Vector property with `@embed("source_text_property")`. This is a **catalog annotation** consumed by the +query typechecker and linter: it records which String property is the embedding source and lets `nearest($v, "string")` auto-embed a query string for comparison against that vector column. +Optionally record the model that produced the stored vectors: +`@embed("source_text_property", model="openai/text-embedding-3-large")`. When a model is recorded, a +`nearest($v, "string")` query is **rejected with a typed error** unless the resolved query embedder uses the +same model — so stored and query vectors are guaranteed same-space instead of silently ranking across spaces. +To fix a mismatch, set `OMNIGRAPH_EMBED_MODEL` (and the matching provider) to the recorded model, or re-embed. +The recorded model is the literal string, so `openai/text-embedding-3-large` (via OpenRouter) and +`text-embedding-3-large` (OpenAI direct) are distinct identities; use the matching string. Changing a recorded +model is a loud `schema apply` refusal (treat it as a re-embed migration). `@embed` without a model keeps +working with no validation. `model` is the only supported `@embed` argument; any other is a parse error. + **It does not embed at ingest.** Stored vectors are supplied directly in your load data, or pre-filled by the offline `omnigraph embed` pipeline below. (Ingest-time execution of `@embed` is a planned enhancement.)