Stage 387: PR #2605

# Conflicts:
#	api/routes.py
This commit is contained in:
nesquena-hermes
2026-05-19 22:10:20 +00:00
3 changed files with 59 additions and 2 deletions
+21 -2
View File
@@ -436,7 +436,14 @@ class Session:
self.read_only = bool(kwargs.get('read_only', False))
self.enabled_toolsets = enabled_toolsets # List[str] or None — per-session toolset override
self.composer_draft = composer_draft if isinstance(composer_draft, dict) else {}
self._metadata_message_count = None
raw_message_count = kwargs.get('message_count')
parsed_message_count = None
if raw_message_count is not None:
try:
parsed_message_count = int(raw_message_count)
except (TypeError, ValueError):
parsed_message_count = None
self._metadata_message_count = parsed_message_count if parsed_message_count is not None and parsed_message_count >= 0 else None
@property
def path(self):
@@ -601,7 +608,19 @@ class Session:
parsed['messages'] = []
parsed['tool_calls'] = []
session = cls(**parsed)
session._metadata_message_count = _lookup_index_message_count(sid)
metadata_message_count = _lookup_index_message_count(sid)
if metadata_message_count is None:
raw_count = parsed.get('message_count')
if isinstance(raw_count, int) and raw_count >= 0:
metadata_message_count = raw_count
else:
try:
parsed_count = int(raw_count)
except (TypeError, ValueError):
parsed_count = None
if parsed_count is not None and parsed_count >= 0:
metadata_message_count = parsed_count
session._metadata_message_count = metadata_message_count
# Mark this session as a metadata-only stub. save() refuses to write
# such a session because doing so would atomically replace the
# on-disk JSON with messages=[], wiping the conversation. Any
+12
View File
@@ -3721,6 +3721,18 @@ def handle_get(handler, parsed) -> bool:
_all_msgs = merge_session_messages_append_only(_metadata_sidecar, state_db_messages)
if not load_messages:
_summary_message_count = len(_all_msgs)
if _summary_message_count == 0:
# Legacy session with no loaded sidecar and no state.db summary —
# fall back to the persisted metadata count from session JSON.
# See PR #2605 (LumenYoung): without this, the metadata poll
# returns 0 and the active-session external-refresh signal
# never trips on legacy sessions.
try:
metadata_count = getattr(s, "_metadata_message_count", None)
if metadata_count is not None:
_summary_message_count = max(0, int(metadata_count))
except (TypeError, ValueError):
pass
try:
_summary_last_message_at = max(
float((m or {}).get("timestamp") or 0)
@@ -135,6 +135,32 @@ def test_api_session_includes_state_db_messages_newer_than_webui_sidecar(monkeyp
assert payload["session"]["message_count"] == 4
def test_metadata_poll_uses_sidecar_message_count_for_external_updates(monkeypatch, tmp_path):
"""Active-session external refresh relies on metadata-only counts.
When no session index exists, metadata-only loads may fall back to
_metadata_message_count=None. The refresh poll must still report the real
sidecar message count; otherwise an external session JSON update can be
invisible until a full reload.
"""
import api.routes as routes
sid = "webui_reconcile_metadata_sidecar"
sidecar_messages = [
{"role": "user", "content": "before external update", "timestamp": 1000.0},
{"role": "assistant", "content": "externally appended", "timestamp": 1001.0},
]
_install_test_session(monkeypatch, tmp_path, sid, sidecar_messages)
handler = _GetHandler(f"/api/session?session_id={sid}&messages=0&resolve_model=0")
routes.handle_get(handler, urlparse(handler.path))
assert handler.status == 200
session = handler.response_json["session"]
assert session["message_count"] == 2
assert session["last_message_at"] == 1001.0
def test_state_db_reconciliation_preserves_sidecar_only_messages(monkeypatch, tmp_path):
import api.routes as routes