From 99b62ccc0406712d628160ba0dcc93d6de15b604 Mon Sep 17 00:00:00 2001 From: "cursor[bot]" <206951365+cursor[bot]@users.noreply.github.com> Date: Thu, 28 May 2026 07:23:01 +0800 Subject: [PATCH] fix(config): sync agent models from merged openclaw.json after write (#297) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #127 — sync_providers_to_agent_models now uses cleaned (merged+stripped) config instead of raw frontend payload, preventing partial writes from dropping other providers from agents/*/agent/models.json. --- src-tauri/src/commands/config.rs | 47 +++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/commands/config.rs b/src-tauri/src/commands/config.rs index a3b3313..435f56e 100644 --- a/src-tauri/src/commands/config.rs +++ b/src-tauri/src/commands/config.rs @@ -851,7 +851,10 @@ pub fn write_openclaw_config(config: Value) -> Result<(), String> { fs::write(&path, &json).map_err(|e| format!("写入失败: {e}"))?; // 同步 provider 配置到所有 agent 的 models.json(运行时注册表) - sync_providers_to_agent_models(&config); + // 必须使用与磁盘一致的 merged+strip 结果,而非前端原始 payload: + // 否则 partial 写入时 merge 保留了其它 provider,但 sync 按 payload 会把 + // agents/*/agent/models.json 里多出的 provider 整棵删掉,造成与 openclaw.json 不一致。 + sync_providers_to_agent_models(&cleaned); Ok(()) } @@ -7002,3 +7005,45 @@ pub fn invalidate_path_cache() -> Result<(), String> { crate::commands::service::invalidate_cli_detection_cache(); Ok(()) } + +#[cfg(test)] +mod write_openclaw_config_merge_tests { + use super::merge_configs_preserving_fields; + use serde_json::json; + + /// Regression guard: Issue #127 merge keeps full provider map when the UI payload + /// only touches one provider — `sync_providers_to_agent_models` must use the same + /// merged view (see `write_openclaw_config`), not the raw `config` argument. + #[test] + fn partial_models_merge_retains_other_providers() { + let existing = json!({ + "models": { + "providers": { + "a": { "models": [{ "id": "m1" }] }, + "b": { "models": [{ "id": "m2" }] } + } + } + }); + let new = json!({ + "models": { + "providers": { + "a": { + "baseUrl": "http://example", + "models": [{ "id": "m1" }] + } + } + } + }); + let merged = merge_configs_preserving_fields(&existing, &new); + let prov = merged + .pointer("/models/providers") + .and_then(|p| p.as_object()) + .expect("merged.models.providers"); + assert!(prov.contains_key("a")); + assert!( + prov.contains_key("b"), + "merged config must retain provider b when the write payload omits it" + ); + assert_eq!(prov["a"]["baseUrl"], json!("http://example")); + } +}