fix(#5021): keep boot model budget across restore paths

This commit is contained in:
Rod Boev authored and nesquena-hermes committed 2026-06-27 02:52:33 +00:00
1 parent a18ddae930
commit 288cbb55bc
4 files changed
+42 -19

No files matched your search

+3 -1
View File
@@ -2278,7 +2278,9 @@ window._applyTitlebarProfileVisibility=_applyTitlebarProfileVisibility;
let consumed=false; let consumed=false;
const readAttempted=(storage=sessionStorage)=>{ const readAttempted=(storage=sessionStorage)=>{
try{ try{
return storage&&storage.getItem?storage.getItem(markerKey)==='1':false; const attempted=storage&&storage.getItem?storage.getItem(markerKey)==='1':false;
if(attempted) consumed=true;
return attempted;
}catch(_){ }catch(_){
return false; return false;
} }
+7 -3
View File
@@ -1294,15 +1294,19 @@ async function loadSession(sid){
S._pendingSessionToolsets=null; S._pendingSessionToolsets=null;
if(typeof populateModelDropdown==='function'){ if(typeof populateModelDropdown==='function'){
const modelRefreshSid=sid; const modelRefreshSid=sid;
if(!S._bootReady&&typeof window!=='undefined'&&typeof window._startBootModelDropdown==='function'){
Promise.resolve().then(()=>{
if(_loadingSessionId!==modelRefreshSid) return;
return window._startBootModelDropdown();
}).catch(()=>{});
}else{
const modelRefreshPromise=Promise.resolve().then(()=>{ const modelRefreshPromise=Promise.resolve().then(()=>{
if(_loadingSessionId!==modelRefreshSid) return; if(_loadingSessionId!==modelRefreshSid) return;
if(!S._bootReady&&typeof window!=='undefined'&&typeof window._startBootModelDropdown==='function'){
return window._startBootModelDropdown();
}
return populateModelDropdown({freshness:'session_visit'}); return populateModelDropdown({freshness:'session_visit'});
}).catch(()=>{}); }).catch(()=>{});
if(typeof window!=='undefined') window._modelDropdownReady=modelRefreshPromise; if(typeof window!=='undefined') window._modelDropdownReady=modelRefreshPromise;
} }
}
if(typeof _hydrateTodosFromSession==='function') _hydrateTodosFromSession(S.session); if(typeof _hydrateTodosFromSession==='function') _hydrateTodosFromSession(S.session);
S.session._modelResolutionDeferred=true; S.session._modelResolutionDeferred=true;
S.lastUsage={...(data.session.last_usage||{})}; S.lastUsage={...(data.session.last_usage||{})};
+20 -6
View File
@@ -12,6 +12,7 @@ ROOT = Path(__file__).resolve().parent.parent
BOOT_JS = ROOT / "static" / "boot.js" BOOT_JS = ROOT / "static" / "boot.js"
UI_JS = ROOT / "static" / "ui.js" UI_JS = ROOT / "static" / "ui.js"
NODE = shutil.which("node") NODE = shutil.which("node")
BOOT_MARKER_KEY = "hermes-webui-active-profile-bootstrap-401"
pytestmark = pytest.mark.skipif( pytestmark = pytest.mark.skipif(
@@ -436,18 +437,31 @@ def test_boot_model_401_does_not_reopen_redirect_after_active_profile_fallback(d
def test_boot_model_401_consumes_budget_when_profile_load_succeeds(driver_path): def test_boot_model_401_consumes_budget_when_profile_load_succeeds(driver_path):
payload = _run( first = _run(
driver_path, driver_path,
{ {
"kind": "boot-model-only", "kind": "boot-model-only",
}, },
) )
assert payload["redirects"] == ["login?next=%2Fsession%2Fabc%3Fworkspace%3Dtest"] second = _run(
assert payload["results"][0]["state"]["status"] == "resolved" driver_path,
assert payload["jsonCalls"] == 0 {
assert payload["activeProvider"] is None "kind": "boot-model-only",
assert payload["defaultModel"] is None "initialStorage": first["storage"],
},
)
assert first["redirects"] == ["login?next=%2Fsession%2Fabc%3Fworkspace%3Dtest"]
assert first["results"][0]["state"]["status"] == "resolved"
assert first["storage"] == {BOOT_MARKER_KEY: "1"}
assert first["jsonCalls"] == 0
assert first["activeProvider"] is None
assert first["defaultModel"] is None
assert second["redirects"] == []
assert second["results"][0]["state"]["status"] == "resolved"
assert second["storage"] == {}
assert second["jsonCalls"] == 0
def test_boot_model_success_still_stores_active_provider(driver_path): def test_boot_model_success_still_stores_active_provider(driver_path):
@@ -328,22 +328,25 @@ def test_load_session_schedules_async_session_visit_model_refresh_after_metadata
body = _extract_function_body(_read_static("sessions.js"), "async function loadSession(") body = _extract_function_body(_read_static("sessions.js"), "async function loadSession(")
assign_idx = body.index("S.session=data.session") assign_idx = body.index("S.session=data.session")
promise_idx = body.index("const modelRefreshPromise=Promise.resolve().then(")
boot_guard_idx = body.index("if(!S._bootReady&&typeof window!=='undefined'&&typeof window._startBootModelDropdown==='function'){") boot_guard_idx = body.index("if(!S._bootReady&&typeof window!=='undefined'&&typeof window._startBootModelDropdown==='function'){")
boot_promise_idx = body.index("Promise.resolve().then(()=>{", boot_guard_idx)
boot_refresh_idx = body.index("return window._startBootModelDropdown();") boot_refresh_idx = body.index("return window._startBootModelDropdown();")
else_idx = body.index("}else{", boot_refresh_idx)
promise_idx = body.index("const modelRefreshPromise=Promise.resolve().then(", else_idx)
ready_idx = body.index("window._modelDropdownReady=modelRefreshPromise") ready_idx = body.index("window._modelDropdownReady=modelRefreshPromise")
refresh_idx = body.index("populateModelDropdown({freshness:'session_visit'})") refresh_idx = body.index("populateModelDropdown({freshness:'session_visit'})")
stale_guard_idx = body.index("_loadingSessionId!==modelRefreshSid") stale_guard_idx = body.index("_loadingSessionId!==modelRefreshSid")
assert assign_idx < promise_idx < boot_guard_idx < boot_refresh_idx < refresh_idx < ready_idx assert assign_idx < boot_guard_idx < boot_promise_idx < boot_refresh_idx < else_idx < promise_idx < refresh_idx < ready_idx
assert promise_idx < stale_guard_idx < boot_guard_idx assert boot_promise_idx < stale_guard_idx < boot_refresh_idx
assert promise_idx < body.index("_loadingSessionId!==modelRefreshSid", promise_idx) < refresh_idx
def test_boot_model_dropdown_reuses_inflight_session_visit_refresh(): def test_boot_model_dropdown_clears_cached_ready_on_401():
body = _extract_function_body(_read_static("boot.js"), "const _startBootModelDropdown=()=>") body = _extract_function_body(_read_static("boot.js"), "const _redirectBootModelDropdownIfUnauth=(res)=>")
ready_idx = body.index("const ready=window._modelDropdownReady;") status_idx = body.index("if(!res||res.status!==401) return false;")
reuse_idx = body.index("if(ready&&typeof ready.then==='function') return ready;") clear_idx = body.index("window._modelDropdownReady=null;")
hydrate_idx = body.index("const next=_hydrateModelDropdown({redirectIfUnauth:_redirectBootModelDropdownIfUnauth});") consumed_idx = body.index("if(_bootActiveProfileUnauthRedirectBudget.isConsumed()) return true;")
assert ready_idx < reuse_idx < hydrate_idx assert status_idx < clear_idx < consumed_idx