Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,11 @@ async def _get_or_load_event_service(
event_services = self._event_services
if event_services is None:
raise ValueError("inactive_service")
event_service = event_services.get(conversation_id)
if event_service is not None and event_service.is_open():
# Cached runtimes do not need lifecycle serialization or disk I/O.
event_service.touch()
return event_service
if (
conversation_id not in event_services
and conversation_id not in self._conversation_records
Expand Down
20 changes: 20 additions & 0 deletions tests/agent_server/test_conversation_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,26 @@ def sample_stored_conversation():
)


@pytest.mark.asyncio
async def test_cached_event_service_bypasses_lifecycle_lock(tmp_path):
service = ConversationService(conversations_dir=tmp_path / "conversations")
conversation_id = uuid4()
event_service = MagicMock(spec=EventService)
event_service.is_open.return_value = True
service._event_services = {conversation_id: event_service}

await service._lifecycle_lock.acquire()
try:
result = await asyncio.wait_for(
service.get_event_service(conversation_id), timeout=0.1
)
finally:
service._lifecycle_lock.release()

assert result is event_service
event_service.touch.assert_called_once_with()


@pytest.mark.asyncio
async def test_meta_json_has_no_agent_and_reload_uses_base_state(tmp_path):
"""End-to-end single-source-of-truth guarantee.
Expand Down