Skip to content

Commit 72ceb59

Browse files
committed
refactor(rust): separate API key and model configuration in EngineBuilder
BREAKING CHANGE: Split the combined model and API key configuration into separate methods. The `with_model` method now only accepts the model name without an optional API key. The API key must be set using the new `with_key` method or through environment variables. Examples updated to reflect the new API: - `with_openai` replaced with `with_key` - `with_model` no longer accepts API key parameter The builder methods are now: - `with_model(name)` - sets only the model name - `with_key(key)` - sets only the API key - `with_endpoint(url)` - sets the API endpoint
1 parent 3436c18 commit 72ceb59

7 files changed

Lines changed: 59 additions & 148 deletions

File tree

examples/rust/advanced.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ async fn main() -> vectorless::Result<()> {
5858
println!(" 2. Auto-detected config file (vectorless.toml, config.toml, .vectorless.toml)");
5959
println!(" 3. Explicit config file (with_config_path)");
6060
println!(" 4. Environment variables (OPENAI_API_KEY, VECTORLESS_MODEL, etc.)");
61-
println!(" 5. Builder methods (with_openai, with_model, etc.)");
61+
println!(" 5. Builder methods (with_key, with_model, with_endpoint)");
6262
println!();
6363
println!("Environment Variables:");
6464
println!(" OPENAI_API_KEY - LLM API key");

examples/rust/custom_config.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ async fn main() -> vectorless::Result<()> {
3434
// Example: Use DeepSeek API
3535
let client = EngineBuilder::new()
3636
.with_workspace("./workspace")
37-
.with_model("deepseek-chat", Some("sk-your-deepseek-key".to_string()))
37+
.with_model("deepseek-chat")
38+
.with_key("sk-your-deepseek-key")
3839
.with_endpoint("https://api.deepseek.com/v1")
3940
.build()
4041
.await
@@ -69,23 +70,25 @@ async fn main() -> vectorless::Result<()> {
6970
// Azure OpenAI:
7071
// let client = EngineBuilder::new()
7172
// .with_workspace("./workspace")
72-
// .with_model("gpt-4o", Some("your-azure-key".to_string()))
73+
// .with_model("gpt-4o")
74+
// .with_key("your-azure-key")
7375
// .with_endpoint("https://your-resource.openai.azure.com/openai/deployments/your-deployment")
7476
// .build()
7577
// .await?;
7678

7779
// Local LLM (e.g., Ollama with OpenAI-compatible API):
7880
// let client = EngineBuilder::new()
7981
// .with_workspace("./workspace")
80-
// .with_model("llama3", None) // No API key needed
82+
// .with_model("llama3")
8183
// .with_endpoint("http://localhost:11434/v1")
8284
// .build()
8385
// .await?;
8486

8587
// Anthropic Claude (via OpenAI-compatible proxy):
8688
// let client = EngineBuilder::new()
8789
// .with_workspace("./workspace")
88-
// .with_model("claude-3-5-sonnet-20241022", Some("sk-ant-...".to_string()))
90+
// .with_model("claude-3-5-sonnet-20241022")
91+
// .with_key("sk-ant-...")
8992
// .with_endpoint("https://api.anthropic.com/v1")
9093
// .build()
9194
// .await?;

examples/rust/events.rs

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

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

2223
#[tokio::main]
2324
async fn main() -> Result<(), Box<dyn std::error::Error>> {

python/src/lib.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -439,19 +439,16 @@ impl PyEngine {
439439
builder = builder.with_workspace(ws);
440440
}
441441

442-
// Set model first (without overriding api_key)
443442
if let Some(m) = &model {
444-
builder = builder.with_model(m, None);
443+
builder = builder.with_model(m);
445444
}
446445

447-
// Set endpoint
448446
if let Some(e) = &endpoint {
449447
builder = builder.with_endpoint(e);
450448
}
451449

452-
// Set API key last (this ensures it's not overwritten)
453450
if let Some(key) = resolved_api_key {
454-
builder = builder.with_openai(key);
451+
builder = builder.with_key(key);
455452
}
456453

457454
builder.build().await

rust/src/client/builder.rs

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -283,10 +283,9 @@ impl EngineBuilder {
283283
// LLM Configuration
284284
// ============================================================
285285

286-
/// Configure for OpenAI API.
286+
/// Set the LLM API key.
287287
///
288-
/// Sets the API key and optionally the model to "gpt-4o" if not already set.
289-
/// Use [`with_model`](EngineBuilder::with_model) before this to specify a different model.
288+
/// If not set, reads from `OPENAI_API_KEY` environment variable.
290289
///
291290
/// # Example
292291
///
@@ -297,29 +296,21 @@ impl EngineBuilder {
297296
/// # async fn main() -> Result<(), vectorless::BuildError> {
298297
/// let engine = EngineBuilder::new()
299298
/// .with_workspace("./data")
300-
/// .with_openai(std::env::var("OPENAI_API_KEY").unwrap())
299+
/// .with_key("sk-...")
301300
/// .build()
302301
/// .await?;
303302
/// # Ok(())
304303
/// # }
305304
/// ```
306305
#[must_use]
307-
pub fn with_openai(self, api_key: impl Into<String>) -> Self {
308-
let mut builder = self;
309-
builder.api_key = Some(api_key.into());
310-
// Only set default model if not already set
311-
if builder.model.is_none() {
312-
builder.model = Some("gpt-4o".to_string());
313-
}
314-
builder
306+
pub fn with_key(mut self, key: impl Into<String>) -> Self {
307+
self.api_key = Some(key.into());
308+
self
315309
}
316310

317-
/// Set the LLM model and optional API key.
311+
/// Set the LLM model name.
318312
///
319-
/// # Arguments
320-
///
321-
/// * `model` - Model name (e.g., "gpt-4o", "gpt-4o-mini", "claude-3-5-sonnet")
322-
/// * `api_key` - Optional API key (uses environment variable if not provided)
313+
/// Default: "gpt-4o".
323314
///
324315
/// # Example
325316
///
@@ -330,18 +321,15 @@ impl EngineBuilder {
330321
/// # async fn main() -> Result<(), vectorless::BuildError> {
331322
/// let engine = EngineBuilder::new()
332323
/// .with_workspace("./data")
333-
/// .with_model("gpt-4o-mini", Some("sk-...".to_string()))
324+
/// .with_model("gpt-4o-mini")
334325
/// .build()
335326
/// .await?;
336327
/// # Ok(())
337328
/// # }
338329
/// ```
339330
#[must_use]
340-
pub fn with_model(mut self, model: impl Into<String>, api_key: Option<String>) -> Self {
331+
pub fn with_model(mut self, model: impl Into<String>) -> Self {
341332
self.model = Some(model.into());
342-
if api_key.is_some() {
343-
self.api_key = api_key;
344-
}
345333
self
346334
}
347335

@@ -358,7 +346,7 @@ impl EngineBuilder {
358346
/// # async fn main() -> Result<(), vectorless::BuildError> {
359347
/// let engine = EngineBuilder::new()
360348
/// .with_workspace("./data")
361-
/// .with_model("deepseek-chat", Some("sk-...".to_string()))
349+
/// .with_model("deepseek-chat")
362350
/// .with_endpoint("https://api.deepseek.com/v1")
363351
/// .build()
364352
/// .await?;
@@ -699,18 +687,27 @@ mod tests {
699687
}
700688

701689
#[test]
702-
fn test_builder_with_openai() {
703-
let builder = EngineBuilder::new().with_openai("sk-test-key");
690+
fn test_builder_with_key() {
691+
let builder = EngineBuilder::new().with_key("sk-test-key");
704692

705-
assert_eq!(builder.model, Some("gpt-4o".to_string()));
706693
assert_eq!(builder.api_key, Some("sk-test-key".to_string()));
707694
}
708695

709696
#[test]
710697
fn test_builder_with_model() {
711-
let builder = EngineBuilder::new().with_model("gpt-4o-mini", Some("sk-test".to_string()));
698+
let builder = EngineBuilder::new().with_model("gpt-4o-mini");
699+
700+
assert_eq!(builder.model, Some("gpt-4o-mini".to_string()));
701+
}
702+
703+
#[test]
704+
fn test_builder_with_key_and_model() {
705+
let builder = EngineBuilder::new()
706+
.with_model("gpt-4o-mini")
707+
.with_key("sk-test");
712708

713709
assert_eq!(builder.model, Some("gpt-4o-mini".to_string()));
710+
assert_eq!(builder.api_key, Some("sk-test".to_string()));
714711
}
715712

716713
#[test]

rust/src/client/mod.rs

Lines changed: 15 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,12 @@
77
//! - [`Engine`] — The main client for indexing and querying documents
88
//! - [`EngineBuilder`] — Builder pattern for client configuration
99
//! - [`IndexContext`] — Unified input for document indexing
10-
//! - [`Session`] — Multi-document session management
11-
//!
12-
//! # Architecture
13-
//!
14-
//! The client module is organized into specialized sub-modules:
15-
//!
16-
//! ```text
17-
//! client/
18-
//! ├── mod.rs → Re-exports and documentation
19-
//! ├── engine.rs → Main orchestrator
20-
//! ├── builder.rs → Builder pattern
21-
//! ├── index_context.rs → Index input types
22-
//! ├── types.rs → Public API types
23-
//! ├── context.rs → Request context and configuration
24-
//! ├── session.rs → Session management
25-
//! ├── indexer.rs → Document indexing operations
26-
//! ├── retriever.rs → Query and retrieval operations
27-
//! ├── workspace.rs → Workspace CRUD operations
28-
//! └── events.rs → Event system and callbacks
29-
//! ```
10+
//! - [`QueryContext`] — Unified input for document queries
3011
//!
3112
//! # Quick Start
3213
//!
3314
//! ```rust,no_run
34-
//! use vectorless::client::{Engine, EngineBuilder, IndexContext};
15+
//! use vectorless::client::{EngineBuilder, IndexContext, QueryContext};
3516
//!
3617
//! # #[tokio::main]
3718
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -41,59 +22,30 @@
4122
//! .build()
4223
//! .await?;
4324
//!
44-
//! // Index a document from file
45-
//! let doc_id = client.index(IndexContext::from_path("./document.md")).await?;
46-
//!
47-
//! // Index HTML content directly
48-
//! let html = "<html><body><h1>Title</h1><p>Content</p></body></html>";
49-
//! let doc_id2 = client.index(
50-
//! IndexContext::from_content(html, vectorless::parser::DocumentFormat::Html)
51-
//! .with_name("webpage")
52-
//! ).await?;
25+
//! // Index a document
26+
//! let result = client.index(IndexContext::from_path("./document.md")).await?;
27+
//! let doc_id = result.doc_id().unwrap();
5328
//!
5429
//! // Query the document
55-
//! let result = client.query(&doc_id, "What is this?").await?;
30+
//! let result = client.query(
31+
//! QueryContext::new("What is this?").with_doc_id(doc_id)
32+
//! ).await?;
5633
//! println!("{}", result.content);
5734
//!
5835
//! // List all documents
59-
//! for doc in client.list_documents().await? {
36+
//! for doc in client.list().await? {
6037
//! println!("{}: {}", doc.id, doc.name);
6138
//! }
6239
//! # Ok(())
6340
//! # }
6441
//! ```
6542
//!
66-
//! # Session-Based Operations
67-
//!
68-
//! For multi-document operations, use sessions:
69-
//!
70-
//! ```rust,no_run
71-
//! # use vectorless::client::{Engine, EngineBuilder, IndexContext};
72-
//! # #[tokio::main]
73-
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
74-
//! let client = EngineBuilder::new()
75-
//! .with_workspace("./workspace")
76-
//! .build()
77-
//! .await?;
78-
//!
79-
//! let session = client.session().await;
80-
//!
81-
//! // Index multiple documents
82-
//! let doc1 = session.index(IndexContext::from_path("./doc1.md")).await?;
83-
//! let doc2 = session.index(IndexContext::from_path("./doc2.md")).await?;
84-
//!
85-
//! // Query across all documents
86-
//! let results = session.query_all("What is the architecture?").await?;
87-
//! # Ok(())
88-
//! # }
89-
//! ```
90-
//!
9143
//! # Events and Progress
9244
//!
9345
//! Monitor operation progress with events:
9446
//!
9547
//! ```rust,no_run
96-
//! # use vectorless::client::{Engine, EngineBuilder, EventEmitter, IndexEvent};
48+
//! # use vectorless::client::{EngineBuilder, EventEmitter, IndexEvent};
9749
//! # #[tokio::main]
9850
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
9951
//! let events = EventEmitter::new()
@@ -109,14 +61,6 @@
10961
//! # Ok(())
11062
//! # }
11163
//! ```
112-
//!
113-
//! # Features
114-
//!
115-
//! - **Document Indexing** — Parse and index Markdown, PDF, and text files
116-
//! - **Tree-Based Structure** — Documents organized as hierarchical trees
117-
//! - **Workspace Persistence** — Save and load indexed documents
118-
//! - **Session Management** — Multi-document operations with caching
119-
//! - **Event System** — Progress callbacks and monitoring
12064
12165
mod builder;
12266
mod context;
@@ -138,61 +82,28 @@ pub use builder::{BuildError, EngineBuilder};
13882
pub use engine::Engine;
13983

14084
// ============================================================
141-
// Index Context
142-
// ============================================================
143-
144-
pub use index_context::{IndexContext, IndexSource};
145-
146-
// ============================================================
147-
// Query Context
85+
// Context Types
14886
// ============================================================
14987

88+
pub use index_context::IndexContext;
15089
pub use query_context::QueryContext;
15190

15291
// ============================================================
153-
// Sub-Clients
92+
// Events
15493
// ============================================================
15594

156-
pub use indexer::IndexerClient;
157-
pub use retriever::RetrieverClient;
158-
pub use session::Session;
159-
pub use workspace::WorkspaceClient;
95+
pub use events::EventEmitter;
16096

16197
// ============================================================
162-
// Context and Events
163-
// ============================================================
164-
165-
pub use context::{ClientContext, FeatureFlags, RequestContextConfig};
166-
pub use events::{
167-
AsyncEventHandler, Event, EventEmitter, EventHandler, IndexEvent, QueryEvent, WorkspaceEvent,
168-
};
169-
170-
// ============================================================
171-
// Types
98+
// Result & Info Types
17299
// ============================================================
173100

174101
pub use types::{
175-
// Error types
176102
ClientError,
177-
// Document info
178103
DocumentInfo,
179-
// Index types
180104
IndexItem,
181105
IndexMode,
182106
IndexOptions,
183107
IndexResult,
184-
// Document types
185-
IndexedDocument,
186-
PageContent,
187-
// Query types
188108
QueryResult,
189109
};
190-
191-
// ============================================================
192-
// Sub-Client Types
193-
// ============================================================
194-
195-
pub use indexer::{IndexerConfig, ValidationResult};
196-
pub use retriever::{NodeContext, RetrieverClientConfig};
197-
pub use session::{EvictionPolicy, PreloadStrategy, SessionConfig, SessionStats};
198-
pub use workspace::{WorkspaceClientConfig, WorkspaceStats};

0 commit comments

Comments
 (0)