ADR-0004 — Embedding dimension and embedding endpoint are configuration, never hardcoded¶
- Status: Accepted
- Date: 2026-06-28
- Sprint: v0.1
- Decider: solution-architect
- Invariants: I9 (reuse running bge-m3; never reinvent), I8 (reproducible)
- Resolves: AQ-1
- Related: ADR-0002 (datastore split)
Context¶
The Qdrant synapse_pages collection must be created with a fixed vector dimension. The
backlog (AC-QD-1) says "bge-m3 = 1024 dims — verify against running instance", and the
functional-analyst flagged AQ-1 because bge-m3 has variants and the running TrueNAS service
must be the authority, not a literature value. A wrong hardcoded dimension makes every
upsert fail at runtime. Equally, I9 forbids loading a local model: embeddings must come from
the already-running service over HTTP.
Two values are involved and both must be externalised:
- the embedding endpoint (
EMBEDDING_URL) — already mandated by AC-QD-4 / AC-WATCH-6, - the vector dimension used to create the Qdrant collection.
Decision¶
EMBEDDING_DIMis a required environment variable. The Qdrant collection is created withsize = int(os.environ["EMBEDDING_DIM"]). No integer literal for the dimension appears anywhere in application code. Default documented in.env.exampleanddocker-compose.ymlas 1024 (the expected bge-m3 value), but the value the service uses is always read from config.- The configured dimension is validated against the live service at startup. On
startup the service requests one embedding from
EMBEDDING_URL(or reads the model's reported dimension) and assertslen(vector) == EMBEDDING_DIM. On mismatch the service fails fast with a clear error rather than creating a malformed collection. This makes the running bge-m3 instance the authority and turns AQ-1 from a guess into a checked invariant. - Collection-exists guard. If
synapse_pagesalready exists with a different dimension, the service does not silently recreate it; it logs an error and refuses to start, so a dimension change is a deliberate, human-acknowledged migration. EMBEDDING_URLis the only path to bge-m3 (I9). No embedding model is imported or spawned in-process; verified by AC-WATCH-6 / AC-QD-4.
Consequences¶
- (+) Resolves AQ-1 without hardcoding: the real running dimension wins, and a misconfig is caught at boot, not at first ingest.
- (+) I9 satisfied: embeddings are always remote; the dimension is a config contract, not a literal.
- (+) Reproducible (I8):
.env.example+ compose document the default; CI sets the var explicitly. - (−) Adds one startup HTTP round-trip to bge-m3. Negligible, and it doubles as a liveness probe for the embedding dependency.
- (−) If
EMBEDDING_DIMis unset the service refuses to start (required var, no silent fallback). Intentional — a silent wrong default is worse than a loud missing one.