mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-07-12 12:37:32 +00:00
fix(stabilization): harden attachment lifecycle and agent guard signals (#5420)
* fix: harden stabilization attachment and agent guards * fix(uploads): preserve durable references during cleanup * fix(uploads): close cleanup and compaction races
This commit is contained in:
@@ -1,13 +1,9 @@
|
||||
"""A plain text message that merely *looks* like a JSON array of objects must
|
||||
NOT be silently re-parsed into a list on reload.
|
||||
"""Persistence contracts for JSON-like text and multimodal chat content.
|
||||
|
||||
_parse_msg_content de-serializes multimodal (image/audio) content back into a
|
||||
list of content blocks. The old heuristic accepted ANY string that started
|
||||
with "[{" and contained the substring '"type"'. A user who pasted an API
|
||||
schema / sample such as `[{"type": "object", "name": "foo"}]` therefore had
|
||||
their text message permanently corrupted into a Python list on the next
|
||||
session hydration. The fix restricts the round-trip to lists whose elements
|
||||
are all recognized content-block types (text/image_url/audio/...).
|
||||
Plain text that resembles a JSON content-block list must remain an exact
|
||||
string. Real provider multimodal blocks follow the durable attachment
|
||||
contract: readable text plus stable attachment metadata is persisted, while
|
||||
raw inline media bytes are omitted.
|
||||
"""
|
||||
import tempfile
|
||||
import uuid
|
||||
@@ -65,16 +61,48 @@ def test_jsonlike_user_string_not_corrupted(manager):
|
||||
assert reloaded.history[0].content == text
|
||||
|
||||
|
||||
def test_real_multimodal_content_still_round_trips(manager):
|
||||
def test_real_multimodal_content_persists_reference_without_base64(manager):
|
||||
sid = "sess-" + uuid.uuid4().hex[:8]
|
||||
_make_session(sid)
|
||||
attachment_id = "a" * 32 + ".png"
|
||||
multimodal = [
|
||||
{"type": "text", "text": "what is this?"},
|
||||
{"type": "image_url", "image_url": {"url": "data:image/png;base64,AAAA"}},
|
||||
]
|
||||
msgs = [ChatMessage(role="user", content=multimodal)]
|
||||
metadata = {
|
||||
"attachments": [
|
||||
{
|
||||
"id": attachment_id,
|
||||
"name": "diagram.png",
|
||||
"mime": "image/png",
|
||||
"size": 4,
|
||||
"checksum_sha256": "sha256-digest",
|
||||
}
|
||||
]
|
||||
}
|
||||
msgs = [ChatMessage(role="user", content=multimodal, metadata=metadata)]
|
||||
assert manager.replace_messages(sid, msgs) is True
|
||||
|
||||
expected = (
|
||||
"what is this?\n"
|
||||
"[1 inline media payload omitted]\n"
|
||||
f"[Attachment: diagram.png | id={attachment_id} | mime=image/png | "
|
||||
"size=4 bytes | sha256=sha256-digest]"
|
||||
)
|
||||
|
||||
db = _TS()
|
||||
try:
|
||||
stored = db.query(cdb.ChatMessage).filter_by(session_id=sid).one()
|
||||
assert stored.content == expected
|
||||
assert "what is this?" in stored.content
|
||||
assert attachment_id in stored.content
|
||||
assert "data:image/png;base64,AAAA" not in stored.content
|
||||
assert "base64" not in stored.content
|
||||
assert "AAAA" not in stored.content
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
manager.sessions.clear()
|
||||
reloaded = manager.get_session(sid)
|
||||
assert reloaded.history[0].content == multimodal
|
||||
assert reloaded.history[0].content == expected
|
||||
assert reloaded.history[0].metadata["attachments"][0]["id"] == attachment_id
|
||||
|
||||
Reference in New Issue
Block a user