Skip to content

Commit a0da791

Browse files
committed
feat: add comprehensive examples for batch indexing, document management, error handling, and PDF indexing
Add four new example projects demonstrating core functionality: - Batch Indexing Example: Shows indexing multiple documents using from_paths, from_dir, and from_bytes with cross-document querying capabilities - Document Management Example: Demonstrates CRUD operations including list(), exists(), remove(), and clear() methods for indexed documents - Error Handling Example: Illustrates proper VectorlessError exception handling with different error categories and inspection techniques - PDF Indexing Example: Showcases PDF file indexing with detailed metrics inspection and querying capabilities Each example includes dedicated README.md files with setup instructions, environment variable documentation, and usage examples. All examples follow consistent configuration patterns with proper async handling and cleanup procedures.
1 parent b9c68da commit a0da791

8 files changed

Lines changed: 671 additions & 0 deletions

File tree

examples/batch_indexing/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Batch Indexing Example
2+
3+
Demonstrates indexing multiple documents at once using:
4+
- `from_paths` -- explicit list of file paths
5+
- `from_dir` -- all supported files in a directory
6+
- `from_bytes` -- raw in-memory content
7+
8+
Also shows cross-document querying with `with_doc_ids`.
9+
10+
## Setup
11+
12+
```bash
13+
pip install vectorless
14+
```
15+
16+
## Run
17+
18+
```bash
19+
python main.py
20+
```
21+
22+
## Environment Variables
23+
24+
| Variable | Description | Default |
25+
|------------------------|----------------------|-----------|
26+
| `VECTORLESS_API_KEY` | LLM API key | `sk-...` |
27+
| `VECTORLESS_MODEL` | LLM model name | `gpt-4o` |
28+
| `VECTORLESS_ENDPOINT` | Custom API endpoint | `None` |

examples/batch_indexing/main.py

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
"""
2+
Batch indexing example -- demonstrates indexing multiple documents at once
3+
using from_paths, from_dir, and from_bytes.
4+
5+
Usage:
6+
pip install vectorless
7+
python main.py
8+
"""
9+
10+
import asyncio
11+
import os
12+
13+
from vectorless import (
14+
Engine,
15+
IndexContext,
16+
IndexOptions,
17+
QueryContext,
18+
VectorlessError,
19+
)
20+
21+
# --- Configuration ---
22+
API_KEY = os.environ.get("VECTORLESS_API_KEY", "sk-...")
23+
MODEL = os.environ.get("VECTORLESS_MODEL", "gpt-4o")
24+
ENDPOINT = os.environ.get("VECTORLESS_ENDPOINT", None)
25+
WORKSPACE = "./workspace"
26+
27+
# Sample documents for demonstration
28+
DOCS = {
29+
"alpha.md": """\
30+
# Alpha Report
31+
32+
## Summary
33+
34+
Alpha is a distributed key-value store designed for low-latency reads.
35+
It uses a log-structured merge tree for storage.
36+
37+
## Architecture
38+
39+
Write requests go through a write-ahead log, then are buffered in memory.
40+
When the buffer is full, it is flushed to disk as an immutable SSTable.
41+
""",
42+
"beta.md": """\
43+
# Beta Report
44+
45+
## Summary
46+
47+
Beta is a stream processing engine that consumes events from Kafka topics
48+
and applies real-time transformations using a DAG-based execution model.
49+
50+
## Performance
51+
52+
Beta processes up to 2 million events per second per node on commodity hardware.
53+
""",
54+
"gamma.md": """\
55+
# Gamma Report
56+
57+
## Summary
58+
59+
Gamma is a feature store that bridges the gap between offline feature
60+
computation and online serving. Features are computed in Spark and served
61+
via a low-latency gRPC endpoint.
62+
63+
## Integration
64+
65+
Gamma integrates with Alpha for feature metadata storage and Beta for
66+
real-time feature updates.
67+
""",
68+
}
69+
70+
71+
def write_sample_docs(base_dir: str) -> list[str]:
72+
"""Write sample markdown files and return their paths."""
73+
paths = []
74+
for name, content in DOCS.items():
75+
path = os.path.join(base_dir, name)
76+
with open(path, "w") as f:
77+
f.write(content)
78+
paths.append(path)
79+
return paths
80+
81+
82+
async def main() -> None:
83+
engine = Engine(
84+
workspace=WORKSPACE,
85+
api_key=API_KEY,
86+
model=MODEL,
87+
endpoint=ENDPOINT,
88+
)
89+
90+
# Create a temp directory with sample documents
91+
docs_dir = "./batch_docs"
92+
os.makedirs(docs_dir, exist_ok=True)
93+
paths = write_sample_docs(docs_dir)
94+
95+
# ---- 1. Index multiple files at once via from_paths ----
96+
print("=" * 50)
97+
print(" from_paths -- index a list of files")
98+
print("=" * 50)
99+
100+
ctx = IndexContext.from_paths(paths)
101+
result = await engine.index(ctx)
102+
103+
print(f" Indexed {len(result.items)} document(s)")
104+
for item in result.items:
105+
print(f" - {item.name} ({item.doc_id[:8]}...)")
106+
if result.has_failures():
107+
for f in result.failed:
108+
print(f" ! Failed: {f.source} -- {f.error}")
109+
print()
110+
111+
doc_ids = [item.doc_id for item in result.items]
112+
113+
# ---- 2. Query across all batch-indexed documents ----
114+
print("=" * 50)
115+
print(" Query across multiple documents")
116+
print("=" * 50)
117+
118+
answer = await engine.query(
119+
QueryContext(
120+
"Which system processes the most events per second?"
121+
).with_doc_ids(doc_ids)
122+
)
123+
for item in answer.items:
124+
print(f" [{item.doc_id[:8]}...] score={item.score:.2f}")
125+
print(f" {item.content[:200]}...")
126+
print()
127+
128+
# ---- 3. Index a directory via from_dir ----
129+
print("=" * 50)
130+
print(" from_dir -- index all supported files in a directory")
131+
print("=" * 50)
132+
133+
# Clear first so we see fresh results
134+
await engine.clear()
135+
136+
ctx = IndexContext.from_dir(docs_dir).with_options(
137+
IndexOptions(generate_summaries=True, generate_description=True)
138+
)
139+
result = await engine.index(ctx)
140+
141+
print(f" Indexed {len(result.items)} document(s)")
142+
for item in result.items:
143+
desc = item.description[:80] if item.description else "N/A"
144+
print(f" - {item.name}: {desc}...")
145+
print()
146+
147+
# ---- 4. Index from raw bytes via from_bytes ----
148+
print("=" * 50)
149+
print(" from_bytes -- index in-memory content")
150+
print("=" * 50)
151+
152+
md_bytes = b"""# Delta Notes
153+
154+
## Key Points
155+
156+
- Delta uses CRDTs for conflict-free replication.
157+
- Writes are locally committed then asynchronously propagated.
158+
- Read repair ensures eventual consistency across all replicas.
159+
"""
160+
161+
ctx = IndexContext.from_bytes(md_bytes, "markdown").with_name("delta")
162+
result = await engine.index(ctx)
163+
164+
print(f" Indexed: {result.doc_id}")
165+
print()
166+
167+
# ---- Cleanup ----
168+
print("=" * 50)
169+
print(" Cleanup")
170+
print("=" * 50)
171+
172+
removed = await engine.clear()
173+
print(f" Removed {removed} document(s)")
174+
175+
# Remove temp files
176+
for p in paths:
177+
os.remove(p)
178+
os.rmdir(docs_dir)
179+
print(f" Cleaned up {docs_dir}/")
180+
181+
182+
if __name__ == "__main__":
183+
asyncio.run(main())
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Document Management Example
2+
3+
Demonstrates CRUD operations on indexed documents:
4+
5+
- `engine.list()` -- list all documents
6+
- `engine.exists(doc_id)` -- check if a document exists
7+
- `engine.remove(doc_id)` -- remove a single document
8+
- `engine.clear()` -- remove all documents
9+
10+
## Setup
11+
12+
```bash
13+
pip install vectorless
14+
```
15+
16+
## Run
17+
18+
```bash
19+
python main.py
20+
```
21+
22+
## Environment Variables
23+
24+
| Variable | Description | Default |
25+
|------------------------|----------------------|-----------|
26+
| `VECTORLESS_API_KEY` | LLM API key | `sk-...` |
27+
| `VECTORLESS_MODEL` | LLM model name | `gpt-4o` |
28+
| `VECTORLESS_ENDPOINT` | Custom API endpoint | `None` |
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
"""
2+
Document management example -- demonstrates CRUD operations on indexed documents:
3+
list, exists, remove, and clear.
4+
5+
Usage:
6+
pip install vectorless
7+
python main.py
8+
"""
9+
10+
import asyncio
11+
import os
12+
13+
from vectorless import (
14+
Engine,
15+
IndexContext,
16+
QueryContext,
17+
VectorlessError,
18+
)
19+
20+
# --- Configuration ---
21+
API_KEY = os.environ.get("VECTORLESS_API_KEY", "sk-...")
22+
MODEL = os.environ.get("VECTORLESS_MODEL", "gpt-4o")
23+
ENDPOINT = os.environ.get("VECTORLESS_ENDPOINT", None)
24+
WORKSPACE = "./workspace"
25+
26+
# Sample documents
27+
SAMPLE_A = """\
28+
# Project Alpha
29+
30+
## Overview
31+
32+
Project Alpha is a next-generation database engine written in Rust.
33+
It supports ACID transactions and serializable isolation.
34+
35+
## Features
36+
37+
- MVCC concurrency control
38+
- B-tree and LSM storage engines
39+
- Query planner with cost-based optimization
40+
"""
41+
42+
SAMPLE_B = """\
43+
# Project Beta
44+
45+
## Overview
46+
47+
Project Beta is a web framework for building real-time applications.
48+
It uses WebSocket-based communication and server-side rendering.
49+
50+
## Features
51+
52+
- Hot module reloading
53+
- Built-in authentication middleware
54+
- Automatic code splitting
55+
"""
56+
57+
58+
async def main() -> None:
59+
engine = Engine(
60+
workspace=WORKSPACE,
61+
api_key=API_KEY,
62+
model=MODEL,
63+
endpoint=ENDPOINT,
64+
)
65+
66+
# ---- Index two documents ----
67+
print("Indexing two documents...")
68+
69+
result_a = await engine.index(
70+
IndexContext.from_content(SAMPLE_A, "markdown").with_name("alpha")
71+
)
72+
doc_id_a = result_a.doc_id
73+
print(f" A: {doc_id_a}")
74+
75+
result_b = await engine.index(
76+
IndexContext.from_content(SAMPLE_B, "markdown").with_name("beta")
77+
)
78+
doc_id_b = result_b.doc_id
79+
print(f" B: {doc_id_b}")
80+
print()
81+
82+
# ---- list() -- show all indexed documents ----
83+
print("--- list() ---")
84+
docs = await engine.list()
85+
for doc in docs:
86+
pages = f", pages={doc.page_count}" if doc.page_count else ""
87+
lines = f", lines={doc.line_count}" if doc.line_count else ""
88+
print(f" {doc.name} id={doc.id[:8]}... format={doc.format}{pages}{lines}")
89+
print(f" Total: {len(docs)} document(s)\n")
90+
91+
# ---- exists() -- check if a document is indexed ----
92+
print("--- exists() ---")
93+
for did, label in [(doc_id_a, "A"), (doc_id_b, "B"), ("nonexistent-id", "?")]:
94+
found = await engine.exists(did)
95+
print(f" {label}: exists={found}")
96+
print()
97+
98+
# ---- Query a specific document ----
99+
print("--- query(doc_id_a) ---")
100+
answer = await engine.query(
101+
QueryContext("What storage engines does Alpha support?").with_doc_id(doc_id_a)
102+
)
103+
item = answer.single()
104+
if item:
105+
print(f" Score: {item.score:.2f}")
106+
print(f" Answer: {item.content[:200]}...\n")
107+
108+
# ---- remove() -- delete a single document ----
109+
print("--- remove(doc_id_a) ---")
110+
removed = await engine.remove(doc_id_a)
111+
print(f" Removed A: {removed}")
112+
113+
# Verify it's gone
114+
exists_a = await engine.exists(doc_id_a)
115+
print(f" exists(A) after removal: {exists_a}")
116+
print()
117+
118+
# ---- list() again -- only B should remain ----
119+
print("--- list() after removal ---")
120+
docs = await engine.list()
121+
for doc in docs:
122+
print(f" {doc.name} id={doc.id[:8]}...")
123+
print(f" Total: {len(docs)} document(s)\n")
124+
125+
# ---- clear() -- remove all remaining documents ----
126+
print("--- clear() ---")
127+
cleared = await engine.clear()
128+
print(f" Cleared {cleared} document(s)")
129+
130+
docs = await engine.list()
131+
print(f" Remaining: {len(docs)} document(s)")
132+
133+
134+
if __name__ == "__main__":
135+
asyncio.run(main())

0 commit comments

Comments
 (0)