mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
80 lines
3.0 KiB
Python
80 lines
3.0 KiB
Python
"""``_institutional_memory``'s RAG search is bounded by
|
|||
|
|
``institutional_memory_timeout_seconds`` — a saturated Ollama embedder must
|
||
|
|
never eat the whole verb timeout budget and 504 a claim. See
|
||
|
|
``roboco.services.gateway.choreographer._impl.Choreographer._institutional_memory``.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import asyncio
|
||
|
|
from unittest.mock import AsyncMock, MagicMock
|
||
|
|
from uuid import uuid4
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from roboco.config import Settings
|
||
|
|
from roboco.services.gateway.choreographer import Choreographer
|
||
|
|
|
||
|
|
_DEFAULT_TIMEOUT = 8.0
|
||
|
|
_OVERRIDE_TIMEOUT = 3.5
|
||
|
|
|
||
|
|
|
||
|
|
def _choreographer(*, similar_memory: AsyncMock) -> Choreographer:
|
||
|
|
repo = AsyncMock()
|
||
|
|
repo.similar_memory = similar_memory
|
||
|
|
task_svc = AsyncMock()
|
||
|
|
task_svc.agent_for.return_value = MagicMock(role="developer")
|
||
|
|
choreo = object.__new__(Choreographer)
|
||
|
|
choreo._deps = MagicMock(evidence_repo=repo, task=task_svc)
|
||
|
|
return choreo
|
||
|
|
|
||
|
|
|
||
|
|
def _task() -> MagicMock:
|
||
|
|
return MagicMock(
|
||
|
|
id=uuid4(), title="Add retry backoff", task_type=MagicMock(value="code")
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class TestInstitutionalMemoryTimeout:
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_slow_search_times_out_without_raising(
|
||
|
|
self, monkeypatch: pytest.MonkeyPatch
|
||
|
|
) -> None:
|
||
|
|
monkeypatch.setattr("roboco.config.settings.org_memory_enabled", True)
|
||
|
|
monkeypatch.setattr(
|
||
|
|
"roboco.config.settings.institutional_memory_timeout_seconds", 0.01
|
||
|
|
)
|
||
|
|
|
||
|
|
async def _slow(**_kwargs: object) -> dict[str, object]:
|
||
|
|
await asyncio.sleep(5)
|
||
|
|
return {"items": [], "status": "ok"}
|
||
|
|
|
||
|
|
choreo = _choreographer(similar_memory=AsyncMock(side_effect=_slow))
|
||
|
|
result = await choreo._institutional_memory(uuid4(), _task())
|
||
|
|
assert result == {"status": "timeout", "lessons": []}
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_fast_search_flows_through_unchanged(
|
||
|
|
self, monkeypatch: pytest.MonkeyPatch
|
||
|
|
) -> None:
|
||
|
|
monkeypatch.setattr("roboco.config.settings.org_memory_enabled", True)
|
||
|
|
monkeypatch.setattr(
|
||
|
|
"roboco.config.settings.institutional_memory_timeout_seconds", 8.0
|
||
|
|
)
|
||
|
|
lesson = {"kind": "learning", "summary": "s", "source": "src", "score": 0.9}
|
||
|
|
similar_memory = AsyncMock(return_value={"items": [lesson], "status": "ok"})
|
||
|
|
choreo = _choreographer(similar_memory=similar_memory)
|
||
|
|
result = await choreo._institutional_memory(uuid4(), _task())
|
||
|
|
assert result == {"status": "ok", "lessons": [lesson]}
|
||
|
|
|
||
|
|
|
||
|
|
class TestInstitutionalMemoryTimeoutConfig:
|
||
|
|
def test_default_is_eight_seconds(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
||
|
|
monkeypatch.delenv("ROBOCO_INSTITUTIONAL_MEMORY_TIMEOUT_SECONDS", raising=False)
|
||
|
|
assert Settings().institutional_memory_timeout_seconds == _DEFAULT_TIMEOUT
|
||
|
|
|
||
|
|
def test_reads_env_override(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
||
|
|
monkeypatch.setenv(
|
||
|
|
"ROBOCO_INSTITUTIONAL_MEMORY_TIMEOUT_SECONDS", str(_OVERRIDE_TIMEOUT)
|
||
|
|
)
|
||
|
|
assert Settings().institutional_memory_timeout_seconds == _OVERRIDE_TIMEOUT
|