use crate::agent::AgentCallback;
use crate::llm::ResponseFormat;
use crate::tools::ToolExecutionConfig;
use std::path::PathBuf;
use std::sync::Arc;
#[derive(Default, Debug, Clone, PartialEq)]
pub enum AgentRole {
Orchestrator,
#[default]
Worker,
}
pub struct AgentConfig {
pub(crate) model_name: String,
pub(crate) system_prompt: String,
pub(crate) agent_name: String,
pub(crate) max_iterations: usize,
pub(crate) allowed_tools: Vec<String>,
pub(crate) role: AgentRole,
pub(crate) enable_tool: bool,
pub(crate) enable_task: bool,
pub(crate) enable_human_in_loop: bool,
pub(crate) enable_subagent: bool,
pub(crate) token_limit: usize,
pub(crate) callbacks: Vec<Arc<dyn AgentCallback>>,
pub(crate) llm_max_retries: usize,
pub(crate) llm_retry_delay_ms: u64,
pub(crate) tool_error_feedback: bool,
pub(crate) enable_cot: bool,
pub(crate) tool_execution: ToolExecutionConfig,
pub(crate) enable_memory: bool,
pub(crate) memory_path: String,
pub(crate) session_id: Option<String>,
pub(crate) conversation_id: Option<String>,
pub(crate) checkpointer_path: String,
pub(crate) response_format: Option<ResponseFormat>,
pub(crate) max_tool_output_tokens: Option<usize>,
pub(crate) compress_threshold_ratio: f64,
pub(crate) temperature: Option<f32>,
pub(crate) max_tokens: Option<u32>,
pub(crate) auto_project_rules: bool,
pub(crate) working_dir: Option<PathBuf>,
}
impl AgentConfig {
pub fn new(model_name: &str, agent_name: &str, system_prompt: &str) -> Self {
Self {
model_name: model_name.to_string(),
system_prompt: system_prompt.to_string(),
agent_name: agent_name.to_string(),
max_iterations: 10,
allowed_tools: Vec::new(),
role: AgentRole::default(),
enable_tool: false,
enable_task: false,
enable_human_in_loop: false,
enable_subagent: false,
token_limit: usize::MAX,
callbacks: Vec::new(),
llm_max_retries: 3,
llm_retry_delay_ms: 500,
tool_error_feedback: true,
enable_cot: true,
tool_execution: ToolExecutionConfig::default(),
enable_memory: false,
memory_path: "~/.echo-agent/store.json".to_string(),
session_id: None,
conversation_id: None,
checkpointer_path: "~/.echo-agent/checkpoints.json".to_string(),
response_format: None,
max_tool_output_tokens: None,
compress_threshold_ratio: 0.2,
temperature: None,
max_tokens: None,
auto_project_rules: true,
working_dir: None,
}
}
pub fn minimal(model_name: &str, system_prompt: &str) -> Self {
Self::new(model_name, "assistant", system_prompt)
.enable_tool(false)
.enable_memory(false)
.enable_cot(false)
}
pub fn standard(model_name: &str, agent_name: &str, system_prompt: &str) -> Self {
Self::new(model_name, agent_name, system_prompt)
.enable_tool(true)
.enable_cot(true)
}
pub fn full_featured(model_name: &str, agent_name: &str, system_prompt: &str) -> Self {
Self::new(model_name, agent_name, system_prompt)
.enable_tool(true)
.enable_memory(true)
.enable_task(true)
.enable_cot(true)
}
pub fn with_full_features(mut self) -> Self {
self.enable_tool = true;
self.enable_memory = true;
self.enable_task = true;
self.enable_cot = true;
self
}
pub fn with_tools(mut self) -> Self {
self.enable_tool = true;
self.enable_cot = true;
self
}
pub fn role(mut self, role: AgentRole) -> Self {
self.role = role;
self
}
pub fn enable_tool(mut self, enabled: bool) -> Self {
self.enable_tool = enabled;
self
}
pub fn enable_task(mut self, enabled: bool) -> Self {
self.enable_task = enabled;
self
}
pub fn enable_human_in_loop(mut self, enabled: bool) -> Self {
self.enable_human_in_loop = enabled;
self
}
pub fn enable_subagent(mut self, enabled: bool) -> Self {
self.enable_subagent = enabled;
self
}
pub fn allowed_tools(mut self, tools: Vec<String>) -> Self {
self.allowed_tools.extend(tools);
self
}
pub fn get_allowed_tools(&self) -> &[String] {
&self.allowed_tools
}
pub fn is_tool_enabled(&self) -> bool {
self.enable_tool
}
pub fn is_task_enabled(&self) -> bool {
self.enable_task
}
pub fn is_human_in_loop_enabled(&self) -> bool {
self.enable_human_in_loop
}
pub fn is_subagent_enabled(&self) -> bool {
self.enable_subagent
}
pub fn max_iterations(mut self, max_iterations: usize) -> Self {
self.max_iterations = max_iterations;
self
}
pub fn agent_name(mut self, agent_name: &str) -> Self {
self.agent_name = agent_name.to_string();
self
}
pub fn model_name(mut self, model_name: &str) -> Self {
self.model_name = model_name.to_string();
self
}
pub fn set_model_name(&mut self, model_name: &str) {
self.model_name = model_name.to_string();
}
pub fn system_prompt(mut self, system_prompt: &str) -> Self {
self.system_prompt = system_prompt.to_string();
self
}
pub fn token_limit(mut self, limit: usize) -> Self {
self.token_limit = limit;
self
}
pub fn with_callback(mut self, callback: Arc<dyn AgentCallback>) -> Self {
self.callbacks.push(callback);
self
}
pub fn llm_max_retries(mut self, retries: usize) -> Self {
self.llm_max_retries = retries;
self
}
pub fn llm_retry_delay_ms(mut self, delay_ms: u64) -> Self {
self.llm_retry_delay_ms = delay_ms;
self
}
pub fn tool_error_feedback(mut self, enabled: bool) -> Self {
self.tool_error_feedback = enabled;
self
}
pub fn get_session_id(&self) -> Option<&str> {
self.session_id.as_deref()
}
pub fn get_conversation_id(&self) -> Option<&str> {
self.conversation_id.as_deref()
}
pub fn get_llm_max_retries(&self) -> usize {
self.llm_max_retries
}
pub fn get_llm_retry_delay_ms(&self) -> u64 {
self.llm_retry_delay_ms
}
pub fn get_tool_error_feedback(&self) -> bool {
self.tool_error_feedback
}
pub fn get_max_iterations(&self) -> usize {
self.max_iterations
}
pub fn get_token_limit(&self) -> usize {
self.token_limit
}
pub fn is_cot_enabled(&self) -> bool {
self.enable_cot
}
pub fn is_memory_enabled(&self) -> bool {
self.enable_memory
}
pub fn get_memory_path(&self) -> &str {
&self.memory_path
}
pub fn get_checkpointer_path(&self) -> &str {
&self.checkpointer_path
}
pub fn get_tool_execution(&self) -> &crate::tools::ToolExecutionConfig {
&self.tool_execution
}
pub fn get_response_format(&self) -> Option<&crate::llm::ResponseFormat> {
self.response_format.as_ref()
}
pub fn get_model_name(&self) -> &str {
&self.model_name
}
pub fn get_system_prompt(&self) -> &str {
&self.system_prompt
}
pub fn get_agent_name(&self) -> &str {
&self.agent_name
}
pub fn enable_cot(mut self, enabled: bool) -> Self {
self.enable_cot = enabled;
self
}
pub fn enable_memory(mut self, enabled: bool) -> Self {
self.enable_memory = enabled;
self
}
pub fn memory_path(mut self, path: &str) -> Self {
self.memory_path = path.to_string();
self
}
pub fn session_id(mut self, id: &str) -> Self {
self.session_id = Some(id.to_string());
self
}
pub fn conversation_id(mut self, id: &str) -> Self {
self.conversation_id = Some(id.to_string());
self
}
pub fn checkpointer_path(mut self, path: &str) -> Self {
self.checkpointer_path = path.to_string();
self
}
pub fn tool_execution(mut self, config: ToolExecutionConfig) -> Self {
self.tool_execution = config;
self
}
pub fn response_format(mut self, fmt: ResponseFormat) -> Self {
self.response_format = Some(fmt);
self
}
pub fn max_tool_output_tokens(mut self, max: usize) -> Self {
self.max_tool_output_tokens = Some(max);
self
}
pub fn get_max_tool_output_tokens(&self) -> Option<usize> {
self.max_tool_output_tokens
}
pub fn compress_threshold_ratio(mut self, ratio: f64) -> Self {
self.compress_threshold_ratio = ratio.clamp(0.0, 1.0);
self
}
pub fn get_compress_threshold_ratio(&self) -> f64 {
self.compress_threshold_ratio
}
pub fn auto_project_rules(mut self, enabled: bool) -> Self {
self.auto_project_rules = enabled;
self
}
pub fn working_dir(mut self, path: Option<PathBuf>) -> Self {
self.working_dir = path;
self
}
pub fn temperature(mut self, temperature: Option<f32>) -> Self {
self.temperature = temperature;
self
}
pub fn get_temperature(&self) -> Option<f32> {
self.temperature
}
pub fn max_tokens(mut self, max_tokens: Option<u32>) -> Self {
self.max_tokens = max_tokens;
self
}
pub fn get_max_tokens(&self) -> Option<u32> {
self.max_tokens
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_agent_config_new() {
let config = AgentConfig::new("qwen3-max", "assistant", "You are a helpful assistant");
assert_eq!(config.get_model_name(), "qwen3-max");
assert_eq!(config.get_agent_name(), "assistant");
assert_eq!(config.get_system_prompt(), "You are a helpful assistant");
assert_eq!(config.get_max_iterations(), 10);
assert_eq!(config.get_token_limit(), usize::MAX);
assert!(!config.is_tool_enabled());
assert!(!config.is_task_enabled());
assert!(!config.is_human_in_loop_enabled());
assert!(!config.is_subagent_enabled());
}
#[test]
fn test_agent_config_minimal() {
let config = AgentConfig::minimal("qwen3-max", "Be helpful");
assert_eq!(config.get_model_name(), "qwen3-max");
assert!(!config.is_tool_enabled());
assert!(!config.is_memory_enabled());
assert!(!config.is_cot_enabled());
}
#[test]
fn test_agent_config_standard() {
let config = AgentConfig::standard("qwen3-max", "agent1", "You are helpful");
assert!(config.is_tool_enabled());
assert!(config.is_cot_enabled());
}
#[test]
fn test_agent_config_full_featured() {
let config = AgentConfig::full_featured("qwen3-max", "agent1", "You are helpful");
assert!(config.is_tool_enabled());
assert!(config.is_memory_enabled());
assert!(config.is_task_enabled());
assert!(config.is_cot_enabled());
}
#[test]
fn test_agent_config_builder_chain() {
let config = AgentConfig::new("model", "agent", "prompt")
.max_iterations(20)
.token_limit(8000)
.enable_tool(true)
.enable_task(true)
.enable_human_in_loop(true)
.enable_subagent(true)
.enable_memory(true)
.enable_cot(false)
.llm_max_retries(5)
.llm_retry_delay_ms(1000)
.tool_error_feedback(false);
assert_eq!(config.get_max_iterations(), 20);
assert_eq!(config.get_token_limit(), 8000);
assert!(config.is_tool_enabled());
assert!(config.is_task_enabled());
assert!(config.is_human_in_loop_enabled());
assert!(config.is_subagent_enabled());
assert!(config.is_memory_enabled());
assert!(!config.is_cot_enabled());
assert_eq!(config.get_llm_max_retries(), 5);
assert_eq!(config.get_llm_retry_delay_ms(), 1000);
assert!(!config.get_tool_error_feedback());
}
#[test]
fn test_agent_config_allowed_tools() {
let config = AgentConfig::new("model", "agent", "prompt")
.allowed_tools(vec!["tool1".to_string(), "tool2".to_string()]);
assert_eq!(config.get_allowed_tools(), &["tool1", "tool2"]);
}
#[test]
fn test_agent_config_session_id() {
let config = AgentConfig::new("model", "agent", "prompt").session_id("session-123");
assert_eq!(config.get_session_id(), Some("session-123"));
}
#[test]
fn test_agent_config_conversation_id() {
let config =
AgentConfig::new("model", "agent", "prompt").conversation_id("conversation-123");
assert_eq!(config.get_conversation_id(), Some("conversation-123"));
}
#[test]
fn test_agent_config_role() {
let config = AgentConfig::new("model", "agent", "prompt").role(AgentRole::Orchestrator);
assert_eq!(config.role, AgentRole::Orchestrator);
}
#[test]
fn test_agent_config_model_name_mutation() {
let mut config = AgentConfig::new("model1", "agent", "prompt");
config.set_model_name("model2");
assert_eq!(config.get_model_name(), "model2");
}
#[test]
fn test_agent_config_with_full_features() {
let config = AgentConfig::new("model", "agent", "prompt").with_full_features();
assert!(config.is_tool_enabled());
assert!(config.is_memory_enabled());
assert!(config.is_task_enabled());
assert!(config.is_cot_enabled());
}
#[test]
fn test_agent_config_with_tools() {
let config = AgentConfig::new("model", "agent", "prompt").with_tools();
assert!(config.is_tool_enabled());
assert!(config.is_cot_enabled());
}
#[test]
fn test_agent_config_memory_path() {
let config =
AgentConfig::new("model", "agent", "prompt").memory_path("/custom/path/store.json");
assert_eq!(config.get_memory_path(), "/custom/path/store.json");
}
#[test]
fn test_agent_config_checkpointer_path() {
let config = AgentConfig::new("model", "agent", "prompt")
.checkpointer_path("/custom/path/checkpoints.json");
assert_eq!(
config.get_checkpointer_path(),
"/custom/path/checkpoints.json"
);
}
#[test]
fn test_agent_role_default() {
assert_eq!(AgentRole::default(), AgentRole::Worker);
}
}