Skip to main content

embspec

Embedding pipeline ops + drift detection for production RAG.

The single failure mode this library prevents: query encoder upgrade ships before the index is re-encoded; every health check stays 200 OK while retrieval accuracy silently collapses. The decompressed.io RAG observability post-mortem (2026-03-09) describes this exact bug — $15K of emergency re-encoding plus 2-5 days of engineer time before someone diagnosed it.

pip install embspec
from embspec import IndexManifest, embed_assert

manifest = IndexManifest.load("s3://my-rag/index-prod/manifest.json")

@embed_assert(manifest, model_id="amazon.titan-embed-text-v2:0", dimension=1024)
def search(query: str):
    qv = embed_query(query)
    return opensearch.knn_search(qv, ...)

If the query encoder ever drifts off the manifest, search raises EmbeddingVersionMismatch instead of silently returning bad results.

What's in v0.1

Primitive What it does Anchored in
IndexManifest + EmbeddingSpec Single-file source of truth for "what does this index contain" — model id, dimension, version, normalization decompressed.io post-mortem
assert_compatible() / embed_assert() decorator Fast-fail on encoder/index drift; raise or warn modes same
DriftAdapter Linear adapter from new-model embeddings to old-model space; lets you swap the query encoder without re-encoding the corpus Drift-Adapter, Vejendla 2025 (arxiv:2509.23471)
neighbor_stability() Compare two retrievers on a frozen probe set; reports overlap, Jaccard, regression list, deploy-safety verdict RAGOps survey, Xu et al. 2025 (arxiv:2506.03401)

Why not Evidently / Phoenix / WhyLogs?

  • Evidently — tabular-ML drift heritage; LLM additions are recent and platform-shaped. Not a drop-in primitive.
  • Phoenix — embedding-drift visualization is a sub-feature of a full observability platform. You adopt the platform.
  • WhyLogs — generic data-logging primitive; not embedding-aware; last commit 2025-01.
  • embspec — three small primitives (IndexManifest, DriftAdapter, neighbor_stability) you compose with whatever vector DB and tracer you already have. No platform, no UI, no agent framework.

Usage

Manifest + version assertion

from datetime import datetime, timezone
from embspec import IndexManifest, EmbeddingSpec

# When you build the index, write a manifest alongside it
manifest = IndexManifest(
    index_name="prod-v3",
    embedding=EmbeddingSpec(
        model_id="amazon.titan-embed-text-v2:0",
        dimension=1024,
        normalization="l2",
    ),
    created_at=datetime.now(timezone.utc),
    doc_count=8_000_000,
)
manifest.save("s3://my-rag/index-prod/manifest.json")
# (or any local path; manifest.save uses pathlib.Path.write_text)

# At query time, assert the encoder matches before searching
from embspec import embed_assert

@embed_assert(
    "s3://my-rag/index-prod/manifest.json",  # path or IndexManifest
    model_id="amazon.titan-embed-text-v2:0",
    dimension=1024,
    mode="raise",  # or "log" for canary rollout
)
def search(query: str) -> list[dict]:
    qv = embed_query(query)
    return opensearch.knn_search(qv, ...)

Drift-Adapter for in-place model migration

When you want to upgrade the query encoder without re-encoding 8M docs:

from embspec import DriftAdapter
import numpy as np

# Sample, e.g., 50K docs and embed them with both old and new models
old_emb = embed_with_old_model(sample_texts)  # shape (50000, 1024)
new_emb = embed_with_new_model(sample_texts)  # shape (50000, 1536)

adapter = DriftAdapter.fit(
    new_embeddings=new_emb,
    old_embeddings=old_emb,
    regularization=0.01,  # ridge; helps when new_emb is rank-deficient
)
adapter.save("s3://my-rag/adapters/v3-to-v4.npz")

# At query time, embed with the new model then transform into old space
qv_new = embed_with_new_model(query)
qv_compatible = adapter.transform(qv_new)  # shape (1024,)
results = opensearch.knn_search(qv_compatible, ...)

Per Vejendla 2025, this typically recovers 95-99% retrieval at ~1% of the cost of re-encoding the full corpus.

Neighbor stability for safe migrations

from embspec import neighbor_stability

# Run a fixed probe set against both indexes
old_results = {pid: retrieve_from_v3(q) for pid, q in probes.items()}
new_results = {pid: retrieve_from_v4(q) for pid, q in probes.items()}

report = neighbor_stability(old_results, new_results, k=10)
print(f"mean overlap@10: {report.mean_overlap_at_k:.1%}")
print(f"regressions: {report.regression_count}/{report.n_probes}")

if report.is_safe_to_deploy(min_mean_overlap=0.85, max_regression_fraction=0.05):
    deploy_v4()
else:
    investigate(report.regression_probe_ids)

What it explicitly does NOT do

  • Not a vector database.
  • Not a RAG framework. No retriever, no chunker, no generator.
  • Not a generic ML drift library. Embedding-and-retrieval-shaped only.
  • Not an eval framework. neighbor_stability is the one judgment you can make without a labeled gold set; for richer evals use ragas, trulens, or a tracer.
  • Does not host or serve embeddings.

Roadmap

  • v0.2: dual_write() context manager for blue/green index migrations across OpenSearch / pgvector / Pinecone / Qdrant.
  • v0.3: ChunkingExperiment A/B harness with optional LLM-judge.
  • v0.4: integration helpers for AWS Bedrock embedding models, OpenAI, Cohere, Voyage.

License

Apache-2.0. See LICENSE.

Metadata

Release files for embspec 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for embspec 0.1.0
File Size Uploaded Provenance
embspec-0.1.0.tar.gz 13.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for embspec 0.1.0
File Interpreter ABI Platform
embspec-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 29.4 kB

Release files / embspec-0.1.0.tar.gz

Download URL embspec-0.1.0.tar.gz
Size 13.1 kB
Tags Source
SHA-256 checksum
How to use checksums
f03d8f05d9cc4e7db508380aa2453cea034a2f6d5970dfccabc343c09fac5ea5
BLAKE2b-256 checksum
How to use checksums
f9169d42ee7594de28bf4817e1d6b81febd79fbe2d8e3df8abf51282f479a7d4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.4

Release files / embspec-0.1.0-py3-none-any.whl

Download URL embspec-0.1.0-py3-none-any.whl
Size 16.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
163b7100c40a971bb989f1675d5332b610e8debd08572316a2a4a1e0ee52cebf
BLAKE2b-256 checksum
How to use checksums
fba9b8f4111fa99a68c37bebf07cc5d337595ee58d9d7c9346194156af53b9dc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.4

Security for embspec 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page