mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-26 13:16:24 +00:00
* fix(memory): exclude hidden framework messages from Honcho writes * fix(memory): isolate hidden-message hook failures
127 lines
4.1 KiB
Python
127 lines
4.1 KiB
Python
"""Regression anchors: Honcho async methods must not block the loop."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import pytest
|
|
from langchain_core.messages import AIMessage, HumanMessage
|
|
|
|
from deerflow.agents.memory.backends.honcho.honcho_manager import HonchoMemoryManager
|
|
|
|
|
|
class _BlockingFakeClient:
|
|
"""Every method does real blocking file IO — trips Blockbuster if run on the loop."""
|
|
|
|
def __init__(self, probe_dir: Path):
|
|
self._probe = probe_dir / "probe.txt"
|
|
|
|
def _block(self) -> None:
|
|
self._probe.write_text("blocked io")
|
|
|
|
def get_or_create_peer(self, ws: str, pid: str) -> None:
|
|
self._block()
|
|
|
|
def get_or_create_session(self, ws: str, sid: str) -> None:
|
|
self._block()
|
|
|
|
def set_session_peers(self, ws: str, sid: str, pids: list[str]) -> None:
|
|
self._block()
|
|
|
|
def add_messages(self, ws: str, sid: str, msgs: list[Any]) -> None:
|
|
self._block()
|
|
|
|
def working_representation(self, ws: str, pid: str, *, max_conclusions: int = 25) -> str:
|
|
self._block()
|
|
return "rep"
|
|
|
|
def search(self, ws: str, query: str, *, limit: int = 5, filters: dict[str, Any] | None = None) -> list[Any]:
|
|
self._block()
|
|
return []
|
|
|
|
def close(self) -> None:
|
|
pass
|
|
|
|
|
|
def _manager(tmp_path: Path, **host_hooks: Any) -> HonchoMemoryManager:
|
|
mgr = HonchoMemoryManager.from_config({"base_url": "http://honcho.test"}, **host_hooks)
|
|
mgr._client = _BlockingFakeClient(tmp_path)
|
|
return mgr
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_honcho_operations_do_not_block_event_loop(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
manager = _manager(tmp_path)
|
|
messages: list[Any] = [
|
|
HumanMessage("hello", id="h1"),
|
|
AIMessage("hi", id="a1"),
|
|
]
|
|
|
|
await manager.aadd(
|
|
"thread-1",
|
|
messages,
|
|
user_id="alice",
|
|
)
|
|
assert await manager.aget_context("alice") is not None
|
|
assert (
|
|
await manager.asearch(
|
|
"query",
|
|
user_id="alice",
|
|
)
|
|
== []
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.parametrize("keep_hidden", [False, True])
|
|
async def test_hidden_message_hook_runs_off_the_event_loop(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, keep_hidden: bool) -> None:
|
|
probe = tmp_path / "hidden-filter.txt"
|
|
seen = []
|
|
outgoing = []
|
|
|
|
def should_keep(metadata: dict[str, Any]) -> bool:
|
|
probe.write_text("called", encoding="utf-8")
|
|
seen.append(metadata)
|
|
return keep_hidden
|
|
|
|
manager = _manager(tmp_path, should_keep_hidden_message=should_keep)
|
|
|
|
def add_messages(workspace: str, session_id: str, messages: list[Any]) -> None:
|
|
manager._client._block()
|
|
outgoing.extend(messages)
|
|
|
|
monkeypatch.setattr(manager._client, "add_messages", add_messages)
|
|
metadata = {"hide_from_ui": True}
|
|
hidden = HumanMessage("hidden context", additional_kwargs=metadata)
|
|
await manager.aadd("thread-1", [hidden, AIMessage("reply")], user_id="alice")
|
|
|
|
assert seen == [metadata]
|
|
assert probe.read_text(encoding="utf-8") == "called"
|
|
assert [item["content"] for item in outgoing] == (["hidden context", "reply"] if keep_hidden else ["reply"])
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_hidden_hook_failure_keeps_visible_writes_off_event_loop(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
probe = tmp_path / "failed-hidden-filter.txt"
|
|
outgoing = []
|
|
|
|
def keep(metadata: dict[str, Any]) -> bool:
|
|
probe.write_text("called", encoding="utf-8")
|
|
raise RuntimeError("private-filter-error")
|
|
|
|
manager = _manager(tmp_path, should_keep_hidden_message=keep)
|
|
|
|
def add_messages(workspace: str, session_id: str, messages: list[Any]) -> None:
|
|
manager._client._block()
|
|
outgoing.extend(messages)
|
|
|
|
monkeypatch.setattr(manager._client, "add_messages", add_messages)
|
|
messages = [HumanMessage("before"), HumanMessage("hidden", additional_kwargs={"hide_from_ui": True}), HumanMessage("after"), AIMessage("reply")]
|
|
await manager.aadd("thread-1", messages, user_id="alice")
|
|
|
|
assert probe.read_text(encoding="utf-8") == "called"
|
|
assert [item["content"] for item in outgoing] == ["before", "after", "reply"]
|