diff --git a/src/harness/providers/openai/test.rs b/src/harness/providers/openai/test.rs index a2b21d4c..1f25cce1 100644 --- a/src/harness/providers/openai/test.rs +++ b/src/harness/providers/openai/test.rs @@ -2007,6 +2007,23 @@ fn auth_headers_bearer_is_the_default() { assert_eq!(AuthStyle::default(), AuthStyle::Bearer); } +#[test] +fn auth_headers_basic_prefixes_the_credential() { + let headers = auth_headers(&AuthStyle::Basic, "base64-credential"); + assert_eq!( + headers, + vec![( + "Authorization".to_string(), + "Basic base64-credential".to_string() + )] + ); +} + +#[test] +fn auth_headers_basic_omits_an_empty_credential() { + assert!(auth_headers(&AuthStyle::Basic, "").is_empty()); +} + #[test] fn auth_headers_x_api_key_sends_bare_key_without_authorization() { let headers = auth_headers(&AuthStyle::XApiKey, "secret"); diff --git a/src/harness/providers/openai/transport.rs b/src/harness/providers/openai/transport.rs index 8f05fd78..ad7be9f2 100644 --- a/src/harness/providers/openai/transport.rs +++ b/src/harness/providers/openai/transport.rs @@ -16,10 +16,10 @@ use crate::harness::model::StreamAccumulator; /// How the provider expects the API credential to be sent on each request. /// /// OpenAI-compatible endpoints diverge on auth: hosted OpenAI and most gateways -/// use `Bearer`, some providers use a bare `x-api-key`, Anthropic's compat -/// endpoint pairs `x-api-key` with an `anthropic-version` header, and a few need -/// an arbitrary custom header carrying the raw credential. Defaults to -/// [`AuthStyle::Bearer`]. +/// use `Bearer`, Inworld uses HTTP Basic credentials, some providers use a bare +/// `x-api-key`, Anthropic's compat endpoint pairs `x-api-key` with an +/// `anthropic-version` header, and a few need an arbitrary custom header +/// carrying the raw credential. Defaults to [`AuthStyle::Bearer`]. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub enum AuthStyle { /// No authentication header (e.g. a local Ollama server). @@ -27,6 +27,8 @@ pub enum AuthStyle { /// `Authorization: Bearer ` — hosted OpenAI and most gateways. #[default] Bearer, + /// `Authorization: Basic ` — used by Inworld and compatible APIs. + Basic, /// `x-api-key: ` — used by some OpenAI-compatible providers. XApiKey, /// `x-api-key: ` + `anthropic-version: 2023-06-01` (Anthropic compat). @@ -199,6 +201,8 @@ pub(super) fn auth_headers(auth: &AuthStyle, api_key: &str) -> Vec<(String, Stri match auth { AuthStyle::None => Vec::new(), AuthStyle::Bearer => vec![("Authorization".to_string(), format!("Bearer {api_key}"))], + AuthStyle::Basic if api_key.is_empty() => Vec::new(), + AuthStyle::Basic => vec![("Authorization".to_string(), format!("Basic {api_key}"))], AuthStyle::XApiKey => vec![("x-api-key".to_string(), api_key.to_string())], AuthStyle::Anthropic => vec![ ("x-api-key".to_string(), api_key.to_string()),