Initial commit
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user