1use std::{collections::HashMap, sync::Arc};
2
3use tokio::sync::RwLock;
4
5use crate::{
6 completion::{CompletionModel, Document},
7 message::ToolChoice,
8 tool::{
9 Tool, ToolSet,
10 server::{ToolServer, ToolServerHandle},
11 },
12 vector_store::VectorStoreIndexDyn,
13};
14
15#[cfg(feature = "rmcp")]
16#[cfg_attr(docsrs, doc(cfg(feature = "rmcp")))]
17use crate::tool::rmcp::McpTool as RmcpTool;
18
19use super::Agent;
20
21pub struct AgentBuilder<M>
43where
44 M: CompletionModel,
45{
46 name: Option<String>,
48 description: Option<String>,
50 model: M,
52 preamble: Option<String>,
54 static_context: Vec<Document>,
56 additional_params: Option<serde_json::Value>,
58 max_tokens: Option<u64>,
60 dynamic_context: Vec<(usize, Box<dyn VectorStoreIndexDyn>)>,
62 temperature: Option<f64>,
64 tool_server_handle: Option<ToolServerHandle>,
66 tool_choice: Option<ToolChoice>,
68}
69
70impl<M> AgentBuilder<M>
71where
72 M: CompletionModel,
73{
74 pub fn new(model: M) -> Self {
75 Self {
76 name: None,
77 description: None,
78 model,
79 preamble: None,
80 static_context: vec![],
81 temperature: None,
82 max_tokens: None,
83 additional_params: None,
84 dynamic_context: vec![],
85 tool_server_handle: None,
86 tool_choice: None,
87 }
88 }
89
90 pub fn name(mut self, name: &str) -> Self {
92 self.name = Some(name.into());
93 self
94 }
95
96 pub fn description(mut self, description: &str) -> Self {
98 self.description = Some(description.into());
99 self
100 }
101
102 pub fn preamble(mut self, preamble: &str) -> Self {
104 self.preamble = Some(preamble.into());
105 self
106 }
107
108 pub fn without_preamble(mut self) -> Self {
110 self.preamble = None;
111 self
112 }
113
114 pub fn append_preamble(mut self, doc: &str) -> Self {
116 self.preamble = Some(format!(
117 "{}\n{}",
118 self.preamble.unwrap_or_else(|| "".into()),
119 doc
120 ));
121 self
122 }
123
124 pub fn context(mut self, doc: &str) -> Self {
126 self.static_context.push(Document {
127 id: format!("static_doc_{}", self.static_context.len()),
128 text: doc.into(),
129 additional_props: HashMap::new(),
130 });
131 self
132 }
133
134 pub fn tool(self, tool: impl Tool + 'static) -> AgentBuilderSimple<M> {
136 let toolname = tool.name();
137 let tools = ToolSet::from_tools(vec![tool]);
138 let static_tools = vec![toolname];
139
140 AgentBuilderSimple {
141 name: self.name,
142 description: self.description,
143 model: self.model,
144 preamble: self.preamble,
145 static_context: self.static_context,
146 static_tools,
147 additional_params: self.additional_params,
148 max_tokens: self.max_tokens,
149 dynamic_context: vec![],
150 dynamic_tools: vec![],
151 temperature: self.temperature,
152 tools,
153 tool_choice: self.tool_choice,
154 }
155 }
156
157 pub fn tool_server_handle(mut self, handle: ToolServerHandle) -> Self {
158 self.tool_server_handle = Some(handle);
159 self
160 }
161
162 #[cfg(feature = "rmcp")]
164 #[cfg_attr(docsrs, doc(cfg(feature = "rmcp")))]
165 pub fn rmcp_tool(
166 self,
167 tool: rmcp::model::Tool,
168 client: rmcp::service::ServerSink,
169 ) -> AgentBuilderSimple<M> {
170 let toolname = tool.name.clone().to_string();
171 let tools = ToolSet::from_tools(vec![RmcpTool::from_mcp_server(tool, client)]);
172 let static_tools = vec![toolname];
173
174 AgentBuilderSimple {
175 name: self.name,
176 description: self.description,
177 model: self.model,
178 preamble: self.preamble,
179 static_context: self.static_context,
180 static_tools,
181 additional_params: self.additional_params,
182 max_tokens: self.max_tokens,
183 dynamic_context: vec![],
184 dynamic_tools: vec![],
185 temperature: self.temperature,
186 tools,
187 tool_choice: self.tool_choice,
188 }
189 }
190
191 #[cfg(feature = "rmcp")]
193 #[cfg_attr(docsrs, doc(cfg(feature = "rmcp")))]
194 pub fn rmcp_tools(
195 self,
196 tools: Vec<rmcp::model::Tool>,
197 client: rmcp::service::ServerSink,
198 ) -> AgentBuilderSimple<M> {
199 let (static_tools, tools) = tools.into_iter().fold(
200 (Vec::new(), Vec::new()),
201 |(mut toolnames, mut toolset), tool| {
202 let tool_name = tool.name.to_string();
203 let tool = RmcpTool::from_mcp_server(tool, client.clone());
204 toolnames.push(tool_name);
205 toolset.push(tool);
206 (toolnames, toolset)
207 },
208 );
209
210 let tools = ToolSet::from_tools(tools);
211
212 AgentBuilderSimple {
213 name: self.name,
214 description: self.description,
215 model: self.model,
216 preamble: self.preamble,
217 static_context: self.static_context,
218 static_tools,
219 additional_params: self.additional_params,
220 max_tokens: self.max_tokens,
221 dynamic_context: vec![],
222 dynamic_tools: vec![],
223 temperature: self.temperature,
224 tools,
225 tool_choice: self.tool_choice,
226 }
227 }
228
229 pub fn dynamic_context(
232 mut self,
233 sample: usize,
234 dynamic_context: impl VectorStoreIndexDyn + 'static,
235 ) -> Self {
236 self.dynamic_context
237 .push((sample, Box::new(dynamic_context)));
238 self
239 }
240
241 pub fn tool_choice(mut self, tool_choice: ToolChoice) -> Self {
242 self.tool_choice = Some(tool_choice);
243 self
244 }
245
246 pub fn dynamic_tools(
249 self,
250 sample: usize,
251 dynamic_tools: impl VectorStoreIndexDyn + 'static,
252 toolset: ToolSet,
253 ) -> AgentBuilderSimple<M> {
254 let thing: Box<dyn VectorStoreIndexDyn + 'static> = Box::new(dynamic_tools);
255 let dynamic_tools = vec![(sample, thing)];
256
257 AgentBuilderSimple {
258 name: self.name,
259 description: self.description,
260 model: self.model,
261 preamble: self.preamble,
262 static_context: self.static_context,
263 static_tools: vec![],
264 additional_params: self.additional_params,
265 max_tokens: self.max_tokens,
266 dynamic_context: vec![],
267 dynamic_tools,
268 temperature: self.temperature,
269 tools: toolset,
270 tool_choice: self.tool_choice,
271 }
272 }
273
274 pub fn temperature(mut self, temperature: f64) -> Self {
276 self.temperature = Some(temperature);
277 self
278 }
279
280 pub fn max_tokens(mut self, max_tokens: u64) -> Self {
282 self.max_tokens = Some(max_tokens);
283 self
284 }
285
286 pub fn additional_params(mut self, params: serde_json::Value) -> Self {
288 self.additional_params = Some(params);
289 self
290 }
291
292 pub fn build(self) -> Agent<M> {
294 let tool_server_handle = if let Some(handle) = self.tool_server_handle {
295 handle
296 } else {
297 ToolServer::new().run()
298 };
299
300 Agent {
301 name: self.name,
302 description: self.description,
303 model: Arc::new(self.model),
304 preamble: self.preamble,
305 static_context: self.static_context,
306 temperature: self.temperature,
307 max_tokens: self.max_tokens,
308 additional_params: self.additional_params,
309 tool_choice: self.tool_choice,
310 dynamic_context: Arc::new(RwLock::new(self.dynamic_context)),
311 tool_server_handle,
312 }
313 }
314}
315
316pub struct AgentBuilderSimple<M>
338where
339 M: CompletionModel,
340{
341 name: Option<String>,
343 description: Option<String>,
345 model: M,
347 preamble: Option<String>,
349 static_context: Vec<Document>,
351 static_tools: Vec<String>,
353 additional_params: Option<serde_json::Value>,
355 max_tokens: Option<u64>,
357 dynamic_context: Vec<(usize, Box<dyn VectorStoreIndexDyn>)>,
359 dynamic_tools: Vec<(usize, Box<dyn VectorStoreIndexDyn>)>,
361 temperature: Option<f64>,
363 tools: ToolSet,
365 tool_choice: Option<ToolChoice>,
367}
368
369impl<M> AgentBuilderSimple<M>
370where
371 M: CompletionModel,
372{
373 pub fn new(model: M) -> Self {
374 Self {
375 name: None,
376 description: None,
377 model,
378 preamble: None,
379 static_context: vec![],
380 static_tools: vec![],
381 temperature: None,
382 max_tokens: None,
383 additional_params: None,
384 dynamic_context: vec![],
385 dynamic_tools: vec![],
386 tools: ToolSet::default(),
387 tool_choice: None,
388 }
389 }
390
391 pub fn name(mut self, name: &str) -> Self {
393 self.name = Some(name.into());
394 self
395 }
396
397 pub fn description(mut self, description: &str) -> Self {
399 self.description = Some(description.into());
400 self
401 }
402
403 pub fn preamble(mut self, preamble: &str) -> Self {
405 self.preamble = Some(preamble.into());
406 self
407 }
408
409 pub fn without_preamble(mut self) -> Self {
411 self.preamble = None;
412 self
413 }
414
415 pub fn append_preamble(mut self, doc: &str) -> Self {
417 self.preamble = Some(format!(
418 "{}\n{}",
419 self.preamble.unwrap_or_else(|| "".into()),
420 doc
421 ));
422 self
423 }
424
425 pub fn context(mut self, doc: &str) -> Self {
427 self.static_context.push(Document {
428 id: format!("static_doc_{}", self.static_context.len()),
429 text: doc.into(),
430 additional_props: HashMap::new(),
431 });
432 self
433 }
434
435 pub fn tool(mut self, tool: impl Tool + 'static) -> Self {
437 let toolname = tool.name();
438 self.tools.add_tool(tool);
439 self.static_tools.push(toolname);
440 self
441 }
442
443 #[cfg(feature = "rmcp")]
445 #[cfg_attr(docsrs, doc(cfg(feature = "rmcp")))]
446 pub fn rmcp_tools(
447 mut self,
448 tools: Vec<rmcp::model::Tool>,
449 client: rmcp::service::ServerSink,
450 ) -> Self {
451 for tool in tools {
452 let tool_name = tool.name.to_string();
453 let tool = RmcpTool::from_mcp_server(tool, client.clone());
454 self.static_tools.push(tool_name);
455 self.tools.add_tool(tool);
456 }
457
458 self
459 }
460
461 pub fn dynamic_context(
464 mut self,
465 sample: usize,
466 dynamic_context: impl VectorStoreIndexDyn + 'static,
467 ) -> Self {
468 self.dynamic_context
469 .push((sample, Box::new(dynamic_context)));
470 self
471 }
472
473 pub fn tool_choice(mut self, tool_choice: ToolChoice) -> Self {
474 self.tool_choice = Some(tool_choice);
475 self
476 }
477
478 pub fn dynamic_tools(
481 mut self,
482 sample: usize,
483 dynamic_tools: impl VectorStoreIndexDyn + 'static,
484 toolset: ToolSet,
485 ) -> Self {
486 self.dynamic_tools.push((sample, Box::new(dynamic_tools)));
487 self.tools.add_tools(toolset);
488 self
489 }
490
491 pub fn temperature(mut self, temperature: f64) -> Self {
493 self.temperature = Some(temperature);
494 self
495 }
496
497 pub fn max_tokens(mut self, max_tokens: u64) -> Self {
499 self.max_tokens = Some(max_tokens);
500 self
501 }
502
503 pub fn additional_params(mut self, params: serde_json::Value) -> Self {
505 self.additional_params = Some(params);
506 self
507 }
508
509 pub fn build(self) -> Agent<M> {
511 let tool_server_handle = ToolServer::new()
512 .static_tool_names(self.static_tools)
513 .add_tools(self.tools)
514 .add_dynamic_tools(self.dynamic_tools)
515 .run();
516
517 Agent {
518 name: self.name,
519 description: self.description,
520 model: Arc::new(self.model),
521 preamble: self.preamble,
522 static_context: self.static_context,
523 temperature: self.temperature,
524 max_tokens: self.max_tokens,
525 additional_params: self.additional_params,
526 tool_choice: self.tool_choice,
527 dynamic_context: Arc::new(RwLock::new(self.dynamic_context)),
528 tool_server_handle,
529 }
530 }
531}