fix: toast when model switched during active session (#419) — PR #529

This commit is contained in:
nesquena-hermes
2026-04-15 08:05:19 +00:00
committed by GitHub
4 changed files with 76 additions and 1 deletions

View File

@@ -1,5 +1,29 @@
# Hermes Web UI -- Changelog
## [v0.50.48] fix: toast when model is switched during active session (#419)
Synthesized from PRs #516 (armorbreak001), #517 and #518 (cloudyun888).
When a user switches the model via the model picker while a session already
has messages, a 3-second toast now reads: "Model change takes effect in
your next conversation." This avoids the confusing situation where the
dropdown shows the new model but the current conversation continues with
the original one.
The toast fires from `modelSelect.onchange` in `static/boot.js`, after the
existing provider-mismatch warning. It checks `S.messages.length > 0` (the
reliable in-memory array, always initialized by `loadSession`). The
`showToast` call is guarded with `typeof` for safety during boot.
Key differences from submitted PRs: placement in boot.js onchange (covers
all selection paths including chip dropdown, since `selectModelFromDropdown`
calls `sel.onchange`), and uses `S.messages` not `S.session.messages`.
4 new tests in `tests/test_provider_mismatch.py::TestModelSwitchToast`.
Total tests: 1272 (was 1268)
## [v0.50.47] fix/feat: batch fixes — root workspace, custom providers, cron cache, system theme
Synthesized from PRs #506, #507, #508, #509, #510, #514, #515, #519, #521.

View File

@@ -420,6 +420,10 @@ $('modelSelect').onchange=async()=>{
const warn=_checkProviderMismatch(selectedModel);
if(warn&&typeof showToast==='function') showToast(warn,4000);
}
// Notify user that model changes only take effect in the next conversation (#419)
if(S.messages && S.messages.length > 0 && typeof showToast==='function'){
showToast('Model change takes effect in your next conversation', 3000);
}
};
$('msg').addEventListener('input',()=>{
autoResize();

View File

@@ -552,7 +552,7 @@
<div class="settings-section-title">System</div>
<div class="settings-section-meta">Instance version and access controls.</div>
</div>
<span class="settings-version-badge">v0.50.47</span>
<span class="settings-version-badge">v0.50.48</span>
</div>
<div class="settings-field" style="border-top:1px solid var(--border);padding-top:12px;margin-top:8px">
<label for="settingsPassword" data-i18n="settings_label_password">Access Password</label>

View File

@@ -264,3 +264,50 @@ def test_api_models_includes_active_provider():
"/api/models response missing 'active_provider' field — "
"frontend needs this to detect provider mismatches"
)
# ── Model switch toast (#419) ─────────────────────────────────────────────────
class TestModelSwitchToast:
"""Toast appears when user switches model during an active session."""
def test_toast_in_model_select_onchange(self):
"""modelSelect.onchange must show a toast when S.messages is non-empty."""
src = _read("static/boot.js")
# Find the onchange block
idx = src.find("modelSelect').onchange")
assert idx != -1, "modelSelect.onchange not found in boot.js"
block = src[idx:idx + 1100]
assert "Model change takes effect in your next conversation" in block, (
"modelSelect.onchange must show a toast when switching model mid-session"
)
def test_toast_guards_on_messages_length(self):
"""Toast must only fire when there are existing messages (active session)."""
src = _read("static/boot.js")
idx = src.find("Model change takes effect in your next conversation")
assert idx != -1
# Look back 200 chars for the S.messages guard
surrounding = src[max(0, idx - 200):idx + 50]
assert "S.messages" in surrounding and ".length" in surrounding, (
"Model switch toast must be gated on S.messages.length > 0"
)
def test_toast_uses_show_toast_not_alert(self):
"""Toast must use showToast(), not alert()."""
src = _read("static/boot.js")
idx = src.find("Model change takes effect in your next conversation")
assert idx != -1
surrounding = src[max(0, idx - 50):idx + 100]
assert "showToast" in surrounding, "Must use showToast() not alert()"
assert "alert(" not in surrounding, "Must not use alert()"
def test_toast_has_typeof_showtoast_guard(self):
"""Toast call must guard typeof showToast to be safe during boot."""
src = _read("static/boot.js")
idx = src.find("Model change takes effect in your next conversation")
assert idx != -1
surrounding = src[max(0, idx - 100):idx + 50]
assert "typeof showToast" in surrounding, (
"showToast call must be guarded with typeof check"
)