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