appam/lib.rs
1//! Rust agent orchestration library for tool-using, long-horizon, traceable AI systems.
2//!
3//! Appam is designed for agent workloads where the hard parts are operational:
4//! repeated tool use across multiple turns, streaming UX, session persistence,
5//! trace capture, and provider portability. The shared runtime stays provider
6//! agnostic while the provider modules own wire-format quirks, auth, retry, and
7//! streaming details.
8//!
9//! # What Appam Gives You
10//!
11//! - Rust-first agent construction through [`AgentBuilder`], [`RuntimeAgent`],
12//! and the shortcut constructors in [`agent::quick`]
13//! - typed and untyped tool definitions via [`Tool`], [`AsyncTool`],
14//! [`ToolRegistry`], [`tool`], and [`Schema`]
15//! - streaming-first execution through [`agent::streaming::StreamEvent`],
16//! [`agent::streaming_builder::StreamBuilder`], and built-in consumers
17//! - durable session history through [`SessionHistory`]
18//! - portable LLM routing across Anthropic, OpenAI, OpenAI Codex, OpenRouter,
19//! Vertex, Azure, and Bedrock via [`LlmProvider`] and [`DynamicLlmClient`]
20//!
21//! # Recommended Entry Points
22//!
23//! Use [`Agent::quick`](crate::agent::quick::Agent::quick) when you want the
24//! smallest working setup:
25//!
26//! ```no_run
27//! use appam::prelude::*;
28//!
29//! #[tokio::main]
30//! async fn main() -> Result<()> {
31//! let agent = Agent::quick(
32//! "anthropic/claude-sonnet-4-5",
33//! "You are a concise Rust assistant.",
34//! vec![],
35//! )?;
36//!
37//! agent
38//! .stream("Explain ownership in Rust in three sentences.")
39//! .on_content(|text| print!("{text}"))
40//! .run()
41//! .await?;
42//!
43//! println!();
44//! Ok(())
45//! }
46//! ```
47//!
48//! Use [`AgentBuilder`] when you need explicit runtime control:
49//!
50//! ```no_run
51//! use appam::prelude::*;
52//! use std::sync::Arc;
53//! # use serde_json::{json, Value};
54//! # struct EchoTool;
55//! # impl Tool for EchoTool {
56//! # fn name(&self) -> &str { "echo" }
57//! # fn spec(&self) -> Result<ToolSpec> {
58//! # Ok(serde_json::from_value(json!({
59//! # "type": "function",
60//! # "function": {
61//! # "name": "echo",
62//! # "description": "Echo a message",
63//! # "parameters": {
64//! # "type": "object",
65//! # "properties": {
66//! # "message": {"type": "string"}
67//! # },
68//! # "required": ["message"]
69//! # }
70//! # }
71//! # }))?)
72//! # }
73//! # fn execute(&self, args: Value) -> Result<Value> {
74//! # Ok(json!({ "output": args["message"].clone() }))
75//! # }
76//! # }
77//!
78//! #[tokio::main]
79//! async fn main() -> Result<()> {
80//! let agent = AgentBuilder::new("assistant")
81//! .provider(LlmProvider::OpenAI)
82//! .model("openai/gpt-5.4")
83//! .system_prompt("You are a careful assistant. Use tools before guessing.")
84//! .with_tool(Arc::new(EchoTool))
85//! .enable_history()
86//! .build()?;
87//!
88//! agent.run("Say hello via the echo tool.").await?;
89//! Ok(())
90//! }
91//! ```
92//!
93//! Use [`TomlAgent`] when configuration lives on disk and needs to be extended
94//! with Rust tools at runtime:
95//!
96//! ```no_run
97//! use appam::prelude::*;
98//! use std::sync::Arc;
99//! # use serde_json::{json, Value};
100//! # struct CustomTool;
101//! # impl Tool for CustomTool {
102//! # fn name(&self) -> &str { "custom" }
103//! # fn spec(&self) -> Result<ToolSpec> {
104//! # Ok(serde_json::from_value(json!({
105//! # "type": "function",
106//! # "function": {
107//! # "name": "custom",
108//! # "description": "Example custom tool",
109//! # "parameters": {"type": "object", "properties": {}}
110//! # }
111//! # }))?)
112//! # }
113//! # fn execute(&self, _: Value) -> Result<Value> { Ok(json!({"ok": true})) }
114//! # }
115//!
116//! #[tokio::main]
117//! async fn main() -> Result<()> {
118//! let agent = TomlAgent::from_file("agent.toml")?
119//! .with_additional_tool(Arc::new(CustomTool));
120//!
121//! agent.run("Use the custom tool if it helps.").await?;
122//! Ok(())
123//! }
124//! ```
125//!
126//! # Architecture
127//!
128//! Appam keeps the crate split along subsystem boundaries:
129//!
130//! - [`agent`] owns runtime orchestration, continuation logic, stream events,
131//! and session history
132//! - [`tools`] owns schemas, execution traits, managed state, and runtime lookup
133//! - [`llm`] owns provider-neutral message types plus provider-specific clients
134//! - [`config`] owns global configuration, env overrides, and builder APIs
135//! - [`logging`] owns tracing setup and persisted session logs
136//!
137//! # Security and Operational Notes
138//!
139//! - Tool calls are model output and must be treated as untrusted input.
140//! - Managed state access fails closed when state was not registered.
141//! - The legacy web API surface remains intentionally disabled; the trace
142//! visualizer helpers in [`web`] remain available.
143//! - Provider-specific secrets are never meant to be logged or embedded in
144//! trace artifacts.
145//!
146//! # Docs.rs Navigation
147//!
148//! If you are new to the crate, the most useful pages are usually:
149//!
150//! - [`prelude`] for the ergonomic import surface
151//! - [`AgentBuilder`] and [`RuntimeAgent`] for programmatic agents
152//! - [`Tool`] and [`AsyncTool`] for tool authoring
153//! - [`tool`] and [`Schema`] for macro-driven tool definitions
154//! - [`StreamBuilder`](crate::agent::streaming_builder::StreamBuilder) for
155//! closure-based streaming
156//! - [`SessionHistory`] for durable sessions
157
158#![warn(missing_docs)]
159#![warn(clippy::all)]
160#![allow(clippy::module_inception)]
161
162pub mod agent;
163pub mod config;
164pub mod http;
165pub mod llm;
166pub mod logging;
167pub mod tools;
168pub mod web;
169
170// TODO: Implement these modules in future phases
171// pub mod interface;
172
173// Re-export commonly used types for convenience
174pub use agent::history::SessionHistory;
175pub use agent::{Agent, AgentBuilder, RuntimeAgent, Session, TomlAgent};
176pub use config::{
177 load_config_from_env, load_global_config, AgentConfigBuilder, AppConfig, AppConfigBuilder,
178 HistoryConfig, LogFormat, LoggingConfig, TraceFormat,
179};
180pub use llm::{
181 DynamicLlmClient, LlmClient, LlmProvider, UnifiedMessage, UnifiedTool, UnifiedToolCall,
182};
183pub use tools::{AsyncTool, SessionState, State, Tool, ToolConcurrency, ToolContext, ToolRegistry};
184
185// Re-export procedural macros
186pub use appam_macros::{tool, Schema};
187pub use async_trait::async_trait;
188
189/// Convenient import surface for the most common Appam workflows.
190///
191/// The prelude intentionally favors the Rust-first SDK path: agent builders,
192/// quick constructors, streaming types, tool traits, macro helpers, and the
193/// most common `anyhow`, `serde`, and `tokio` re-exports used by examples.
194///
195/// Import it when you want to prototype quickly or write concise examples:
196///
197/// ```
198/// use appam::prelude::*;
199/// ```
200///
201/// For library code that exposes Appam types in its own public API, prefer
202/// importing only the specific items you need so your dependency surface stays
203/// explicit.
204///
205/// # Examples
206///
207/// ```no_run
208/// use appam::prelude::*;
209/// use anyhow::Result;
210///
211/// #[tokio::main]
212/// async fn main() -> Result<()> {
213/// // Use Agent::quick() for one-liner creation
214/// let agent = Agent::quick(
215/// "anthropic/claude-sonnet-4-5",
216/// "You are helpful.",
217/// vec![],
218/// )?;
219///
220/// // Use closure-based streaming
221/// agent
222/// .stream("Hello")
223/// .on_content(|text| print!("{}", text))
224/// .run()
225/// .await?;
226///
227/// Ok(())
228/// }
229/// ```
230pub mod prelude {
231 // Core agent types
232 pub use crate::agent::Agent as AgentTrait;
233 pub use crate::agent::{AgentBuilder, RuntimeAgent, Session, TomlAgent};
234
235 // Quick constructors and shortcuts
236 pub use crate::agent::quick::{
237 Agent, AgentBuilderAsyncToolExt, AgentBuilderToolExt, AgentQuick,
238 };
239
240 // Streaming types
241 pub use crate::agent::streaming::{StreamConsumer, StreamEvent};
242 pub use crate::agent::streaming_builder::StreamBuilder;
243
244 // Consumers
245 pub use crate::agent::consumers::{
246 CallbackConsumer, ChannelConsumer, ConsoleConsumer, TraceConsumer,
247 };
248
249 // Error types
250 pub use crate::agent::errors::{analyze_tool_error, ToolExecutionError};
251
252 // History
253 pub use crate::agent::history::{SessionHistory, SessionSummary};
254
255 // Configuration
256 pub use crate::config::{
257 load_config_from_env, load_global_config, AgentConfigBuilder, AppConfig, AppConfigBuilder,
258 HistoryConfig, LogFormat, LoggingConfig, TraceFormat,
259 };
260
261 // LLM types
262 pub use crate::llm::{
263 ChatMessage, DynamicLlmClient, LlmClient, LlmProvider, Role, ToolSpec, UnifiedMessage,
264 UnifiedTool, UnifiedToolCall,
265 };
266
267 // Tool system
268 pub use crate::tools::register::{ClosureTool, ToolRegistryExt};
269 pub use crate::tools::{
270 AsyncTool, SessionState, State, Tool, ToolConcurrency, ToolContext, ToolRegistry,
271 };
272
273 // Procedural macros
274 pub use appam_macros::{tool, Schema};
275
276 // Re-export common external types for convenience
277 pub use anyhow::{anyhow, bail, Context, Result};
278 pub use serde::{Deserialize, Serialize};
279 pub use serde_json::{json, Value};
280 pub use std::sync::Arc;
281 pub use tokio;
282}