use std::sync::Arc;
use std::sync::atomic::AtomicU32;
use crate::error::KovaError;
use crate::mcp::McpClient;
use crate::mcp::tool::McpTool;
use crate::models::InferenceConfig;
use crate::provider::{LlmProvider, RetryConfig};
use crate::telemetry::MetricsCollector;
use crate::tool::Tool;
use crate::tool::ToolLifecycleHook;
use crate::tool::approval::ToolApprovalHandler;
use crate::tool::registry::ToolRegistry;
use super::Agent;
pub struct AgentBuilder {
provider: Option<Arc<dyn LlmProvider>>,
tools: Vec<Arc<dyn Tool>>,
tool_registry: ToolRegistry,
system_prompt: Option<String>,
max_iterations: usize,
context_budget: Option<u32>,
max_concurrent_tools: usize,
inference_config: InferenceConfig,
approval_handler: Option<Arc<dyn ToolApprovalHandler>>,
lifecycle_hook: Option<Arc<dyn ToolLifecycleHook>>,
metrics: Option<Arc<MetricsCollector>>,
retry_config: RetryConfig,
}
impl AgentBuilder {
pub fn new() -> Self {
Self {
provider: None,
tools: Vec::new(),
tool_registry: ToolRegistry::new(),
system_prompt: None,
max_iterations: 10,
context_budget: None,
max_concurrent_tools: 10,
inference_config: InferenceConfig::default(),
approval_handler: None,
lifecycle_hook: None,
metrics: None,
retry_config: RetryConfig::default(),
}
}
pub fn provider(mut self, provider: Arc<dyn LlmProvider>) -> Self {
self.provider = Some(provider);
self
}
pub fn system_prompt(mut self, prompt: impl Into<String>) -> Self {
self.system_prompt = Some(prompt.into());
self
}
pub fn context_budget(mut self, max_prompt_tokens: u32) -> Self {
self.context_budget = Some(max_prompt_tokens);
self
}
pub fn max_iterations(mut self, n: usize) -> Self {
self.max_iterations = n;
self
}
pub fn max_concurrent_tools(mut self, n: usize) -> Self {
self.max_concurrent_tools = n;
self
}
pub fn inference_config(mut self, config: InferenceConfig) -> Self {
self.inference_config = config;
self
}
pub fn tool(mut self, tool: Arc<dyn Tool>) -> Self {
self.tools.push(tool);
self
}
pub fn tool_registry(mut self, registry: ToolRegistry) -> Self {
self.tool_registry = registry;
self
}
pub async fn mcp_client(
mut self,
client: Arc<McpClient>,
server_name: &str,
) -> Result<Self, KovaError> {
let tool_defs = client.tools_list().await?;
for def in tool_defs {
self.tools.push(Arc::new(McpTool::new(
def,
Arc::clone(&client),
server_name,
)));
}
Ok(self)
}
pub fn with_approval_handler(mut self, handler: Arc<dyn ToolApprovalHandler>) -> Self {
self.approval_handler = Some(handler);
self
}
pub fn retry_config(mut self, config: RetryConfig) -> Self {
self.retry_config = config;
self
}
pub fn metrics(mut self, collector: Arc<MetricsCollector>) -> Self {
self.metrics = Some(collector);
self
}
pub fn with_lifecycle_hook(mut self, hook: Arc<dyn ToolLifecycleHook>) -> Self {
self.lifecycle_hook = Some(hook);
self
}
pub fn build(self) -> Result<Agent, KovaError> {
let provider = self
.provider
.ok_or_else(|| KovaError::Build("LlmProvider is required".into()))?;
let tool_registry = self.tool_registry;
for tool in self.tools {
tool_registry.register(tool);
}
Ok(Agent {
provider,
tool_registry,
system_prompt: self.system_prompt,
max_iterations: self.max_iterations,
context_budget: self.context_budget,
max_concurrent_tools: self.max_concurrent_tools,
inference_config: self.inference_config,
approval_handler: self.approval_handler,
lifecycle_hook: self.lifecycle_hook,
approval_cache: Arc::new(std::sync::RwLock::new(std::collections::HashMap::new())),
metrics: self.metrics,
retry_config: self.retry_config,
last_turn_input_tokens: Arc::new(AtomicU32::new(0)),
})
}
}
impl Default for AgentBuilder {
fn default() -> Self {
Self::new()
}
}