use crate::error::{AppError, Result};
#[derive(Debug, Clone)]
pub struct Config {
pub model: ModelConfig,
pub anthropic: AnthropicConfig,
}
#[derive(Debug, Clone)]
pub struct ModelConfig {
pub provider: String,
pub name: String,
pub max_tokens: u32,
}
#[derive(Debug, Clone)]
pub struct AnthropicConfig {
pub api_key: Option<String>,
pub base_url: String,
}
impl Default for Config {
fn default() -> Self {
Self {
model: ModelConfig {
provider: "anthropic".to_string(),
name: "claude-sonnet-4-6".to_string(),
max_tokens: 8192,
},
anthropic: AnthropicConfig {
api_key: None,
base_url: "https://api.anthropic.com".to_string(),
},
}
}
}
impl Config {
pub fn from_env() -> Result<Self> {
Self::from_env_with(|name| std::env::var(name).ok())
}
pub fn from_env_with<F>(lookup: F) -> Result<Self>
where
F: Fn(&str) -> Option<String>,
{
let mut cfg = Self::default();
if let Some(name) = lookup("CAPO_MODEL_NAME") {
cfg.model.name = name.trim().to_string();
}
if let Some(provider) = lookup("CAPO_MODEL_PROVIDER") {
cfg.model.provider = provider.trim().to_string();
}
if let Some(max_tokens) = lookup("CAPO_MODEL_MAX_TOKENS") {
let max_tokens = max_tokens.trim().to_string();
cfg.model.max_tokens = max_tokens.parse().map_err(|_| {
AppError::Config(format!(
"CAPO_MODEL_MAX_TOKENS not an integer: {max_tokens}"
))
})?;
}
if let Some(key) = lookup("ANTHROPIC_API_KEY") {
cfg.anthropic.api_key = Some(key.trim().to_string());
}
if let Some(url) = lookup("CAPO_ANTHROPIC_BASE_URL") {
cfg.anthropic.base_url = url.trim().to_string();
}
Ok(cfg)
}
}