use serde::{Deserialize, Serialize};
use crate::VTCodeConfig;
use crate::constants::{env_vars, models::anthropic::normalize_model_id, urls};
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum AnthropicSpeed {
Standard,
Fast,
}
impl AnthropicSpeed {
pub const fn as_str(self) -> &'static str {
match self {
Self::Standard => "standard",
Self::Fast => "fast",
}
}
pub fn supports_model(model: &str) -> bool {
matches!(normalize_model_id(model), "claude-opus-5-5" | "claude-opus-5" | "claude-opus-4-8")
}
pub fn requires_beta(model: &str) -> bool {
matches!(normalize_model_id(model), "claude-opus-5" | "claude-opus-4-8")
}
pub fn is_native_endpoint(base_url: &str) -> bool {
let Ok(url) = url::Url::parse(base_url.trim()) else {
return false;
};
url.scheme() == "https"
&& url.host_str() == Some("api.anthropic.com")
&& url.port_or_known_default() == Some(443)
&& url.username().is_empty()
&& url.password().is_none()
&& matches!(url.path(), "/v1" | "/v1/")
&& url.query().is_none()
&& url.fragment().is_none()
}
pub fn resolve_base_url(configured: Option<&str>) -> String {
configured
.map(str::trim)
.filter(|url| !url.is_empty())
.map(str::to_owned)
.or_else(|| {
std::env::var(env_vars::ANTHROPIC_BASE_URL)
.ok()
.map(|url| url.trim().to_owned())
.filter(|url| !url.is_empty())
})
.unwrap_or_else(|| urls::ANTHROPIC_API_BASE.to_owned())
}
}
impl VTCodeConfig {
pub fn anthropic_speed_supported(&self, provider: &str, model: &str) -> bool {
vtcode_commons::provider::Provider::Anthropic.name_matches(provider)
&& self.custom_provider(provider).is_none()
&& AnthropicSpeed::supports_model(model)
&& AnthropicSpeed::is_native_endpoint(&AnthropicSpeed::resolve_base_url(
self.provider_overrides
.get("anthropic")
.and_then(|config| config.base_url.as_deref()),
))
}
}
#[cfg(test)]
mod tests;