diff --git a/src/openjarvis/server/agent_manager_routes.py b/src/openjarvis/server/agent_manager_routes.py index cd6061a3..21936445 100644 --- a/src/openjarvis/server/agent_manager_routes.py +++ b/src/openjarvis/server/agent_manager_routes.py @@ -111,24 +111,35 @@ def _make_lightweight_system( model: str, config: Any = None, ) -> _LightweightSystem: - """Build a minimal system with a plain OllamaEngine. + """Build a minimal system with a fresh inference engine. The server's ``app.state.engine`` is heavily wrapped (MultiEngine -> InstrumentedEngine -> GuardrailsEngine) and can - return empty content from background threads. Create a fresh - OllamaEngine directly (no health checks or model discovery that - could interfere with in-flight Ollama requests). + return empty content from background threads. Create a fresh + engine directly (no health checks or model discovery that + could interfere with in-flight requests). """ try: - from openjarvis.engine.ollama import OllamaEngine + from openjarvis.engine._discovery import get_engine cfg = config if cfg is None: from openjarvis.core.config import load_config cfg = load_config() - host = cfg.engine.ollama.host if cfg else "" - plain_engine = OllamaEngine(host=host) if host else OllamaEngine() + + pref = cfg.intelligence.preferred_engine + key = pref or cfg.engine.default + resolved = get_engine(cfg, key) + + if resolved is not None: + plain_engine = resolved[1] + else: + from openjarvis.engine.ollama import OllamaEngine + + host = cfg.engine.ollama.host if cfg else "" + plain_engine = OllamaEngine(host=host) if host else OllamaEngine() + # Wrap with InstrumentedEngine so agent ticks are recorded # in telemetry (FLOPs, energy, cost savings). try: @@ -842,9 +853,7 @@ async def _stream_managed_agent( app_config = load_config() - final_system_prompt = _build_managed_system_prompt( - system_prompt or "", app_config - ) + final_system_prompt = _build_managed_system_prompt(system_prompt or "", app_config) if final_system_prompt and final_system_prompt.strip(): llm_messages.append( diff --git a/tests/server/test_agent_manager_routes.py b/tests/server/test_agent_manager_routes.py index 738144db..4902563c 100644 --- a/tests/server/test_agent_manager_routes.py +++ b/tests/server/test_agent_manager_routes.py @@ -5,6 +5,7 @@ from __future__ import annotations import json import tempfile from pathlib import Path +from types import SimpleNamespace from unittest.mock import MagicMock import pytest @@ -488,3 +489,51 @@ class TestResolveToolSpecs: assert _resolve_tool_specs(None) == [] assert _resolve_tool_specs([]) == [] + + +class TestLightweightSystemEngineResolution: + """Regression for #477 / #514: the managed-agent lightweight system must + resolve the user's *configured* engine (preferred_engine, else + engine.default), not a hardcoded OllamaEngine. We assert the key passed to + ``get_engine`` — captured before the system is built — rather than the final + (telemetry-wrapped) engine object. + """ + + @staticmethod + def _cfg(preferred, default): + # context_from_memory absent on .agent -> memory backend resolves to None + return SimpleNamespace( + intelligence=SimpleNamespace(preferred_engine=preferred), + engine=SimpleNamespace(default=default, ollama=SimpleNamespace(host="")), + agent=SimpleNamespace(), + ) + + def _capture_get_engine(self, monkeypatch): + captured = {} + + def fake_get_engine(cfg, key): + captured["key"] = key + return ("resolved", MagicMock()) + + monkeypatch.setattr("openjarvis.engine._discovery.get_engine", fake_get_engine) + return captured + + def test_resolves_preferred_engine_over_default(self, monkeypatch): + pytest.importorskip("fastapi") + from openjarvis.server import agent_manager_routes as amr + + captured = self._capture_get_engine(monkeypatch) + amr._make_lightweight_system( + engine=MagicMock(), model="m", config=self._cfg("vllm", "ollama") + ) + assert captured["key"] == "vllm" + + def test_falls_back_to_engine_default_without_preference(self, monkeypatch): + pytest.importorskip("fastapi") + from openjarvis.server import agent_manager_routes as amr + + captured = self._capture_get_engine(monkeypatch) + amr._make_lightweight_system( + engine=MagicMock(), model="m", config=self._cfg(None, "llamacpp") + ) + assert captured["key"] == "llamacpp"