IBEX Harness
DocsBenchmarksBlogChangelogRoadmap
GitHub
IBEX Harness

Documentation

Architecture Decision RecordsADR-0002: Repository foundation bootstrapADR-0003: Branch protection and merge policyADR-0004: Protobuf and code generation policyADR-0005: Postgres migration strategyADR-0006: Auth protobuf contract (`ibex.auth.v1`)ADR-0007: Auth token validation implementationADR-0008: Security scanning and CI quality gatesADR-0009: Permission bitmap layoutADR-0010: Cryptography policyADR-0011: Proxy auth gRPC client and middlewareADR-0012: Proxy request normalization (OpenAI chat)ADR-0013: Proxy input validation and stable error envelopeADR-0014: Core domain migration sequencingADR-0015: Proxy rate limit skeleton (Phase 1)ADR-0016: Proxy agent identity verification (Phase 1)ADR-0017: Request ID and trace context strategy (Phase 1)ADR-0018: Graceful shutdown contract (Phase 1)ADR-0019: OpenTelemetry provider configuration (Phase 1)ADR-0020: Shared package boundaries — `packages/config` and `packages/apierror`ADR-0021: Prometheus Metric Catalog (Phase 1)ADR-0022: Health check contract (Phase 1)ADR-0023: Docs site architecture (Phase 1.5)ADR-0024: Benchmark data publishing modelADR-0025: LLM provider abstractionADR-0026: OpenAI client designADR-0027: Streaming dual-write strategyADR-0028: Auth cache designADR-0029: Token revocation propagation via Redis pub/subADR-0030: Directive versioning strategyADR-0031: System prompt injection strategyADR-0032: Session data model and retentionADR-0033: ClickHouse llm_traces schema and retentionADR-0034: Proxy overhead performance measurement methodologyADR-0035: Chat Idempotency-Key Redis dedupeADR-0038: Context assembly service design and gRPC contractADR-0039: Proxy Postgres ownership for session and directive storesADR-0040: Anthropic provider adapterADR-0041: Model capability registryADR-0042: Self-hosted OpenAI-compatible LLM adapterADR-0043: Tokenizer registry architectureADR-0044: Non-streaming response pipelineADR-0045: Streaming response transformationADR-0046: Embedder interface and profile registryADR-0047: Memory temporal validity foundationADR-0048: Memory multi-label categoriesADR-0049: Memory relationship graph readinessADR-0050: MCP server skeleton (transport, auth, audit)ADR-0051: Local LGTM observability stack (Phase 2.5 exit pull-forward)ADR-0052: Memory schema v2 expand (HNSW, quality columns)ADR-0053: Vector store abstraction and composite scoring v2
ADRs›ADR-0046: Embedder interface and profile registry
ADRs

ADR-0046: Embedder interface and profile registry

Architecture decision record 0046 — deployment-time embedding profiles, Go/Python contracts, geometry validation, and stub-only M1 boundary.

ADR-0046: Embedder interface and profile registry

  • Status: Accepted
  • Date: 2026-08-23
  • Authors: IBEX Harness team
  • Milestone: 2.5.G4.M1 Embedder interface and registry

Context

Phase 3 memory write/search requires embedding vectors in pgvector. Different models produce incompatible coordinate systems (all-MiniLM-L6-v2 = 384-dim; BAAI/bge-m3 = 1024-dim). Mixing them in one index yields silent bad recall, not hard errors.

Track D (2.5.G4) needs a shared contract before TEI (G4.M2), hosted API (G4.M3), and content-hash cache (G4.M4).

Patterns to mirror: ADR-0025 (provider.Provider/Registry) and ADR-0043.

Decision

1) Deployment profile, not per-request selection

Profiles: cpu | gpu | hosted. Selected at deployment via IBEX_EMBEDDING_PROFILE (with IBEX_EMBEDDING_DIM / IBEX_EMBEDDING_MODEL). Per-request embedder selection is forbidden.

Documented default geometry:

ProfileDefault modelDim
cpuall-MiniLM-L6-v2384
gpuBAAI/bge-m31024
hostedOpenAI text-embedding-3-large / 3072 (Cohere 1024); G4.M3varies

Never silently fall back across profiles (geometry change).

2) Go contract — packages/embedder

Go
type Embedder interface {
    Embed(ctx context.Context, texts []string) ([][]float32, error)
    Name() string
    ModelID() string
    Dimensions() int
    Profile() Profile
}
  • Registry keyed by Profile (fail-closed on nil/duplicate/mismatch).
  • ValidateGeometry(e, wantDim, wantModel) for startup.
  • Input limits: MaxBatchTexts=64, MaxTextBytes=32KiB; empty batch rejected.
  • Outputs must be L2-normalized; callers validate length/finiteness.
  • M1 ships a deterministic stub only (Name()=="stub"). No TEI/OpenAI client in this package.

3) Python — services/embedder

EmbeddingBackend ABC mirrors Go (embed, name, model_id, dimensions, profile). Profile registry + stub + FastAPI /health//ready with startup geometry validation. Real backends land in G4.M2/M3.

4) Org columns vs deployment

ibex_core.organizations stores provisioned geometry defaults for new org rows:

  • embedding_profile, embedding_dim, embedding_model_id (migration defaults: cpu / 384 / all-MiniLM-L6-v2)

The running embedder process is configured and validated only through IBEX_EMBEDDING_* at startup — it does not load org rows or derive geometry from deployment env beyond those variables. Per-org write-time mismatch enforcement (memory writes vs org geometry) is Phase 3 work.

5) Library vs inference process

Go package = shared contract for future Go callers. Python service = inference HTTP owner. TEI/OpenAI are backends behind the Python service (not an extra IBEX hop in front of TEI for M1).

6) Security / observability

Never log raw text or vector payloads. Log profile, model_id, batch size, latency, cache hit/miss (later), error class only.

7) Sequencing

MilestoneScope
G4.M1 (this)Interface, registry, stub, org columns, contract tests
G4.M2TEI backend (gpu)
G4.M3Hosted API backend
G4.M4Content-hash cache (SHA-256 length-prefixed model_id+dim+text; {org_id}:embed:v1:{hex})

Consequences

Positive: Single contract before backends; fail-closed geometry; org schema ready for Phase 3.

Negative / follow-ups: Stub is not production inference on cpu; Voyage remains fail-closed; dual Go/Python contracts must stay aligned manually until codegen exists.

G4.M2 Update (2026-08-24)

G4.M2 has shipped the TEI GPU backend within this contract:

  • EmbeddingBackend.embed() converted to async def (one contract break now, not three later).
  • Package layout refactored: app/backends/, app/tei/, app/api/ sub-packages.
  • TEIBackend (name=="tei", profile=="gpu") backed by TeiClient (httpx, retries, jittered backoff).
  • Startup: fail-closed /health poll + /info model-id geometry check; no gpu→stub fallback.
  • POST /v1/embed internal endpoint (Bearer IBEX_EMBEDDING_API_TOKEN; probes stay unauthenticated).
  • Reference compose: infra/reference/tei-embeddings.compose.yaml; Dockerfile at services/embedder/Dockerfile.
  • Env: IBEX_EMBEDDING_API_TOKEN, IBEX_EMBEDDING_TEI_BASE_URL, IBEX_EMBEDDING_TEI_ALLOW_INSECURE, IBEX_EMBEDDING_TEI_API_KEY, IBEX_EMBEDDING_TEI_TIMEOUT_SECONDS, IBEX_EMBEDDING_TEI_CONNECT_TIMEOUT_SECONDS, IBEX_EMBEDDING_TEI_MAX_RETRIES, IBEX_EMBEDDING_TEI_HEALTH_TIMEOUT_SECONDS — see ENVIRONMENT_VARIABLES.md.
  • M4 (cache decorator) plugs into the same EmbeddingBackend ABC without a further contract break.

G4.M3 Update (2026-08-24)

G4.M3 has shipped the hosted-API backend within this contract:

  • HostedAPIBackend (name=="openai"|"cohere", profile=="hosted") via HostedClient (httpx, retries, jittered backoff).
  • OpenAI POST /v1/embeddings reuses parse_openai_compat_embed_response; Cohere POST /v2/embed is isolated in app/hosted/protocol.py.
  • Fail-closed: hosted without IBEX_EMBEDDING_HOSTED_API_KEY never falls back to stub; voyage is accepted in settings and rejected at factory.
  • Startup probe embed confirms observed dimensions (OpenAI has no TEI /info); mismatch blocks readiness.
  • Output is L2-normalized then re-validated (OpenAI does not guarantee unit norm).
  • Env: IBEX_EMBEDDING_HOSTED_PROVIDER, IBEX_EMBEDDING_HOSTED_API_KEY, IBEX_EMBEDDING_HOSTED_BASE_URL, IBEX_EMBEDDING_HOSTED_TIMEOUT_SECONDS, IBEX_EMBEDDING_HOSTED_CONNECT_TIMEOUT_SECONDS, IBEX_EMBEDDING_HOSTED_MAX_RETRIES; optional OpenAI-only alias OPENAI_EMBEDDING_API_KEY.
  • CPU MiniLM remains stub (not part of M3). Cache decorator shipped in G4.M4.

G4.M4 Update (2026-08-24)

G4.M4 has shipped the Redis content-hash embedding cache within this contract:

  • CachingEmbeddingBackend decorator wraps any EmbeddingBackend; ABC embed(texts) unchanged.
  • Content address: SHA-256 of length-prefixed (model_id, dim, utf-8 text) (ADR-0010; not MD5/xxHash/BLAKE3).
  • Redis keys are org-scoped: {org_id}:embed:v1:{hex} (sketch embed:{model}:{hash} was rejected for tenancy / embedding inversion).
  • POST /v1/embed requires org_id (UUID); FastAPI sets a ContextVar for the decorator.
  • Fail-open on Redis errors at request time; fail-closed at startup when cache is enabled without Redis or PING fails.
  • Values are float32 little-endian (<f4) bytes only (never pickle). Mixed batches use one MGET + one pipeline SET EX. Corrupt / non-finite / non-L2 blobs count as misses and are overwritten.
  • Prometheus: ibex_embedder_cache_requests_total{backend,result} per text; authenticated GET /metrics (Bearer IBEX_EMBEDDING_API_TOKEN). JSON backend stays the inner name (not cached:…).
  • Env: IBEX_EMBEDDING_CACHE_ENABLED (default false), IBEX_EMBEDDING_CACHE_TTL_SECONDS (default 86400), IBEX_EMBEDDING_CACHE_REDIS_URL / REDIS_URL, IBEX_EMBEDDING_CACHE_REDIS_TIMEOUT_SECONDS.
  • Dependencies: redis (redis-py ≥5 async, wheel-only / no hiredis) for MGET+pipeline; prometheus-client for scrape counters. Alternatives rejected: fakeredis-only (not CI proof), hiredis (native build), BLAKE3/xxHash (not ADR-0010). Both are pure-Python wheels compatible with uv --no-build Docker builds; redis-py is the stdlib-adjacent client already used elsewhere in the org via go-redis patterns.

References

  • ADR-0025 LLM provider abstraction
  • ADR-0043 Tokenizer registry
  • ENVIRONMENT_VARIABLES.md — IBEX_EMBEDDING_*
  • Milestone 2.5.G4.M1

Was this page helpful?

Edit on GitHub

Last updated on

PreviousADR-0045: Streaming response transformationNextADR-0047: Memory temporal validity foundation

On this page

  • Context
  • Decision
  • 1) Deployment profile, not per-request selection
  • 2) Go contract — packages/embedder
  • 3) Python — services/embedder
  • 4) Org columns vs deployment
  • 5) Library vs inference process
  • 6) Security / observability
  • 7) Sequencing
  • Consequences
  • G4.M2 Update (2026-08-24)
  • G4.M3 Update (2026-08-24)
  • G4.M4 Update (2026-08-24)
  • References
0%