1use super::api::{create_async_deep_agent_from_config, create_deep_agent_from_config};
7use super::config::{DeepAgentConfig, SubAgentConfig, SummarizationConfig};
8use super::runtime::DeepAgent;
9use crate::middleware::{
10 token_tracking::{TokenTrackingConfig, TokenTrackingMiddleware},
11 HitlPolicy,
12};
13use crate::planner::LlmBackedPlanner;
14use crate::providers::{
15 AnthropicConfig, AnthropicMessagesModel, GeminiChatModel, GeminiConfig, OpenAiChatModel,
16 OpenAiConfig,
17};
18use agents_core::agent::PlannerHandle;
19use agents_core::llm::LanguageModel;
20use agents_core::persistence::Checkpointer;
21use agents_core::tools::ToolBox;
22use std::collections::{HashMap, HashSet};
23use std::sync::Arc;
24
25pub struct ConfigurableAgentBuilder {
28 instructions: String,
29 planner: Option<Arc<dyn PlannerHandle>>,
30 tools: Vec<ToolBox>,
31 subagents: Vec<SubAgentConfig>,
32 summarization: Option<SummarizationConfig>,
33 tool_interrupts: HashMap<String, HitlPolicy>,
34 builtin_tools: Option<HashSet<String>>,
35 auto_general_purpose: bool,
36 enable_prompt_caching: bool,
37 checkpointer: Option<Arc<dyn Checkpointer>>,
38 event_dispatcher: Option<Arc<agents_core::events::EventDispatcher>>,
39 enable_pii_sanitization: bool,
40 token_tracking_config: Option<TokenTrackingConfig>,
41}
42
43impl ConfigurableAgentBuilder {
44 pub fn new(instructions: impl Into<String>) -> Self {
45 Self {
46 instructions: instructions.into(),
47 planner: None,
48 tools: Vec::new(),
49 subagents: Vec::new(),
50 summarization: None,
51 tool_interrupts: HashMap::new(),
52 builtin_tools: None,
53 auto_general_purpose: true,
54 enable_prompt_caching: false,
55 checkpointer: None,
56 event_dispatcher: None,
57 enable_pii_sanitization: true, token_tracking_config: None,
59 }
60 }
61
62 pub fn with_model(mut self, model: Arc<dyn LanguageModel>) -> Self {
64 let planner: Arc<dyn PlannerHandle> = Arc::new(LlmBackedPlanner::new(model));
65 self.planner = Some(planner);
66 self
67 }
68
69 pub fn with_planner(mut self, planner: Arc<dyn PlannerHandle>) -> Self {
71 self.planner = Some(planner);
72 self
73 }
74
75 pub fn with_openai_chat(self, config: OpenAiConfig) -> anyhow::Result<Self> {
77 let model = Arc::new(OpenAiChatModel::new(config)?);
78 Ok(self.with_model(model))
79 }
80
81 pub fn with_anthropic_messages(self, config: AnthropicConfig) -> anyhow::Result<Self> {
83 let model = Arc::new(AnthropicMessagesModel::new(config)?);
84 Ok(self.with_model(model))
85 }
86
87 pub fn with_gemini_chat(self, config: GeminiConfig) -> anyhow::Result<Self> {
89 let model = Arc::new(GeminiChatModel::new(config)?);
90 Ok(self.with_model(model))
91 }
92
93 pub fn with_tool(mut self, tool: ToolBox) -> Self {
95 self.tools.push(tool);
96 self
97 }
98
99 pub fn with_tools<I>(mut self, tools: I) -> Self
101 where
102 I: IntoIterator<Item = ToolBox>,
103 {
104 self.tools.extend(tools);
105 self
106 }
107
108 pub fn with_subagent_config<I>(mut self, cfgs: I) -> Self
109 where
110 I: IntoIterator<Item = SubAgentConfig>,
111 {
112 self.subagents.extend(cfgs);
113 self
114 }
115
116 pub fn with_subagent_tools<I>(mut self, tools: I) -> Self
119 where
120 I: IntoIterator<Item = ToolBox>,
121 {
122 for tool in tools {
123 let tool_name = tool.schema().name.clone();
124 let subagent_config = SubAgentConfig::new(
125 format!("{}-agent", tool_name),
126 format!("Specialized agent for {} operations", tool_name),
127 format!(
128 "You are a specialized agent. Use the {} tool to complete tasks efficiently.",
129 tool_name
130 ),
131 )
132 .with_tools(vec![tool]);
133 self.subagents.push(subagent_config);
134 }
135 self
136 }
137
138 pub fn with_summarization(mut self, config: SummarizationConfig) -> Self {
139 self.summarization = Some(config);
140 self
141 }
142
143 pub fn with_tool_interrupt(mut self, tool_name: impl Into<String>, policy: HitlPolicy) -> Self {
144 self.tool_interrupts.insert(tool_name.into(), policy);
145 self
146 }
147
148 pub fn with_builtin_tools<I, S>(mut self, names: I) -> Self
149 where
150 I: IntoIterator<Item = S>,
151 S: Into<String>,
152 {
153 self.builtin_tools = Some(names.into_iter().map(|s| s.into()).collect());
154 self
155 }
156
157 pub fn with_auto_general_purpose(mut self, enabled: bool) -> Self {
158 self.auto_general_purpose = enabled;
159 self
160 }
161
162 pub fn with_prompt_caching(mut self, enabled: bool) -> Self {
163 self.enable_prompt_caching = enabled;
164 self
165 }
166
167 pub fn with_checkpointer(mut self, checkpointer: Arc<dyn Checkpointer>) -> Self {
168 self.checkpointer = Some(checkpointer);
169 self
170 }
171
172 pub fn with_event_broadcaster(
179 mut self,
180 broadcaster: Arc<dyn agents_core::events::EventBroadcaster>,
181 ) -> Self {
182 if self.event_dispatcher.is_none() {
184 self.event_dispatcher = Some(Arc::new(agents_core::events::EventDispatcher::new()));
185 }
186
187 if let Some(dispatcher) = &self.event_dispatcher {
189 dispatcher.add_broadcaster(broadcaster);
190 }
191
192 self
193 }
194
195 pub fn with_event_broadcasters(
206 mut self,
207 broadcasters: Vec<Arc<dyn agents_core::events::EventBroadcaster>>,
208 ) -> Self {
209 if self.event_dispatcher.is_none() {
211 self.event_dispatcher = Some(Arc::new(agents_core::events::EventDispatcher::new()));
212 }
213
214 if let Some(dispatcher) = &self.event_dispatcher {
216 for broadcaster in broadcasters {
217 dispatcher.add_broadcaster(broadcaster);
218 }
219 }
220
221 self
222 }
223
224 pub fn with_event_dispatcher(
226 mut self,
227 dispatcher: Arc<agents_core::events::EventDispatcher>,
228 ) -> Self {
229 self.event_dispatcher = Some(dispatcher);
230 self
231 }
232
233 pub fn with_pii_sanitization(mut self, enabled: bool) -> Self {
259 self.enable_pii_sanitization = enabled;
260 self
261 }
262
263 pub fn with_token_tracking(mut self, enabled: bool) -> Self {
290 self.token_tracking_config = Some(TokenTrackingConfig {
291 enabled,
292 emit_events: enabled,
293 log_usage: enabled,
294 custom_costs: None,
295 });
296 self
297 }
298
299 pub fn with_token_tracking_config(mut self, config: TokenTrackingConfig) -> Self {
319 self.token_tracking_config = Some(config);
320 self
321 }
322
323 pub fn build(self) -> anyhow::Result<DeepAgent> {
324 self.finalize(create_deep_agent_from_config)
325 }
326
327 pub fn build_async(self) -> anyhow::Result<DeepAgent> {
330 self.finalize(create_async_deep_agent_from_config)
331 }
332
333 fn finalize(self, ctor: fn(DeepAgentConfig) -> DeepAgent) -> anyhow::Result<DeepAgent> {
334 let Self {
335 instructions,
336 planner,
337 tools,
338 subagents,
339 summarization,
340 tool_interrupts,
341 builtin_tools,
342 auto_general_purpose,
343 enable_prompt_caching,
344 checkpointer,
345 event_dispatcher,
346 enable_pii_sanitization,
347 token_tracking_config,
348 } = self;
349
350 let planner = planner
351 .ok_or_else(|| anyhow::anyhow!("model must be set (use with_model or with_*_chat)"))?;
352
353 let final_planner = if let Some(token_config) = token_tracking_config {
355 if token_config.enabled {
356 let planner_any = planner.as_any();
358 if let Some(llm_planner) = planner_any.downcast_ref::<LlmBackedPlanner>() {
359 let model = llm_planner.model().clone();
360 let tracked_model = Arc::new(TokenTrackingMiddleware::new(
361 token_config,
362 model,
363 event_dispatcher.clone(),
364 ));
365 Arc::new(LlmBackedPlanner::new(tracked_model)) as Arc<dyn PlannerHandle>
366 } else {
367 planner
368 }
369 } else {
370 planner
371 }
372 } else {
373 planner
374 };
375
376 let mut cfg = DeepAgentConfig::new(instructions, final_planner)
377 .with_auto_general_purpose(auto_general_purpose)
378 .with_prompt_caching(enable_prompt_caching)
379 .with_pii_sanitization(enable_pii_sanitization);
380
381 if let Some(ckpt) = checkpointer {
382 cfg = cfg.with_checkpointer(ckpt);
383 }
384 if let Some(dispatcher) = event_dispatcher {
385 cfg = cfg.with_event_dispatcher(dispatcher);
386 }
387 if let Some(sum) = summarization {
388 cfg = cfg.with_summarization(sum);
389 }
390 if let Some(selected) = builtin_tools {
391 cfg = cfg.with_builtin_tools(selected);
392 }
393 for (name, policy) in tool_interrupts {
394 cfg = cfg.with_tool_interrupt(name, policy);
395 }
396 for tool in tools {
397 cfg = cfg.with_tool(tool);
398 }
399 for sub_cfg in subagents {
400 cfg = cfg.with_subagent_config(sub_cfg);
401 }
402
403 Ok(ctor(cfg))
404 }
405}