mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-25 13:45:03 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 60343cec2c | |||
| e50ba8b7d4 |
@@ -121,9 +121,10 @@ pub async fn fetch_models(
|
|||||||
///
|
///
|
||||||
/// 候选顺序:
|
/// 候选顺序:
|
||||||
/// 1. `models_url_override` 非空 → 只返回它
|
/// 1. `models_url_override` 非空 → 只返回它
|
||||||
/// 2. baseURL 直接拼 `/v1/models`(若已有 `/v1` 结尾则拼 `/models`)
|
/// 2. baseURL 拼 `/v1/models`;若已以版本段 `/v{N}` 结尾(`/v1`、智谱
|
||||||
/// 3. 若 baseURL 命中 [`KNOWN_COMPAT_SUFFIXES`],剥离后缀再拼 `/v1/models`
|
/// `/api/coding/paas/v4` 等),版本号已在路径里,改拼 `/models`
|
||||||
/// 4. 同上,但拼 `/models`(部分站点如 DeepSeek 官方只暴露 `/models`)
|
/// 3. 版本段非 `/v1`(如 `/v4`)时再追加 `/v1/models` 作为兜底次候选
|
||||||
|
/// 4. 若 baseURL 命中 [`KNOWN_COMPAT_SUFFIXES`],剥离后缀再拼 `/v1/models`、`/models`
|
||||||
///
|
///
|
||||||
/// 结果已去重且保持首次出现顺序。
|
/// 结果已去重且保持首次出现顺序。
|
||||||
pub fn build_models_url_candidates(
|
pub fn build_models_url_candidates(
|
||||||
@@ -160,12 +161,18 @@ pub fn build_models_url_candidates(
|
|||||||
return Ok(candidates);
|
return Ok(candidates);
|
||||||
}
|
}
|
||||||
|
|
||||||
let primary = if trimmed.ends_with("/v1") {
|
// baseURL 已以版本段 /v{N} 结尾时(如 `/v1`、智谱 `/api/coding/paas/v4`),
|
||||||
format!("{trimmed}/models")
|
// OpenAI 惯例的模型端点是 `{base}/models`,不能再补 `/v1`
|
||||||
|
// (否则 .../coding/paas/v4/v1/models → 404)。
|
||||||
|
if ends_with_version_segment(trimmed) {
|
||||||
|
candidates.push(format!("{trimmed}/models"));
|
||||||
|
// 版本段非 /v1 时,保留旧的 /v1/models 作为兜底次候选(正确路径已在前)。
|
||||||
|
if !trimmed.ends_with("/v1") {
|
||||||
|
candidates.push(format!("{trimmed}/v1/models"));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
format!("{trimmed}/v1/models")
|
candidates.push(format!("{trimmed}/v1/models"));
|
||||||
};
|
}
|
||||||
candidates.push(primary);
|
|
||||||
|
|
||||||
if let Some(stripped) = strip_compat_suffix(trimmed) {
|
if let Some(stripped) = strip_compat_suffix(trimmed) {
|
||||||
let root = stripped.trim_end_matches('/');
|
let root = stripped.trim_end_matches('/');
|
||||||
@@ -210,6 +217,15 @@ fn strip_compat_suffix(base_url: &str) -> Option<&str> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 判断 baseURL 是否以 OpenAI 风格的版本段 `/v{N}` 结尾(`N` 为一个或多个数字),
|
||||||
|
/// 例如 `/v1`、`.../paas/v4`。这类 URL 版本号已在路径中,模型端点应为
|
||||||
|
/// `{base}/models`,不能再补 `/v1`(智谱 Coding Plan 即 `.../coding/paas/v4`)。
|
||||||
|
fn ends_with_version_segment(url: &str) -> bool {
|
||||||
|
let last = url.rsplit('/').next().unwrap_or("");
|
||||||
|
last.strip_prefix('v')
|
||||||
|
.is_some_and(|digits| !digits.is_empty() && digits.bytes().all(|b| b.is_ascii_digit()))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -232,6 +248,48 @@ mod tests {
|
|||||||
assert_eq!(c, vec!["https://api.example.com/v1/models"]);
|
assert_eq!(c, vec!["https://api.example.com/v1/models"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_candidates_zhipu_coding_paas_v4() {
|
||||||
|
// 智谱 Coding Plan 端点以 /v4 版本段结尾:模型端点是 {base}/models,
|
||||||
|
// 正确路径必须排在 .../v4/v1/models(404)之前。
|
||||||
|
let c =
|
||||||
|
build_models_url_candidates("https://open.bigmodel.cn/api/coding/paas/v4", false, None)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
c,
|
||||||
|
vec![
|
||||||
|
"https://open.bigmodel.cn/api/coding/paas/v4/models",
|
||||||
|
"https://open.bigmodel.cn/api/coding/paas/v4/v1/models",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_candidates_zai_coding_paas_v4() {
|
||||||
|
let c = build_models_url_candidates("https://api.z.ai/api/coding/paas/v4", false, None)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
c,
|
||||||
|
vec![
|
||||||
|
"https://api.z.ai/api/coding/paas/v4/models",
|
||||||
|
"https://api.z.ai/api/coding/paas/v4/v1/models",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ends_with_version_segment() {
|
||||||
|
assert!(ends_with_version_segment("https://x.com/v1"));
|
||||||
|
assert!(ends_with_version_segment(
|
||||||
|
"https://open.bigmodel.cn/api/coding/paas/v4"
|
||||||
|
));
|
||||||
|
assert!(ends_with_version_segment("https://x.com/v10"));
|
||||||
|
assert!(!ends_with_version_segment("https://x.com/api"));
|
||||||
|
assert!(!ends_with_version_segment("https://x.com/vX"));
|
||||||
|
assert!(!ends_with_version_segment("https://x.com/models"));
|
||||||
|
assert!(!ends_with_version_segment("https://api.siliconflow.cn"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_candidates_full_url() {
|
fn test_candidates_full_url() {
|
||||||
let c = build_models_url_candidates(
|
let c = build_models_url_candidates(
|
||||||
|
|||||||
@@ -286,10 +286,10 @@ requires_openai_auth = true`,
|
|||||||
auth: generateThirdPartyAuth(""),
|
auth: generateThirdPartyAuth(""),
|
||||||
config: generateThirdPartyConfig(
|
config: generateThirdPartyConfig(
|
||||||
"zhipu_glm",
|
"zhipu_glm",
|
||||||
"https://open.bigmodel.cn/api/paas/v4",
|
"https://open.bigmodel.cn/api/coding/paas/v4",
|
||||||
"glm-5.1",
|
"glm-5.1",
|
||||||
),
|
),
|
||||||
endpointCandidates: ["https://open.bigmodel.cn/api/paas/v4"],
|
endpointCandidates: ["https://open.bigmodel.cn/api/coding/paas/v4"],
|
||||||
apiFormat: "openai_chat",
|
apiFormat: "openai_chat",
|
||||||
modelCatalog: modelCatalog([
|
modelCatalog: modelCatalog([
|
||||||
{ model: "glm-5.1", displayName: "GLM-5.1", contextWindow: 200000 },
|
{ model: "glm-5.1", displayName: "GLM-5.1", contextWindow: 200000 },
|
||||||
@@ -312,10 +312,10 @@ requires_openai_auth = true`,
|
|||||||
auth: generateThirdPartyAuth(""),
|
auth: generateThirdPartyAuth(""),
|
||||||
config: generateThirdPartyConfig(
|
config: generateThirdPartyConfig(
|
||||||
"zhipu_glm_en",
|
"zhipu_glm_en",
|
||||||
"https://api.z.ai/api/paas/v4",
|
"https://api.z.ai/api/coding/paas/v4",
|
||||||
"glm-5.1",
|
"glm-5.1",
|
||||||
),
|
),
|
||||||
endpointCandidates: ["https://api.z.ai/api/paas/v4"],
|
endpointCandidates: ["https://api.z.ai/api/coding/paas/v4"],
|
||||||
apiFormat: "openai_chat",
|
apiFormat: "openai_chat",
|
||||||
modelCatalog: modelCatalog([
|
modelCatalog: modelCatalog([
|
||||||
{ model: "glm-5.1", displayName: "GLM-5.1", contextWindow: 200000 },
|
{ model: "glm-5.1", displayName: "GLM-5.1", contextWindow: 200000 },
|
||||||
|
|||||||
@@ -393,7 +393,7 @@ export const hermesProviderPresets: HermesProviderPreset[] = [
|
|||||||
apiKeyUrl: "https://www.bigmodel.cn/claude-code?ic=RRVJPB5SII",
|
apiKeyUrl: "https://www.bigmodel.cn/claude-code?ic=RRVJPB5SII",
|
||||||
settingsConfig: {
|
settingsConfig: {
|
||||||
name: "zhipu_glm",
|
name: "zhipu_glm",
|
||||||
base_url: "https://open.bigmodel.cn/api/paas/v4",
|
base_url: "https://open.bigmodel.cn/api/coding/paas/v4",
|
||||||
api_key: "",
|
api_key: "",
|
||||||
api_mode: "chat_completions",
|
api_mode: "chat_completions",
|
||||||
models: [{ id: "glm-5.1", name: "GLM-5.1" }],
|
models: [{ id: "glm-5.1", name: "GLM-5.1" }],
|
||||||
@@ -411,7 +411,7 @@ export const hermesProviderPresets: HermesProviderPreset[] = [
|
|||||||
apiKeyUrl: "https://z.ai/subscribe?ic=8JVLJQFSKB",
|
apiKeyUrl: "https://z.ai/subscribe?ic=8JVLJQFSKB",
|
||||||
settingsConfig: {
|
settingsConfig: {
|
||||||
name: "zhipu_glm_en",
|
name: "zhipu_glm_en",
|
||||||
base_url: "https://api.z.ai/api/paas/v4",
|
base_url: "https://api.z.ai/api/coding/paas/v4",
|
||||||
api_key: "",
|
api_key: "",
|
||||||
api_mode: "chat_completions",
|
api_mode: "chat_completions",
|
||||||
models: [{ id: "glm-5.1", name: "GLM-5.1" }],
|
models: [{ id: "glm-5.1", name: "GLM-5.1" }],
|
||||||
|
|||||||
@@ -306,7 +306,7 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [
|
|||||||
websiteUrl: "https://open.bigmodel.cn",
|
websiteUrl: "https://open.bigmodel.cn",
|
||||||
apiKeyUrl: "https://www.bigmodel.cn/claude-code?ic=RRVJPB5SII",
|
apiKeyUrl: "https://www.bigmodel.cn/claude-code?ic=RRVJPB5SII",
|
||||||
settingsConfig: {
|
settingsConfig: {
|
||||||
baseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
||||||
apiKey: "",
|
apiKey: "",
|
||||||
api: "openai-completions",
|
api: "openai-completions",
|
||||||
models: [
|
models: [
|
||||||
@@ -324,8 +324,8 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [
|
|||||||
templateValues: {
|
templateValues: {
|
||||||
baseUrl: {
|
baseUrl: {
|
||||||
label: "Base URL",
|
label: "Base URL",
|
||||||
placeholder: "https://open.bigmodel.cn/api/paas/v4",
|
placeholder: "https://open.bigmodel.cn/api/coding/paas/v4",
|
||||||
defaultValue: "https://open.bigmodel.cn/api/paas/v4",
|
defaultValue: "https://open.bigmodel.cn/api/coding/paas/v4",
|
||||||
editorValue: "",
|
editorValue: "",
|
||||||
},
|
},
|
||||||
apiKey: {
|
apiKey: {
|
||||||
@@ -344,7 +344,7 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [
|
|||||||
websiteUrl: "https://z.ai",
|
websiteUrl: "https://z.ai",
|
||||||
apiKeyUrl: "https://z.ai/subscribe?ic=8JVLJQFSKB",
|
apiKeyUrl: "https://z.ai/subscribe?ic=8JVLJQFSKB",
|
||||||
settingsConfig: {
|
settingsConfig: {
|
||||||
baseUrl: "https://api.z.ai/v1",
|
baseUrl: "https://api.z.ai/api/coding/paas/v4",
|
||||||
apiKey: "",
|
apiKey: "",
|
||||||
api: "openai-completions",
|
api: "openai-completions",
|
||||||
models: [
|
models: [
|
||||||
@@ -362,8 +362,8 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [
|
|||||||
templateValues: {
|
templateValues: {
|
||||||
baseUrl: {
|
baseUrl: {
|
||||||
label: "Base URL",
|
label: "Base URL",
|
||||||
placeholder: "https://api.z.ai/v1",
|
placeholder: "https://api.z.ai/api/coding/paas/v4",
|
||||||
defaultValue: "https://api.z.ai/v1",
|
defaultValue: "https://api.z.ai/api/coding/paas/v4",
|
||||||
editorValue: "",
|
editorValue: "",
|
||||||
},
|
},
|
||||||
apiKey: {
|
apiKey: {
|
||||||
|
|||||||
@@ -442,7 +442,7 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [
|
|||||||
npm: "@ai-sdk/openai-compatible",
|
npm: "@ai-sdk/openai-compatible",
|
||||||
name: "Zhipu GLM",
|
name: "Zhipu GLM",
|
||||||
options: {
|
options: {
|
||||||
baseURL: "https://open.bigmodel.cn/api/paas/v4",
|
baseURL: "https://open.bigmodel.cn/api/coding/paas/v4",
|
||||||
apiKey: "",
|
apiKey: "",
|
||||||
setCacheKey: true,
|
setCacheKey: true,
|
||||||
},
|
},
|
||||||
@@ -456,8 +456,8 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [
|
|||||||
templateValues: {
|
templateValues: {
|
||||||
baseURL: {
|
baseURL: {
|
||||||
label: "Base URL",
|
label: "Base URL",
|
||||||
placeholder: "https://open.bigmodel.cn/api/paas/v4",
|
placeholder: "https://open.bigmodel.cn/api/coding/paas/v4",
|
||||||
defaultValue: "https://open.bigmodel.cn/api/paas/v4",
|
defaultValue: "https://open.bigmodel.cn/api/coding/paas/v4",
|
||||||
editorValue: "",
|
editorValue: "",
|
||||||
},
|
},
|
||||||
apiKey: {
|
apiKey: {
|
||||||
@@ -475,7 +475,7 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [
|
|||||||
npm: "@ai-sdk/openai-compatible",
|
npm: "@ai-sdk/openai-compatible",
|
||||||
name: "Zhipu GLM en",
|
name: "Zhipu GLM en",
|
||||||
options: {
|
options: {
|
||||||
baseURL: "https://api.z.ai/v1",
|
baseURL: "https://api.z.ai/api/coding/paas/v4",
|
||||||
apiKey: "",
|
apiKey: "",
|
||||||
setCacheKey: true,
|
setCacheKey: true,
|
||||||
},
|
},
|
||||||
@@ -489,8 +489,8 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [
|
|||||||
templateValues: {
|
templateValues: {
|
||||||
baseURL: {
|
baseURL: {
|
||||||
label: "Base URL",
|
label: "Base URL",
|
||||||
placeholder: "https://api.z.ai/v1",
|
placeholder: "https://api.z.ai/api/coding/paas/v4",
|
||||||
defaultValue: "https://api.z.ai/v1",
|
defaultValue: "https://api.z.ai/api/coding/paas/v4",
|
||||||
editorValue: "",
|
editorValue: "",
|
||||||
},
|
},
|
||||||
apiKey: {
|
apiKey: {
|
||||||
|
|||||||
@@ -44,14 +44,14 @@ const expectedChatPresets = new Map<
|
|||||||
[
|
[
|
||||||
"Zhipu GLM",
|
"Zhipu GLM",
|
||||||
{
|
{
|
||||||
baseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
||||||
contextWindows: { "glm-5.1": 200000 },
|
contextWindows: { "glm-5.1": 200000 },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"Zhipu GLM en",
|
"Zhipu GLM en",
|
||||||
{
|
{
|
||||||
baseUrl: "https://api.z.ai/api/paas/v4",
|
baseUrl: "https://api.z.ai/api/coding/paas/v4",
|
||||||
contextWindows: { "glm-5.1": 200000 },
|
contextWindows: { "glm-5.1": 200000 },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user