fix: null/empty session model must not trigger index rebuild — v0.50.101 (#753)
## Summary Follow-up to #751/#752. Code review identified a case where `_normalize_session_model_in_place` could call `session.save()` (which triggers a full session index rebuild) for sessions with `model: null` or missing model field. Root cause: `_resolve_compatible_session_model(None)` returns `(default_model, True)` when a default exists — which was interpreted as "changed, needs save." But there's nothing to correct for a session with no model; the default is just a fallback for display purposes, not a cross-provider correction worth persisting. Fix: capture `original_model` before calling `_resolve_compatible_session_model`. Only call `session.save()` if `original_model` was non-empty and actually changed. Adds a test asserting `save_calls == []` when `session.model is None`. No behavior change for sessions with a real model (the primary use case of #751 is unaffected).
This commit is contained in:
@@ -1,5 +1,10 @@
|
|||||||
# Hermes Web UI -- Changelog
|
# Hermes Web UI -- Changelog
|
||||||
|
|
||||||
|
## [v0.50.101] — 2026-04-20
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **Session model normalization: null/empty model no longer triggers index rebuild** — sessions with no stored model (`model: null` or missing) now return the provider default without writing to disk. Previously a spurious `session.save()` (and full session index rebuild) could fire for any such session. (#751 follow-up)
|
||||||
|
|
||||||
## [v0.50.100] — 2026-04-20
|
## [v0.50.100] — 2026-04-20
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -227,8 +227,12 @@ def _resolve_compatible_session_model(model_id: str | None) -> tuple[str, bool]:
|
|||||||
|
|
||||||
|
|
||||||
def _normalize_session_model_in_place(session) -> str:
|
def _normalize_session_model_in_place(session) -> str:
|
||||||
effective_model, changed = _resolve_compatible_session_model(getattr(session, "model", None))
|
original_model = getattr(session, "model", None) or ""
|
||||||
if changed and effective_model and getattr(session, "model", None) != effective_model:
|
effective_model, changed = _resolve_compatible_session_model(original_model or None)
|
||||||
|
# Only persist the correction if the session had an explicit model that needed changing.
|
||||||
|
# Sessions with no model stored (empty/None) get the effective default returned without
|
||||||
|
# a disk write — no need to rebuild the index for a fill-in-blank operation.
|
||||||
|
if changed and effective_model and original_model and original_model != effective_model:
|
||||||
session.model = effective_model
|
session.model = effective_model
|
||||||
session.save(touch_updated_at=False)
|
session.save(touch_updated_at=False)
|
||||||
return effective_model
|
return effective_model
|
||||||
|
|||||||
@@ -459,3 +459,36 @@ def test_unknown_prefix_model_passes_through_unchanged(monkeypatch):
|
|||||||
assert effective == custom_model, (
|
assert effective == custom_model, (
|
||||||
f"Expected '{custom_model}', got '{effective}'"
|
f"Expected '{custom_model}', got '{effective}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_empty_model_session_does_not_trigger_save(monkeypatch):
|
||||||
|
"""Sessions with no model stored must not trigger session.save() — index rebuild is expensive."""
|
||||||
|
import api.routes as routes
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
routes,
|
||||||
|
"get_available_models",
|
||||||
|
lambda: {
|
||||||
|
"active_provider": "openai-codex",
|
||||||
|
"default_model": "gpt-5.4-mini",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
save_calls = []
|
||||||
|
|
||||||
|
class DummySession:
|
||||||
|
def __init__(self):
|
||||||
|
self.model = None # no model stored
|
||||||
|
|
||||||
|
def save(self, touch_updated_at=True):
|
||||||
|
save_calls.append(touch_updated_at)
|
||||||
|
|
||||||
|
session = DummySession()
|
||||||
|
effective = routes._normalize_session_model_in_place(session)
|
||||||
|
|
||||||
|
# Must return the default, but must NOT write to disk
|
||||||
|
assert effective == "gpt-5.4-mini"
|
||||||
|
assert save_calls == [], (
|
||||||
|
"_normalize_session_model_in_place must not call session.save() when "
|
||||||
|
"the session has no stored model — no correction needed, just a fallback."
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user