codex-agent-3 / tests /test_router.py
m5ike's picture
upd
0f9caed
Raw
History Blame Contribute Delete
4.86 kB
"""Testy hybrid routingu — AI klasifikace + fallbacky."""
import pytest
@pytest.fixture()
def hybrid(app_module):
"""App v hybrid módu s 'ready' enginem (bez reálného subprocessu)."""
import engine as engine_module
app_module.SETTINGS.update({"agent_mode": "hybrid", "router_mode": "ai"})
app_module.ENGINE.state = engine_module.EngineState.READY
return app_module
def test_explicit_tags_override_ai(hybrid, monkeypatch):
# tagy mají absolutní přednost — AI router se vůbec nesmí volat
def _boom(messages):
raise AssertionError("router se nemá volat")
monkeypatch.setattr(hybrid, "_router_llm_call", _boom)
assert hybrid._route_task("[kimi] narhni schéma") == "api"
assert hybrid._route_task("@api udělej X") == "api"
assert hybrid._route_task("[local] oprav překlep") == "local"
assert hybrid._route_task("[qwen] oprav překlep") == "local"
def test_context_limit_overrides_ai(hybrid, monkeypatch):
monkeypatch.setattr(hybrid, "_router_llm_call",
lambda m: (_ for _ in ()).throw(AssertionError))
hybrid.SETTINGS.update({"local_context_limit": 100})
assert hybrid._route_task("x" * 200) == "api"
def test_ai_router_decides(hybrid, monkeypatch):
calls = []
def fake_llm(messages):
calls.append(messages)
return "API"
monkeypatch.setattr(hybrid, "_router_llm_call", fake_llm)
assert hybrid._route_task("oprav mi tenhle test") == "api"
# system prompt + user zpráva
assert calls[0][0]["role"] == "system"
assert "LOCAL or API" in calls[0][0]["content"]
monkeypatch.setattr(hybrid, "_router_llm_call", lambda m: "LOCAL")
assert hybrid._route_task("oprav mi tenhle test") == "local"
def test_ai_router_parses_verbose_answers(hybrid, monkeypatch):
monkeypatch.setattr(hybrid, "_router_llm_call",
lambda m: "Odpověď: LOCAL — jde o rutinní úpravu.")
assert hybrid._route_task("přejmenuj proměnnou") == "local"
# obě slova v odpovědi => rozhoduje první výskyt
monkeypatch.setattr(hybrid, "_router_llm_call",
lambda m: "API (not LOCAL) because of architecture")
assert hybrid._route_task("běžná otázka") == "api"
def test_ai_router_garbage_falls_back_to_keywords(hybrid, monkeypatch):
monkeypatch.setattr(hybrid, "_router_llm_call", lambda m: "žblepty 42")
assert hybrid._route_task("proveď komplexní analýza modulu") == "api" # keyword
assert hybrid._route_task("oprav překlep v readme") == "local"
def test_ai_router_exception_falls_back(hybrid, monkeypatch):
def _fail(messages):
raise RuntimeError("engine timeout")
monkeypatch.setattr(hybrid, "_router_llm_call", _fail)
assert hybrid._route_task("navrhni architektura systému") == "api" # keyword
assert hybrid._route_task("vypiš soubory") == "local"
def test_engine_not_ready_falls_back(hybrid, monkeypatch):
import engine as engine_module
hybrid.ENGINE.state = engine_module.EngineState.STOPPED
monkeypatch.setattr(hybrid, "_router_llm_call",
lambda m: (_ for _ in ()).throw(AssertionError))
assert hybrid._route_task("root cause tohoto pádu") == "api" # keyword
assert hybrid._route_task("malý fix") == "local"
def test_keywords_mode_skips_ai(hybrid, monkeypatch):
hybrid.SETTINGS.update({"router_mode": "keywords"})
monkeypatch.setattr(hybrid, "_router_llm_call",
lambda m: (_ for _ in ()).throw(AssertionError))
assert hybrid._route_task("security audit") == "api"
assert hybrid._route_task("napiš unit test") == "local"
def test_router_mode_validation(settings_module):
mgr = settings_module.SettingsManager()
assert mgr.get().router_mode == "ai" # default
applied, reload_needed, _ = mgr.update({"router_mode": "keywords"})
assert applied == {"router_mode": "keywords"} and reload_needed is False
# case-insensitive normalizace
applied, _, errors = mgr.update({"router_mode": "AI"})
assert applied == {"router_mode": "ai"} and errors == {}
_, _, errors = mgr.update({"router_mode": "nesmysl"})
assert "router_mode" in errors
def test_new_default_model_and_presets(app_module):
from presets import PRESETS, infer_parsers
assert app_module.SETTINGS.get().model == "Qwen/Qwen3-Coder-Next-FP8"
assert "qwen3-coder-next-fp8" in PRESETS
assert "minimax-m2.7" in PRESETS
assert "minimax-m2" not in PRESETS
# parser rodiny: Coder-Next dědí qwen3_coder, Devstral -> mistral
assert infer_parsers("Qwen/Qwen3-Coder-Next-FP8") == ("qwen3_coder", "")
assert infer_parsers("mistralai/Devstral-2-123B-Instruct-2512") == ("mistral", "")
assert infer_parsers("MiniMaxAI/MiniMax-M2.7") == ("minimax_m2", "minimax_m2_append_think")