diff --git a/src/adapter/adapters/anthropic/adapter_impl.rs b/src/adapter/adapters/anthropic/adapter_impl.rs index 789b3b77..30da5ad1 100644 --- a/src/adapter/adapters/anthropic/adapter_impl.rs +++ b/src/adapter/adapters/anthropic/adapter_impl.rs @@ -97,19 +97,25 @@ impl Adapter for AnthropicAdapter { ) -> Result { let ServiceTarget { endpoint, auth, model } = target; - // -- api_key - let api_key = get_api_key(auth, &model)?; - // -- url let url = Self::get_service_url(&model, service_type, endpoint)?; - // -- headers - let headers = Headers::from(vec![ - // headers - ("x-api-key".to_string(), api_key), - ("anthropic-beta".to_string(), "effort-2025-11-24".to_string()), - ("anthropic-version".to_string(), ANTHROPIC_VERSION.to_string()), - ]); + // -- headers (Bearer token uses Authorization header, otherwise x-api-key) + let headers = match &auth { + AuthData::BearerToken(token) => Headers::from(vec![ + ("Authorization".to_string(), format!("Bearer {}", token)), + ("anthropic-beta".to_string(), "effort-2025-11-24".to_string()), + ("anthropic-version".to_string(), ANTHROPIC_VERSION.to_string()), + ]), + _ => { + let api_key = get_api_key(auth, &model)?; + Headers::from(vec![ + ("x-api-key".to_string(), api_key), + ("anthropic-beta".to_string(), "effort-2025-11-24".to_string()), + ("anthropic-version".to_string(), ANTHROPIC_VERSION.to_string()), + ]) + } + }; // -- Parts let AnthropicRequestParts { diff --git a/src/resolver/auth_data.rs b/src/resolver/auth_data.rs index 74027adc..b8a95191 100644 --- a/src/resolver/auth_data.rs +++ b/src/resolver/auth_data.rs @@ -10,6 +10,9 @@ pub enum AuthData { /// The key value itself. Key(String), + /// OAuth Bearer token for providers that support `Authorization: Bearer` auth. + BearerToken(String), + /// Override headers and request url for unorthodox authentication schemes RequestOverride { url: String, headers: Headers }, @@ -44,6 +47,8 @@ impl AuthData { match self { // Overrides don't use an api key AuthData::RequestOverride { .. } => Ok(String::new()), + // Bearer tokens return the token value + AuthData::BearerToken(value) => Ok(value.to_string()), AuthData::FromEnv(env_name) => { // Get value from the environment name. let value = std::env::var(env_name).map_err(|_| Error::ApiKeyEnvNotFound { @@ -66,6 +71,7 @@ impl std::fmt::Debug for AuthData { // NOTE: Here we also redact for `FromEnv` in case the developer confuses this with a key. AuthData::FromEnv(_env_name) => write!(f, "AuthData::FromEnv(REDACTED)"), AuthData::Key(_) => write!(f, "AuthData::Single(REDACTED)"), + AuthData::BearerToken(_) => write!(f, "AuthData::BearerToken(REDACTED)"), AuthData::MultiKeys(_) => write!(f, "AuthData::Multi(REDACTED)"), AuthData::RequestOverride { .. } => { write!(f, "AuthData::RequestOverride {{ url: REDACTED, headers: REDACTED }}") @@ -75,3 +81,44 @@ impl std::fmt::Debug for AuthData { } // endregion: --- AuthData Std Impls + +// region: --- Tests + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_bearer_token_single_key_value() { + let auth = AuthData::BearerToken("sk-ant-oat01-test-token".to_string()); + let value = auth.single_key_value().unwrap(); + assert_eq!(value, "sk-ant-oat01-test-token"); + } + + #[test] + fn test_bearer_token_debug_redacted() { + let auth = AuthData::BearerToken("secret-token".to_string()); + let debug = format!("{:?}", auth); + assert_eq!(debug, "AuthData::BearerToken(REDACTED)"); + assert!(!debug.contains("secret-token")); + } + + #[test] + fn test_key_single_key_value() { + let auth = AuthData::Key("test-api-key".to_string()); + let value = auth.single_key_value().unwrap(); + assert_eq!(value, "test-api-key"); + } + + #[test] + fn test_request_override_returns_empty() { + let auth = AuthData::RequestOverride { + url: "https://example.com".to_string(), + headers: Headers::default(), + }; + let value = auth.single_key_value().unwrap(); + assert_eq!(value, ""); + } +} + +// endregion: --- Tests