fix(providers/openai): preserve custom API path in base_url derivation (#9649)

Signed-off-by: Douwe M Osinga <douwe@sidewalklabs.com>
Co-authored-by: Douwe M Osinga <douwe@sidewalklabs.com>
This commit is contained in:
Steve Beaulac
2026-06-16 20:40:40 -04:00
committed by GitHub
parent c9a82f3607
commit 34b20e3083
+43 -2
View File
@@ -475,13 +475,21 @@ impl OpenAiProvider {
let normalized = stripped.trim_end_matches('/');
if normalized.is_empty() {
"v1/chat/completions".to_string()
} else if normalized == "v1" || normalized.ends_with("/v1") {
} else if normalized.ends_with("chat/completions") {
stripped.to_string()
} else if Self::ends_with_version_segment(normalized) {
format!("{}/chat/completions", normalized)
} else {
stripped.to_string()
format!("{}/v1/chat/completions", normalized)
}
}
fn ends_with_version_segment(path: &str) -> bool {
let last = path.rsplit('/').next().unwrap_or(path);
last.strip_prefix('v')
.is_some_and(|rest| !rest.is_empty() && rest.bytes().all(|b| b.is_ascii_digit()))
}
fn normalize_base_path(base_path: &str) -> String {
if let Some(path) = base_path.strip_prefix('/') {
format!("/{}", path.trim_end_matches('/'))
@@ -1280,6 +1288,39 @@ mod tests {
assert!(!r.has_v1);
}
#[test]
fn derive_base_path_not_removing_api_path() {
let r = OpenAiProvider::derive_base_path("https://opencode.ai/zen/go");
assert_eq!(r, "https://opencode.ai/zen/go/v1/chat/completions");
}
#[test]
fn derive_base_path_should_support_v1() {
let r = OpenAiProvider::derive_base_path("https://opencode.ai/zen/go/v1");
assert_eq!(r, "https://opencode.ai/zen/go/v1/chat/completions");
}
#[test]
fn derive_base_path_should_support_no_base_path() {
let r = OpenAiProvider::derive_base_path("https://opencode.ai/");
assert_eq!(r, "https://opencode.ai/v1/chat/completions");
}
#[test]
fn derive_base_path_preserves_non_v1_version_prefix() {
// Zhipu's default base_url is https://open.bigmodel.cn/api/paas/v4 and
// from_custom_config passes url.path() ("/api/paas/v4") here. The
// existing /api/paas/v4 version must not gain an extra /v1 segment.
let r = OpenAiProvider::derive_base_path("/api/paas/v4");
assert_eq!(r, "api/paas/v4/chat/completions");
}
#[test]
fn derive_base_path_does_not_treat_v_word_as_version() {
let r = OpenAiProvider::derive_base_path("/api/voice");
assert_eq!(r, "api/voice/v1/chat/completions");
}
#[test]
fn parse_base_url_preserves_query_params() {
let r = OpenAiProvider::parse_base_url("https://gw.example.com/v1?api-version=2024-02-01")