# ChronoVec

> Vector memory for data that changes.

ChronoVec is an approximate-nearest-neighbour index for mutable agent memory and RAG workloads, with snapshot isolation, atomic insert batches, bounded deletion, and branchable memory. Every record carries a version interval, so queries can read the index as of any past moment. Agents can fork memory, explore speculatively, and discard or merge — all without rebuilding the index.

## Installation

```bash
pip install chronovec
```

## Core classes

- `Collection` — high-level API: string IDs, metadata, filters, `snapshot()`. The default choice.
- `AgentMemory` — branching memory: fork, speculate, discard or merge. For agentic workloads.
- `AsyncCollection` — asyncio wrapper around `Collection` for FastAPI / async frameworks.
- `Index` — raw engine: int64 IDs, NumPy vectors, maximum throughput.

## Docs

- [Getting started](https://mchl-labs.github.io/chronovec/getting-started): Install, build, 3-minute tour
- [Migrating from Chroma](https://mchl-labs.github.io/chronovec/migration-from-chroma): Familiar API and versioned-memory migration path
- [API reference](https://mchl-labs.github.io/chronovec/api-reference): All public classes and methods
- [Agent memory](https://mchl-labs.github.io/chronovec/use-cases/agent-memory): Branching, speculation, time travel
- [RAG with history](https://mchl-labs.github.io/chronovec/use-cases/rag-with-history): Snapshot-isolated retrieval
- [Architecture](https://mchl-labs.github.io/chronovec/architecture): MVCC, page structure, reclamation
- [LangChain integration](https://mchl-labs.github.io/chronovec/integrations/langchain): VectorStore subclass with branching
- [LlamaIndex integration](https://mchl-labs.github.io/chronovec/integrations/llamaindex): Node storage with snapshot reads
- [LangGraph integration](https://mchl-labs.github.io/chronovec/integrations/langgraph): One graph thread_id per isolated branch
- [Tree-search agents (LATS)](https://mchl-labs.github.io/chronovec/use-cases/lats): Worked pattern, not a packaged integration — every search trajectory owns a live branch delta

## Quick examples

### Text search with metadata filter

```python
from chronovec import Collection

col = Collection(dimensions=384, embedding_function=my_embed)
col.add(ids=["a"], documents=["user prefers dark mode"], metadatas=[{"kind": "pref"}])
results = col.query(query_text="appearance settings", k=5, where={"kind": "pref"})
for r in results:
    print(r.id, r.distance, r.document, r.metadata)
```

### Custom text embedding

```python
from chronovec import Collection, CustomEmbedding

embedding = CustomEmbedding(
    embed_documents=my_embed_documents,
    embed_query=my_embed_query,
)
col = Collection(384, embedding_function=embedding)
col.add(ids=["a"], documents=["user prefers dark mode"])
results = col.query(query_text="appearance settings", k=5)
```

ChronoVec also accepts objects with `embed_documents`/`embed_query` or
LlamaIndex-style `get_text_embedding_batch`/`get_query_embedding` methods.
Provider packages remain application choices rather than ChronoVec
dependencies.

For local Sentence Transformers:

```bash
pip install "chronovec[sentence-transformers]"
```

```python
from chronovec import SentenceTransformerEmbedding

embedding = SentenceTransformerEmbedding("sentence-transformers/all-MiniLM-L6-v2")
```

For hosted providers, ChronoVec wraps LiteLLM so one adapter covers OpenAI,
Cohere, Bedrock, Azure, and the rest of LiteLLM's provider list:

```bash
pip install "chronovec[litellm]"
```

```python
import os

from chronovec import ProviderEmbedding

embedding = ProviderEmbedding(
    provider="openai",
    model="text-embedding-3-small",
    api_key=os.environ["OPENAI_API_KEY"],
)
```

### Snapshot — query the past

```python
t = col.snapshot()                               # capture state before a write
col.add(ids=["a"], documents=["new version"])    # overwrite
old = col.query(query_text="...", snapshot=t)   # "a" returns old version here
```

### Async (FastAPI / asyncio)

```python
from chronovec import AsyncCollection

col = AsyncCollection(384, embedding_function=my_embed)
results = await col.query(query_text="...", k=5)
```

### Agent memory with branching

```python
from chronovec import AgentMemory

mem = AgentMemory(384)
mem.add("fact-1", embedding, text="user writes Python")

branch = mem.branch("hypothesis")
branch.add("guess-1", other_emb, text="user might prefer TypeScript")

for hit, record in branch.search(query):   # sees both
    print(record.id, hit.distance)

mem.search(query)    # never saw the speculation
branch.discard()     # abandon speculation; purge later at a safe horizon
```

### Persistence (crash recovery)

```python
col = Collection(384, wal_path="memory.wal", embedding_function=my_embed)
# Restart after a crash — WAL replays automatically, nothing lost
col = Collection(384, wal_path="memory.wal", embedding_function=my_embed)
```

### Persistence (explicit checkpoint) and disk-backed growth

```python
col.save("snapshot")                                       # snapshot.cvec + snapshot.meta
restored = Collection.load("snapshot", embedding_function=my_embed)

# arena_path + max_vectors: disk-backed, kernel-evictable vector storage.
# With wal_path also set, the arena grows automatically (max_vectors * growth_factor)
# when it fills, instead of raising MemoryError.
col = Collection(384, wal_path="mem.wal", arena_path="mem.arena", max_vectors=100_000)
```

## Claude Code skill

A Claude Code skill at `.claude/skills/chronovec/chronovec/SKILL.md` teaches coding agents the full API — snapshot timing, branch lifecycle, filter operators, and footguns. Invoke it with `/chronovec`.
