use crate::params::AnthropicParams;
use crate::providers::AnthropicClient;
use crate::retry_api::RetryOptions;
use crate::{CommonParams, HttpConfig, LlmBuilder, LlmError};
use std::collections::HashMap;
use std::time::Duration;
pub struct AnthropicBuilder {
pub(crate) base: LlmBuilder,
api_key: Option<String>,
base_url: Option<String>,
model: Option<String>,
common_params: CommonParams,
anthropic_params: AnthropicParams,
http_config: HttpConfig,
tracing_config: Option<crate::tracing::TracingConfig>,
retry_options: Option<RetryOptions>,
}
impl AnthropicBuilder {
pub fn new(base: LlmBuilder) -> Self {
Self {
base,
api_key: None,
base_url: None,
model: None,
common_params: CommonParams::default(),
anthropic_params: AnthropicParams::default(),
http_config: HttpConfig::default(),
tracing_config: None,
retry_options: None,
}
}
pub fn api_key<S: Into<String>>(mut self, key: S) -> Self {
self.api_key = Some(key.into());
self
}
pub fn base_url<S: Into<String>>(mut self, url: S) -> Self {
self.base_url = Some(url.into());
self
}
pub fn model<S: Into<String>>(mut self, model: S) -> Self {
let model_str = model.into();
self.model = Some(model_str.clone());
self.common_params.model = model_str;
self
}
pub const fn temperature(mut self, temp: f32) -> Self {
self.common_params.temperature = Some(temp);
self
}
pub const fn max_tokens(mut self, tokens: u32) -> Self {
self.common_params.max_tokens = Some(tokens);
self
}
pub const fn top_p(mut self, top_p: f32) -> Self {
self.common_params.top_p = Some(top_p);
self
}
pub fn cache_control(mut self, cache: crate::params::anthropic::CacheControl) -> Self {
self.anthropic_params.cache_control = Some(cache);
self
}
pub const fn thinking_budget(mut self, budget: u32) -> Self {
self.anthropic_params.thinking_budget = Some(budget);
self
}
pub const fn with_thinking_enabled(mut self) -> Self {
self.anthropic_params.thinking_budget = Some(10000);
self
}
pub const fn with_thinking_mode(mut self, budget_tokens: Option<u32>) -> Self {
self.anthropic_params.thinking_budget = budget_tokens;
self
}
pub fn system_message<S: Into<String>>(mut self, system: S) -> Self {
self.anthropic_params.system = Some(system.into());
self
}
pub fn tracing(mut self, config: crate::tracing::TracingConfig) -> Self {
self.tracing_config = Some(config);
self
}
pub fn debug_tracing(self) -> Self {
self.tracing(crate::tracing::TracingConfig::development())
}
pub fn minimal_tracing(self) -> Self {
self.tracing(crate::tracing::TracingConfig::minimal())
}
pub fn json_tracing(self) -> Self {
self.tracing(crate::tracing::TracingConfig::json_production())
}
pub fn pretty_json(mut self, pretty: bool) -> Self {
let config = self
.tracing_config
.take()
.unwrap_or_else(crate::tracing::TracingConfig::development)
.with_pretty_json(pretty);
self.tracing_config = Some(config);
self
}
pub fn mask_sensitive_values(mut self, mask: bool) -> Self {
let config = self
.tracing_config
.take()
.unwrap_or_else(crate::tracing::TracingConfig::development)
.with_mask_sensitive_values(mask);
self.tracing_config = Some(config);
self
}
pub fn with_retry(mut self, options: RetryOptions) -> Self {
self.retry_options = Some(options);
self
}
pub fn metadata<K, V>(mut self, key: K, value: V) -> Self
where
K: Into<String>,
V: Into<String>,
{
if self.anthropic_params.metadata.is_none() {
self.anthropic_params.metadata = Some(HashMap::new());
}
self.anthropic_params
.metadata
.as_mut()
.unwrap()
.insert(key.into(), value.into());
self
}
pub async fn build(self) -> Result<AnthropicClient, LlmError> {
let api_key = self
.api_key
.or_else(|| std::env::var("ANTHROPIC_API_KEY").ok())
.ok_or(LlmError::MissingApiKey(
"Anthropic API key not provided".to_string(),
))?;
let base_url = self
.base_url
.unwrap_or_else(|| "https://api.anthropic.com".to_string());
let _tracing_guard = if let Some(ref tracing_config) = self.tracing_config {
crate::tracing::init_tracing(tracing_config.clone())?
} else {
None
};
let http_client = self.base.http_client.unwrap_or_else(|| {
reqwest::Client::builder()
.timeout(self.base.timeout.unwrap_or(Duration::from_secs(30)))
.build()
.unwrap()
});
let specific_params = crate::providers::anthropic::types::AnthropicSpecificParams {
beta_features: self
.anthropic_params
.beta_features
.clone()
.unwrap_or_default(),
cache_control: self.anthropic_params.cache_control.as_ref().map(|_cc| {
crate::providers::anthropic::cache::CacheControl::ephemeral() }),
thinking_config: self.anthropic_params.thinking_budget.map(|budget| {
crate::providers::anthropic::thinking::ThinkingConfig::enabled(budget)
}),
metadata: self.anthropic_params.metadata.as_ref().map(|m| {
let mut json_map = serde_json::Map::new();
for (k, v) in m {
json_map.insert(k.clone(), serde_json::Value::String(v.clone()));
}
serde_json::Value::Object(json_map)
}),
};
let mut client = AnthropicClient::new(
api_key,
base_url,
http_client,
self.common_params,
self.anthropic_params,
self.http_config,
);
client = client.with_specific_params(specific_params);
client.set_tracing_guard(_tracing_guard);
client.set_tracing_config(self.tracing_config);
client.set_retry_options(self.retry_options.clone());
Ok(client)
}
}