24
CHANGELOG.md
24
CHANGELOG.md
@@ -1,5 +1,29 @@
|
|||||||
# Hermes Web UI -- Changelog
|
# 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
|
## [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.
|
Synthesized from PRs #506, #507, #508, #509, #510, #514, #515, #519, #521.
|
||||||
|
|||||||
@@ -420,6 +420,10 @@ $('modelSelect').onchange=async()=>{
|
|||||||
const warn=_checkProviderMismatch(selectedModel);
|
const warn=_checkProviderMismatch(selectedModel);
|
||||||
if(warn&&typeof showToast==='function') showToast(warn,4000);
|
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',()=>{
|
$('msg').addEventListener('input',()=>{
|
||||||
autoResize();
|
autoResize();
|
||||||
|
|||||||
@@ -552,7 +552,7 @@
|
|||||||
<div class="settings-section-title">System</div>
|
<div class="settings-section-title">System</div>
|
||||||
<div class="settings-section-meta">Instance version and access controls.</div>
|
<div class="settings-section-meta">Instance version and access controls.</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="settings-version-badge">v0.50.47</span>
|
<span class="settings-version-badge">v0.50.48</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-field" style="border-top:1px solid var(--border);padding-top:12px;margin-top:8px">
|
<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>
|
<label for="settingsPassword" data-i18n="settings_label_password">Access Password</label>
|
||||||
|
|||||||
@@ -264,3 +264,50 @@ def test_api_models_includes_active_provider():
|
|||||||
"/api/models response missing 'active_provider' field — "
|
"/api/models response missing 'active_provider' field — "
|
||||||
"frontend needs this to detect provider mismatches"
|
"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"
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user