Files
OpenOPC/tests/test_runtime_v2_durable_memory.py
T
2026-07-01 17:56:31 +08:00

109 lines
4.0 KiB
Python

from __future__ import annotations
import contextlib
import json
import shutil
import unittest
import uuid
from pathlib import Path
from opc.core.config import OPCConfig
from opc.core.models import Task, TaskStatus
from opc.database.store import OPCStore
from opc.layer3_agent.runtime_v2.runtime import NativeRuntimeV2
from opc.layer4_tools.registry import ToolRegistry
from opc.layer5_memory.memory_manager import MemoryManager
@contextlib.contextmanager
def _workspace_tempdir() -> Path:
base = Path.cwd() / ".tmp-test" / f"durable-memory-{uuid.uuid4().hex}"
base.mkdir(parents=True, exist_ok=True)
try:
yield base
finally:
shutil.rmtree(base, ignore_errors=True)
class _StubExtractionLLM:
def __init__(self) -> None:
self.config = type("Cfg", (), {"max_tokens": 4096})()
def prepare_user_message_content(self, content: str, attachment_refs=None):
_ = attachment_refs
return content
def get_tool_definitions(self, tools):
return tools
def is_context_overflow_error(self, error: Exception) -> bool:
_ = error
return False
async def chat_stream(self, messages, tools=None):
_ = (messages, tools)
yield type("Evt", (), {"event_type": "message_start", "payload": {}, "model": "stub"})()
yield type("Evt", (), {"event_type": "assistant_delta", "payload": {"text": "Repository convention captured."}, "model": "stub"})()
yield type("Evt", (), {"event_type": "message_stop", "payload": {"finish_reason": "stop"}, "model": "stub"})()
async def simple_chat(self, prompt: str, system: str | None = None, task_type: str | None = None) -> str:
_ = (prompt, task_type)
if system and "extracting durable reusable memory" in system:
return json.dumps(
{
"project_memory": [
{
"title": "Search Conventions",
"content": "- Prefer rg for repo-wide search before editing files.",
}
],
"global_memory": [],
"reason": "Stable repo runtime preference.",
},
ensure_ascii=False,
)
raise AssertionError(f"Unexpected system prompt: {system}")
class RuntimeDurableMemoryExtractionTests(unittest.IsolatedAsyncioTestCase):
async def test_runtime_does_not_auto_extract_durable_memory_on_completed_turn(self) -> None:
with _workspace_tempdir() as root:
store = OPCStore(root / "tasks.db")
await store.initialize()
memory = MemoryManager(root, "proj1", store=store)
config = OPCConfig()
config.system.native_runtime.durable_memory_extract_min_messages = 2
runtime = NativeRuntimeV2(
llm=_StubExtractionLLM(),
tool_registry=ToolRegistry(),
memory_manager=memory,
config=config,
)
task = Task(
id="task-durable",
title="Capture convention",
description="Inspect the repository and remember reusable conventions.",
session_id="sess-durable",
project_id="proj1",
assigned_to="executor",
)
result = await runtime.run(
system_prompt="You are a test runtime.",
user_message="Please remember that we prefer rg for search.",
task=task,
)
self.assertEqual(result.status, TaskStatus.DONE)
self.assertNotIn("durable_memory_extraction", result.artifacts)
project_memory = memory.load_project_memory_markdown("proj1")
self.assertEqual(project_memory, "")
session = await store.get_session("sess-durable")
self.assertFalse(str(session.metadata.get("durable_memory_cursor_message_id", "")).strip())
await store.close()
if __name__ == "__main__":
unittest.main()