feat: add Claude subagent model config (#4830)

* feat: add Claude subagent takeover config

* feat: add Claude subagent model field

* i18n: add Claude subagent model labels

* fix(proxy): preserve configured subagent model mapping

* fix(providers): exclude subagent model from Claude common config

* style: format rust code
This commit is contained in:
秋澪Akimio
2026-07-07 10:28:36 +08:00
committed by GitHub
parent e606adfae7
commit b3e5e32c89
12 changed files with 257 additions and 33 deletions
+50
View File
@@ -12,6 +12,7 @@ pub struct ModelMapping {
pub sonnet_model: Option<String>,
pub opus_model: Option<String>,
pub fable_model: Option<String>,
pub subagent_model: Option<String>,
pub default_model: Option<String>,
}
@@ -41,6 +42,11 @@ impl ModelMapping {
.and_then(|v| v.as_str())
.filter(|s| !s.is_empty())
.map(String::from),
subagent_model: env
.and_then(|e| e.get("CLAUDE_CODE_SUBAGENT_MODEL"))
.and_then(|v| v.as_str())
.filter(|s| !s.is_empty())
.map(String::from),
default_model: env
.and_then(|e| e.get("ANTHROPIC_MODEL"))
.and_then(|v| v.as_str())
@@ -55,6 +61,7 @@ impl ModelMapping {
|| self.sonnet_model.is_some()
|| self.opus_model.is_some()
|| self.fable_model.is_some()
|| self.subagent_model.is_some()
|| self.default_model.is_some()
}
@@ -89,6 +96,13 @@ impl ModelMapping {
}
}
if let Some(ref m) = self.subagent_model {
if strip_one_m_suffix_for_upstream(original_model) == strip_one_m_suffix_for_upstream(m)
{
return original_model.to_string();
}
}
// 2. 默认模型
if let Some(ref m) = self.default_model {
return m.clone();
@@ -327,6 +341,42 @@ mod tests {
assert_eq!(mapped, Some("default-model".to_string()));
}
#[test]
fn test_subagent_model_preserved_before_default_fallback() {
let mut provider = create_provider_with_mapping();
provider.settings_config = json!({
"env": {
"ANTHROPIC_MODEL": "default-model",
"CLAUDE_CODE_SUBAGENT_MODEL": "gpt-5.4-mini"
}
});
let body = json!({"model": "gpt-5.4-mini"});
let (result, original, mapped) = apply_model_mapping(body, &provider);
assert_eq!(result["model"], "gpt-5.4-mini");
assert_eq!(original, Some("gpt-5.4-mini".to_string()));
assert!(mapped.is_none());
}
#[test]
fn test_subagent_model_preserved_with_one_m_suffix_before_default_fallback() {
let mut provider = create_provider_with_mapping();
provider.settings_config = json!({
"env": {
"ANTHROPIC_MODEL": "default-model",
"CLAUDE_CODE_SUBAGENT_MODEL": "gpt-5.4-mini"
}
});
let body = json!({"model": "gpt-5.4-mini[1M]"});
let (result, original, mapped) = apply_model_mapping(body, &provider);
assert_eq!(result["model"], "gpt-5.4-mini[1M]");
assert_eq!(original, Some("gpt-5.4-mini[1M]".to_string()));
assert!(mapped.is_none());
}
#[test]
fn test_no_mapping_configured() {
let provider = create_provider_without_mapping();