diff --git a/backend/tests/test_thread_memory_updater.py b/backend/tests/test_thread_memory_updater.py index 7499ccc3..3525241a 100644 --- a/backend/tests/test_thread_memory_updater.py +++ b/backend/tests/test_thread_memory_updater.py @@ -1,3 +1,5 @@ +from unittest.mock import patch + from deerflow.agents.memory.thread_updater import ThreadMemoryUpdater @@ -18,3 +20,65 @@ def test_scrub_sensitive_tolerates_non_numeric_confidence(): assert len(cleaned["facts"]) == 2 assert cleaned["facts"][0]["confidence"] == 0.5 assert cleaned["facts"][1]["confidence"] == 0.5 + + +def test_update_memory_repairs_model_json_with_unescaped_inner_quotes(): + class _Storage: + def __init__(self): + self.saved = None + + def load(self, _thread_id): + return None + + def save(self, _thread_id, data, expected_version=None): + self.saved = { + "thread_id": _thread_id, + "data": data, + "expected_version": expected_version, + } + return True + + fake_storage = _Storage() + fake_model = type( + "M", + (), + { + "invoke": lambda self, prompt: type( + "R", + (), + { + "content": """ +{ + "user": { + "topOfMind": { + "summary": "反感“作为 AI"这种句式,认为回答不用寒暄直接说重点。", + "updatedAt": "2026-06-11T07:13:11Z" + } + }, + "history": {}, + "facts": [ + { + "content": "偏好直接回答,不喜欢“作为 AI"式开场", + "category": "preference", + "confidence": 0.92 + } + ] +} +""".strip() + }, + )() + }, + )() + messages = [type("Msg", (), {"type": "human", "content": "请直接回答重点,不要寒暄。"})()] + + with ( + patch("deerflow.agents.memory.thread_updater.get_thread_memory_storage", return_value=fake_storage), + patch.object(ThreadMemoryUpdater, "_get_model", return_value=fake_model), + ): + result = ThreadMemoryUpdater().update_memory(messages, "thread-test") + + assert result is True + assert fake_storage.saved is not None + assert fake_storage.saved["expected_version"] == 0 + assert fake_storage.saved["data"]["user"]["topOfMind"]["summary"].startswith("反感“作为 AI") + assert fake_storage.saved["data"]["facts"][0]["content"].startswith("偏好直接回答")