fix(model-fetch): try compatible-mode models endpoint for /apps/anthropic base URLs (#8755)

Qianwen / Bailian Token Plan Anthropic endpoints (e.g.
https://token-plan.cn-beijing.maas.aliyuncs.com/apps/anthropic) return 404 for
/v1/models and /models; the model list is only served under the
OpenAI-compatible path {root}/compatible-mode/v1/models. Append it as the last
candidate when the base URL ends with /apps/anthropic.

Co-authored-by: Claude Code <noreply@anthropic.com>
This commit is contained in:
ClarkChenandClaude Code authored and GitHub committed 2026-10-23 17:34:54 +08:00
1 parent 2e1e29795a
commit c3ad9bb0bd
1 file changed
+28 -1
+28 -1
View File
@@ -308,10 +308,15 @@ pub fn build_models_url_candidates(
if !root.is_empty() && root.contains("://") {
candidates.push(format!("{root}/v1/models"));
candidates.push(format!("{root}/models"));
// 阿里云百炼 / 千问 Token Plan:`/apps/anthropic` 入口不提供 /v1/models,
// 模型列表只在 OpenAI 兼容路径 `/compatible-mode/v1/models` 下。
if trimmed.ends_with("/apps/anthropic") {
candidates.push(format!("{root}/compatible-mode/v1/models"));
}
}
}
// 候选最多 3 条,线性去重即可,不值得上 HashSet。
// 候选最多 4 条,线性去重即可,不值得上 HashSet。
let mut unique: Vec<String> = Vec::with_capacity(candidates.len());
for url in candidates {
if !unique.iter().any(|u| u == &url) {
@@ -557,10 +562,32 @@ mod tests {
"https://dashscope.aliyuncs.com/apps/anthropic/v1/models",
"https://dashscope.aliyuncs.com/v1/models",
"https://dashscope.aliyuncs.com/models",
"https://dashscope.aliyuncs.com/compatible-mode/v1/models",
]
);
}
#[test]
fn test_candidates_qianwen_token_plan_compatible_mode() {
let c = build_models_url_candidates(
"https://token-plan.maas.qianwenaiapi.com/apps/anthropic/",
false,
None,
)
.unwrap();
assert_eq!(
c.last().map(String::as_str),
Some("https://token-plan.maas.qianwenaiapi.com/compatible-mode/v1/models")
);
}
#[test]
fn test_candidates_plain_anthropic_suffix_skips_compatible_mode() {
let c =
build_models_url_candidates("https://api.example.com/anthropic", false, None).unwrap();
assert!(!c.iter().any(|u| u.contains("compatible-mode")));
}
#[test]
fn test_candidates_stepfun_strip_step_plan() {
let c =