Skip to main content

open_agent/
lib.rs

1//! # Open Agent SDK - Rust Implementation
2//!
3//! A production-ready, streaming-first Rust SDK for building AI agents with local OpenAI-compatible servers.
4//!
5//! ## Overview
6//!
7//! This SDK provides a clean, ergonomic API for working with local LLM servers such as:
8//! - LM Studio
9//! - Ollama
10//! - llama.cpp
11//! - vLLM
12//!
13//! ## Key Features
14//!
15//! - **Zero API Costs**: Run models on your own hardware
16//! - **Privacy-First**: All data stays local on your machine
17//! - **High Performance**: Native async/await with Tokio runtime
18//! - **Streaming Responses**: Real-time token-by-token streaming
19//! - **Finish Reasons**: Every stream reports why generation stopped
20//! - **Tool Calling**: Define and execute tools with automatic schema generation
21//! - **Lifecycle Hooks**: Intercept and control execution at key points
22//! - **Interrupts**: Gracefully cancel long-running operations
23//! - **Context Management**: Manual token estimation and history truncation
24//! - **Retry Logic**: Exponential backoff with jitter for reliability
25//!
26//! ## Two Interaction Modes
27//!
28//! ### 1. Simple Query Function (`query()`)
29//! For single-turn interactions without conversation state:
30//!
31//! ```rust,no_run
32//! use open_agent::{query, AgentOptions, ContentBlock, FinishReason, StreamEvent};
33//! use futures::StreamExt;
34//!
35//! #[tokio::main]
36//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
37//!     // Configure the agent with required settings
38//!     let options = AgentOptions::builder()
39//!         .system_prompt("You are a helpful assistant")
40//!         .model("qwen2.5-32b-instruct")
41//!         .base_url("http://localhost:1234/v1")
42//!         .build()?;
43//!
44//!     // Send a single query and stream the response
45//!     let mut stream = query("What's the capital of France?", &options).await?;
46//!
47//!     // Process each event as it arrives; the stream always ends with one Finish
48//!     while let Some(event) = stream.next().await {
49//!         match event? {
50//!             StreamEvent::Block(ContentBlock::Text(text_block)) => {
51//!                 print!("{}", text_block.text);
52//!             }
53//!             StreamEvent::Block(ContentBlock::ToolUse(tool_block)) => {
54//!                 println!("Tool called: {}", tool_block.name());
55//!             }
56//!             StreamEvent::Finish(FinishReason::Length) => {
57//!                 eprintln!("response was truncated at the token cap");
58//!             }
59//!             _ => {}
60//!         }
61//!     }
62//!
63//!     Ok(())
64//! }
65//! ```
66//!
67//! ### 2. Client Object (`Client`)
68//! For multi-turn conversations with persistent state:
69//!
70//! ```rust,no_run
71//! use open_agent::{Client, AgentOptions, ContentBlock};
72//! use futures::StreamExt;
73//!
74//! #[tokio::main]
75//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
76//!     let options = AgentOptions::builder()
77//!         .system_prompt("You are a helpful assistant")
78//!         .model("qwen2.5-32b-instruct")
79//!         .base_url("http://localhost:1234/v1")
80//!         .build()?;
81//!
82//!     // Create a stateful client that maintains conversation history
83//!     let mut client = Client::new(options)?;
84//!
85//!     // First turn
86//!     client.send("What's 2+2?").await?;
87//!     while let Some(block) = client.receive().await? {
88//!         match block {
89//!             ContentBlock::Text(text) => print!("{}", text.text),
90//!             ContentBlock::ToolUse(_) | ContentBlock::ToolResult(_) | ContentBlock::Image(_) => {}
91//!         }
92//!     }
93//!
94//!     // Second turn - client remembers previous context
95//!     client.send("What about if we multiply that by 3?").await?;
96//!     while let Some(block) = client.receive().await? {
97//!         match block {
98//!             ContentBlock::Text(text) => print!("{}", text.text),
99//!             ContentBlock::ToolUse(_) | ContentBlock::ToolResult(_) | ContentBlock::Image(_) => {}
100//!         }
101//!     }
102//!
103//!     Ok(())
104//! }
105//! ```
106//!
107//! ## Architecture
108//!
109//! The SDK is organized into several modules, each with a specific responsibility:
110//!
111//! - **client**: Core streaming query engine and multi-turn client
112//! - **types**: Data structures for messages, content blocks, and configuration
113//! - **tools**: Tool definition system with automatic JSON schema generation
114//! - **hooks**: Lifecycle event system for intercepting execution
115//! - **config**: Provider-specific configuration helpers
116//! - **error**: Comprehensive error types and conversions
117//! - **context**: Token estimation and message truncation utilities
118//! - **retry**: Exponential backoff retry logic with jitter
119//! - **utils**: Internal utilities for SSE parsing and tool aggregation
120
121// ============================================================================
122// MODULE DECLARATIONS
123// ============================================================================
124// These modules are private (internal implementation details) unless explicitly
125// re-exported through `pub use` statements below.
126
127/// Core client implementation providing streaming queries and stateful conversations.
128/// Contains the `query()` function for single-turn queries and `Client` struct
129/// for multi-turn conversations with automatic state management.
130mod client;
131
132/// Provider configuration helpers for LM Studio, Ollama, llama.cpp, and vLLM.
133/// Simplifies endpoint and model name resolution with environment variable support.
134mod config;
135
136/// Context window management utilities for token estimation and history truncation.
137/// Provides manual control over conversation memory to prevent context overflow.
138mod context;
139
140/// Error types and conversions for comprehensive error handling throughout the SDK.
141/// Defines the `Error` enum and `Result<T>` type alias used across all public APIs.
142mod error;
143
144/// Lifecycle hooks system for intercepting and controlling execution at key points.
145/// Enables security gates, audit logging, input/output modification, and compliance checks.
146mod hooks;
147
148/// Tool definition and execution system with automatic JSON schema generation.
149/// Allows LLMs to call Rust functions with type-safe parameter handling.
150mod tools;
151
152/// Core type definitions for messages, content blocks, and agent configuration.
153/// Includes builder patterns for ergonomic configuration and OpenAI API serialization.
154mod types;
155
156/// Internal utilities for Server-Sent Events (SSE) parsing and tool call aggregation.
157/// Handles the low-level details of streaming response parsing.
158mod utils;
159
160// ============================================================================
161// PUBLIC EXPORTS
162// ============================================================================
163// These items form the public API of the SDK. Everything else is internal.
164
165/// Retry utilities with exponential backoff and jitter.
166/// Made public as a module so users can access retry configuration and functions
167/// for their own operations that need retry logic.
168pub mod retry;
169
170// --- Core Client API ---
171
172pub use client::{Client, EventStream, query};
173
174// --- Provider Configuration ---
175
176pub use config::{Provider, get_base_url, get_model};
177
178// --- Context Management ---
179
180pub use context::{estimate_tokens, is_approaching_limit, truncate_messages};
181
182// --- Error Handling ---
183
184pub use error::{Error, Result};
185
186// --- Lifecycle Hooks ---
187
188pub use hooks::{
189    HOOK_POST_TOOL_USE, HOOK_PRE_TOOL_USE, HOOK_USER_PROMPT_SUBMIT, HookDecision, Hooks,
190    PostToolUseEvent, PreToolUseEvent, UserPromptSubmitEvent,
191};
192
193// --- Tool System ---
194
195pub use tools::{Tool, ToolBuilder, tool};
196
197// --- Core Types ---
198
199pub use types::{
200    AgentOptions, AgentOptionsBuilder, ApiProtocol, BaseUrl, ContentBlock, FinishReason,
201    ImageBlock, ImageDetail, Message, MessageRole, ModelName, OpenAIContent, OpenAIContentPart,
202    StreamEvent, Temperature, TextBlock, ToolResultBlock, ToolUseBlock,
203};
204
205// --- Anthropic Wire Format ---
206//
207// Exported for the same reason the OpenAI wire types are: a caller building a gateway, a
208// recording proxy, or a test double needs to name what goes over the wire.
209
210pub use types::{
211    AnthropicBlockStart, AnthropicDelta, AnthropicErrorBody, AnthropicEvent, AnthropicMessage,
212    AnthropicMessageDelta, AnthropicRequest, anthropic_finish_reason,
213};
214
215// `AnthropicRequest::from_openai` takes an `OpenAIRequest`, so the request half of the
216// OpenAI wire format has to be nameable for that constructor to be callable at all.
217pub use types::{OpenAIFunction, OpenAIMessage, OpenAIRequest, OpenAIToolCall};
218
219// ============================================================================
220// CONVENIENCE PRELUDE
221// ============================================================================
222
223/// Convenience module containing the most commonly used types and functions.
224/// Import with `use open_agent::prelude::*;` to get everything you need for typical usage.
225///
226/// This includes:
227/// - Configuration: AgentOptions, AgentOptionsBuilder
228/// - Client: Client, query()
229/// - Content: ContentBlock, TextBlock, ToolUseBlock
230/// - Streaming: StreamEvent, FinishReason
231/// - Tools: Tool, tool()
232/// - Hooks: Hooks, HookDecision, hook event types
233/// - Errors: Error, Result
234pub mod prelude {
235    pub use crate::{
236        AgentOptions, AgentOptionsBuilder, ApiProtocol, BaseUrl, Client, ContentBlock, Error,
237        FinishReason, HookDecision, Hooks, ModelName, PostToolUseEvent, PreToolUseEvent, Result,
238        StreamEvent, Temperature, TextBlock, Tool, ToolUseBlock, UserPromptSubmitEvent, query,
239        tool,
240    };
241}