108 lines
3.8 KiB
Python
108 lines
3.8 KiB
Python
from __future__ import annotations
|
|
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock
|
|
|
|
from opc.core.models import SessionMessageRecord, SessionRecord, Task, TaskResult, TaskStatus
|
|
from opc.engine import OPCEngine
|
|
from opc.layer5_memory.memory_manager import MemoryManager
|
|
|
|
|
|
class SharedRoleSessionIdTests(unittest.TestCase):
|
|
def test_final_decider_reuses_root_session(self) -> None:
|
|
self.assertEqual(
|
|
OPCEngine._shared_company_role_session_id(
|
|
"app14",
|
|
"ceo",
|
|
final_decider_role_id="ceo",
|
|
),
|
|
"app14",
|
|
)
|
|
|
|
def test_same_role_reuses_same_session_id(self) -> None:
|
|
first = OPCEngine._shared_company_role_session_id("app14", "cmo")
|
|
second = OPCEngine._shared_company_role_session_id("app14", "cmo")
|
|
self.assertEqual(first, "app14:role:cmo")
|
|
self.assertEqual(first, second)
|
|
|
|
|
|
class _MemoryStoreStub:
|
|
def __init__(self) -> None:
|
|
self.sessions: dict[str, SessionRecord] = {}
|
|
self.session_messages: list[SessionMessageRecord] = []
|
|
|
|
async def get_session(self, session_id: str) -> SessionRecord | None:
|
|
return self.sessions.get(session_id)
|
|
|
|
async def save_session(self, session: SessionRecord) -> None:
|
|
self.sessions[session.session_id] = session
|
|
|
|
async def save_session_link(self, _link: object) -> None:
|
|
return None
|
|
|
|
async def save_session_message(self, message: SessionMessageRecord) -> None:
|
|
self.session_messages.append(message)
|
|
|
|
async def save_session_part(self, _part: object) -> None:
|
|
return None
|
|
|
|
|
|
class MemoryManagerCompactionTests(unittest.IsolatedAsyncioTestCase):
|
|
async def test_append_session_message_does_not_trigger_history_compaction(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
memory = MemoryManager(Path(tmpdir), project_id="proj", store=_MemoryStoreStub())
|
|
compactor = SimpleNamespace(maybe_compact_after_message=AsyncMock())
|
|
memory.set_history_compactor(compactor)
|
|
|
|
await memory.append_session_message(
|
|
"sess-1",
|
|
"assistant",
|
|
text="hello",
|
|
project_id="proj",
|
|
metadata={"role_id": "cmo"},
|
|
)
|
|
|
|
compactor.maybe_compact_after_message.assert_not_awaited()
|
|
|
|
|
|
class SharedRoleSessionExecutionTests(unittest.IsolatedAsyncioTestCase):
|
|
async def test_company_shared_role_session_keeps_results_local(self) -> None:
|
|
engine = OPCEngine()
|
|
engine.store = SimpleNamespace(
|
|
get_task=AsyncMock(return_value=None),
|
|
save_task=AsyncMock(),
|
|
)
|
|
engine.memory = SimpleNamespace(
|
|
record_assistant_turn=AsyncMock(),
|
|
record_child_session_result=AsyncMock(),
|
|
record_task_completion_async=AsyncMock(),
|
|
)
|
|
engine._run_task_once = AsyncMock(
|
|
return_value=TaskResult(status=TaskStatus.DONE, content="done", artifacts={})
|
|
)
|
|
engine._apply_runtime_state_to_task = lambda task, result: None
|
|
|
|
task = Task(
|
|
id="task-cmo-review",
|
|
title="Review Turn: cmo",
|
|
assigned_to="cmo",
|
|
status=TaskStatus.PENDING,
|
|
project_id="new16",
|
|
session_id="app14:role:cmo",
|
|
parent_session_id="app14",
|
|
metadata={
|
|
"shared_role_session": True,
|
|
"execution_mode": "company_mode",
|
|
"work_item_projection_id": "review::demo",
|
|
"employee_assignment": {"employee_id": "emp-cmo", "role_id": "cmo"},
|
|
},
|
|
)
|
|
|
|
await engine._execute_task(task)
|
|
|
|
engine.memory.record_assistant_turn.assert_awaited_once()
|
|
engine.memory.record_child_session_result.assert_not_awaited()
|