1use crate::{AgentError, BaseAgent, FunctionCallingAgent};
17use lc_core::language_models::BaseChatModel;
18use lc_core::tools::BaseTool;
19use lc_providers::ProviderError;
20use std::sync::Arc;
21
22pub struct AgentBuilder {
46 llm: Option<Arc<dyn BaseChatModel<Error = ProviderError> + Send + Sync>>,
47 system_prompt: Option<String>,
48 tools: Vec<Arc<dyn BaseTool>>,
49 max_iterations: usize,
50}
51
52impl AgentBuilder {
53 pub fn new() -> Self {
55 Self {
56 llm: None,
57 system_prompt: None,
58 tools: Vec::new(),
59 max_iterations: 10,
60 }
61 }
62
63 pub fn llm<L>(mut self, llm: L) -> Self
67 where
68 L: BaseChatModel + Send + Sync + 'static,
69 L::Error: Into<ProviderError>,
70 {
71 self.llm = Some(lc_providers::wrap_chat_model(llm));
72 self
73 }
74
75 pub fn llm_from_arc(
79 mut self,
80 llm: Arc<dyn BaseChatModel<Error = ProviderError> + Send + Sync>,
81 ) -> Self {
82 self.llm = Some(llm);
83 self
84 }
85
86 pub fn system(mut self, prompt: impl Into<String>) -> Self {
88 self.system_prompt = Some(prompt.into());
89 self
90 }
91
92 pub fn tool<T: BaseTool + 'static>(mut self, tool: T) -> Self {
94 self.tools.push(Arc::new(tool));
95 self
96 }
97
98 pub fn tools(mut self, tools: Vec<Arc<dyn BaseTool>>) -> Self {
100 self.tools.extend(tools);
101 self
102 }
103
104 pub fn max_iterations(mut self, n: usize) -> Self {
106 self.max_iterations = n;
107 self
108 }
109
110 pub fn build(self) -> Result<FunctionCallingAgent, AgentError> {
116 let llm = self.llm.ok_or_else(|| {
117 AgentError::Other("AgentBuilder: LLM is required. Call .llm() first.".into())
118 })?;
119
120 Ok(FunctionCallingAgent::from_arc(
121 llm,
122 self.tools,
123 self.system_prompt,
124 ))
125 }
126
127 pub fn build_as_agent(self) -> Result<Arc<dyn BaseAgent>, AgentError> {
133 let agent = self.build()?;
134 Ok(Arc::new(agent) as Arc<dyn BaseAgent>)
135 }
136
137 pub fn get_max_iterations(&self) -> usize {
139 self.max_iterations
140 }
141}
142
143impl Default for AgentBuilder {
144 fn default() -> Self {
145 Self::new()
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152 use lc_providers::{OpenAIChat, OpenAIConfig};
153 use lc_tools::Calculator;
154
155 #[test]
156 fn test_builder_with_openai() {
157 let config = OpenAIConfig::new("test_key").with_base_url("http://localhost:8080/v1");
158 let agent = AgentBuilder::new()
159 .llm(OpenAIChat::new(config))
160 .system("You are a test assistant.")
161 .tool(Calculator::new())
162 .build()
163 .unwrap();
164
165 assert_eq!(agent.tools_count(), 1);
166 assert_eq!(agent.system_prompt(), Some("You are a test assistant."));
167 }
168
169 #[test]
170 fn test_builder_missing_llm() {
171 let result = AgentBuilder::new().system("test").build();
172
173 assert!(result.is_err());
174 let err = result.unwrap_err();
175 assert!(err.to_string().contains("LLM is required"));
176 }
177
178 #[test]
179 fn test_builder_multiple_tools() {
180 let config = OpenAIConfig::new("test_key").with_base_url("http://localhost:8080/v1");
181 let agent = AgentBuilder::new()
182 .llm(OpenAIChat::new(config))
183 .tool(Calculator::new())
184 .tool(Calculator::new())
185 .build()
186 .unwrap();
187
188 assert_eq!(agent.tools_count(), 2);
189 }
190
191 #[test]
192 fn test_builder_tools_vec() {
193 let config = OpenAIConfig::new("test_key").with_base_url("http://localhost:8080/v1");
194 let tools: Vec<Arc<dyn BaseTool>> =
195 vec![Arc::new(Calculator::new()), Arc::new(Calculator::new())];
196 let agent = AgentBuilder::new()
197 .llm(OpenAIChat::new(config))
198 .tools(tools)
199 .build()
200 .unwrap();
201
202 assert_eq!(agent.tools_count(), 2);
203 }
204
205 #[test]
206 fn test_builder_build_as_agent() {
207 let config = OpenAIConfig::new("test_key").with_base_url("http://localhost:8080/v1");
208 let agent = AgentBuilder::new()
209 .llm(OpenAIChat::new(config))
210 .system("test")
211 .build_as_agent()
212 .unwrap();
213
214 let allowed = agent.get_allowed_tools();
215 assert!(allowed.is_some());
216 }
217
218 #[test]
219 fn test_builder_max_iterations() {
220 let builder = AgentBuilder::new().max_iterations(5);
221 assert_eq!(builder.get_max_iterations(), 5);
222 }
223
224 #[test]
225 fn test_builder_default() {
226 let builder = AgentBuilder::default();
227 assert_eq!(builder.get_max_iterations(), 10);
228 assert!(builder.llm.is_none());
229 }
230}