From 4f3cf4aa097c7c28308d18ef82c64a824bbeb01a Mon Sep 17 00:00:00 2001 From: Douwe Osinga Date: Thu, 14 May 2026 15:25:26 -0400 Subject: [PATCH] fix: make azure api-version query param conditional on endpoint style Newer Azure OpenAI deployments using /v1 paths reject the api-version query parameter with a 400 error. Now we infer from the endpoint URL: - /v1 endpoints: omit api-version (unless explicitly set) - Legacy endpoints: default to 2024-10-21 (preserving existing behavior) Users can always override via AZURE_OPENAI_API_VERSION. Also removes the default hint from the ConfigKey so the desktop UI doesn't pre-fill a value for new-style endpoint users. Based on the approach from #8256 by @octo-patch. Fixes #8236 Signed-off-by: Douwe Osinga --- crates/goose/src/providers/azure.rs | 57 +++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/crates/goose/src/providers/azure.rs b/crates/goose/src/providers/azure.rs index 4072ae68234d..74c966b7ff6c 100644 --- a/crates/goose/src/providers/azure.rs +++ b/crates/goose/src/providers/azure.rs @@ -12,9 +12,15 @@ const AZURE_PROVIDER_NAME: &str = "azure_openai"; pub const AZURE_DEFAULT_MODEL: &str = "gpt-4o"; pub const AZURE_DOC_URL: &str = "https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models"; -pub const AZURE_DEFAULT_API_VERSION: &str = "2024-10-21"; +const AZURE_DEFAULT_API_VERSION: &str = "2024-10-21"; pub const AZURE_OPENAI_KNOWN_MODELS: &[&str] = &["gpt-4o", "gpt-4o-mini", "gpt-4"]; +/// New-style Azure AI endpoints use `/v1/` paths and reject the `api-version` query param. +fn is_v1_endpoint(endpoint: &str) -> bool { + let normalized = endpoint.trim_end_matches('/'); + normalized.ends_with("/v1") || endpoint.contains("/v1/") +} + pub struct AzureProvider; // Custom auth provider that wraps AzureAuth @@ -57,13 +63,7 @@ impl ProviderDef for AzureProvider { vec![ ConfigKey::new("AZURE_OPENAI_ENDPOINT", true, false, None, true), ConfigKey::new("AZURE_OPENAI_DEPLOYMENT_NAME", true, false, None, true), - ConfigKey::new( - "AZURE_OPENAI_API_VERSION", - true, - false, - Some("2024-10-21"), - false, - ), + ConfigKey::new("AZURE_OPENAI_API_VERSION", false, false, None, false), ConfigKey::new("AZURE_OPENAI_API_KEY", false, true, Some(""), true), ], ) @@ -77,9 +77,16 @@ impl ProviderDef for AzureProvider { let config = crate::config::Config::global(); let endpoint: String = config.get_param("AZURE_OPENAI_ENDPOINT")?; let deployment_name: String = config.get_param("AZURE_OPENAI_DEPLOYMENT_NAME")?; - let api_version: String = config + let api_version: Option = config .get_param("AZURE_OPENAI_API_VERSION") - .unwrap_or_else(|_| AZURE_DEFAULT_API_VERSION.to_string()); + .ok() + .or_else(|| { + if is_v1_endpoint(&endpoint) { + None + } else { + Some(AZURE_DEFAULT_API_VERSION.to_string()) + } + }); let api_key = config .get_secret("AZURE_OPENAI_API_KEY") @@ -92,8 +99,10 @@ impl ProviderDef for AzureProvider { let auth_provider = AzureAuthProvider { auth }; let host = format!("{}/openai", endpoint.trim_end_matches('/')); - let api_client = ApiClient::new(host, AuthMethod::Custom(Box::new(auth_provider)))? - .with_query(vec![("api-version".to_string(), api_version)]); + let mut api_client = ApiClient::new(host, AuthMethod::Custom(Box::new(auth_provider)))?; + if let Some(version) = api_version { + api_client = api_client.with_query(vec![("api-version".to_string(), version)]); + } Ok(OpenAiCompatibleProvider::new( AZURE_PROVIDER_NAME.to_string(), @@ -104,3 +113,27 @@ impl ProviderDef for AzureProvider { }) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_is_v1_endpoint() { + assert!(is_v1_endpoint( + "https://my-resource.services.ai.azure.com/api/projects/my-proj/openai/v1" + )); + assert!(is_v1_endpoint( + "https://my-resource.services.ai.azure.com/api/projects/my-proj/openai/v1/" + )); + assert!(is_v1_endpoint( + "https://my-resource.services.ai.azure.com/v1/some/path" + )); + + assert!(!is_v1_endpoint("https://my-resource.openai.azure.com")); + assert!(!is_v1_endpoint("https://my-resource.openai.azure.com/")); + assert!(!is_v1_endpoint( + "https://my-resource.openai.azure.com/openai" + )); + } +}