feat(providers): add Anthropic to the three provider surfaces — ApiProvider/ProviderKind variants, provider metadata (x-api-key auth env, api.anthropic.com, claude-sonnet-4-6 default), AnthropicMessages wire format, ModelRegistry rows for claude-opus-4-8 / claude-sonnet-4-6 / claude-haiku-4-5 (#3014, WIP)
Co-Authored-By: Claude <noreply@anthropic.com> https://claude.ai/code/session_018zaP8vUfTAsrE38L6h6fw5
This commit is contained in:
@@ -607,6 +607,28 @@ impl Default for ModelRegistry {
|
||||
supports_tools: true,
|
||||
supports_reasoning: true,
|
||||
},
|
||||
// Anthropic native Messages API models (#3014)
|
||||
ModelInfo {
|
||||
id: "claude-opus-4-8".to_string(),
|
||||
provider: ProviderKind::Anthropic,
|
||||
aliases: vec!["opus".to_string(), "claude-opus".to_string()],
|
||||
supports_tools: true,
|
||||
supports_reasoning: true,
|
||||
},
|
||||
ModelInfo {
|
||||
id: "claude-sonnet-4-6".to_string(),
|
||||
provider: ProviderKind::Anthropic,
|
||||
aliases: vec!["sonnet".to_string(), "claude-sonnet".to_string()],
|
||||
supports_tools: true,
|
||||
supports_reasoning: true,
|
||||
},
|
||||
ModelInfo {
|
||||
id: "claude-haiku-4-5".to_string(),
|
||||
provider: ProviderKind::Anthropic,
|
||||
aliases: vec!["haiku".to_string(), "claude-haiku".to_string()],
|
||||
supports_tools: true,
|
||||
supports_reasoning: false,
|
||||
},
|
||||
// MiniMax 2.7 (OpenRouter)
|
||||
ModelInfo {
|
||||
id: "minimax/minimax-2.7".to_string(),
|
||||
|
||||
@@ -27,6 +27,8 @@ const DEFAULT_OPENAI_MODEL: &str = "deepseek-v4-pro";
|
||||
const DEFAULT_DEEPSEEK_BASE_URL: &str = "https://api.deepseek.com/beta";
|
||||
const DEFAULT_NVIDIA_NIM_BASE_URL: &str = "https://integrate.api.nvidia.com/v1";
|
||||
const DEFAULT_OPENAI_CODEX_MODEL: &str = "gpt-5.5";
|
||||
const DEFAULT_ANTHROPIC_MODEL: &str = "claude-sonnet-4-6";
|
||||
const DEFAULT_ANTHROPIC_BASE_URL: &str = "https://api.anthropic.com";
|
||||
const DEFAULT_OPENAI_CODEX_BASE_URL: &str = "https://chatgpt.com/backend-api";
|
||||
const DEFAULT_OPENAI_BASE_URL: &str = "https://api.openai.com/v1";
|
||||
const DEFAULT_ATLASCLOUD_MODEL: &str = "deepseek-ai/deepseek-v4-flash";
|
||||
@@ -152,10 +154,12 @@ pub enum ProviderKind {
|
||||
alias = "chatgpt_codex"
|
||||
)]
|
||||
OpenaiCodex,
|
||||
#[serde(alias = "claude")]
|
||||
Anthropic,
|
||||
}
|
||||
|
||||
impl ProviderKind {
|
||||
pub const ALL: [Self; 20] = [
|
||||
pub const ALL: [Self; 21] = [
|
||||
Self::Deepseek,
|
||||
Self::NvidiaNim,
|
||||
Self::Openai,
|
||||
@@ -176,6 +180,7 @@ impl ProviderKind {
|
||||
Self::Huggingface,
|
||||
Self::Together,
|
||||
Self::OpenaiCodex,
|
||||
Self::Anthropic,
|
||||
];
|
||||
|
||||
#[must_use]
|
||||
@@ -201,6 +206,7 @@ impl ProviderKind {
|
||||
Self::Huggingface => "huggingface",
|
||||
Self::Together => "together",
|
||||
Self::OpenaiCodex => "openai-codex",
|
||||
Self::Anthropic => "anthropic",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,6 +237,7 @@ impl ProviderKind {
|
||||
"ollama" | "ollama-local" => Some(Self::Ollama),
|
||||
"huggingface" | "hugging-face" | "hugging_face" | "hf" => Some(Self::Huggingface),
|
||||
"together" | "together-ai" | "together_ai" => Some(Self::Together),
|
||||
"anthropic" | "claude" => Some(Self::Anthropic),
|
||||
"openai-codex" | "openai_codex" | "openaicodex" | "codex" | "chatgpt"
|
||||
| "chatgpt-codex" | "chatgpt_codex" | "chatgptcodex" => Some(Self::OpenaiCodex),
|
||||
_ => None,
|
||||
@@ -312,6 +319,8 @@ pub struct ProvidersToml {
|
||||
alias = "chatgpt-codex"
|
||||
)]
|
||||
pub openai_codex: ProviderConfigToml,
|
||||
#[serde(default)]
|
||||
pub anthropic: ProviderConfigToml,
|
||||
}
|
||||
|
||||
/// Sibling `permissions.toml` schema.
|
||||
@@ -361,6 +370,7 @@ impl ProvidersToml {
|
||||
ProviderKind::Huggingface => &self.huggingface,
|
||||
ProviderKind::Together => &self.together,
|
||||
ProviderKind::OpenaiCodex => &self.openai_codex,
|
||||
ProviderKind::Anthropic => &self.anthropic,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,6 +395,7 @@ impl ProvidersToml {
|
||||
ProviderKind::Huggingface => &mut self.huggingface,
|
||||
ProviderKind::Together => &mut self.together,
|
||||
ProviderKind::OpenaiCodex => &mut self.openai_codex,
|
||||
ProviderKind::Anthropic => &mut self.anthropic,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2022,6 +2033,7 @@ impl ConfigToml {
|
||||
ProviderKind::Huggingface => DEFAULT_HUGGINGFACE_BASE_URL.to_string(),
|
||||
ProviderKind::Together => DEFAULT_TOGETHER_BASE_URL.to_string(),
|
||||
ProviderKind::OpenaiCodex => DEFAULT_OPENAI_CODEX_BASE_URL.to_string(),
|
||||
ProviderKind::Anthropic => DEFAULT_ANTHROPIC_BASE_URL.to_string(),
|
||||
})
|
||||
};
|
||||
// CLI flag wins outright. Otherwise: config-file → injected secrets/env.
|
||||
@@ -2454,6 +2466,7 @@ fn default_model_for_provider(provider: ProviderKind) -> &'static str {
|
||||
ProviderKind::Huggingface => DEFAULT_HUGGINGFACE_MODEL,
|
||||
ProviderKind::Together => DEFAULT_TOGETHER_MODEL,
|
||||
ProviderKind::OpenaiCodex => DEFAULT_OPENAI_CODEX_MODEL,
|
||||
ProviderKind::Anthropic => DEFAULT_ANTHROPIC_MODEL,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2479,6 +2492,7 @@ fn default_base_url_for_provider(provider: ProviderKind) -> &'static str {
|
||||
ProviderKind::Huggingface => DEFAULT_HUGGINGFACE_BASE_URL,
|
||||
ProviderKind::Together => DEFAULT_TOGETHER_BASE_URL,
|
||||
ProviderKind::OpenaiCodex => DEFAULT_OPENAI_CODEX_BASE_URL,
|
||||
ProviderKind::Anthropic => DEFAULT_ANTHROPIC_BASE_URL,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3231,6 +3245,8 @@ struct EnvRuntimeOverrides {
|
||||
together_model: Option<String>,
|
||||
openai_codex_base_url: Option<String>,
|
||||
openai_codex_model: Option<String>,
|
||||
anthropic_base_url: Option<String>,
|
||||
anthropic_model: Option<String>,
|
||||
}
|
||||
|
||||
impl EnvRuntimeOverrides {
|
||||
@@ -3394,6 +3410,12 @@ impl EnvRuntimeOverrides {
|
||||
.or_else(|_| std::env::var("CODEX_MODEL"))
|
||||
.ok()
|
||||
.filter(|v| !v.trim().is_empty()),
|
||||
anthropic_base_url: std::env::var("ANTHROPIC_BASE_URL")
|
||||
.ok()
|
||||
.filter(|v| !v.trim().is_empty()),
|
||||
anthropic_model: std::env::var("ANTHROPIC_MODEL")
|
||||
.ok()
|
||||
.filter(|v| !v.trim().is_empty()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3422,6 +3444,7 @@ impl EnvRuntimeOverrides {
|
||||
ProviderKind::Huggingface => self.huggingface_base_url.clone(),
|
||||
ProviderKind::Together => self.together_base_url.clone(),
|
||||
ProviderKind::OpenaiCodex => self.openai_codex_base_url.clone(),
|
||||
ProviderKind::Anthropic => self.anthropic_base_url.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3441,6 +3464,7 @@ impl EnvRuntimeOverrides {
|
||||
ProviderKind::Huggingface => self.huggingface_model.clone(),
|
||||
ProviderKind::Together => self.together_model.clone(),
|
||||
ProviderKind::OpenaiCodex => self.openai_codex_model.clone(),
|
||||
ProviderKind::Anthropic => self.anthropic_model.clone(),
|
||||
_ => None,
|
||||
}?;
|
||||
|
||||
@@ -5132,10 +5156,12 @@ unix_socket_path = "/tmp/cw-hooks.sock"
|
||||
);
|
||||
assert!(!provider.display_name().trim().is_empty());
|
||||
assert!(!provider.env_vars().is_empty());
|
||||
// OpenAI Codex (ChatGPT) speaks the Responses API; every other
|
||||
// built-in provider is OpenAI-compatible Chat Completions.
|
||||
// OpenAI Codex (ChatGPT) speaks the Responses API and Anthropic
|
||||
// speaks the native Messages API; every other built-in provider
|
||||
// is OpenAI-compatible Chat Completions.
|
||||
let expected_wire = match kind {
|
||||
ProviderKind::OpenaiCodex => provider::WireFormat::Responses,
|
||||
ProviderKind::Anthropic => provider::WireFormat::AnthropicMessages,
|
||||
_ => provider::WireFormat::ChatCompletions,
|
||||
};
|
||||
assert_eq!(provider.wire(), expected_wire);
|
||||
|
||||
@@ -27,6 +27,8 @@ pub enum WireFormat {
|
||||
ChatCompletions,
|
||||
/// OpenAI Responses API (`/responses`).
|
||||
Responses,
|
||||
/// Native Anthropic Messages API (`/v1/messages`).
|
||||
AnthropicMessages,
|
||||
}
|
||||
|
||||
/// Static metadata for a built-in model provider.
|
||||
@@ -320,6 +322,39 @@ impl Provider for OpenaiCodex {
|
||||
}
|
||||
}
|
||||
|
||||
/// Native Anthropic Messages API provider (#3014).
|
||||
pub struct Anthropic;
|
||||
|
||||
impl Provider for Anthropic {
|
||||
fn kind(&self) -> ProviderKind {
|
||||
ProviderKind::Anthropic
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &'static str {
|
||||
"Anthropic"
|
||||
}
|
||||
|
||||
fn default_base_url(&self) -> &'static str {
|
||||
crate::DEFAULT_ANTHROPIC_BASE_URL
|
||||
}
|
||||
|
||||
fn default_model(&self) -> &'static str {
|
||||
crate::DEFAULT_ANTHROPIC_MODEL
|
||||
}
|
||||
|
||||
fn env_vars(&self) -> &'static [&'static str] {
|
||||
&["ANTHROPIC_API_KEY"]
|
||||
}
|
||||
|
||||
fn provider_config_key(&self) -> &'static str {
|
||||
"anthropic"
|
||||
}
|
||||
|
||||
fn wire(&self) -> WireFormat {
|
||||
WireFormat::AnthropicMessages
|
||||
}
|
||||
}
|
||||
|
||||
static DEEPSEEK: Deepseek = Deepseek;
|
||||
static NVIDIA_NIM: NvidiaNim = NvidiaNim;
|
||||
static OPENAI: Openai = Openai;
|
||||
@@ -340,8 +375,9 @@ static OLLAMA: Ollama = Ollama;
|
||||
static HUGGINGFACE: Huggingface = Huggingface;
|
||||
static TOGETHER: Together = Together;
|
||||
static OPENAI_CODEX: OpenaiCodex = OpenaiCodex;
|
||||
static ANTHROPIC: Anthropic = Anthropic;
|
||||
|
||||
static PROVIDER_REGISTRY: [&dyn Provider; 20] = [
|
||||
static PROVIDER_REGISTRY: [&dyn Provider; 21] = [
|
||||
&DEEPSEEK,
|
||||
&NVIDIA_NIM,
|
||||
&OPENAI,
|
||||
@@ -362,6 +398,7 @@ static PROVIDER_REGISTRY: [&dyn Provider; 20] = [
|
||||
&HUGGINGFACE,
|
||||
&TOGETHER,
|
||||
&OPENAI_CODEX,
|
||||
&ANTHROPIC,
|
||||
];
|
||||
|
||||
/// Return all built-in provider metadata entries in `ProviderKind::ALL` order.
|
||||
@@ -410,5 +447,6 @@ pub fn provider_for_kind(kind: ProviderKind) -> &'static dyn Provider {
|
||||
ProviderKind::Huggingface => &HUGGINGFACE,
|
||||
ProviderKind::Together => &TOGETHER,
|
||||
ProviderKind::OpenaiCodex => &OPENAI_CODEX,
|
||||
ProviderKind::Anthropic => &ANTHROPIC,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +188,7 @@ pub enum ApiProvider {
|
||||
Huggingface,
|
||||
Together,
|
||||
OpenaiCodex,
|
||||
Anthropic,
|
||||
}
|
||||
|
||||
impl ApiProvider {
|
||||
@@ -237,6 +238,7 @@ impl ApiProvider {
|
||||
"ollama" | "ollama-local" => Some(Self::Ollama),
|
||||
"huggingface" | "hugging-face" | "hugging_face" | "hf" => Some(Self::Huggingface),
|
||||
"together" | "together-ai" | "together_ai" => Some(Self::Together),
|
||||
"anthropic" | "claude" => Some(Self::Anthropic),
|
||||
"openai-codex" | "openai_codex" | "openaicodex" | "codex" | "chatgpt"
|
||||
| "chatgpt-codex" | "chatgpt_codex" | "chatgptcodex" => Some(Self::OpenaiCodex),
|
||||
_ => None,
|
||||
@@ -267,6 +269,7 @@ impl ApiProvider {
|
||||
Self::Huggingface => "huggingface",
|
||||
Self::Together => "together",
|
||||
Self::OpenaiCodex => "openai-codex",
|
||||
Self::Anthropic => "anthropic",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +298,7 @@ impl ApiProvider {
|
||||
Self::Huggingface => "Hugging Face",
|
||||
Self::Together => "Together AI",
|
||||
Self::OpenaiCodex => "OpenAI Codex (ChatGPT)",
|
||||
Self::Anthropic => "Anthropic",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,6 +326,7 @@ impl ApiProvider {
|
||||
Self::Huggingface,
|
||||
Self::Together,
|
||||
Self::OpenaiCodex,
|
||||
Self::Anthropic,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user