1use std::collections::HashSet;
2use std::sync::Arc;
3
4use crate::llm::{LlmClient, ReasoningConfig};
5use crate::tool::{Tool, ToolPolicy, ToolRegistry};
6use crate::types::{AgentConfig, ResponseFormat, RetryConfig, SessionIdGenerator, AtomicU64SessionIdGenerator};
7
8use super::approval::ApprovalHandler;
9use super::context::ContextWindowManager;
10use super::middleware::{Middleware, MiddlewareRef};
11use super::recovery::{StopOnError, ToolErrorRecovery};
12use super::session_store::{InMemorySessionStore, SessionStore};
13use super::AgentRuntime;
14
15pub struct AgentBuilder {
16 client: Arc<dyn LlmClient>,
17 config: AgentConfig,
18 tools: ToolRegistry,
19 tool_names: HashSet<String>,
20 approval_handler: Option<Arc<dyn ApprovalHandler>>,
21 tool_policy: Option<Arc<dyn ToolPolicy>>,
22 middlewares: Vec<MiddlewareRef>,
23 context_manager: Option<ContextWindowManager>,
24 session_store: Option<Arc<dyn SessionStore>>,
25 error_recovery: Option<Arc<dyn ToolErrorRecovery>>,
26 event_bus_capacity: usize,
27 session_id_generator: Option<Arc<dyn SessionIdGenerator>>,
28}
29
30impl AgentBuilder {
31 pub fn new(client: Arc<dyn LlmClient>) -> Self {
32 Self {
33 client,
34 config: AgentConfig::default(),
35 tools: ToolRegistry::default(),
36 tool_names: HashSet::new(),
37 approval_handler: None,
38 tool_policy: None,
39 middlewares: Vec::new(),
40 context_manager: None,
41 session_store: None,
42 error_recovery: None,
43 event_bus_capacity: 2048,
44 session_id_generator: None,
45 }
46 }
47
48 pub fn event_bus_capacity(mut self, capacity: usize) -> Self {
49 self.event_bus_capacity = capacity;
50 self
51 }
52
53 pub fn session_id_generator(mut self, generator: Arc<dyn SessionIdGenerator>) -> Self {
54 self.session_id_generator = Some(generator);
55 self
56 }
57
58 pub fn system_prompt(mut self, system_prompt: impl Into<String>) -> Self {
59 self.config.system_prompt = Some(system_prompt.into());
60 self
61 }
62
63 pub fn enable_thought(mut self, enable: bool) -> Self {
69 self.config.enable_thought = enable;
70 self
71 }
72
73 pub fn reasoning(mut self, config: ReasoningConfig) -> Self {
74 self.config.reasoning = Some(config);
75 self
76 }
77
78 pub fn enable_thinking(mut self, enable: bool) -> Self {
83 let mut config = self.config.reasoning.take().unwrap_or_default();
84 config.enabled = Some(enable);
85 self.config.reasoning = Some(config);
86 self
87 }
88
89 pub fn thinking_budget(mut self, budget: u64) -> Self {
90 let mut config = self.config.reasoning.take().unwrap_or_default();
91 config.budget_tokens = Some(budget);
92 self.config.reasoning = Some(config);
93 self
94 }
95
96 pub fn tool_timeout(mut self, timeout_ms: u64) -> Self {
97 self.config.tool.tool_timeout_ms = Some(timeout_ms);
98 self
99 }
100
101 pub fn max_tool_output_chars(mut self, max_chars: usize) -> Self {
102 self.config.tool.max_tool_output_chars = Some(max_chars);
103 self
104 }
105
106 pub fn register_tool(mut self, tool: impl Tool + 'static) -> Self {
107 self.tools.register(tool);
108 self
109 }
110
111 pub fn register_tool_arc(mut self, tool: Arc<dyn Tool>) -> Self {
112 self.tools.register_arc(tool);
113 self
114 }
115
116 pub fn approval_handler(mut self, handler: Arc<dyn ApprovalHandler>) -> Self {
117 self.approval_handler = Some(handler);
118 self
119 }
120
121 pub fn tool_policy(mut self, policy: Arc<dyn ToolPolicy>) -> Self {
122 self.tool_policy = Some(policy);
123 self
124 }
125
126 pub fn middleware(mut self, mw: impl Middleware + 'static) -> Self {
127 self.middlewares.push(Arc::new(mw));
128 self
129 }
130
131 pub fn context_window(mut self, max_tokens: usize) -> Self {
132 self.context_manager = Some(ContextWindowManager::new(max_tokens));
133 self
134 }
135
136 pub fn context_window_manager(mut self, manager: ContextWindowManager) -> Self {
137 self.context_manager = Some(manager);
138 self
139 }
140
141 pub fn response_format(mut self, format: ResponseFormat) -> Self {
142 self.config.llm.response_format = Some(format);
143 self
144 }
145
146 pub fn llm_retry(mut self, retry: RetryConfig) -> Self {
147 self.config.llm.llm_retry = Some(retry);
148 self
149 }
150
151 pub fn session_store(mut self, store: Arc<dyn SessionStore>) -> Self {
152 self.session_store = Some(store);
153 self
154 }
155
156 pub fn error_recovery(mut self, recovery: Arc<dyn ToolErrorRecovery>) -> Self {
157 self.error_recovery = Some(recovery);
158 self
159 }
160
161 pub fn max_sessions(mut self, max: usize) -> Self {
162 self.config.session.max_sessions = Some(max);
163 self
164 }
165
166 pub fn max_turns_per_session(mut self, max: usize) -> Self {
167 self.config.session.max_turns_per_session = Some(max);
168 self
169 }
170
171 pub fn max_message_tokens(mut self, max: usize) -> Self {
172 self.config.session.max_message_tokens = Some(max);
173 self
174 }
175
176 pub fn tool_error_retry_prompt(mut self, prompt: impl Into<String>) -> Self {
177 self.config.tool.tool_error_retry_prompt = Some(prompt.into());
178 self
179 }
180
181 pub fn language(mut self, language: crate::types::Language) -> Self {
182 self.config.language = language;
183 self
184 }
185
186 pub fn apply_if<T>(self, value: Option<T>, f: impl FnOnce(Self, T) -> Self) -> Self {
195 match value {
196 Some(v) => f(self, v),
197 None => self,
198 }
199 }
200
201 pub fn build(self) -> crate::types::AgentResult<AgentRuntime> {
202 self.config.validate()?;
203
204 tracing::info!(
205 tool_count = self.tools.len(),
206 middleware_count = self.middlewares.len(),
207 has_approval = self.approval_handler.is_some(),
208 has_context_window = self.context_manager.is_some(),
209 "building agent runtime"
210 );
211
212 let event_bus = super::runtime::EventBus::new(self.event_bus_capacity);
213
214 self.tools.inject_event_bus(&event_bus);
217
218 let session_store = self
219 .session_store
220 .unwrap_or_else(|| Arc::new(InMemorySessionStore::new()));
221 let error_recovery = self
222 .error_recovery
223 .unwrap_or_else(|| Arc::new(StopOnError));
224 let session_id_generator = self
225 .session_id_generator
226 .unwrap_or_else(|| Arc::new(AtomicU64SessionIdGenerator::default()));
227
228 let session_manager = super::runtime::SessionManager::new(
229 session_id_generator,
230 session_store,
231 self.config.session.clone(),
232 );
233
234 let llm_engine = super::runtime::LlmEngine::new(
235 self.client.clone(),
236 event_bus.clone(),
237 );
238
239 let tool_engine = super::runtime::ToolEngine::new(
240 self.tools,
241 self.approval_handler,
242 self.tool_policy,
243 error_recovery,
244 event_bus.clone(),
245 );
246
247 let runner = Arc::new(super::runtime::RuntimeCore::new(
248 self.config,
249 llm_engine,
250 tool_engine,
251 session_manager,
252 event_bus,
253 self.context_manager,
254 self.middlewares,
255 ));
256
257 Ok(AgentRuntime { runner })
258 }
259}