Files
hermes-webui/tests/test_merge_append_only_normalization_cache.py
T
totalitarianandtotalitarian 963e1bae8a Decode state.db content sentinel in the WebUI message projection (#7492)
Decode the state.db content sentinel in the WebUI message projection so structured/multimodal content renders instead of a placeholder, with an out-of-band tuple identity that no scalar can forge and a per-call ContextVar memo for the canonical serialisation.

Co-authored-by: totalitarian <totalitarian@users.noreply.github.com>
2026-09-15 17:12:45 -07:00

252 lines
8.5 KiB
Python

from collections import defaultdict
import api.models as models
class _CountingList(list):
def __init__(self, *args, str_counter):
super().__init__(*args)
self._str_counter = str_counter
def __str__(self):
self._str_counter["count"] += 1
return super().__str__()
class _ExplodingList(list):
def __init__(self, *args, str_counter):
super().__init__(*args)
self._str_counter = str_counter
def __str__(self):
self._str_counter["count"] += 1
raise RuntimeError("content __str__ must not run")
def _count_content_normalizations(monkeypatch):
"""Count how often structured content is actually normalized.
These tests originally counted ``__str__`` on the content list, because the
key builders stringified content with ``str()``. Structured content is now
serialized canonically (type-namespaced) instead, so ``str()`` never runs on
a list and the old counter reads zero.
The property under test is unchanged -- expensive normalization of the same
content must happen a bounded number of times, not once per key -- so the
counter now observes the normalization helper itself and ignores the cheap
passthrough calls where content has already been reduced to a string.
"""
counts = {"count": 0}
original = models._canonical_structured_content
def _wrapped(content):
counts["count"] += 1
return original(content)
# Observe the serialisation itself, not the identity lookups: every key
# derivation asks for the identity, but a list must be serialised only once
# per merge call.
monkeypatch.setattr(models, "_canonical_structured_content", _wrapped)
return counts
def _install_key_wrappers(monkeypatch):
call_counts: dict[str, dict[int, int]] = {
"merge": defaultdict(int),
"dedup": defaultdict(int),
"content": defaultdict(int),
"visible": defaultdict(int),
}
def _install(name):
original = getattr(models, f"_session_message_{name}_key")
def _wrapped(message, *args, **kwargs):
if isinstance(message, dict):
call_counts[name][id(message)] += 1
return original(message, *args, **kwargs)
monkeypatch.setattr(models, f"_session_message_{name}_key", _wrapped)
for name in call_counts:
_install(name)
return call_counts
def test_merge_append_only_caches_canonical_keys_and_preserves_identity(monkeypatch):
"""Verify exact key-call fanout and output identity for shared row objects."""
str_calls = {"count": 0}
repeated_user_content = _CountingList(["hello", "world"], str_counter=str_calls)
repeated_assistant_content = _CountingList(["reply", "with", "spacing"], str_counter=str_calls)
repeated_user = {
"role": "user",
"content": repeated_user_content,
"timestamp": 1000,
}
repeated_assistant = {
"role": "assistant",
"content": repeated_assistant_content,
"timestamp": 1001,
}
sidecar_messages = [repeated_user, repeated_user, repeated_user, repeated_assistant]
state_messages = [repeated_assistant, repeated_user]
normalizations = _count_content_normalizations(monkeypatch)
call_counts = _install_key_wrappers(monkeypatch)
merged = models.merge_session_messages_append_only(sidecar_messages, state_messages)
assert [id(msg) for msg in merged] == [
id(repeated_user),
id(repeated_user),
id(repeated_user),
id(repeated_assistant),
]
assert merged == [repeated_user, repeated_user, repeated_user, repeated_assistant]
assert normalizations["count"] == 2
assert str_calls["count"] == 0
assert dict(call_counts["merge"]) == {
id(repeated_user): 1,
id(repeated_assistant): 1,
}
assert len(dict(call_counts["dedup"])) == 2
assert len(dict(call_counts["content"])) == 2
assert len(dict(call_counts["visible"])) == 2
assert set(dict(call_counts["dedup"]).values()) == {1}
# Source-aware reconciliation deliberately computes content/visible keys
# once for sidecar ownership and once for state.db ownership. Reusing one
# cached key here would reintroduce the literal-workspace-prefix collision.
assert set(dict(call_counts["content"]).values()) == {2}
assert set(dict(call_counts["visible"]).values()) == {2}
def test_merge_append_only_helpers_do_not_see_mutated_content(monkeypatch):
original_content = _CountingList(["streamed", "content"], str_counter={"count": 0})
sidecar_messages = [
{
"role": "user",
"content": original_content,
"timestamp": 1000,
}
]
state_messages = [
{
"role": "assistant",
"content": "ack",
"timestamp": 1001,
}
]
observed = {"helpers_invoked": False}
call_counts: dict[str, dict[int, int]] = {
"merge": defaultdict(int),
"dedup": defaultdict(int),
"content": defaultdict(int),
"visible": defaultdict(int),
}
def _install(name, *, assert_content_content=False):
original = getattr(models, f"_session_message_{name}_key")
def _wrapped(message, *args, **kwargs):
if isinstance(message, dict):
call_counts[name][id(message)] += 1
if assert_content_content and isinstance(message, dict):
# Verify the aliased sidecar dict still owns the original object
# during helper execution.
assert sidecar_messages[0]["content"] is original_content
observed["helpers_invoked"] = True
return original(message, *args, **kwargs)
monkeypatch.setattr(models, f"_session_message_{name}_key", _wrapped)
_install("merge")
_install("dedup")
_install("visible")
_install("content", assert_content_content=True)
models.merge_session_messages_append_only(sidecar_messages, state_messages)
assert observed["helpers_invoked"] is True
assert len(dict(call_counts["content"])) == 2
assert set(dict(call_counts["content"]).values()) == {1}
def test_merge_append_only_state_only_stable_id_does_not_stringify_content(monkeypatch):
"""State-only merge keeps stable-ID rows on the helper fast path."""
str_calls = {"count": 0}
message = {
"id": "stable-id",
"role": "assistant",
"content": _ExplodingList(["danger", "zone"], str_counter=str_calls),
"timestamp": 2000,
}
call_counts = _install_key_wrappers(monkeypatch)
merged = models.merge_session_messages_append_only([], [message])
assert merged == [message]
assert merged[0] is message
assert str_calls["count"] == 0
assert dict(call_counts["merge"]) == {}
assert dict(call_counts["dedup"]) == {
id(message): 1,
}
assert dict(call_counts["content"]) == {}
assert dict(call_counts["visible"]) == {}
def test_merge_append_only_distinct_equal_dicts_remain_distinct_inputs(monkeypatch):
"""Two equal dict rows still pass through as separate inputs."""
msg_1 = {
"role": "user",
"content": "same",
"timestamp": 1000,
}
msg_2 = {
"role": "user",
"content": "same",
"timestamp": 1000,
}
call_counts = _install_key_wrappers(monkeypatch)
merged = models.merge_session_messages_append_only([], [msg_1, msg_2])
assert merged == [msg_1]
assert merged[0] is msg_1
assert dict(call_counts["dedup"]) == {
id(msg_1): 1,
id(msg_2): 1,
}
assert dict(call_counts["merge"]) == {}
def test_merge_append_only_recomputes_after_mutation(monkeypatch):
"""Per-call key caches reset; mutation between calls changes work input."""
str_calls = {"count": 0}
state_message = {
"role": "assistant",
"content": _CountingList(["first"], str_counter=str_calls),
"timestamp": 3000,
}
normalizations = _count_content_normalizations(monkeypatch)
call_counts = _install_key_wrappers(monkeypatch)
first = models.merge_session_messages_append_only([], [state_message])
state_message["content"].append("second")
second = models.merge_session_messages_append_only([], [state_message])
assert first == [state_message]
assert second == [state_message]
assert first is not second
assert normalizations["count"] == 2
assert str_calls["count"] == 0
assert dict(call_counts["dedup"]) == {
id(state_message): 2,
}
assert dict(call_counts["merge"]) == {}
assert dict(call_counts["content"]) == {}
assert dict(call_counts["visible"]) == {}