1use std::collections::HashMap;
2
3use crate::{
4 completion::{CompletionModel, Document},
5 tool::{Tool, ToolSet},
6 vector_store::VectorStoreIndexDyn,
7};
8
9#[allow(deprecated)]
10#[cfg(feature = "mcp")]
11use crate::tool::mcp::McpTool;
12
13#[cfg(feature = "rmcp")]
14use crate::tool::rmcp::McpTool as RmcpTool;
15
16use super::Agent;
17
18pub struct AgentBuilder<M: CompletionModel> {
40 model: M,
42 preamble: Option<String>,
44 static_context: Vec<Document>,
46 static_tools: Vec<String>,
48 additional_params: Option<serde_json::Value>,
50 max_tokens: Option<u64>,
52 dynamic_context: Vec<(usize, Box<dyn VectorStoreIndexDyn>)>,
54 dynamic_tools: Vec<(usize, Box<dyn VectorStoreIndexDyn>)>,
56 temperature: Option<f64>,
58 tools: ToolSet,
60}
61
62impl<M: CompletionModel> AgentBuilder<M> {
63 pub fn new(model: M) -> Self {
64 Self {
65 model,
66 preamble: None,
67 static_context: vec![],
68 static_tools: vec![],
69 temperature: None,
70 max_tokens: None,
71 additional_params: None,
72 dynamic_context: vec![],
73 dynamic_tools: vec![],
74 tools: ToolSet::default(),
75 }
76 }
77
78 pub fn preamble(mut self, preamble: &str) -> Self {
80 self.preamble = Some(preamble.into());
81 self
82 }
83
84 pub fn append_preamble(mut self, doc: &str) -> Self {
86 self.preamble = Some(format!(
87 "{}\n{}",
88 self.preamble.unwrap_or_else(|| "".into()),
89 doc
90 ));
91 self
92 }
93
94 pub fn context(mut self, doc: &str) -> Self {
96 self.static_context.push(Document {
97 id: format!("static_doc_{}", self.static_context.len()),
98 text: doc.into(),
99 additional_props: HashMap::new(),
100 });
101 self
102 }
103
104 pub fn tool(mut self, tool: impl Tool + 'static) -> Self {
106 let toolname = tool.name();
107 self.tools.add_tool(tool);
108 self.static_tools.push(toolname);
109 self
110 }
111
112 #[cfg(feature = "mcp")]
114 pub fn mcp_tool<T: mcp_core::transport::Transport>(
115 mut self,
116 tool: mcp_core::types::Tool,
117 client: mcp_core::client::Client<T>,
118 ) -> Self {
119 let toolname = tool.name.clone();
120 #[allow(deprecated)]
121 self.tools.add_tool(McpTool::from_mcp_server(tool, client));
122 self.static_tools.push(toolname);
123 self
124 }
125
126 #[cfg(feature = "rmcp")]
128 pub fn rmcp_tool(mut self, tool: rmcp::model::Tool, client: rmcp::service::ServerSink) -> Self {
129 let toolname = tool.name.clone();
130 self.tools.add_tool(RmcpTool::from_mcp_server(tool, client));
131 self.static_tools.push(toolname.to_string());
132 self
133 }
134
135 pub fn dynamic_context(
138 mut self,
139 sample: usize,
140 dynamic_context: impl VectorStoreIndexDyn + 'static,
141 ) -> Self {
142 self.dynamic_context
143 .push((sample, Box::new(dynamic_context)));
144 self
145 }
146
147 pub fn dynamic_tools(
150 mut self,
151 sample: usize,
152 dynamic_tools: impl VectorStoreIndexDyn + 'static,
153 toolset: ToolSet,
154 ) -> Self {
155 self.dynamic_tools.push((sample, Box::new(dynamic_tools)));
156 self.tools.add_tools(toolset);
157 self
158 }
159
160 pub fn temperature(mut self, temperature: f64) -> Self {
162 self.temperature = Some(temperature);
163 self
164 }
165
166 pub fn max_tokens(mut self, max_tokens: u64) -> Self {
168 self.max_tokens = Some(max_tokens);
169 self
170 }
171
172 pub fn additional_params(mut self, params: serde_json::Value) -> Self {
174 self.additional_params = Some(params);
175 self
176 }
177
178 pub fn build(self) -> Agent<M> {
180 Agent {
181 model: self.model,
182 preamble: self.preamble.unwrap_or_default(),
183 static_context: self.static_context,
184 static_tools: self.static_tools,
185 temperature: self.temperature,
186 max_tokens: self.max_tokens,
187 additional_params: self.additional_params,
188 dynamic_context: self.dynamic_context,
189 dynamic_tools: self.dynamic_tools,
190 tools: self.tools,
191 }
192 }
193}