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 append_preamble(mut self, doc: &str) -> Self {
98 self.preamble = Some(format!(
99 "{}\n{}",
100 self.preamble.unwrap_or_else(|| "".into()),
101 doc
102 ));
103 self
104 }
105
106 pub fn context(mut self, doc: &str) -> Self {
108 self.static_context.push(Document {
109 id: format!("static_doc_{}", self.static_context.len()),
110 text: doc.into(),
111 additional_props: HashMap::new(),
112 });
113 self
114 }
115
116 pub fn tool(mut self, tool: impl Tool + 'static) -> Self {
118 let toolname = tool.name();
119 self.tools.add_tool(tool);
120 self.static_tools.push(toolname);
121 self
122 }
123
124 #[cfg_attr(docsrs, doc(cfg(feature = "rmcp")))]
126 #[cfg(feature = "rmcp")]
127 pub fn rmcp_tool(mut self, tool: rmcp::model::Tool, client: rmcp::service::ServerSink) -> Self {
128 let toolname = tool.name.clone();
129 self.tools.add_tool(RmcpTool::from_mcp_server(tool, client));
130 self.static_tools.push(toolname.to_string());
131 self
132 }
133
134 pub fn dynamic_context(
137 mut self,
138 sample: usize,
139 dynamic_context: impl VectorStoreIndexDyn + 'static,
140 ) -> Self {
141 self.dynamic_context
142 .push((sample, Box::new(dynamic_context)));
143 self
144 }
145
146 pub fn dynamic_tools(
149 mut self,
150 sample: usize,
151 dynamic_tools: impl VectorStoreIndexDyn + 'static,
152 toolset: ToolSet,
153 ) -> Self {
154 self.dynamic_tools.push((sample, Box::new(dynamic_tools)));
155 self.tools.add_tools(toolset);
156 self
157 }
158
159 pub fn temperature(mut self, temperature: f64) -> Self {
161 self.temperature = Some(temperature);
162 self
163 }
164
165 pub fn max_tokens(mut self, max_tokens: u64) -> Self {
167 self.max_tokens = Some(max_tokens);
168 self
169 }
170
171 pub fn additional_params(mut self, params: serde_json::Value) -> Self {
173 self.additional_params = Some(params);
174 self
175 }
176
177 pub fn build(self) -> Agent<M> {
179 Agent {
180 name: self.name,
181 model: Arc::new(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: Arc::new(self.dynamic_context),
189 dynamic_tools: Arc::new(self.dynamic_tools),
190 tools: Arc::new(self.tools),
191 }
192 }
193}