1use crate::context::{CompactionConfig, ContextCompactor};
2use crate::hooks::{AgentHooks, DefaultHooks};
3use crate::llm::LlmProvider;
4use crate::skills::Skill;
5use crate::stores::{InMemoryStore, MessageStore, StateStore, ToolExecutionStore};
6use crate::tools::ToolRegistry;
7use crate::types::AgentConfig;
8use std::sync::Arc;
9
10use super::AgentLoop;
11
12pub struct AgentLoopBuilder<Ctx, P, H, M, S> {
24 provider: Option<P>,
25 tools: Option<ToolRegistry<Ctx>>,
26 hooks: Option<H>,
27 message_store: Option<M>,
28 state_store: Option<S>,
29 config: Option<AgentConfig>,
30 compaction_config: Option<CompactionConfig>,
31 compactor: Option<Arc<dyn ContextCompactor>>,
32 execution_store: Option<Arc<dyn ToolExecutionStore>>,
33}
34
35impl<Ctx> AgentLoopBuilder<Ctx, (), (), (), ()> {
36 #[must_use]
38 pub fn new() -> Self {
39 Self {
40 provider: None,
41 tools: None,
42 hooks: None,
43 message_store: None,
44 state_store: None,
45 config: None,
46 compaction_config: None,
47 compactor: None,
48 execution_store: None,
49 }
50 }
51}
52
53impl<Ctx> Default for AgentLoopBuilder<Ctx, (), (), (), ()> {
54 fn default() -> Self {
55 Self::new()
56 }
57}
58
59impl<Ctx, P, H, M, S> AgentLoopBuilder<Ctx, P, H, M, S> {
60 #[must_use]
62 pub fn provider<P2: LlmProvider>(self, provider: P2) -> AgentLoopBuilder<Ctx, P2, H, M, S> {
63 AgentLoopBuilder {
64 provider: Some(provider),
65 tools: self.tools,
66 hooks: self.hooks,
67 message_store: self.message_store,
68 state_store: self.state_store,
69 config: self.config,
70 compaction_config: self.compaction_config,
71 compactor: self.compactor,
72 execution_store: self.execution_store,
73 }
74 }
75
76 #[must_use]
78 pub fn tools(mut self, tools: ToolRegistry<Ctx>) -> Self {
79 self.tools = Some(tools);
80 self
81 }
82
83 #[must_use]
85 pub fn hooks<H2: AgentHooks>(self, hooks: H2) -> AgentLoopBuilder<Ctx, P, H2, M, S> {
86 AgentLoopBuilder {
87 provider: self.provider,
88 tools: self.tools,
89 hooks: Some(hooks),
90 message_store: self.message_store,
91 state_store: self.state_store,
92 config: self.config,
93 compaction_config: self.compaction_config,
94 compactor: self.compactor,
95 execution_store: self.execution_store,
96 }
97 }
98
99 #[must_use]
101 pub fn message_store<M2: MessageStore>(
102 self,
103 message_store: M2,
104 ) -> AgentLoopBuilder<Ctx, P, H, M2, S> {
105 AgentLoopBuilder {
106 provider: self.provider,
107 tools: self.tools,
108 hooks: self.hooks,
109 message_store: Some(message_store),
110 state_store: self.state_store,
111 config: self.config,
112 compaction_config: self.compaction_config,
113 compactor: self.compactor,
114 execution_store: self.execution_store,
115 }
116 }
117
118 #[must_use]
120 pub fn state_store<S2: StateStore>(
121 self,
122 state_store: S2,
123 ) -> AgentLoopBuilder<Ctx, P, H, M, S2> {
124 AgentLoopBuilder {
125 provider: self.provider,
126 tools: self.tools,
127 hooks: self.hooks,
128 message_store: self.message_store,
129 state_store: Some(state_store),
130 config: self.config,
131 compaction_config: self.compaction_config,
132 compactor: self.compactor,
133 execution_store: self.execution_store,
134 }
135 }
136
137 #[must_use]
155 pub fn execution_store(mut self, store: impl ToolExecutionStore + 'static) -> Self {
156 self.execution_store = Some(Arc::new(store));
157 self
158 }
159
160 #[must_use]
162 pub fn config(mut self, config: AgentConfig) -> Self {
163 self.config = Some(config);
164 self
165 }
166
167 #[must_use]
183 pub const fn with_compaction(mut self, config: CompactionConfig) -> Self {
184 self.compaction_config = Some(config);
185 self
186 }
187
188 #[must_use]
195 pub fn with_auto_compaction(self) -> Self {
196 self.with_compaction(CompactionConfig::default())
197 }
198
199 #[must_use]
201 pub fn with_custom_compactor(mut self, compactor: impl ContextCompactor + 'static) -> Self {
202 self.compactor = Some(Arc::new(compactor));
203 self
204 }
205
206 #[must_use]
224 pub fn with_skill(mut self, skill: Skill) -> Self
225 where
226 Ctx: Send + Sync + 'static,
227 {
228 if let Some(ref mut tools) = self.tools {
230 tools.filter(|name| skill.is_tool_allowed(name));
231 }
232
233 let mut config = self.config.take().unwrap_or_default();
235 if config.system_prompt.is_empty() {
236 config.system_prompt = skill.system_prompt;
237 } else {
238 config.system_prompt = format!("{}\n\n{}", config.system_prompt, skill.system_prompt);
239 }
240 self.config = Some(config);
241
242 self
243 }
244}
245
246impl<Ctx, P> AgentLoopBuilder<Ctx, P, (), (), ()>
247where
248 Ctx: Send + Sync + 'static,
249 P: LlmProvider + 'static,
250{
251 #[must_use]
263 pub fn build(self) -> AgentLoop<Ctx, P, DefaultHooks, InMemoryStore, InMemoryStore> {
264 let provider = self.provider.expect("provider is required");
265 let tools = self.tools.unwrap_or_default();
266 let config = self.config.unwrap_or_default();
267
268 AgentLoop {
269 provider: Arc::new(provider),
270 tools: Arc::new(tools),
271 hooks: Arc::new(DefaultHooks),
272 message_store: Arc::new(InMemoryStore::new()),
273 state_store: Arc::new(InMemoryStore::new()),
274 config,
275 compaction_config: self.compaction_config,
276 compactor: self.compactor,
277 execution_store: self.execution_store,
278 }
279 }
280}
281
282impl<Ctx, P, H, M, S> AgentLoopBuilder<Ctx, P, H, M, S>
283where
284 Ctx: Send + Sync + 'static,
285 P: LlmProvider + 'static,
286 H: AgentHooks + 'static,
287 M: MessageStore + 'static,
288 S: StateStore + 'static,
289{
290 #[must_use]
300 pub fn build_with_stores(self) -> AgentLoop<Ctx, P, H, M, S> {
301 let provider = self.provider.expect("provider is required");
302 let tools = self.tools.unwrap_or_default();
303 let hooks = self
304 .hooks
305 .expect("hooks is required when using build_with_stores");
306 let message_store = self
307 .message_store
308 .expect("message_store is required when using build_with_stores");
309 let state_store = self
310 .state_store
311 .expect("state_store is required when using build_with_stores");
312 let config = self.config.unwrap_or_default();
313
314 AgentLoop {
315 provider: Arc::new(provider),
316 tools: Arc::new(tools),
317 hooks: Arc::new(hooks),
318 message_store: Arc::new(message_store),
319 state_store: Arc::new(state_store),
320 config,
321 compaction_config: self.compaction_config,
322 compactor: self.compactor,
323 execution_store: self.execution_store,
324 }
325 }
326}