from __future__ import annotations import json from app.core.agent.intent_router import ConversationState, IntentRouterV2 from app.core.agent.intent_router.docs_mvp.llm_classifier import DocsMvpLlmClassifier from app.core.agent.intent_router.intent.classifier import IntentClassifierV2 from tests.unit_tests.rag.intent_router_testkit import repo_context class FakeLlm: def __init__(self, response: str, *, fail: bool = False) -> None: self.response = response self.fail = fail self.calls: list[tuple[str, str]] = [] def generate(self, prompt_name: str, user_input: str, *, log_context: str | None = None) -> str: self.calls.append((prompt_name, user_input)) if self.fail: raise RuntimeError("llm unavailable") return self.response def _router(llm: FakeLlm) -> IntentRouterV2: return IntentRouterV2( classifier=IntentClassifierV2(), docs_llm_classifier=DocsMvpLlmClassifier(llm), enable_llm_disambiguation=True, ) def test_docs_technical_query_keeps_deterministic_routing_without_llm_call() -> None: llm = FakeLlm("{}") result = _router(llm).route("Объясни endpoint /health", ConversationState(), repo_context()) assert result.docs_routing is not None assert result.docs_routing.sub_intent == "API_METHOD_EXPLAIN" assert result.docs_routing.routing_mode == "deterministic" assert result.llm_router_used is False assert llm.calls == [] def test_ambiguous_query_can_be_resolved_by_llm() -> None: llm = FakeLlm( json.dumps( { "intent": "DOCS_DISCOVERY", "sub_intent": "FIND_DOCUMENTS_BY_DOMAIN", "confidence": 0.83, "anchors": {"entity_name": "health", "doc_query": "документация по health"}, "scope": {"level": "domain"}, "reason_short": "health here is a docs topic", }, ensure_ascii=False, ) ) result = _router(llm).route("документация по health", ConversationState(), repo_context()) assert result.docs_routing is not None assert result.docs_routing.routing_mode == "llm_assisted" assert result.docs_routing.sub_intent == "FIND_DOCUMENTS_BY_DOMAIN" assert result.retrieval_plan is not None assert result.retrieval_plan.plan_id == "docs_find_documents_by_domain_v1" assert result.llm_router_used is True def test_ambiguous_query_falls_back_to_general_docs_when_llm_fails() -> None: llm = FakeLlm("{}", fail=True) result = _router(llm).route("health документация", ConversationState(), repo_context()) assert result.docs_routing is not None assert result.docs_routing.routing_mode == "llm_fallback" assert result.docs_routing.sub_intent == "GENERAL_DOCS_QA" assert result.retrieval_plan is not None assert result.retrieval_plan.plan_id == "docs_general_docs_qa_v1" def test_llm_classifier_rejects_unknown_labels() -> None: llm = FakeLlm(json.dumps({"intent": "DOCS_QA", "sub_intent": "MADE_UP"})) classifier = DocsMvpLlmClassifier(llm) assert classifier.classify({"query": "test"}) is None