Observed since 20 Oct, 20:09 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Migrate a vector database to a new embedding model without re-embedding the corpus. Fits a linear map from ~2K calibration texts; 87-91% retrieval retention measured on BEIR.
Embedding providers deprecate models constantly — ada-002 is gone, text-embedding-3 is next. When that happens, you either re-embed your entire corpus (expensive, slow, risky) or get stuck on a dead model. AECP lets you switch without re-embedding: fit a lightweight linear transform from ~2K calibration texts, apply it to stored vectors, and gate the migration on measured retrieval retention. 87-91% retention on BEIR benchmarks.
pip install aecp
Python >= 3.10. Core deps: numpy, scikit-learn, typer, rich.
Optional extras:
pip install aecp[chroma] — ChromaDB adapterpip install aecp[langchain] — LangChain embeddings shimpip install aecp[llamaindex] — LlamaIndex query wrapperpip install aecp[sentence-transformers] — local model supportpip install aecp[qdrant] — Qdrant store adapterpip install aecp[openai] — OpenAI client shimpip install aecp[all] — everything aboveZero writes to your vector store. Map new-model queries into legacy space on-the-fly. Fully reversible.
from aecp.wrappers.llamaindex import AECPEmbedding
from aecp.mapping.registry import load_mapping
mapping = load_mapping("mapping.aecp")
wrapper = AECPEmbedding(
new_model_embedder=your_llamaindex_embedder,
transform_artifact_path="mapping.aecp",
)
# Use wrapper anywhere LlamaIndex expects a BaseEmbedding
# Queries are mapped; document embeddings raise AECPWrapperUsageError
import openai
from aecp.wrappers.openai_shim import AECPOpenAI
client = openai.OpenAI()
shim = AECPOpenAI(client, "mapping.aecp")
response = shim.embeddings.create(input=["query text"], model="text-embedding-3-small")
# response.data[0].embedding is now in legacy-model space
from aecp.adapters.langchain import AECPEmbeddings
from langchain_openai import OpenAIEmbeddings
mapping = Mapping.load("mapping.aecp")
base = OpenAIEmbeddings(model="text-embedding-3-small")
ae = AECPEmbeddings(mapping, base)
from langchain_chroma import Chroma
db = Chroma.from_documents(docs, embedding=ae)
results = db.similarity_search("query", k=10)
Before migrating anything, verify the transform preserves retrieval quality:
aecp gate --mapping mapping.aecp \
--source-vectors X_sample.npy \
--target-vectors Y_sample.npy
Output: retention table (Recall@1/5/10, MRR), bootstrap confidence intervals, per-metric pass/fail, and a one-line verdict. Exit code 0 for PASS, 1 for WARN/FAIL — use it in CI.
# 1. Plan cost
aecp plan --source-model ada-002 --target-model te3-large --corpus-size 1000000
# 2. Calibrate
aecp calibrate --source-vectors X.npy --target-vectors Y.npy -o mapping.aecp
# 3. Gate
aecp gate --mapping mapping.aecp --source-vectors X.npy --target-vectors Y.npy
# 4. Migrate
aecp transform --mapping mapping.aecp --source-dir ./old_store --target-dir ./new_store
Map queries on-the-fly without touching stored data:
from aecp.serve import QueryAdapter
qa = QueryAdapter.load("mapping.aecp")
legacy_vec = qa.map_query(new_model_embed(query))
| Store | Serve mode | Offline migration | Status |
|---|---|---|---|
| ChromaDB | AECPChromaFunction | migrate_collection() | Supported |
| LangChain | AECPEmbeddings | via store adapter | Supported |
| LlamaIndex | AECPEmbedding wrapper | via store adapter | Query wrapper |
| OpenAI | AECPOpenAI shim | N/A | Query shim |
| Qdrant | QdrantStore | checkpointed in-place | Supported |
| Pinecone | — | shadow-namespace | Planned |
Every quantitative claim in this README or docs references a committed artifact in benchmarks/results/ and a row in aecp-python/CLAIMS.md. No exceptions. If a number isn't in CLAIMS.md, it isn't a claim.
| Adapter | nDCG@10 retention | Notes |
|---|---|---|
| Ridge | 0.871 ± 0.006 | Default. Fast, stable. |
| LowRank | 0.857 ± 0.009 | Compressed matrix. ~1% worse. |
| MLP | 0.727 ± 0.007 | No tuning. Linear wins. |
| K | nDCG@10 retention | Gate |
|---|---|---|
| 500 | 0.671 ± 0.041 | WARN |
| 1000 | 0.735 ± 0.058 | WARN |
| 2000 | 0.785 ± 0.052 | PASS |
| 4000 | 0.832 ± 0.061 | PASS |
| Metric | Value |
|---|---|
| Floor (raw cross-space) | 0.0 |
| AECP (mapped) | 0.667 |
| Ceiling (full re-embed) | 0.722 |
| Retention | 0.923 ± 0.010 |
Same dimension ≠ same space. e5 models require "query: "/"passage: " prefixes; without them ceiling drops to 0.36.
| Pair | High-conf R@10 | Low-conf R@10 | Gap |
|---|---|---|---|
| bge→e5 | 0.955 | 0.637 | 0.318 |
| MiniLM→bge | 0.875 | 0.651 | 0.224 |
| Threshold | Raw recall | + Recalibration | Δ |
|---|---|---|---|
| τ = 0.60 | 78% | 100% | +22% |
| τ = 0.70 | 27% | 67% | +40% |
Engineering, not research. Built on:
Embedding translation enables inversion-style attacks. Treat mapped vectors with same sensitivity as source text.
Apache-2.0
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.2.1 | 22 Jul 2026 | 23 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 20:09 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:deb367ea86882ae8b8dcc46dd42dae2f3e64cd8ff5e8bc45903997af83a18239