fix(agents): record model mapping changes without file changes
Include Claude Desktop mappings in history change detection and preserve the previous selection. Show mapping changes in history previews and invalidate previews when metadata changes. Validation: 15 history tests pass, including identical-profile model changes and repeated no-op saves.
This commit is contained in:
1 parent
0a019ad1d7
commit
60a3683976
2 files changed
+68
-4
No files matched your search
@@ -610,7 +610,9 @@ fn commit_history_with_writer(
|
||||
if image_revision(&history_images(paths)?) != image_revision(before) {
|
||||
return Err("配置已被其他程序修改,请刷新后重试".into());
|
||||
}
|
||||
if changed.is_empty() {
|
||||
let prior_mappings = matching_desktop_mappings(client, paths, before);
|
||||
let next_mappings = mappings.or_else(|| matching_desktop_mappings(client, paths, after));
|
||||
if changed.is_empty() && prior_mappings == next_mappings {
|
||||
return Ok(action_result(
|
||||
"unchanged",
|
||||
true,
|
||||
@@ -620,9 +622,12 @@ fn commit_history_with_writer(
|
||||
));
|
||||
}
|
||||
let mut prior = make_version(client, paths, before, &format!("before-{source}"), None);
|
||||
prior.mappings = matching_desktop_mappings(client, paths, before);
|
||||
prior.mappings = prior_mappings;
|
||||
if let Some(mappings) = prior.mappings.as_ref() {
|
||||
prior.model = Some(mappings.opus.clone());
|
||||
}
|
||||
let mut next = make_version(client, paths, after, source, model.clone());
|
||||
next.mappings = mappings.or_else(|| matching_desktop_mappings(client, paths, after));
|
||||
next.mappings = next_mappings;
|
||||
write_version(paths, &prior)?;
|
||||
if image_revision(&history_images(paths)?) != image_revision(before) {
|
||||
return Err("备份期间配置发生变化,请刷新后重试".into());
|
||||
@@ -823,9 +828,27 @@ fn preview(
|
||||
});
|
||||
}
|
||||
}
|
||||
let current_mappings = matching_desktop_mappings(client, paths, ¤t);
|
||||
if client == "claude-desktop" && current_mappings != version.mappings {
|
||||
let before = serde_json::to_value(¤t_mappings).map_err(|e| e.to_string())?;
|
||||
let after = serde_json::to_value(&version.mappings).map_err(|e| e.to_string())?;
|
||||
let mut fields = BTreeSet::new();
|
||||
collect_changes(Some(&before), Some(&after), Vec::new(), &mut fields);
|
||||
for field in fields {
|
||||
differences.push(HistoryDifference {
|
||||
file: path_to_string(&paths[2]),
|
||||
field: format!("modelMappings.{}", field.join(".")),
|
||||
before: hidden(get(&before, &field), &field),
|
||||
after: hidden(get(&after, &field), &field),
|
||||
});
|
||||
}
|
||||
}
|
||||
let revision = sha256_bytes(
|
||||
format!("{}:{}", image_revision(¤t), serde_json::to_string(¤t_mappings).map_err(|e| e.to_string())?).as_bytes(),
|
||||
);
|
||||
Ok((
|
||||
HistoryPreview {
|
||||
revision: image_revision(¤t),
|
||||
revision,
|
||||
differences,
|
||||
},
|
||||
current,
|
||||
|
||||
@@ -405,3 +405,44 @@ fn directory_at_config_path_is_an_error() {
|
||||
fs::create_dir_all(&paths[0]).unwrap();
|
||||
assert!(history_images(&paths).is_err());
|
||||
}
|
||||
|
||||
fn desktop_test_models() -> Vec<AgentModelOption> {
|
||||
["model-a", "model-b"].into_iter().map(|name| AgentModelOption {
|
||||
name: name.into(), alias: None, is_alias: false, context_window: Some(200_000),
|
||||
}).collect()
|
||||
}
|
||||
|
||||
fn apply_desktop_test_model(home: &Path, model: &str) -> AgentConfigActionResult {
|
||||
let mappings = ClaudeDesktopModelMappings::all(model);
|
||||
apply_agent_configuration_with_oauth(
|
||||
AgentClient::ClaudeDesktop, home, 8317, "test-key", model,
|
||||
AgentConfigurationOptions {
|
||||
models: &desktop_test_models(), codex_catalog: None, oauth_configuration: false,
|
||||
claude_code_model_mappings: None, claude_desktop_model_mappings: Some(&mappings),
|
||||
},
|
||||
).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn desktop_mapping_changes_are_saved_when_profile_bytes_are_identical() {
|
||||
let home = Home::new();
|
||||
let paths = history_paths("claude-desktop", &home.0).unwrap();
|
||||
let first = apply_desktop_test_model(&home.0, "model-a");
|
||||
let images = history_images(&paths).unwrap();
|
||||
let first_id = first.history_version.unwrap();
|
||||
let original_preview = preview("claude-desktop", &paths, &first_id).unwrap().0;
|
||||
let second = apply_desktop_test_model(&home.0, "model-b");
|
||||
assert_eq!(second.outcome, "updated");
|
||||
assert!(second.changed_files.is_empty());
|
||||
assert!(second.history_version.is_some());
|
||||
assert_eq!(images, history_images(&paths).unwrap());
|
||||
assert_eq!(versions("claude-desktop", &paths).unwrap().0.len(), 4);
|
||||
assert_eq!(current_desktop_history_mappings(&home.0).unwrap().opus, "model-b");
|
||||
let next_preview = preview("claude-desktop", &paths, &first_id).unwrap().0;
|
||||
assert_ne!(original_preview.revision, next_preview.revision);
|
||||
assert!(next_preview.differences.iter().any(|diff| diff.field == "modelMappings.opus"));
|
||||
let unchanged = apply_desktop_test_model(&home.0, "model-b");
|
||||
assert_eq!(unchanged.outcome, "unchanged");
|
||||
assert!(unchanged.history_version.is_none());
|
||||
assert_eq!(versions("claude-desktop", &paths).unwrap().0.len(), 4);
|
||||
}
|
||||
Reference in new issue
Block a user