feat(cmf): live-apply managed-server changes; auto-resolve import id collisions
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s

- Saving the Local models list now applies IMMEDIATELY: removed or redefined
  rows stop their `cortiq serve` child (memory freed on the spot), new or
  changed rows are spawned and health-checked — no gateway restart needed.
- A managed row that shares its id with a same-id STATIC pool entry pointing at
  a local server (the shape Import→register creates) now supersedes it: the
  static entry is dropped automatically instead of failing the save with
  "duplicate managed model id".
- validate() now tells the two duplicate cases apart with actionable messages
  (two managed rows vs managed-vs-pool collision).
This commit is contained in:
infosave2007
2026-07-14 14:34:30 +03:00
parent 851cc7f1cb
commit 46c11b9b88
7 changed files with 124 additions and 17 deletions
+28 -12
View File
@@ -908,28 +908,44 @@ async fn put_settings(State(state): State<SharedState>, Json(b): Json<SettingsBo
needs_restart = true;
cfg.cache = v;
}
let mut cmf_apply: Option<(Vec<crate::config::CmfServer>, CmfCfg)> = None;
if let Some(v) = b.cmf {
// manage_server / local_model / threads / gpu apply on restart (the server
// is spawned once at startup with those env vars); local_only / router
// routing apply immediately.
if v.manage_server != cfg.cmf.manage_server
|| v.local_model != cfg.cmf.local_model
|| v.threads != cfg.cmf.threads
|| v.gpu != cfg.cmf.gpu
|| v.servers != cfg.cmf.servers
{
needs_restart = true;
}
let old_servers = cfg.cmf.effective_servers();
cfg.cmf = v;
// A managed row supersedes a same-id STATIC pool entry that points at a
// local server (the shape Import→register creates): drop the static one
// instead of failing validation with "duplicate managed model id".
let managed_ids: std::collections::HashSet<String> = cfg
.cmf
.effective_servers()
.into_iter()
.map(|s| s.id)
.collect();
cfg.models.retain(|m| {
!(managed_ids.contains(&m.id)
&& (m.base_url.contains("//127.0.0.1:") || m.base_url.contains("//localhost:")))
});
// Replacing or renaming a managed model must not brick the save: routing
// may still reference the old id. Map a vanished id to its replacement
// (the new server on the same port), strip ids that are truly gone, and
// repair the default so validate() passes.
heal_routing_after_cmf_change(&mut cfg, &old_servers);
// Server-list changes apply LIVE after a successful reload: removed /
// redefined servers are stopped (RAM freed), new ones are spawned.
if cfg.cmf.effective_servers() != old_servers {
cmf_apply = Some((old_servers, cfg.cmf.clone()));
}
}
state.reload(cfg)?;
ok(json!({ "ok": true, "needs_restart": needs_restart }))
let applied_live = cmf_apply.is_some();
if let Some((old, new_cfg)) = cmf_apply {
tokio::spawn(crate::cmf_runtime::apply_server_changes(
state.cmf.clone(),
old,
new_cfg,
));
}
ok(json!({ "ok": true, "needs_restart": needs_restart, "applied_live": applied_live }))
}
/// After the managed-servers list changed, rewrite [routing] so it only