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}