Skip to main content

agent_sdk/subagent/
factory.rs

1//! Factory for creating subagents with common configurations.
2//!
3//! This module provides a convenient API for creating subagents with
4//! pre-configured tool registries and common patterns.
5//!
6//! # Example
7//!
8//! ```ignore
9//! use agent_sdk::subagent::{SubagentFactory, SubagentConfig};
10//! use agent_sdk::ToolRegistry;
11//!
12//! // Create tool registries for different access levels
13//! let mut read_tools = ToolRegistry::new();
14//! read_tools.register(glob_tool);
15//! read_tools.register(grep_tool);
16//! read_tools.register(read_tool);
17//!
18//! let factory = SubagentFactory::new(provider.clone())
19//!     .with_read_only_registry(read_tools);
20//!
21//! // Create a read-only subagent for exploration
22//! let explorer = factory.create_read_only(
23//!     SubagentConfig::new("explorer")
24//!         .with_system_prompt("You explore codebases...")
25//!         .with_max_turns(20)
26//! );
27//! ```
28
29use std::sync::Arc;
30
31use anyhow::{Context, Result};
32
33use crate::llm::LlmProvider;
34use crate::stores::EventStore;
35use crate::tools::ToolRegistry;
36
37use super::{SubagentConfig, SubagentTool};
38
39/// Factory for creating subagents with shared resources.
40///
41/// The factory holds references to shared resources (provider, tool registries)
42/// and provides convenient methods to create subagents with common configurations.
43pub struct SubagentFactory<P>
44where
45    P: LlmProvider + 'static,
46{
47    provider: Arc<P>,
48    event_store_factory: Arc<dyn Fn() -> Arc<dyn EventStore> + Send + Sync>,
49    read_only_registry: Option<Arc<ToolRegistry<()>>>,
50    full_registry: Option<Arc<ToolRegistry<()>>>,
51}
52
53impl<P> SubagentFactory<P>
54where
55    P: LlmProvider + 'static,
56{
57    /// Creates a new factory with the given LLM provider and event store factory.
58    #[must_use]
59    pub fn new<EF>(provider: Arc<P>, event_store_factory: EF) -> Self
60    where
61        EF: Fn() -> Arc<dyn EventStore> + Send + Sync + 'static,
62    {
63        Self {
64            provider,
65            event_store_factory: Arc::new(event_store_factory),
66            read_only_registry: None,
67            full_registry: None,
68        }
69    }
70
71    /// Sets the read-only tool registry (typically glob, grep, read tools).
72    ///
73    /// This registry is used when creating read-only subagents.
74    #[must_use]
75    pub fn with_read_only_registry(mut self, registry: ToolRegistry<()>) -> Self {
76        self.read_only_registry = Some(Arc::new(registry));
77        self
78    }
79
80    /// Sets the full tool registry for full-access subagents.
81    #[must_use]
82    pub fn with_full_registry(mut self, registry: ToolRegistry<()>) -> Self {
83        self.full_registry = Some(Arc::new(registry));
84        self
85    }
86
87    /// Creates a read-only subagent with only read/search tools.
88    ///
89    /// This is useful for exploration, research, and investigation tasks
90    /// where the subagent should not modify any files.
91    ///
92    /// # Errors
93    ///
94    /// Returns an error if no read-only registry has been set.
95    pub fn create_read_only(&self, config: SubagentConfig) -> Result<SubagentTool<P>> {
96        let registry = self
97            .read_only_registry
98            .as_ref()
99            .context("read-only registry not set; call with_read_only_registry first")?;
100        let event_store_factory = Arc::clone(&self.event_store_factory);
101        Ok(SubagentTool::new(
102            config,
103            Arc::clone(&self.provider),
104            Arc::clone(registry),
105            move || event_store_factory(),
106        ))
107    }
108
109    /// Creates a subagent with all available tools.
110    ///
111    /// # Errors
112    ///
113    /// Returns an error if no full registry has been set.
114    pub fn create_full_access(&self, config: SubagentConfig) -> Result<SubagentTool<P>> {
115        let registry = self
116            .full_registry
117            .as_ref()
118            .context("full registry not set; call with_full_registry first")?;
119        let event_store_factory = Arc::clone(&self.event_store_factory);
120        Ok(SubagentTool::new(
121            config,
122            Arc::clone(&self.provider),
123            Arc::clone(registry),
124            move || event_store_factory(),
125        ))
126    }
127
128    /// Creates a subagent with a custom tool registry.
129    #[must_use]
130    pub fn create_with_registry(
131        &self,
132        config: SubagentConfig,
133        registry: Arc<ToolRegistry<()>>,
134    ) -> SubagentTool<P> {
135        let event_store_factory = Arc::clone(&self.event_store_factory);
136        SubagentTool::new(config, Arc::clone(&self.provider), registry, move || {
137            event_store_factory()
138        })
139    }
140
141    /// Returns the provider for manual subagent construction.
142    #[must_use]
143    pub fn provider(&self) -> Arc<P> {
144        Arc::clone(&self.provider)
145    }
146}
147
148impl<P> Clone for SubagentFactory<P>
149where
150    P: LlmProvider + 'static,
151{
152    fn clone(&self) -> Self {
153        Self {
154            provider: Arc::clone(&self.provider),
155            event_store_factory: Arc::clone(&self.event_store_factory),
156            read_only_registry: self.read_only_registry.clone(),
157            full_registry: self.full_registry.clone(),
158        }
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165
166    #[test]
167    fn test_subagent_config_builder() {
168        let config = SubagentConfig::new("test")
169            .with_system_prompt("You are a test agent")
170            .with_max_turns(5)
171            .with_timeout_ms(30000);
172
173        assert_eq!(config.name, "test");
174        assert_eq!(config.system_prompt, "You are a test agent");
175        assert_eq!(config.max_turns, Some(5));
176        assert_eq!(config.timeout_ms, Some(30000));
177    }
178}