mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-25 11:10:18 +00:00
fix(chat): keep restored session model visible on hard refresh
This commit is contained in:
committed by
nesquena-hermes
parent
765e5aa091
commit
ebcf0dabb5
+46
-20
@@ -869,6 +869,24 @@ function _applyModelToDropdown(modelId, sel, preferredProviderId){
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function _ensureModelOptionInDropdown(modelId, sel, preferredProviderId){
|
||||
if(!modelId||!sel) return null;
|
||||
const applied=_applyModelToDropdown(modelId,sel,preferredProviderId);
|
||||
if(applied) return applied;
|
||||
const opt=document.createElement('option');
|
||||
opt.value=modelId;
|
||||
opt.textContent=typeof getModelLabel==='function'?getModelLabel(modelId):modelId;
|
||||
opt.dataset.custom='1';
|
||||
const provider=preferredProviderId||_providerFromModelValue(modelId)||'';
|
||||
if(provider) opt.dataset.provider=provider;
|
||||
sel.appendChild(opt);
|
||||
sel.value=modelId;
|
||||
if(sel.id==='modelSelect'){
|
||||
if(typeof syncModelChip==='function') syncModelChip();
|
||||
_refreshOpenModelDropdown();
|
||||
}
|
||||
return modelId;
|
||||
}
|
||||
function _modelStateFromAppliedDropdown(sel, modelValue){
|
||||
const state=(typeof _modelStateForSelect==='function')
|
||||
? _modelStateForSelect(sel,modelValue)
|
||||
@@ -4813,28 +4831,36 @@ function syncTopbar(){
|
||||
}
|
||||
} else {
|
||||
const applied=_applyModelToDropdown(currentModel,modelSel,S.session.model_provider||null);
|
||||
// If the model isn't in the current provider list, reset to the configured
|
||||
// default rather than silently retaining the previous chat's selection (#1771).
|
||||
// If the session model is missing from the current provider list, inject
|
||||
// a session-scoped option instead of displaying the previous/static
|
||||
// selection. Only fall back if that repair path is unavailable.
|
||||
if(!applied){
|
||||
const deferModelCorrection=Boolean(S.session._modelResolutionDeferred);
|
||||
const missingModelIsRoutable=_providerDefersMissingModelFallback(S.session.model_provider||window._activeProvider||null);
|
||||
// Also defer if a live model fetch is still in flight — the model may be
|
||||
// in the list once the fetch completes. Persisting now would corrupt the
|
||||
// session with the wrong model before live models arrive (#1169).
|
||||
const liveStillPending=window._activeProvider&&_liveModelFetchPending.has(window._activeProvider);
|
||||
if(liveStillPending||missingModelIsRoutable){
|
||||
// Live fetch in flight — don't touch sel.value or S.session.model yet.
|
||||
// _addLiveModelsToSelect() will re-apply S.session.model once done (#1169).
|
||||
// Named custom providers/OpenRouter can also route vendor-prefixed IDs
|
||||
// outside the static catalog, so preserve the user's explicit choice.
|
||||
const sessionOption=(typeof _ensureModelOptionInDropdown==='function')
|
||||
? _ensureModelOptionInDropdown(currentModel,modelSel,S.session.model_provider||null)
|
||||
: null;
|
||||
if(sessionOption){
|
||||
currentModel=sessionOption;
|
||||
} else {
|
||||
const fallback=_applySessionModelFallback(modelSel);
|
||||
if(fallback&&!deferModelCorrection){
|
||||
S.session.model=fallback.model;
|
||||
S.session.model_provider=fallback.model_provider||null;
|
||||
currentModel=fallback.model;
|
||||
// Persist the correction so the session doesn't re-inject on next load.
|
||||
_persistSessionModelCorrection(fallback.model,S.session.model_provider||null);
|
||||
const deferModelCorrection=Boolean(S.session._modelResolutionDeferred);
|
||||
const missingModelIsRoutable=_providerDefersMissingModelFallback(S.session.model_provider||window._activeProvider||null);
|
||||
// Also defer if a live model fetch is still in flight — the model may be
|
||||
// in the list once the fetch completes. Persisting now would corrupt the
|
||||
// session with the wrong model before live models arrive (#1169).
|
||||
const liveStillPending=window._activeProvider&&_liveModelFetchPending.has(window._activeProvider);
|
||||
if(liveStillPending||missingModelIsRoutable){
|
||||
// Live fetch in flight — don't touch sel.value or S.session.model yet.
|
||||
// _addLiveModelsToSelect() will re-apply S.session.model once done (#1169).
|
||||
// Named custom providers/OpenRouter can also route vendor-prefixed IDs
|
||||
// outside the static catalog, so preserve the user's explicit choice.
|
||||
} else {
|
||||
const fallback=_applySessionModelFallback(modelSel);
|
||||
if(fallback&&!deferModelCorrection){
|
||||
S.session.model=fallback.model;
|
||||
S.session.model_provider=fallback.model_provider||null;
|
||||
currentModel=fallback.model;
|
||||
// Persist the correction so the session doesn't re-inject on next load.
|
||||
_persistSessionModelCorrection(fallback.model,S.session.model_provider||null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,27 @@ def test_hard_refresh_hydrates_saved_session_model_before_revealing_model_chip()
|
||||
)
|
||||
|
||||
|
||||
def test_hard_refresh_injects_missing_active_session_model_option():
|
||||
boot_js = Path("static/boot.js").read_text(encoding="utf-8")
|
||||
marker = "if(!applied&&sessionModelState&&typeof _ensureModelOptionInDropdown==='function')"
|
||||
assert marker in boot_js
|
||||
branch = boot_js[boot_js.index(marker) : boot_js.index("else if(!applied&&!sessionModelState", boot_js.index(marker))]
|
||||
assert "_ensureModelOptionInDropdown(sessionModelState.model,$('modelSelect'),sessionModelState.model_provider||null)" in branch
|
||||
|
||||
|
||||
def test_sync_topbar_preserves_missing_session_model_as_dropdown_option():
|
||||
ui_js = Path("static/ui.js").read_text(encoding="utf-8")
|
||||
assert "function _ensureModelOptionInDropdown" in ui_js
|
||||
sync_topbar = _extract_function(ui_js, "function syncTopbar")
|
||||
branch_start = sync_topbar.index("const applied=_applyModelToDropdown(currentModel,modelSel,S.session.model_provider||null);")
|
||||
session_model_branch = sync_topbar[branch_start:]
|
||||
assert "_ensureModelOptionInDropdown(currentModel,modelSel,S.session.model_provider||null)" in session_model_branch
|
||||
assert "const fallback=_applySessionModelFallback(modelSel);" in session_model_branch
|
||||
assert session_model_branch.index("_ensureModelOptionInDropdown(currentModel,modelSel,S.session.model_provider||null)") < session_model_branch.index("const fallback=_applySessionModelFallback(modelSel);"), (
|
||||
"active session models missing from the current catalog must be injected before fallback can select the static/default model"
|
||||
)
|
||||
|
||||
|
||||
def test_new_chat_does_not_send_stale_dropdown_model_when_session_has_default_model():
|
||||
assert "model:S.session.model||$('modelSelect').value" in MESSAGES_JS
|
||||
assert "model_provider:S.session.model_provider||null" in MESSAGES_JS
|
||||
|
||||
Reference in New Issue
Block a user