fix(sessions): persist Bot Chat override provenance atomically
This commit is contained in:
@@ -115,6 +115,7 @@ def test_apply_model_switch_does_not_leak_process_env():
|
||||
}
|
||||
sess_a = {"agent": _FakeAgent(), "session_key": "k-A", "model_override": None}
|
||||
|
||||
persisted_composer_profiles = []
|
||||
with (
|
||||
patch("hermes_cli.model_switch.parse_model_flags",
|
||||
return_value=("glm-5.1", None, False, False, True)),
|
||||
@@ -125,6 +126,11 @@ def test_apply_model_switch_does_not_leak_process_env():
|
||||
patch("tui_gateway.server._restart_slash_worker"),
|
||||
patch("tui_gateway.server._session_info", return_value={}),
|
||||
patch("hermes_cli.model_switch.persist_model_selection") as mock_persist,
|
||||
patch(
|
||||
"tui_gateway.server._persist_live_session_runtime",
|
||||
side_effect=lambda session: persisted_composer_profiles.append(
|
||||
session.get("composer_override_profile")),
|
||||
) as persist_runtime,
|
||||
patch("tui_gateway.server._config_model_target", return_value=("minimax/m3", "minimax")),
|
||||
):
|
||||
before = {k: os.environ.get(k) for k in env_keys}
|
||||
@@ -140,6 +146,10 @@ def test_apply_model_switch_does_not_leak_process_env():
|
||||
assert sess_b["model_override"]["model"] == "zai/glm-5.1"
|
||||
assert sess_b["model_override"]["provider"] == "zai"
|
||||
assert sess_b["composer_override_profile"] == {"model": "minimax/m3", "provider": "minimax"}
|
||||
# _commit_agent_switch owns the runtime transaction; provenance must be present
|
||||
# on its first (and only) DB write rather than relying on a second best-effort write.
|
||||
persist_runtime.assert_called_once_with(sess_b)
|
||||
assert persisted_composer_profiles == [{"model": "minimax/m3", "provider": "minimax"}]
|
||||
# The switched agent mutated in place.
|
||||
assert sess_b["agent"].model == "zai/glm-5.1"
|
||||
# Sibling session is completely untouched.
|
||||
|
||||
@@ -283,8 +283,26 @@ def _apply_model_switch(
|
||||
confirm = _expensive_model_confirm(result, current_base_url, current_api_key, agent)
|
||||
if confirm is not None:
|
||||
return confirm
|
||||
if agent:
|
||||
_commit_agent_switch(sid, session, agent, result, current_model, restore_snapshot)
|
||||
records_composer_override = (
|
||||
pin_session_override and isinstance(session, dict) and not one_turn
|
||||
and not persist_global and session.get("follow_profile_config"))
|
||||
had_composer_profile = "composer_override_profile" in session
|
||||
previous_composer_profile = session.get("composer_override_profile")
|
||||
if records_composer_override:
|
||||
profile_model, profile_provider = _config_model_target()
|
||||
session["composer_override_profile"] = {
|
||||
"model": profile_model, "provider": profile_provider}
|
||||
try:
|
||||
if agent:
|
||||
# Provenance must exist before this transaction persists the switched runtime.
|
||||
_commit_agent_switch(sid, session, agent, result, current_model, restore_snapshot)
|
||||
except Exception:
|
||||
if records_composer_override:
|
||||
if had_composer_profile:
|
||||
session["composer_override_profile"] = previous_composer_profile
|
||||
else:
|
||||
session.pop("composer_override_profile", None)
|
||||
raise
|
||||
# PER-SESSION override so a rebuild of THIS session (/new, resume) re-derives the model.
|
||||
# Deliberately NOT written to process-global env (HERMES_MODEL & co.): the desktop hosts
|
||||
# every same-profile session in one process, so os.environ would leak the switch to all.
|
||||
@@ -292,13 +310,6 @@ def _apply_model_switch(
|
||||
session["model_override"] = {
|
||||
"model": result.new_model, "provider": result.target_provider,
|
||||
"base_url": result.base_url, "api_key": result.api_key, "api_mode": result.api_mode}
|
||||
if not persist_global and session.get("follow_profile_config"):
|
||||
profile_model, profile_provider = _config_model_target()
|
||||
session["composer_override_profile"] = {
|
||||
"model": profile_model, "provider": profile_provider}
|
||||
if agent:
|
||||
# _commit_agent_switch persists before the session pin and its provenance exist.
|
||||
_persist_live_session_runtime(session)
|
||||
if persist_global:
|
||||
from hermes_cli.model_switch import persist_model_selection
|
||||
persist_model_selection(result)
|
||||
|
||||
Reference in New Issue
Block a user