Skip to main content

open_agent/
lib.rs

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