Redis SHA-256 content-hash → vector cache sitting in front of whichever EmbeddingBackend is active. Backend-agnostic; exact-match cache for identical text (same model + dim). Ops target >80% hit rate on repetitive memory embeddings.
Milestone 2.5.G4.M4 — Content-Hash Embedding Cache
Status: Completed (2026-08-24)
Goal: Track D — Pluggable Embedding Service
Phase: 2.5 — Provider Generalization & Foundation
Estimated effort: 1 day
Why This Milestone Exists
Embedding the same text repeatedly is expensive (GPU time or API cost) and unnecessary. A content-hash cache sits in front of whichever EmbeddingBackend is active — backend-agnostic, no logic duplication per backend.
Memory content is often highly repetitive — the same user preference, factual statement, or procedural note gets re-embedded each time it's updated or re-ranked. A >80% hit rate target is an ops SLO (not a CI gate) from Prometheus hit/(hit+miss).
Non-Goals
- Semantic deduplication (separate concern in Phase 3 memory dedup)
- Explicit cache flush on model upgrade (model_id + dim in the digest invalidate naturally)
- Go Redis client in
packages/embedder(Python service owns inference + cache)
Advancement vs early sketch: org-scoped keys were originally listed as a non-goal. Shipped code does isolate by org_id because embedding vectors are tenant data (inversion risk) and Redis tenancy rules require {org_id}:… prefixes.
What shipped
CachingEmbeddingBackenddecorator over any backend; ABCembed(texts)unchanged- Digest:
sha256(u32be(len(model_id)) || utf8(model_id) || u32be(dim) || u32be(len(text)) || text) - Redis key:
{org_id}:embed:v1:{hex}(not sketchembed:{model}:{hash}) POST /v1/embedrequiresorg_idUUID; ContextVar bridges into the decorator- Fail-open on Redis errors; fail-closed startup when cache enabled without Redis / PING fail
- Env:
IBEX_EMBEDDING_CACHE_*(default enabled=false),REDIS_URLfallback - Metrics:
ibex_embedder_cache_requests_total,GET /metrics - Unit + real Redis CI (
redis:7onembedder-test); cross-tenant covered
Package layout: services/embedder/app/cache/{keys,redis_store,backend,metrics,env,context}.py
Success signals
- Decorator wraps any
EmbeddingBackend, transparent to callers ofembed(texts) - Cache key uses SHA-256 content address (model + dim + text); no text in keys
- Cache hit: no call to underlying backend
- Cache miss: underlying backend called, result stored with TTL
- Batch requests: mixed hit/miss via single
MGET+ one pipelineSET EX - TTL via
IBEX_EMBEDDING_CACHE_TTL_SECONDS(default 86400) - Prometheus hit/miss counters (labels:
backend,result=hit|miss) - Unit + real Redis tests; Redis failure does not fail the embedding request
- Org isolation tested (org B does not hit org A’s keys)
Prerequisites
- 2.5.G4.M1 (Embedder interface) —
EmbeddingBackendABC - 2.5.G4.M2 or 2.5.G4.M3 — at least one real backend to wrap
Last updated on