use lc_core::tools::ToolDefinition;
use serde::{Deserialize, Serialize};
use std::env;
use crate::ProviderError;
pub const ANTHROPIC_BASE_URL: &str = "https://api.anthropic.com/v1";
pub const CLAUDE_MODELS: [&str; 5] = [
"claude-opus-4-1", "claude-sonnet-4-5", "claude-haiku-4-5", "claude-3-5-sonnet-latest", "claude-3-5-haiku-latest", ];
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum ThinkingType {
Enabled,
#[default]
Disabled,
}
#[derive(Debug, Clone)]
pub struct ThinkingConfig {
pub budget_tokens: usize,
pub r#type: ThinkingType,
}
impl ThinkingConfig {
pub fn enabled(budget_tokens: usize) -> Self {
Self {
budget_tokens,
r#type: ThinkingType::Enabled,
}
}
pub fn disabled() -> Self {
Self {
budget_tokens: 0,
r#type: ThinkingType::Disabled,
}
}
pub fn is_enabled(&self) -> bool {
self.r#type == ThinkingType::Enabled
}
}
impl Default for ThinkingConfig {
fn default() -> Self {
Self::disabled()
}
}
#[derive(Clone)]
pub struct AnthropicConfig {
pub api_key: String,
pub base_url: String,
pub model: String,
pub max_tokens: usize,
pub temperature: Option<f32>,
pub system_prompt: Option<String>,
pub thinking: ThinkingConfig,
pub tools: Option<Vec<ToolDefinition>>,
pub tool_choice: Option<String>,
pub prompt_caching: bool,
}
impl std::fmt::Debug for AnthropicConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AnthropicConfig")
.field("api_key", &"***")
.field("base_url", &self.base_url)
.field("model", &self.model)
.field("max_tokens", &self.max_tokens)
.field("temperature", &self.temperature)
.field("system_prompt", &self.system_prompt)
.field("thinking", &self.thinking)
.field("tools", &self.tools)
.field("tool_choice", &self.tool_choice)
.field("prompt_caching", &self.prompt_caching)
.finish()
}
}
impl Default for AnthropicConfig {
fn default() -> Self {
Self {
api_key: String::new(),
base_url: ANTHROPIC_BASE_URL.to_string(),
model: "claude-3-5-sonnet-20241022".to_string(),
max_tokens: 4096,
temperature: None,
system_prompt: None,
thinking: ThinkingConfig::default(),
tools: None,
tool_choice: None,
prompt_caching: false,
}
}
}
impl AnthropicConfig {
pub fn new(api_key: impl Into<String>) -> Self {
Self {
api_key: api_key.into(),
..Default::default()
}
}
pub fn from_env_result() -> Result<Self, ProviderError> {
let api_key = env::var("ANTHROPIC_API_KEY").map_err(|_| {
ProviderError::Config("ANTHROPIC_API_KEY environment variable not set".to_string())
})?;
let base_url =
env::var("ANTHROPIC_BASE_URL").unwrap_or_else(|_| ANTHROPIC_BASE_URL.to_string());
let model = env::var("ANTHROPIC_MODEL")
.unwrap_or_else(|_| "claude-3-5-sonnet-20241022".to_string());
let max_tokens = env::var("ANTHROPIC_MAX_TOKENS")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(4096);
Ok(Self {
api_key,
base_url,
model,
max_tokens,
..Default::default()
})
}
pub fn with_model(mut self, model: impl Into<String>) -> Self {
self.model = model.into();
self
}
pub fn with_base_url(mut self, url: impl Into<String>) -> Self {
self.base_url = url.into();
self
}
pub fn with_max_tokens(mut self, max: usize) -> Self {
self.max_tokens = max;
self
}
pub fn with_temperature(mut self, temp: f32) -> Self {
self.temperature = Some(temp);
self
}
pub fn with_system_prompt(mut self, prompt: impl Into<String>) -> Self {
self.system_prompt = Some(prompt.into());
self
}
pub fn with_thinking(mut self, thinking: ThinkingConfig) -> Self {
self.thinking = thinking;
self
}
pub fn with_prompt_caching(mut self, on: bool) -> Self {
self.prompt_caching = on;
self
}
}