Skip to content

Commit 0d60bcd

Browse files
docs: v2.0 documentation — guides, reference, and examples (#92)
Adds: - Getting started, memory, sessions, MCP, deployment guides - API and configuration reference - LangChain and RAG pipeline examples - Documentation index Closes #8 Co-authored-by: Ona <no-reply@ona.com>
1 parent 6cd87aa commit 0d60bcd

10 files changed

Lines changed: 994 additions & 0 deletions

File tree

docs/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Distill Documentation
2+
3+
## Guides
4+
5+
- [Getting Started](guides/getting-started.md) — Install, configure, and run your first dedup
6+
- [Memory](guides/memory.md) — Persistent context memory across sessions
7+
- [Sessions](guides/sessions.md) — Token-budgeted context windows
8+
- [MCP Integration](guides/mcp.md) — Use Distill with Claude Desktop, Cursor, and other MCP clients
9+
- [Deployment](guides/deployment.md) — Docker, binary, and cloud deployment
10+
11+
## Reference
12+
13+
- [API Reference](reference/api.md) — All REST endpoints
14+
- [Configuration](reference/configuration.md) — Config file, environment variables, CLI flags
15+
- [OpenAPI Spec](../openapi.yaml) — Machine-readable API specification
16+
17+
## Examples
18+
19+
- [LangChain Integration](examples/langchain.md)
20+
- [RAG Pipeline](examples/rag-pipeline.md)

docs/examples/langchain.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# LangChain Integration
2+
3+
Use Distill as a persistent memory layer for LangChain agents.
4+
5+
## Setup
6+
7+
```bash
8+
pip install langchain requests
9+
```
10+
11+
## Memory wrapper
12+
13+
```python
14+
import requests
15+
from langchain.memory import BaseMemory
16+
17+
class DistillMemory(BaseMemory):
18+
"""LangChain memory backed by Distill's memory API."""
19+
20+
base_url: str = "http://localhost:8080"
21+
api_key: str | None = None
22+
agent_id: str = "langchain-agent"
23+
memory_key: str = "history"
24+
25+
@property
26+
def memory_variables(self) -> list[str]:
27+
return [self.memory_key]
28+
29+
def _headers(self) -> dict:
30+
h = {"Content-Type": "application/json"}
31+
if self.api_key:
32+
h["Authorization"] = f"Bearer {self.api_key}"
33+
return h
34+
35+
def load_memory_variables(self, inputs: dict) -> dict:
36+
query = inputs.get("input", "")
37+
resp = requests.post(
38+
f"{self.base_url}/v1/memory/recall",
39+
headers=self._headers(),
40+
json={
41+
"query": query,
42+
"agent_id": self.agent_id,
43+
"top_k": 5,
44+
},
45+
)
46+
resp.raise_for_status()
47+
memories = resp.json().get("memories", [])
48+
text = "\n".join(m["content"] for m in memories)
49+
return {self.memory_key: text}
50+
51+
def save_context(self, inputs: dict, outputs: dict) -> None:
52+
content = f"User: {inputs.get('input', '')}\nAssistant: {outputs.get('output', '')}"
53+
requests.post(
54+
f"{self.base_url}/v1/memory/store",
55+
headers=self._headers(),
56+
json={
57+
"content": content,
58+
"agent_id": self.agent_id,
59+
"tags": ["conversation"],
60+
"auto_classify": True,
61+
},
62+
).raise_for_status()
63+
64+
def clear(self) -> None:
65+
requests.post(
66+
f"{self.base_url}/v1/memory/forget",
67+
headers=self._headers(),
68+
json={"agent_id": self.agent_id},
69+
).raise_for_status()
70+
```
71+
72+
## Usage with an agent
73+
74+
```python
75+
from langchain.chat_models import ChatOpenAI
76+
from langchain.chains import ConversationChain
77+
78+
memory = DistillMemory(
79+
base_url="http://localhost:8080",
80+
agent_id="my-agent",
81+
)
82+
83+
chain = ConversationChain(
84+
llm=ChatOpenAI(model="gpt-4o"),
85+
memory=memory,
86+
)
87+
88+
# Memories persist across restarts
89+
response = chain.predict(input="What did we discuss yesterday?")
90+
```
91+
92+
## Why use Distill instead of LangChain's built-in memory?
93+
94+
- **Persistence** — survives process restarts, stored in SQLite
95+
- **Deduplication** — repeated context is stored once
96+
- **Sensitivity tagging** — PII and credentials are flagged automatically
97+
- **Decay** — old memories lose relevance over time
98+
- **Conflict detection** — contradictory memories are surfaced

docs/examples/rag-pipeline.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# RAG Pipeline with Distill
2+
3+
Use Distill's dedup + memory pipeline to build a retrieval-augmented generation system that avoids redundant context and remembers past interactions.
4+
5+
## Architecture
6+
7+
```
8+
Documents → Distill (dedup + embed) → Memory Store
9+
10+
User Query → Distill (recall) → Ranked Context → LLM → Response
11+
12+
Distill (store answer)
13+
```
14+
15+
## Ingest documents
16+
17+
Deduplicate and store document chunks:
18+
19+
```python
20+
import requests
21+
22+
DISTILL = "http://localhost:8080"
23+
24+
def ingest(chunks: list[str], source: str):
25+
"""Dedup chunks, then store unique ones as memories."""
26+
# Deduplicate
27+
resp = requests.post(f"{DISTILL}/v1/dedupe", json={
28+
"chunks": chunks,
29+
})
30+
resp.raise_for_status()
31+
unique = resp.json()["unique_chunks"]
32+
33+
# Store each unique chunk
34+
for chunk in unique:
35+
requests.post(f"{DISTILL}/v1/memory/store", json={
36+
"content": chunk,
37+
"agent_id": "rag-pipeline",
38+
"tags": ["document", source],
39+
"auto_classify": True,
40+
}).raise_for_status()
41+
42+
print(f"Stored {len(unique)}/{len(chunks)} chunks from {source}")
43+
```
44+
45+
## Query with context
46+
47+
```python
48+
def query(question: str, llm_fn) -> str:
49+
"""Recall relevant context and generate an answer."""
50+
# Recall from memory
51+
resp = requests.post(f"{DISTILL}/v1/memory/recall", json={
52+
"query": question,
53+
"agent_id": "rag-pipeline",
54+
"top_k": 10,
55+
"min_relevance": 0.3,
56+
"boost_tags": ["document"],
57+
})
58+
resp.raise_for_status()
59+
memories = resp.json()["memories"]
60+
61+
# Build prompt
62+
context = "\n---\n".join(m["content"] for m in memories)
63+
prompt = f"Context:\n{context}\n\nQuestion: {question}\nAnswer:"
64+
65+
answer = llm_fn(prompt)
66+
67+
# Store the Q&A pair for future recall
68+
requests.post(f"{DISTILL}/v1/memory/store", json={
69+
"content": f"Q: {question}\nA: {answer}",
70+
"agent_id": "rag-pipeline",
71+
"tags": ["qa"],
72+
}).raise_for_status()
73+
74+
return answer
75+
```
76+
77+
## Handle stale knowledge
78+
79+
When documents are updated, supersede old memories:
80+
81+
```python
82+
def update_document(old_memory_id: str, new_content: str):
83+
"""Replace outdated memory with new version."""
84+
# Store new version
85+
resp = requests.post(f"{DISTILL}/v1/memory/store", json={
86+
"content": new_content,
87+
"agent_id": "rag-pipeline",
88+
"tags": ["document"],
89+
})
90+
resp.raise_for_status()
91+
new_id = resp.json()["id"]
92+
93+
# Supersede old version
94+
requests.post(f"{DISTILL}/v1/memory/supersede", json={
95+
"id": old_memory_id,
96+
"new_id": new_id,
97+
}).raise_for_status()
98+
```
99+
100+
## Session-based context window
101+
102+
For multi-turn conversations, use sessions to manage token budgets:
103+
104+
```python
105+
def create_session():
106+
resp = requests.post(f"{DISTILL}/v1/session/create", json={
107+
"max_tokens": 4000,
108+
"strategy": "sliding_window",
109+
})
110+
return resp.json()["session_id"]
111+
112+
def add_to_session(session_id: str, role: str, content: str):
113+
requests.post(f"{DISTILL}/v1/session/push", json={
114+
"session_id": session_id,
115+
"entries": [{"role": role, "content": content}],
116+
}).raise_for_status()
117+
118+
def get_context(session_id: str) -> list[dict]:
119+
resp = requests.post(f"{DISTILL}/v1/session/context", json={
120+
"session_id": session_id,
121+
})
122+
return resp.json()["entries"]
123+
```
124+
125+
## Full example
126+
127+
```python
128+
from openai import OpenAI
129+
130+
client = OpenAI()
131+
132+
def llm(prompt: str) -> str:
133+
resp = client.chat.completions.create(
134+
model="gpt-4o",
135+
messages=[{"role": "user", "content": prompt}],
136+
)
137+
return resp.choices[0].message.content
138+
139+
# Ingest
140+
chunks = [
141+
"Distill deduplicates context before sending to LLMs.",
142+
"Memory entries decay over time based on access patterns.",
143+
"Sensitivity classification detects PII automatically.",
144+
]
145+
ingest(chunks, source="distill-docs")
146+
147+
# Query
148+
answer = query("How does Distill handle sensitive data?", llm)
149+
print(answer)
150+
```

docs/guides/deployment.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Deployment
2+
3+
## Docker
4+
5+
```bash
6+
docker run -p 8080:8080 \
7+
-e OPENAI_API_KEY=sk-... \
8+
ghcr.io/siddhant-k-code/distill:latest \
9+
api --memory --session
10+
```
11+
12+
With a persistent volume for memory:
13+
14+
```bash
15+
docker run -p 8080:8080 \
16+
-v distill-data:/data \
17+
-e OPENAI_API_KEY=sk-... \
18+
ghcr.io/siddhant-k-code/distill:latest \
19+
api --memory --memory-db /data/memory.db --session --session-db /data/sessions.db
20+
```
21+
22+
## Docker Compose
23+
24+
```yaml
25+
version: "3.8"
26+
services:
27+
distill:
28+
image: ghcr.io/siddhant-k-code/distill:latest
29+
ports:
30+
- "8080:8080"
31+
environment:
32+
- OPENAI_API_KEY=${OPENAI_API_KEY}
33+
command: api --memory --session
34+
volumes:
35+
- distill-data:/data
36+
37+
volumes:
38+
distill-data:
39+
```
40+
41+
## Binary
42+
43+
Download from [GitHub Releases](https://github.com/Siddhant-K-code/distill/releases) and run directly:
44+
45+
```bash
46+
distill api --memory --session
47+
```
48+
49+
## Fly.io
50+
51+
A `fly.toml` is included in the repository:
52+
53+
```bash
54+
fly launch
55+
fly secrets set OPENAI_API_KEY=sk-...
56+
fly deploy
57+
```
58+
59+
## Render
60+
61+
A `render.yaml` is included for one-click deployment to Render.
62+
63+
## Environment variables
64+
65+
| Variable | Description |
66+
|----------|-------------|
67+
| `OPENAI_API_KEY` | OpenAI API key for embeddings |
68+
| `COHERE_API_KEY` | Cohere API key (when using `--embedding-provider cohere`) |
69+
| `DISTILL_API_KEYS` | Comma-separated API keys for authentication |
70+
71+
## Observability
72+
73+
### Prometheus metrics
74+
75+
Available at `/metrics`:
76+
77+
```bash
78+
curl localhost:8080/metrics
79+
```
80+
81+
### OpenTelemetry tracing
82+
83+
```bash
84+
distill api --otel-endpoint localhost:4317
85+
# or
86+
distill api --otel-stdout # print traces to stdout
87+
```
88+
89+
### Grafana
90+
91+
Import the dashboard template from `grafana/dashboard.json`.

0 commit comments

Comments
 (0)