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