fix(profile): preserve context when starting chats#1728
fix(profile): preserve context when starting chats#1728starship-s wants to merge 1 commit intonesquena:masterfrom
Conversation
5222ec3 to
8a462a8
Compare
SummaryPulled the branch and walked all four files. Two distinct fixes in here, both addressing the same symptom (profile-switch context loss on first turn) but at different layers: a path-aware reload in Code referenceThe def get_config() -> dict:
"""Return the cached config dict, loading from disk if needed."""
if not _cfg_cache:
reload_config()
return _cfg_cacheNow it watches both the path and the mtime, and reloads when either changes — but only when there are no in-memory overrides: config_path = _get_config_path()
try:
current_mtime = config_path.stat().st_mtime
except OSError:
current_mtime = 0.0
cache_stale = current_mtime != _cfg_mtime or _cfg_path != config_path
if not _cfg_cache or (cache_stale and not _cfg_has_in_memory_overrides()):
reload_config()( DiagnosisReading the matching agent contract — The chat-start retag on requested_profile = str(body.get("profile") or "").strip()
if requested_profile:
try:
from api.profiles import _PROFILE_ID_RE
if requested_profile != "default" and not _PROFILE_ID_RE.fullmatch(requested_profile):
return bad(handler, "invalid profile", 400)
except ImportError:
requested_profile = ""
if requested_profile and not _profiles_match(getattr(s, "profile", None), requested_profile):
has_persisted_turns = bool(
getattr(s, "messages", None)
or getattr(s, "context_messages", None)
or getattr(s, "pending_user_message", None)
)
if not has_persisted_turns:
s.profile = requested_profileThe "empty session" check uses three predicates. One questionThe Nit
Verification
|
|
Thanks @starship-s — this shipped in v0.51.8 (commit GitHub didn't auto-close because the merge commit only references the squash-merged stage branch, not your fork's commit directly — closing manually for hygiene. Live now on https://get-hermes.ai/ and on existing installs after Release notes: https://github.com/nesquena/hermes-webui/releases/tag/v0.51.8 |
PR nesquena#1728's path/mtime-aware get_config() reload broke the common test idiom monkeypatch.setattr(config, 'cfg', {...}). The cfg = _cfg_cache alias bound at import time means the rebinding only changes the module attribute; _cfg_cache stays unchanged, so _cfg_has_in_memory_overrides() returned False and the path-aware reload silently overwrote the test's override. test_issue1426_openrouter_* and test_issue1680_codex_* failed in the full suite while passing standalone — exact polluter signature. Fix: - _cfg_has_in_memory_overrides() now also detects cfg-rebind via cfg is not _cfg_cache. - get_config() returns cfg (the override) when it differs from _cfg_cache, so callers see the test's intended override. - 4 new regression tests pin both prongs in test_stage302_config_override_regression.py. Defense-in-depth (prong 2 of test-isolation-flake-recipe): - test_sprint3.py::test_skills_list and test_skills_list_has_required_fields now skip on empty skills list rather than asserting > 0 / IndexError, so future profile-switch / SKILLS_DIR repointing pollutions don't break the build. The contract under test is 'API returns a non-empty list when there are entries' — empty list signals a polluter elsewhere. Pre-existing wall-clock flake fix (absorb-in-release): - test_issue1144_session_time_sync.py::test_relative_time_uses_server_clock now pins Date.now() to a fixed instant. Without pinning, when CI runs near 08:00 UTC the projected server time crosses midnight and '5 minutes ago' silently becomes '1d'. Same time-of-day-pin pattern as the sibling test_session_bucket_uses_server_clock used. Test count: 4580 → 4584 (+4 regression tests). 0 failures, stably green across multiple runs.
…p + test-isolation fix Constituent PRs: - nesquena#1725 (@Michaelyklam) — simplify compact Activity row summary - nesquena#1726 (@Michaelyklam) — delegate generic provider catalogs to Hermes CLI (slice of nesquena#1240) - nesquena#1727 (@Michaelyklam) — link Claude Code OAuth in onboarding (closes nesquena#1362) - nesquena#1728 (@starship-s) — preserve profile context when starting chats - nesquena#1729 (@Michaelyklam) — persist compact Activity disclosure state - nesquena#1730 (@Michaelyklam) — prevent sticky sidebar hover drag state - nesquena#1732 (@Sanjays2402) — unpin scroll on small upward motion during streaming (closes nesquena#1731) Plus 2 in-stage absorbed fixes: - test-isolation fix: monkeypatch.setattr(config, 'cfg', X) survives PR nesquena#1728's path/mtime-aware get_config() reload. Mandatory before tag (Opus stage-302). - Opus SHOULD-FIX #1: _lastScrollTop reset on session switch (nesquena#1732 follow-up). Tests: 4537 → 4584 passing (+47). 0 regressions. Full suite ~128s. Stably green. Pre-release verification: - All 7 PRs CI-green individually + rebased onto master - pytest 4584 passed, 0 failed (multiple runs) - node -c clean on all 4 modified .js files - 11/11 browser API endpoints PASS on isolated port 8789 - 20 QA tests via webui_qa_agent.sh PASS - Opus advisor: SHIP, 5/5 verification clean, 0 MUST-FIX, 1 SHOULD-FIX absorbed (_lastScrollTop reset), 1 SHOULD-FIX deferred (nesquena#1736 — _clear_anthropic_env_values race, onboarding-time-only) Closes nesquena#1362, nesquena#1731.
Thinking Path
config.yamlmtime, so a request for one profile could keep using the config loaded from another profile path when mtimes did not force a reload.What Changed
config.yamlpath alongside the cached config and reload when a request resolves to a different profile path.cfgoverrides while still reloading unchanged disk-loaded config on profile-path changes./api/chat/startpayload.Why It Matters
Verification
Targeted profile-switch coverage:
env -u HERMES_CONFIG_PATH -u HERMES_HOME -u HERMES_BASE_HOME -u HERMES_WEBUI_HOST -u HERMES_WEBUI_PORT -u HERMES_WEBUI_STATE_DIR python -m pytest tests/test_profile_switch_1200.py tests/test_bugbatch_apr2026.py -qResult:
28 passedProvider/model profile coverage:
env -u HERMES_CONFIG_PATH -u HERMES_HOME -u HERMES_BASE_HOME -u HERMES_WEBUI_HOST -u HERMES_WEBUI_PORT -u HERMES_WEBUI_STATE_DIR python -m pytest tests/test_issue604_all_providers_model_picker.py tests/test_issue1106_custom_providers_models.py tests/test_issue1611_session_profile_filtering.py -qResult:
32 passedCI failure reproduction subset:
env -u HERMES_CONFIG_PATH -u HERMES_HOME -u HERMES_BASE_HOME -u HERMES_WEBUI_HOST -u HERMES_WEBUI_PORT -u HERMES_WEBUI_STATE_DIR python -m pytest tests/test_issue1094_provider_bugs.py::TestBug1094HasKeyFalsePositive::test_model_api_key_only_marks_active_provider tests/test_issue1499_keyless_onboarding.py::TestKeylessChatReady::test_lmstudio_keyless_chat_ready_via_full_status tests/test_minimax_provider.py::test_minimax_cn_empty_config_provider_gets_static_models tests/test_provider_quota_status.py::test_openrouter_quota_fetches_key_endpoint_and_sanitizes_response -qResult:
4 passedSyntax / hygiene checks:
python -m py_compile api/config.py api/routes.pynode --check static/messages.jsgit diff --checkResult: all passed.
Risks / Follow-ups
api/config.pynear the existing model-list cache invalidation path, so concurrent model-cache work may need a straightforward rebase..Models Used