Skip to content

Commit 3436c18

Browse files
committed
feat: update API to return IndexResult and add QueryContext
- Change Python Engine.index() to return IndexResult instead of doc_id string - Add new PyIndexResult and PyIndexItem classes for better indexing feedback - Update Python examples to handle IndexResult and extract doc_id - Introduce QueryContext for structured query parameters - Rename Python list_docs() method to list() for consistency - Remove deprecated len() method from Python Engine - Update README and all Rust examples to use new API patterns - Replace direct doc_id parameter with QueryContext in query methods
1 parent 6852923 commit 3436c18

15 files changed

Lines changed: 578 additions & 510 deletions

File tree

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ from vectorless import Engine, IndexContext
4343
engine = Engine(workspace="./data")
4444

4545
# Index a document (PDF, Markdown, DOCX, HTML)
46-
doc_id = engine.index(IndexContext.from_file("./report.pdf"))
46+
result = engine.index(IndexContext.from_file("./report.pdf"))
47+
doc_id = result.doc_id
4748

4849
# Query
4950
result = engine.query(doc_id, "What is the total revenue?")
@@ -60,7 +61,7 @@ vectorless = "0.1"
6061
```
6162

6263
```rust
63-
use vectorless::client::{EngineBuilder, IndexContext};
64+
use vectorless::client::{EngineBuilder, IndexContext, QueryContext};
6465

6566
#[tokio::main]
6667
async fn main() -> vectorless::Result<()> {
@@ -70,10 +71,13 @@ async fn main() -> vectorless::Result<()> {
7071
.await?;
7172

7273
// Index
73-
let doc_id = engine.index(IndexContext::from_path("./report.pdf")).await?;
74+
let result = engine.index(IndexContext::from_path("./report.pdf")).await?;
75+
let doc_id = result.doc_id().unwrap();
7476

7577
// Query
76-
let result = engine.query(&doc_id, "What is the total revenue?").await?;
78+
let result = engine.query(
79+
QueryContext::new("What is the total revenue?").with_doc_id(doc_id)
80+
).await?;
7781
println!("Answer: {}", result.content);
7882

7983
Ok(())

examples/rust/advanced.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
//! cargo run --example advanced
1717
//! ```
1818
19-
use vectorless::{EngineBuilder, IndexContext};
19+
use vectorless::{EngineBuilder, IndexContext, QueryContext};
2020

2121
#[tokio::main]
2222
async fn main() -> vectorless::Result<()> {
@@ -33,11 +33,14 @@ async fn main() -> vectorless::Result<()> {
3333
println!("✓ Client created with config file\n");
3434

3535
// Index a document
36-
let doc_id = client.index(IndexContext::from_path("./README.md")).await?;
36+
let result = client.index(IndexContext::from_path("./README.md")).await?;
37+
let doc_id = result.doc_id().unwrap().to_string();
3738
println!("✓ Indexed: {}\n", doc_id);
3839

3940
// Query
40-
let result = client.query(&doc_id, "What features does Vectorless provide?").await?;
41+
let result = client
42+
.query(QueryContext::new("What features does Vectorless provide?").with_doc_id(&doc_id))
43+
.await?;
4144
println!("Query: What features does Vectorless provide?");
4245
println!("Score: {:.2}", result.score);
4346
if !result.content.is_empty() {

examples/rust/basic.rs

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,46 @@
33

44
//! Basic usage example for Vectorless.
55
//!
6-
//! This example demonstrates the core API in ~30 lines.
7-
//!
86
//! # Usage
97
//!
108
//! ```bash
119
//! cargo run --example basic
1210
//! ```
1311
14-
use vectorless::{EngineBuilder, IndexContext};
12+
use vectorless::{EngineBuilder, IndexContext, QueryContext};
1513

1614
#[tokio::main]
1715
async fn main() -> vectorless::Result<()> {
1816
println!("=== Vectorless Basic Example ===\n");
1917

20-
// 1. Create a client
21-
let client = EngineBuilder::new()
18+
// 1. Create an engine
19+
let engine = EngineBuilder::new()
2220
.with_workspace("./workspace")
2321
.build()
2422
.await
2523
.map_err(|e: vectorless::BuildError| vectorless::Error::Config(e.to_string()))?;
2624

27-
println!("✓ Client created\n");
25+
println!("Engine created\n");
2826

2927
// 2. Index a document
30-
let doc_id = client.index(IndexContext::from_path("./README.md")).await?;
31-
println!("✓ Indexed: {}\n", doc_id);
28+
let result = engine.index(IndexContext::from_path("./README.md")).await?;
29+
let doc_id = result.doc_id().unwrap().to_string();
30+
println!("Indexed: {}\n", doc_id);
3231

3332
// 3. List documents
3433
println!("Documents:");
35-
for doc in client.list_documents().await? {
34+
for doc in engine.list().await? {
3635
println!(" - {} ({})", doc.name, doc.id);
3736
}
3837
println!();
3938

4039
// 4. Query
41-
match client.query(&doc_id, "What is vectorless?").await {
40+
match engine
41+
.query(QueryContext::new("What is vectorless?").with_doc_id(&doc_id))
42+
.await
43+
{
4244
Ok(result) => {
43-
println!("Query score: {:.2}", result.score);
45+
println!("Score: {:.2}", result.score);
4446
if !result.content.is_empty() {
4547
let preview: String = result.content.chars().take(150).collect();
4648
println!("Result: {}...", preview);
@@ -50,14 +52,9 @@ async fn main() -> vectorless::Result<()> {
5052
}
5153
println!();
5254

53-
// 5. Clone for concurrent use (client is Clone + Send + Sync)
54-
let _client1 = client.clone();
55-
let _client2 = client.clone();
56-
println!("✓ Client cloned for concurrent use\n");
57-
58-
// 6. Cleanup
59-
client.remove(&doc_id).await?;
60-
println!("✓ Removed: {}", doc_id);
55+
// 5. Cleanup
56+
engine.remove(&doc_id).await?;
57+
println!("Removed: {}", doc_id);
6158

6259
println!("\n=== Done ===");
6360
Ok(())

examples/rust/batch_processing.rs

Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,29 @@
44
//! Batch document processing example.
55
//!
66
//! This example demonstrates how to efficiently process
7-
//! multiple documents in batch mode using sessions.
7+
//! multiple documents in batch mode.
88
//!
99
//! # Usage
1010
//!
1111
//! ```bash
1212
//! cargo run --example batch_processing
1313
//! ```
1414
15-
use vectorless::client::{EngineBuilder, IndexContext};
15+
use vectorless::client::{EngineBuilder, IndexContext, QueryContext};
1616

1717
#[tokio::main]
1818
async fn main() -> Result<(), Box<dyn std::error::Error>> {
1919
println!("=== Batch Document Processing Example ===\n");
2020

21-
// 1. Create engine and session
21+
// 1. Create engine
2222
println!("Step 1: Setting up...");
2323
let engine = EngineBuilder::new()
2424
.with_workspace("./workspace_batch_example")
2525
.build()
2626
.await
2727
.map_err(|e: vectorless::BuildError| vectorless::Error::Config(e.to_string()))?;
2828

29-
let session = engine.session().await;
30-
println!(" ✓ Session created: {}\n", session.id());
29+
println!(" ✓ Engine created\n");
3130

3231
// 2. Create sample documents
3332
println!("Step 2: Creating sample documents...");
@@ -1059,8 +1058,9 @@ Implement authentication for production.
10591058

10601059
for (name, _) in &documents {
10611060
let path = temp_dir.path().join(name);
1062-
match session.index(IndexContext::from_path(&path)).await {
1063-
Ok(doc_id) => {
1061+
match engine.index(IndexContext::from_path(&path)).await {
1062+
Ok(result) => {
1063+
let doc_id = result.doc_id().unwrap().to_string();
10641064
doc_ids.push(doc_id);
10651065
}
10661066
Err(e) => {
@@ -1077,14 +1077,13 @@ Implement authentication for production.
10771077
);
10781078
println!();
10791079

1080-
// 4. Show session stats
1081-
println!("Step 4: Session statistics:");
1082-
let stats = session.stats();
1080+
// 4. Show indexed documents
1081+
println!("Step 4: Indexed documents:");
1082+
let docs = engine.list().await?;
10831083
println!(
1084-
" - Documents in session: {}",
1085-
session.list_documents().len()
1084+
" - Documents indexed: {}",
1085+
docs.len()
10861086
);
1087-
println!(" - Queries: {}", stats.query_count.get());
10881087
println!();
10891088

10901089
// 5. Batch query with progress
@@ -1106,16 +1105,22 @@ Implement authentication for production.
11061105
let mut success_count = 0;
11071106

11081107
for query in &queries {
1109-
match session.query_all(query).await {
1110-
Ok(results) => {
1111-
if !results.is_empty() {
1112-
success_count += 1;
1108+
// Query against each indexed document, count as success if any returns content
1109+
let mut found = false;
1110+
for doc_id in &doc_ids {
1111+
match engine.query(QueryContext::new(*query).with_doc_id(doc_id)).await {
1112+
Ok(result) => {
1113+
if !result.content.is_empty() {
1114+
found = true;
1115+
break;
1116+
}
11131117
}
1114-
}
1115-
Err(e) => {
1116-
eprintln!(" ✗ Query failed: {}", e);
1118+
Err(_) => {}
11171119
}
11181120
}
1121+
if found {
1122+
success_count += 1;
1123+
}
11191124
}
11201125

11211126
let elapsed = start.elapsed();
@@ -1132,16 +1137,8 @@ Implement authentication for production.
11321137

11331138
// 6. Final statistics
11341139
println!("Step 6: Final statistics:");
1135-
let stats = session.stats();
1136-
println!(" - Total documents: {}", session.list_documents().len());
1137-
println!(" - Total queries: {}", stats.query_count.get());
1138-
println!(" - Cache hits: {}", stats.cache_hits.get());
1139-
println!(" - Cache misses: {}", stats.cache_misses.get());
1140-
println!(" - Cache hit rate: {:.1}%", stats.cache_hit_rate() * 100.0);
1141-
if let Some(avg_time) = stats.avg_query_time() {
1142-
println!(" - Avg query time: {:?}", avg_time);
1143-
}
1144-
println!(" - Session age: {:?}", session.age());
1140+
let docs = engine.list().await?;
1141+
println!(" - Total documents: {}", docs.len());
11451142
println!();
11461143

11471144
// 7. Cleanup

examples/rust/custom_config.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
//! cargo run --example custom_config
1313
//! ```
1414
15-
use vectorless::{EngineBuilder, IndexContext};
15+
use vectorless::{EngineBuilder, IndexContext, QueryContext};
1616

1717
#[tokio::main]
1818
async fn main() -> vectorless::Result<()> {
@@ -43,11 +43,14 @@ async fn main() -> vectorless::Result<()> {
4343
println!("✓ Client created with custom settings\n");
4444

4545
// Index a document
46-
let doc_id = client.index(IndexContext::from_path("./README.md")).await?;
46+
let index_result = client.index(IndexContext::from_path("./README.md")).await?;
47+
let doc_id = index_result.doc_id().unwrap().to_string();
4748
println!("✓ Indexed: {}\n", doc_id);
4849

4950
// Query
50-
let result = client.query(&doc_id, "What is Vectorless?").await?;
51+
let result = client
52+
.query(QueryContext::new("What is Vectorless?").with_doc_id(&doc_id))
53+
.await?;
5154
println!("Query: What is Vectorless?");
5255
println!("Score: {:.2}", result.score);
5356
if !result.content.is_empty() {

examples/rust/events.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use std::sync::Arc;
1818
use std::sync::atomic::{AtomicUsize, Ordering};
1919

20-
use vectorless::client::{EngineBuilder, EventEmitter, IndexContext, IndexEvent, QueryEvent};
20+
use vectorless::client::{EngineBuilder, EventEmitter, IndexContext, IndexEvent, QueryContext, QueryEvent};
2121

2222
#[tokio::main]
2323
async fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -123,14 +123,15 @@ The event system uses handlers that can be attached to the engine builder.
123123
let doc_path = temp_dir.path().join("example.md");
124124
tokio::fs::write(&doc_path, doc_content).await?;
125125

126-
let doc_id = engine.index(IndexContext::from_path(&doc_path)).await?;
126+
let index_result = engine.index(IndexContext::from_path(&doc_path)).await?;
127+
let doc_id = index_result.doc_id().unwrap().to_string();
127128
println!();
128129

129130
// 4. Query the document (events will fire)
130131
println!("Step 4: Querying document (watch events)...\n");
131132

132133
let result = engine
133-
.query(&doc_id, "What features are available?")
134+
.query(QueryContext::new("What features are available?").with_doc_id(&doc_id))
134135
.await?;
135136
println!();
136137

examples/rust/html_parser.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ fn demo_engine_integration() {
244244
println!("Integration with Engine:\n");
245245

246246
println!("```rust");
247-
println!("use vectorless::{{EngineBuilder, IndexContext}};");
247+
println!("use vectorless::{{EngineBuilder, IndexContext, QueryContext}};");
248248
println!("use vectorless::parser::DocumentFormat;");
249249
println!();
250250
println!("# #[tokio::main]");
@@ -255,9 +255,10 @@ fn demo_engine_integration() {
255255
println!(" .await?;");
256256
println!();
257257
println!(" // Method 1: From HTML file");
258-
println!(" let doc_id = engine.index(");
258+
println!(" let result = engine.index(");
259259
println!(" IndexContext::from_path(\"./documentation.html\")");
260260
println!(" ).await?;");
261+
println!(" let doc_id = result.doc_id().unwrap().to_string();");
261262
println!();
262263
println!(" // Method 2: From HTML content");
263264
println!(" let html = r#\"");
@@ -270,13 +271,16 @@ fn demo_engine_integration() {
270271
println!("</html>");
271272
println!("\"#;");
272273
println!();
273-
println!(" let doc_id = engine.index(");
274+
println!(" let result = engine.index(");
274275
println!(" IndexContext::from_content(html, DocumentFormat::Html)");
275276
println!(" .with_name(\"my-document\")");
276277
println!(" ).await?;");
278+
println!(" let doc_id = result.doc_id().unwrap().to_string();");
277279
println!();
278280
println!(" // Query the indexed document");
279-
println!(" let result = engine.query(&doc_id, \"What is the introduction?\").await?;");
281+
println!(" let result = engine.query(");
282+
println!(" QueryContext::new(\"What is the introduction?\").with_doc_id(&doc_id)");
283+
println!(" ).await?;");
280284
println!(" println!(\"{{}}\", result.content);");
281285
println!();
282286
println!(" Ok(())");

examples/rust/markdownflow.rs

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
//! ```
2121
2222
use vectorless::EngineBuilder;
23-
use vectorless::client::{IndexContext, IndexOptions};
23+
use vectorless::client::{IndexContext, IndexOptions, QueryContext};
2424

2525
/// Sample markdown content for demonstration.
2626
const SAMPLE_MARKDOWN: &str = r#"
@@ -62,29 +62,19 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
6262

6363
// Check if we should generate summaries (requires API key)
6464
println!(" - API key detected, generating summaries...");
65-
let doc_id = client
65+
let index_result = client
6666
.index(IndexContext::from_path(&md_path).with_options(IndexOptions::new().with_summaries()))
6767
.await?;
68+
let doc_id = index_result.doc_id().unwrap().to_string();
6869

6970
println!(" - Document indexed successfully");
7071
println!(" - Document ID: {}", doc_id);
7172
println!();
7273

73-
// Step 3: Show document structure in JSON format
74-
println!("Step 3: Document structure (JSON):");
75-
println!();
76-
77-
match client.get_structure(&doc_id).await {
78-
Ok(tree) => {
79-
// Export to JSON format (PageIndex compatible)
80-
let structure = tree.to_structure_json("sample.md");
81-
let json = serde_json::to_string_pretty(&structure)
82-
.unwrap_or_else(|_| "Failed to serialize".to_string());
83-
println!("{}", json);
84-
}
85-
Err(e) => {
86-
println!(" - Error getting structure: {}", e);
87-
}
74+
// Step 3: List indexed documents
75+
println!("Step 3: Indexed documents:");
76+
for doc in client.list().await? {
77+
println!(" - {} ({})", doc.name, doc.id);
8878
}
8979
println!();
9080

@@ -96,7 +86,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
9686
for query in queries {
9787
println!(" Query: \"{}\"", query);
9888

99-
match client.query(&doc_id, query).await {
89+
match client.query(QueryContext::new(query).with_doc_id(&doc_id)).await {
10090
Ok(result) => {
10191
if result.content.is_empty() {
10292
println!(" - No relevant content found");

0 commit comments

Comments
 (0)