fix(streaming): preserve profile env for checkpoints

This commit is contained in:
Frank Song committed 2026-05-25 21:29:06 +08:00
1 parent 4ea762ae0d
commit 524ed6cce3
3 files changed
+54 -1

No files matched your search

+4
View File
@@ -3,6 +3,10 @@
## [Unreleased] ## [Unreleased]
### Fixed
- Streaming checkpoint saves now run under the session's profile environment, so periodic background checkpoints no longer risk falling back to the process-global profile when a WebUI tab is using a non-default profile.
## [v0.51.134] — 2026-05-25 — Release DF (stage-batch16 — single-PR Windows path defaults) ## [v0.51.134] — 2026-05-25 — Release DF (stage-batch16 — single-PR Windows path defaults)
### Fixed ### Fixed
+13 -1
View File
@@ -2557,6 +2557,18 @@ def _drop_checkpointed_current_user_from_context(messages, msg_text):
return history return history
def _save_streaming_checkpoint(session):
"""Persist a streaming checkpoint under the session's profile context."""
from api import profiles as profiles_api
with profiles_api.profile_env_for_background_worker(
session,
"streaming checkpoint",
logger_override=logger,
):
session.save(skip_index=True)
def _normalize_fresh_chat_text(text): def _normalize_fresh_chat_text(text):
text = _strip_workspace_prefix(str(text or ''), include_legacy=True) text = _strip_workspace_prefix(str(text or ''), include_legacy=True)
text = re.sub(r"\s+", " ", text).strip().lower() text = re.sub(r"\s+", " ", text).strip().lower()
@@ -4552,7 +4564,7 @@ def _run_agent_streaming(
cur = _checkpoint_activity[0] cur = _checkpoint_activity[0]
if cur > last_saved_activity: if cur > last_saved_activity:
with _agent_lock: with _agent_lock:
s.save(skip_index=True) _save_streaming_checkpoint(s)
last_saved_activity = cur last_saved_activity = cur
except Exception as e: except Exception as e:
logger.debug("Periodic checkpoint save failed: %s", e) logger.debug("Periodic checkpoint save failed: %s", e)
@@ -0,0 +1,37 @@
"""Regression coverage for #2848 checkpoint saves in background threads."""
from __future__ import annotations
from pathlib import Path
def test_checkpoint_save_uses_session_profile_env(monkeypatch, tmp_path):
"""Checkpoint saves run on their own thread, outside request TLS.
They must route profile-scoped helpers through the session's profile instead
of falling back to the process-global/default profile.
"""
from api.models import Session
from api.streaming import _save_streaming_checkpoint
import api.config as config
import api.profiles as profiles
profile_home = tmp_path / "profiles" / "maiko"
profile_home.mkdir(parents=True)
captured = {}
monkeypatch.setattr(profiles, "get_hermes_home_for_profile", lambda profile: profile_home)
monkeypatch.setattr(profiles, "get_profile_runtime_env", lambda home: {"HERMES_CONFIG_PATH": str(Path(home) / "config.yaml")})
def fake_save(self, *args, **kwargs):
captured["kwargs"] = kwargs
captured["thread_env"] = dict(getattr(config._thread_ctx, "env", {}) or {})
monkeypatch.setattr(Session, "save", fake_save)
session = Session(session_id="issue2848", profile="maiko")
_save_streaming_checkpoint(session)
assert captured["kwargs"] == {"skip_index": True}
assert captured["thread_env"]["HERMES_HOME"] == str(profile_home)
assert captured["thread_env"]["HERMES_CONFIG_PATH"] == str(profile_home / "config.yaml")