mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-02 02:05:57 +08:00
refactor(claude-desktop): lock route IDs to sonnet/opus/haiku roles
Adapt to Claude Desktop 1.6259.1+ fail-all validation which only accepts claude-(sonnet|opus|haiku)-* route IDs. Branded model names (DeepSeek, Kimi, GLM, etc.) now live in a new labelOverride field instead of being embedded in route IDs. - Backend auto-repairs legacy unsafe routes to the next free sonnet/opus/haiku slot instead of erroring - Frontend swaps the free-form route input for a role dropdown plus menu display name field - Add CLAUDE_DESKTOP_ROLE_ROUTE_IDS as the single source of truth for role-to-route mapping; presets and form both consume it - Drop the dead displayName alias on ClaudeDesktopModelRoute and the ineffective /v1/models display_name injection (UI ignores it) - Update i18n (en/ja/zh) and form focus test for the new fields
This commit is contained in:
@@ -260,6 +260,7 @@ pub(crate) fn suggested_claude_desktop_routes(
|
||||
fn add_route(
|
||||
routes: &mut std::collections::HashMap<String, crate::provider::ClaudeDesktopModelRoute>,
|
||||
env: &serde_json::Map<String, serde_json::Value>,
|
||||
route_key: &str,
|
||||
env_key: &str,
|
||||
supports_1m_default: bool,
|
||||
) {
|
||||
@@ -287,27 +288,58 @@ pub(crate) fn suggested_claude_desktop_routes(
|
||||
return;
|
||||
}
|
||||
let effective_supports_1m = supports_1m_default || has_1m_marker;
|
||||
let route_key = crate::claude_desktop_config::derive_desktop_route_id(stripped_model);
|
||||
let label_override =
|
||||
(!crate::claude_desktop_config::is_claude_safe_model_id(stripped_model))
|
||||
.then(|| stripped_model.to_string());
|
||||
|
||||
if let Some(existing) = routes
|
||||
.values_mut()
|
||||
.find(|existing| existing.model == stripped_model)
|
||||
{
|
||||
let merged = existing.supports_1m.unwrap_or(false) || effective_supports_1m;
|
||||
existing.supports_1m = Some(merged);
|
||||
if existing.label_override.is_none() {
|
||||
existing.label_override = label_override;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
routes
|
||||
.entry(route_key)
|
||||
.entry(route_key.to_string())
|
||||
.and_modify(|existing| {
|
||||
let merged = existing.supports_1m.unwrap_or(false) || effective_supports_1m;
|
||||
existing.supports_1m = Some(merged);
|
||||
if existing.label_override.is_none() {
|
||||
existing.label_override = label_override.clone();
|
||||
}
|
||||
})
|
||||
.or_insert_with(|| crate::provider::ClaudeDesktopModelRoute {
|
||||
model: stripped_model.to_string(),
|
||||
label_override,
|
||||
supports_1m: Some(effective_supports_1m),
|
||||
});
|
||||
}
|
||||
|
||||
for spec in crate::claude_desktop_config::DEFAULT_PROXY_ROUTES {
|
||||
add_route(&mut routes, env, spec.env_key, supports_1m_default);
|
||||
add_route(
|
||||
&mut routes,
|
||||
env,
|
||||
spec.route_id,
|
||||
spec.env_key,
|
||||
supports_1m_default,
|
||||
);
|
||||
}
|
||||
|
||||
// 三个 default env_key 全空时用 ANTHROPIC_MODEL 派生兜底路由。
|
||||
if routes.is_empty() {
|
||||
add_route(&mut routes, env, "ANTHROPIC_MODEL", supports_1m_default);
|
||||
let primary_route = crate::claude_desktop_config::DEFAULT_PROXY_ROUTES[0].route_id;
|
||||
add_route(
|
||||
&mut routes,
|
||||
env,
|
||||
primary_route,
|
||||
"ANTHROPIC_MODEL",
|
||||
supports_1m_default,
|
||||
);
|
||||
}
|
||||
|
||||
(!routes.is_empty()).then_some(routes)
|
||||
@@ -729,15 +761,15 @@ mod import_claude_desktop_tests {
|
||||
None,
|
||||
);
|
||||
let routes = suggested_claude_desktop_routes(&p).expect("routes built");
|
||||
// env 已带 claude- 前缀,派生 key 不重复加前缀
|
||||
let r = routes
|
||||
.get("claude-sonnet-4-5-20250929")
|
||||
.expect("derived route present");
|
||||
.get("claude-sonnet-4-6")
|
||||
.expect("sonnet route present");
|
||||
assert_eq!(r.model, "claude-sonnet-4-5-20250929");
|
||||
assert!(
|
||||
!r.model.to_ascii_lowercase().contains("[1m]"),
|
||||
"model must not contain [1m] suffix"
|
||||
);
|
||||
assert_eq!(r.label_override, None);
|
||||
assert_eq!(r.supports_1m, Some(true));
|
||||
}
|
||||
|
||||
@@ -750,8 +782,11 @@ mod import_claude_desktop_tests {
|
||||
None,
|
||||
);
|
||||
let routes = suggested_claude_desktop_routes(&p).expect("routes built");
|
||||
let r = routes.get("claude-kimi-k2").expect("derived route present");
|
||||
let r = routes
|
||||
.get("claude-sonnet-4-6")
|
||||
.expect("sonnet route present");
|
||||
assert_eq!(r.model, "kimi-k2");
|
||||
assert_eq!(r.label_override.as_deref(), Some("kimi-k2"));
|
||||
// 默认 provider_type 缺省 → supports_1m_default = true
|
||||
assert_eq!(r.supports_1m, Some(true));
|
||||
}
|
||||
@@ -767,9 +802,10 @@ mod import_claude_desktop_tests {
|
||||
);
|
||||
let routes = suggested_claude_desktop_routes(&p).expect("routes built");
|
||||
let r = routes
|
||||
.get("claude-gpt-5-codex")
|
||||
.expect("derived route present");
|
||||
.get("claude-sonnet-4-6")
|
||||
.expect("sonnet route present");
|
||||
assert_eq!(r.model, "gpt-5-codex");
|
||||
assert_eq!(r.label_override.as_deref(), Some("gpt-5-codex"));
|
||||
assert_eq!(r.supports_1m, Some(true));
|
||||
}
|
||||
|
||||
@@ -783,9 +819,10 @@ mod import_claude_desktop_tests {
|
||||
);
|
||||
let routes = suggested_claude_desktop_routes(&p).expect("routes built");
|
||||
let r = routes
|
||||
.get("claude-gpt-5-codex")
|
||||
.expect("derived route present");
|
||||
.get("claude-sonnet-4-6")
|
||||
.expect("sonnet route present");
|
||||
assert_eq!(r.model, "gpt-5-codex");
|
||||
assert_eq!(r.label_override.as_deref(), Some("gpt-5-codex"));
|
||||
assert_eq!(r.supports_1m, Some(false));
|
||||
}
|
||||
|
||||
@@ -802,9 +839,10 @@ mod import_claude_desktop_tests {
|
||||
let routes = suggested_claude_desktop_routes(&p).expect("routes built");
|
||||
assert_eq!(routes.len(), 1, "three aliases → one merged route");
|
||||
let r = routes
|
||||
.get("claude-MiniMax-M2")
|
||||
.get("claude-sonnet-4-6")
|
||||
.expect("merged route present");
|
||||
assert_eq!(r.model, "MiniMax-M2");
|
||||
assert_eq!(r.label_override.as_deref(), Some("MiniMax-M2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -821,7 +859,7 @@ mod import_claude_desktop_tests {
|
||||
let routes = suggested_claude_desktop_routes(&p).expect("routes built");
|
||||
assert_eq!(routes.len(), 1);
|
||||
let r = routes
|
||||
.get("claude-MiniMax-M2")
|
||||
.get("claude-sonnet-4-6")
|
||||
.expect("merged route present");
|
||||
assert_eq!(r.supports_1m, Some(true));
|
||||
}
|
||||
@@ -838,11 +876,16 @@ mod import_claude_desktop_tests {
|
||||
);
|
||||
let routes = suggested_claude_desktop_routes(&p).expect("routes built");
|
||||
assert_eq!(routes.len(), 3);
|
||||
assert_eq!(routes.get("claude-GLM-4.6").unwrap().model, "GLM-4.6");
|
||||
assert_eq!(routes.get("claude-GLM-4-Air").unwrap().model, "GLM-4-Air");
|
||||
assert_eq!(routes.get("claude-sonnet-4-6").unwrap().model, "GLM-4.6");
|
||||
assert_eq!(routes.get("claude-opus-4-7").unwrap().model, "GLM-4-Air");
|
||||
assert_eq!(routes.get("claude-haiku-4-5").unwrap().model, "GLM-4-Flash");
|
||||
assert_eq!(
|
||||
routes.get("claude-GLM-4-Flash").unwrap().model,
|
||||
"GLM-4-Flash"
|
||||
routes
|
||||
.get("claude-sonnet-4-6")
|
||||
.unwrap()
|
||||
.label_override
|
||||
.as_deref(),
|
||||
Some("GLM-4.6")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -858,9 +901,10 @@ mod import_claude_desktop_tests {
|
||||
let routes = suggested_claude_desktop_routes(&p).expect("routes built");
|
||||
assert_eq!(routes.len(), 1);
|
||||
let r = routes
|
||||
.get("claude-kimi-k2")
|
||||
.get("claude-sonnet-4-6")
|
||||
.expect("fallback route present");
|
||||
assert_eq!(r.model, "kimi-k2");
|
||||
assert_eq!(r.label_override.as_deref(), Some("kimi-k2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -872,8 +916,14 @@ mod import_claude_desktop_tests {
|
||||
None,
|
||||
);
|
||||
let routes = suggested_claude_desktop_routes(&p).expect("routes built");
|
||||
// key 应是 "claude-sonnet-4-5-20250929",不出现双重 claude- 前缀
|
||||
assert!(routes.contains_key("claude-sonnet-4-5-20250929"));
|
||||
assert!(routes.contains_key("claude-sonnet-4-6"));
|
||||
assert!(!routes.contains_key("claude-claude-sonnet-4-5-20250929"));
|
||||
assert_eq!(
|
||||
routes
|
||||
.get("claude-sonnet-4-6")
|
||||
.expect("route")
|
||||
.label_override,
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user