From e84e576d1d641637d5e1af124b01fb53ced9a361 Mon Sep 17 00:00:00 2001 From: lzt404 <2596933790@qq.com> Date: Thu, 10 Sep 2026 20:38:08 +0800 Subject: [PATCH] fix(aliases): delete API aliases without reasoning overrides Determine deletability from the actual provider model mapping instead of requiring an effort payload rule. Keep source models and unrelated aliases intact. Validation: cargo test --locked tests::alias_delete (3 passed), covering all four API provider sections, removing every option in the editor, and real-model name collisions. --- src-tauri/src/core_config/aliases.rs | 14 +++--- src-tauri/src/tests.rs | 1 + src-tauri/src/tests/alias_delete.rs | 65 ++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 src-tauri/src/tests/alias_delete.rs diff --git a/src-tauri/src/core_config/aliases.rs b/src-tauri/src/core_config/aliases.rs index 1367035..e5356e0 100644 --- a/src-tauri/src/core_config/aliases.rs +++ b/src-tauri/src/core_config/aliases.rs @@ -1906,10 +1906,10 @@ pub(crate) fn remove_thinking_alias_from_yaml_for_channel( .ok_or_else(|| "内核配置顶层必须是 YAML 映射".to_string())?; let mut removed = remove_oauth_model_alias(root, alias, oauth_channel)?; if oauth_channel.is_none() { - removed |= remove_config_thinking_alias(root, "codex-api-key", "codex", alias)?; - removed |= remove_config_thinking_alias(root, "openai-compatibility", "openai", alias)?; - removed |= remove_config_thinking_alias(root, "claude-api-key", "claude", alias)?; - removed |= remove_config_thinking_alias(root, "gemini-api-key", "gemini", alias)?; + removed |= remove_config_model_alias(root, "codex-api-key", alias)?; + removed |= remove_config_model_alias(root, "openai-compatibility", alias)?; + removed |= remove_config_model_alias(root, "claude-api-key", alias)?; + removed |= remove_config_model_alias(root, "gemini-api-key", alias)?; } if !removed { return Err(format!("别名模型 {alias} 不存在,请刷新后重试")); @@ -2021,15 +2021,11 @@ pub(crate) fn configured_model_alias_exists(root: &serde_norway::Mapping, alias: .any(|value| value.eq_ignore_ascii_case(alias)) } -pub(crate) fn remove_config_thinking_alias( +pub(crate) fn remove_config_model_alias( root: &mut serde_norway::Mapping, section: &str, - protocol: &str, alias: &str, ) -> Result { - if find_thinking_alias_effort(root, alias, protocol).is_none() { - return Ok(false); - } let Some(providers) = yaml_mapping_value_mut(root, section) else { return Ok(false); }; diff --git a/src-tauri/src/tests.rs b/src-tauri/src/tests.rs index 8f14da8..2a5e902 100644 --- a/src-tauri/src/tests.rs +++ b/src-tauri/src/tests.rs @@ -4,6 +4,7 @@ mod agent_configuration; mod agent_paths; mod agent_state; mod agent_transactions; +mod alias_delete; mod alias_edit; mod alias_edit_regressions; mod alias_save; diff --git a/src-tauri/src/tests/alias_delete.rs b/src-tauri/src/tests/alias_delete.rs new file mode 100644 index 0000000..5edc7be --- /dev/null +++ b/src-tauri/src/tests/alias_delete.rs @@ -0,0 +1,65 @@ +use super::support::*; +use super::*; + +fn json(content: &str) -> serde_json::Value { + serde_json::to_value(serde_norway::from_str::(content).unwrap()).unwrap() +} + +#[test] +fn plain_api_aliases_can_be_deleted_for_every_provider() { + for section in MODEL_ALIAS_CONFIG_SECTIONS { + let original = format!("{section}:\n - name: provider\n models: [{{name: model-a}}, {{name: model-b, alias: keep}}]\n"); + let source = + resolved_alias_sources(&original, &[], &test_agent_models(&["model-a"]), false) + .unwrap() + .into_iter() + .find(|source| source.source.model == "model-a") + .unwrap(); + let created = add_model_alias_to_yaml(&original, &source, "plain", "", false).unwrap(); + assert!(thinking_aliases_from_yaml(&created) + .unwrap() + .iter() + .any(|entry| entry.alias == "plain" && entry.effort.is_none())); + let deleted = remove_thinking_alias_from_yaml(&created, "plain").unwrap(); + assert_eq!(json(&deleted), json(&original), "{section}"); + } +} + +#[test] +fn api_alias_can_be_deleted_after_editing_away_all_options() { + for section in MODEL_ALIAS_CONFIG_SECTIONS { + let original = format!("{section}:\n - name: provider\n models: [{{name: model-a}}]\n"); + let source = + resolved_alias_sources(&original, &[], &test_agent_models(&["model-a"]), false) + .unwrap() + .remove(0); + let created = add_model_alias_to_yaml( + &original, + &source, + "editable", + "high", + alias_source_supports_fast(&source), + ) + .unwrap(); + let edit_source = resolve_model_alias_edit_source(&created, "editable", &[]).unwrap(); + let edited = + edit_model_alias_in_yaml(&created, "editable", &edit_source, "editable", "", false) + .unwrap(); + let context = model_alias_edit_context(&edited, "editable", &[]).unwrap(); + assert_eq!(context.effort, None); + assert!(!context.fast); + let deleted = remove_thinking_alias_from_yaml(&edited, "editable").unwrap(); + assert_eq!(json(&deleted), json(&original), "{section}"); + } +} + +#[test] +fn plain_alias_deletion_preserves_real_models_with_the_same_name() { + let original = "openai-compatibility:\n - name: provider\n models: [plain, {name: plain}, {name: plain, alias: plain}, {name: model-a, alias: plain}]\n"; + let deleted = remove_thinking_alias_from_yaml(original, "plain").unwrap(); + assert_eq!( + json(&deleted)["openai-compatibility"][0]["models"], + serde_json::json!(["plain", {"name": "plain"}, {"name": "plain", "alias": "plain"}]) + ); + assert!(remove_thinking_alias_from_yaml(&deleted, "plain").is_err()); +}