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>
252 lines
8.5 KiB
Python
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"]) == {}
|