use crate::config::Config;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Profile {
pub name: String,
pub description: String,
pub config_overrides: ConfigOverrides,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ConfigOverrides {
pub max_tokens: Option<usize>,
pub temperature: Option<f32>,
pub max_iterations: Option<usize>,
pub step_timeout_secs: Option<u64>,
pub concurrency: Option<ConcurrencyOverrides>,
pub agent: Option<AgentOverrides>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConcurrencyOverrides {
pub max_parallel_requests: Option<usize>,
pub timeout_secs: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentOverrides {
pub streaming: Option<bool>,
pub native_function_calling: Option<bool>,
pub enable_thinking: Option<bool>,
}
pub struct ProfileManager {
profiles: HashMap<String, Profile>,
}
impl ProfileManager {
pub fn new() -> Self {
let mut profiles = HashMap::new();
profiles.insert(
"architect".to_string(),
Profile {
name: "Architect".to_string(),
description: "Deep thinking, single agent for complex design tasks".to_string(),
config_overrides: ConfigOverrides {
max_tokens: Some(8192),
temperature: Some(0.7),
max_iterations: Some(100),
step_timeout_secs: Some(900),
concurrency: Some(ConcurrencyOverrides {
max_parallel_requests: Some(4),
timeout_secs: Some(120),
}),
agent: Some(AgentOverrides {
streaming: Some(false),
native_function_calling: Some(true),
enable_thinking: Some(true),
}),
},
},
);
profiles.insert(
"swarm-8".to_string(),
Profile {
name: "Swarm-8".to_string(),
description: "8 concurrent agents for collaborative tasks".to_string(),
config_overrides: ConfigOverrides {
max_tokens: Some(4096),
temperature: Some(0.6),
max_iterations: Some(50),
step_timeout_secs: Some(600),
concurrency: Some(ConcurrencyOverrides {
max_parallel_requests: Some(16),
timeout_secs: Some(300),
}),
agent: Some(AgentOverrides {
streaming: Some(false),
native_function_calling: Some(true),
enable_thinking: Some(false),
}),
},
},
);
profiles.insert(
"batch-16".to_string(),
Profile {
name: "Batch-16".to_string(),
description: "16 concurrent agents - optimal for 2x RTX 4090".to_string(),
config_overrides: ConfigOverrides {
max_tokens: Some(4096),
temperature: Some(0.6),
max_iterations: Some(30),
step_timeout_secs: Some(900),
concurrency: Some(ConcurrencyOverrides {
max_parallel_requests: Some(24),
timeout_secs: Some(600),
}),
agent: Some(AgentOverrides {
streaming: Some(false),
native_function_calling: Some(true),
enable_thinking: Some(false),
}),
},
},
);
profiles.insert(
"batch-32".to_string(),
Profile {
name: "Batch-32".to_string(),
description: "Maximum throughput for simple tasks".to_string(),
config_overrides: ConfigOverrides {
max_tokens: Some(2048),
temperature: Some(0.5),
max_iterations: Some(10),
step_timeout_secs: Some(300),
concurrency: Some(ConcurrencyOverrides {
max_parallel_requests: Some(32),
timeout_secs: Some(120),
}),
agent: Some(AgentOverrides {
streaming: Some(false),
native_function_calling: Some(false),
enable_thinking: Some(false),
}),
},
},
);
profiles.insert(
"visual".to_string(),
Profile {
name: "Visual".to_string(),
description: "Website building with visual validation".to_string(),
config_overrides: ConfigOverrides {
max_tokens: Some(4096),
temperature: Some(0.6),
max_iterations: Some(50),
step_timeout_secs: Some(600),
concurrency: Some(ConcurrencyOverrides {
max_parallel_requests: Some(8),
timeout_secs: Some(300),
}),
agent: Some(AgentOverrides {
streaming: Some(false),
native_function_calling: Some(true),
enable_thinking: Some(false),
}),
},
},
);
profiles.insert(
"quick".to_string(),
Profile {
name: "Quick".to_string(),
description: "Fast responses, minimal verification".to_string(),
config_overrides: ConfigOverrides {
max_tokens: Some(2048),
temperature: Some(0.5),
max_iterations: Some(10),
step_timeout_secs: Some(120),
concurrency: Some(ConcurrencyOverrides {
max_parallel_requests: Some(32),
timeout_secs: Some(60),
}),
agent: Some(AgentOverrides {
streaming: Some(false),
native_function_calling: Some(false),
enable_thinking: Some(false),
}),
},
},
);
Self { profiles }
}
pub fn get(&self, name: &str) -> Option<&Profile> {
self.profiles.get(name)
}
pub fn list(&self) -> Vec<&Profile> {
self.profiles.values().collect()
}
pub fn apply_profile(&self, config: &mut Config, profile_name: &str) -> Result<()> {
let profile = self
.get(profile_name)
.ok_or_else(|| anyhow::anyhow!("Profile '{}' not found", profile_name))?;
let o = &profile.config_overrides;
if let Some(max_tokens) = o.max_tokens {
config.max_tokens = max_tokens;
}
if let Some(temperature) = o.temperature {
config.temperature = temperature;
}
if let Some(max_iterations) = o.max_iterations {
config.agent.max_iterations = max_iterations;
}
if let Some(step_timeout_secs) = o.step_timeout_secs {
config.agent.step_timeout_secs = step_timeout_secs;
}
if let Some(agent) = &o.agent {
if let Some(streaming) = agent.streaming {
config.agent.streaming = streaming;
}
if let Some(native_fc) = agent.native_function_calling {
config.agent.native_function_calling = native_fc;
}
}
if let Some(concurrency) = &o.concurrency {
if let Some(max_parallel) = concurrency.max_parallel_requests {
config.concurrency.max_streams = max_parallel;
}
}
Ok(())
}
pub fn describe(&self, name: &str) -> Option<String> {
self.get(name)
.map(|p| format!("{}: {}", p.name, p.description))
}
}
impl Default for ProfileManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[path = "../../tests/unit/profiles/mod_test.rs"]
mod tests;