mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-25 11:10:18 +00:00
fix: clear runtime fields on compression snapshots
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Context-compression snapshot preservation now clears archived parent runtime fields (`active_stream_id`, `pending_user_message`, attachments, and start timestamp) before saving the old session id. This prevents a completed continuation session from leaving its parent looking permanently active/running after compression.
|
||||
|
||||
### Added
|
||||
|
||||
- **PR #2099** by @dobby-d-elf — Adds an opt-in `Settings → Preferences → Fade text effect` toggle (off by default). When enabled, newly streamed output tokens are revealed through an adaptive playout buffer and animated with an opacity-only fade similar to ChatGPT and other frontier LLM apps. Implementation details: fade locked per stream to avoid mid-stream toggle rewind; reduced-motion users get non-animated text; live cursor hidden while fade is active; custom renderer on `streaming-markdown` parser wraps only newly-appended words; animated spans replace themselves with plain text on `animationend` (no long-lived wrapper buildup in long responses); unsafe streamed `href`/`src` values blocked in fade renderer `set_attr` path. Performance tuning: 200ms base fade duration scaling to 350ms for fast output, 16ms word stagger, 320ms done-drain wait cap, 160 wps visual cap, max 2-3 words/frame, brief pauses after sentence punctuation. Default-off means existing users see no change. 293-line regression test pinning the contract.
|
||||
|
||||
+32
-5
@@ -1862,6 +1862,37 @@ def _drop_checkpointed_current_user_from_context(messages, msg_text):
|
||||
return history
|
||||
|
||||
|
||||
def _save_pre_compression_snapshot(session, old_session_id):
|
||||
"""Persist the archived pre-compression session without live turn state.
|
||||
|
||||
During context compression the same ``Session`` object is reused for the new
|
||||
continuation id. Before the final continuation save clears
|
||||
``active_stream_id`` and ``pending_*``, we also preserve an old-id snapshot so
|
||||
the full pre-compression transcript remains recoverable. That archived
|
||||
parent must not keep the current stream bookkeeping, otherwise the sidebar can
|
||||
reopen the parent as a permanently running session while the child already
|
||||
contains the completed answer.
|
||||
"""
|
||||
saved_sid = session.session_id
|
||||
saved_active_stream_id = getattr(session, 'active_stream_id', None)
|
||||
saved_pending_user_message = getattr(session, 'pending_user_message', None)
|
||||
saved_pending_attachments = list(getattr(session, 'pending_attachments', []) or [])
|
||||
saved_pending_started_at = getattr(session, 'pending_started_at', None)
|
||||
session.session_id = old_session_id
|
||||
session.active_stream_id = None
|
||||
session.pending_user_message = None
|
||||
session.pending_attachments = []
|
||||
session.pending_started_at = None
|
||||
try:
|
||||
session.save(touch_updated_at=False, skip_index=True)
|
||||
finally:
|
||||
session.session_id = saved_sid
|
||||
session.active_stream_id = saved_active_stream_id
|
||||
session.pending_user_message = saved_pending_user_message
|
||||
session.pending_attachments = saved_pending_attachments
|
||||
session.pending_started_at = saved_pending_started_at
|
||||
|
||||
|
||||
def _stream_writeback_is_current(session, stream_id):
|
||||
"""Return True only while a worker still owns the session writeback.
|
||||
|
||||
@@ -3651,18 +3682,14 @@ def _run_agent_streaming(
|
||||
# before save then restored after — but the on-disk
|
||||
# copy persisted with parent=None, breaking
|
||||
# fork-of-fork lineage traversal. (#2227 + #2223)
|
||||
saved_sid = s.session_id
|
||||
s.session_id = old_sid
|
||||
try:
|
||||
s.save(touch_updated_at=False, skip_index=True)
|
||||
_save_pre_compression_snapshot(s, old_sid)
|
||||
logger.info(
|
||||
"Preserved pre-compression session %s (%d messages) to disk",
|
||||
old_sid, len(s.messages),
|
||||
)
|
||||
except Exception:
|
||||
logger.debug("Failed to preserve pre-compression session file", exc_info=True)
|
||||
finally:
|
||||
s.session_id = saved_sid
|
||||
except OSError:
|
||||
logger.debug("Could not read old session file before preservation")
|
||||
# Always link the continuation session to its immediate predecessor
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
from api import streaming
|
||||
|
||||
|
||||
class FakeSession:
|
||||
def __init__(self):
|
||||
self.session_id = "new_session"
|
||||
self.parent_session_id = "original_parent"
|
||||
self.active_stream_id = "live-stream"
|
||||
self.pending_user_message = "current prompt"
|
||||
self.pending_attachments = [{"name": "file.txt"}]
|
||||
self.pending_started_at = 123.0
|
||||
self.messages = [{"role": "user", "content": "current prompt"}]
|
||||
self.saved_payload = None
|
||||
|
||||
def save(self, *, touch_updated_at=True, skip_index=False):
|
||||
self.saved_payload = {
|
||||
"session_id": self.session_id,
|
||||
"parent_session_id": self.parent_session_id,
|
||||
"active_stream_id": self.active_stream_id,
|
||||
"pending_user_message": self.pending_user_message,
|
||||
"pending_attachments": list(self.pending_attachments),
|
||||
"pending_started_at": self.pending_started_at,
|
||||
"touch_updated_at": touch_updated_at,
|
||||
"skip_index": skip_index,
|
||||
}
|
||||
|
||||
|
||||
def test_pre_compression_snapshot_clears_runtime_fields_while_restoring_continuation_state():
|
||||
session = FakeSession()
|
||||
|
||||
streaming._save_pre_compression_snapshot(session, "old_session")
|
||||
|
||||
assert session.saved_payload == {
|
||||
"session_id": "old_session",
|
||||
"parent_session_id": "original_parent",
|
||||
"active_stream_id": None,
|
||||
"pending_user_message": None,
|
||||
"pending_attachments": [],
|
||||
"pending_started_at": None,
|
||||
"touch_updated_at": False,
|
||||
"skip_index": True,
|
||||
}
|
||||
assert session.session_id == "new_session"
|
||||
assert session.active_stream_id == "live-stream"
|
||||
assert session.pending_user_message == "current prompt"
|
||||
assert session.pending_attachments == [{"name": "file.txt"}]
|
||||
assert session.pending_started_at == 123.0
|
||||
Reference in New Issue
Block a user