Роутер работает нормально в process v2
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from app.modules.agent.intent_router_v2 import IntentRouterV2
|
||||
from app.modules.agent.runtime.docs_qa_pipeline import DocsQAPipelineRunner
|
||||
from app.core.agent.intent_router import IntentRouterV2
|
||||
from app.core.agent.runtime.docs_qa_pipeline import DocsQAPipelineRunner, DocsTaskPlanner
|
||||
from tests.docs_qa_eval.fixture_adapter import InMemoryDocsRetrievalAdapter
|
||||
from tests.unit_tests.rag.intent_router_testkit import repo_context
|
||||
|
||||
@@ -135,6 +135,52 @@ def test_openapi_partial_contract_returns_partial_mode() -> None:
|
||||
assert "/orders" in result.answer
|
||||
|
||||
|
||||
def test_docs_pipeline_accepts_precomputed_task_plan_without_rerouting() -> None:
|
||||
rows = [
|
||||
{
|
||||
"layer": "D1_DOCUMENT_CATALOG",
|
||||
"path": "docs/api/health.md",
|
||||
"title": "GET /health",
|
||||
"content": "/health returns runtime and component statuses.",
|
||||
"metadata": {"document_id": "api.health_endpoint", "type": "api_method", "endpoint": "/health"},
|
||||
},
|
||||
{
|
||||
"layer": "D2_FACT_INDEX",
|
||||
"path": "docs/api/health.md",
|
||||
"title": "api.health_endpoint:response",
|
||||
"content": "Returns health summary and component diagnostics.",
|
||||
"metadata": {"subject_id": "api.health_endpoint", "type": "api_method"},
|
||||
},
|
||||
]
|
||||
route_result = IntentRouterV2().route(
|
||||
"Объясни API метод /health",
|
||||
repo_context=repo_context(),
|
||||
)
|
||||
task_plan = DocsTaskPlanner().plan(
|
||||
"Объясни API метод /health",
|
||||
"docs-session",
|
||||
route_result=route_result,
|
||||
)
|
||||
|
||||
class FailingRouter:
|
||||
def route(self, *_args, **_kwargs):
|
||||
raise AssertionError("runner should use the precomputed task plan")
|
||||
|
||||
runner = DocsQAPipelineRunner(FailingRouter(), InMemoryDocsRetrievalAdapter(rows), repo_context=repo_context())
|
||||
|
||||
result = runner.run(
|
||||
"Объясни API метод /health",
|
||||
"docs-session",
|
||||
mode="pre_llm_only",
|
||||
task_plan=task_plan,
|
||||
)
|
||||
|
||||
assert result.router_result.intent == "DOCUMENTATION_EXPLAIN"
|
||||
assert result.router_result.query_plan.sub_intent == "API_METHOD_EXPLAIN"
|
||||
assert result.diagnostics.selected_primary_documents == ["api.health_endpoint"]
|
||||
assert result.diagnostics.gate_decision == "allow_exact"
|
||||
|
||||
|
||||
def test_pre_llm_mode_returns_diagnostic_only_without_answer_generation() -> None:
|
||||
rows = [
|
||||
{
|
||||
@@ -172,7 +218,7 @@ def test_pre_llm_mode_detects_path_anchor_candidates() -> None:
|
||||
|
||||
assert "/health" in result.diagnostics.query_anchor_candidates
|
||||
assert "/health" in result.diagnostics.resolved_anchor_candidates
|
||||
assert result.diagnostics.planned_layers == ["D2_FACT_INDEX", "D4_WORKFLOW_INDEX", "D1_DOCUMENT_CATALOG", "D0_DOC_CHUNKS"]
|
||||
assert result.diagnostics.planned_layers == ["D1_DOCUMENT_CATALOG", "D2_FACT_INDEX", "D0_DOC_CHUNKS", "D4_WORKFLOW_INDEX"]
|
||||
assert set(result.diagnostics.executed_layers) == {"D1_DOCUMENT_CATALOG", "D2_FACT_INDEX", "D4_WORKFLOW_INDEX", "D0_DOC_CHUNKS"}
|
||||
|
||||
|
||||
@@ -318,8 +364,311 @@ def test_openapi_request_fragment_uses_fragment_aware_gate() -> None:
|
||||
assert result.answer_mode in {"ready", "ready_partial"}
|
||||
assert result.answer
|
||||
assert "type: object" in result.answer
|
||||
assert "message:" in result.answer
|
||||
assert "chat_id:" in result.answer
|
||||
|
||||
|
||||
def test_api_method_explain_prefers_api_method_primary_doc() -> None:
|
||||
rows = [
|
||||
{
|
||||
"layer": "D1_DOCUMENT_CATALOG",
|
||||
"path": "docs/domain/runtime-health.md",
|
||||
"title": "Сущность runtime health",
|
||||
"content": "Runtime health describes overall service health.",
|
||||
"metadata": {"document_id": "domain.runtime_health", "type": "domain_entity"},
|
||||
},
|
||||
{
|
||||
"layer": "D1_DOCUMENT_CATALOG",
|
||||
"path": "docs/api/health.md",
|
||||
"title": "GET /health",
|
||||
"content": "/health returns runtime and component statuses.",
|
||||
"metadata": {"document_id": "api.health_endpoint", "type": "api_method", "endpoint": "/health"},
|
||||
},
|
||||
{
|
||||
"layer": "D5_RELATION_GRAPH",
|
||||
"path": "docs/domain/runtime-health.md",
|
||||
"title": "Runtime health links",
|
||||
"content": "runtime health used by health endpoint",
|
||||
"metadata": {"document_id": "domain.runtime_health", "target_doc_id": "api.health_endpoint"},
|
||||
},
|
||||
]
|
||||
runner = DocsQAPipelineRunner(IntentRouterV2(), InMemoryDocsRetrievalAdapter(rows), repo_context=repo_context())
|
||||
|
||||
result = runner.run("Что делает метод health?", "docs-session", mode="pre_llm_only")
|
||||
|
||||
assert result.router_result.query_plan.sub_intent == "API_METHOD_EXPLAIN"
|
||||
assert result.diagnostics.target_anchor in {"health", "/health"}
|
||||
assert result.diagnostics.api_method_match_found is True
|
||||
assert result.diagnostics.selected_primary_documents == ["api.health_endpoint"]
|
||||
assert "api.health_endpoint" in result.diagnostics.primary_doc_candidates
|
||||
assert result.diagnostics.evidence_gate_require_target_api_spec is True
|
||||
assert result.diagnostics.evidence_gate_target_api_spec_found is True
|
||||
assert result.answer_mode == "exact"
|
||||
assert result.diagnostics.gate_decision == "allow_exact"
|
||||
|
||||
|
||||
def test_api_method_explain_promotes_api_doc_via_links() -> None:
|
||||
rows = [
|
||||
{
|
||||
"layer": "D1_DOCUMENT_CATALOG",
|
||||
"path": "docs/domain/runtime-health.md",
|
||||
"title": "Сущность runtime health",
|
||||
"content": "Runtime health is the domain model for observability.",
|
||||
"metadata": {
|
||||
"document_id": "domain.runtime_health",
|
||||
"type": "domain_entity",
|
||||
"links": [{"target": "api.health_endpoint", "type": "used_by"}],
|
||||
},
|
||||
},
|
||||
{
|
||||
"layer": "D0_DOC_CHUNKS",
|
||||
"path": "docs/api/health.md",
|
||||
"title": "api.health_endpoint:Overview",
|
||||
"content": "Endpoint /health returns overall runtime status and component diagnostics.",
|
||||
"metadata": {"document_id": "api.health_endpoint", "type": "api_method", "endpoint": "/health"},
|
||||
},
|
||||
]
|
||||
runner = DocsQAPipelineRunner(IntentRouterV2(), InMemoryDocsRetrievalAdapter(rows), repo_context=repo_context())
|
||||
|
||||
result = runner.run("Как работает health endpoint?", "docs-session", mode="pre_llm_only")
|
||||
|
||||
assert result.diagnostics.promoted_via_links == ["api.health_endpoint"]
|
||||
assert result.diagnostics.selected_primary_documents == ["api.health_endpoint"]
|
||||
assert result.diagnostics.api_method_match_found is True
|
||||
|
||||
|
||||
def test_api_method_explain_rejects_cross_endpoint_primary_candidates() -> None:
|
||||
rows = [
|
||||
{
|
||||
"layer": "D1_DOCUMENT_CATALOG",
|
||||
"path": "docs/documentation/api/control-actions-endpoint.md",
|
||||
"title": "HTTP API /actions/{action}",
|
||||
"content": "Endpoint for controlling actions.",
|
||||
"metadata": {
|
||||
"document_id": "api.control_actions_endpoint",
|
||||
"type": "api_method",
|
||||
"endpoint": "/actions/{action}",
|
||||
},
|
||||
},
|
||||
{
|
||||
"layer": "D1_DOCUMENT_CATALOG",
|
||||
"path": "docs/documentation/api/health-endpoint.md",
|
||||
"title": "HTTP API /health",
|
||||
"content": "Health endpoint returns runtime health and component diagnostics.",
|
||||
"metadata": {
|
||||
"document_id": "api.health_endpoint",
|
||||
"type": "api_method",
|
||||
"endpoint": "/health",
|
||||
},
|
||||
},
|
||||
{
|
||||
"layer": "D0_DOC_CHUNKS",
|
||||
"path": "docs/documentation/api/actions-endpoint.md",
|
||||
"title": "api.control_actions_endpoint:Scenario",
|
||||
"content": "The /actions/{action} endpoint triggers runtime actions.",
|
||||
"metadata": {
|
||||
"document_id": "api.control_actions_endpoint",
|
||||
"type": "api_method",
|
||||
"endpoint": "/actions/{action}",
|
||||
},
|
||||
},
|
||||
]
|
||||
runner = DocsQAPipelineRunner(IntentRouterV2(), InMemoryDocsRetrievalAdapter(rows), repo_context=repo_context())
|
||||
|
||||
result = runner.run("Как работает метод health?", "docs-session", mode="pre_llm_only")
|
||||
|
||||
assert result.router_result.query_plan.sub_intent == "API_METHOD_EXPLAIN"
|
||||
assert result.diagnostics.target_endpoint_identity["normalized_doc_id"] == "api.health_endpoint"
|
||||
assert result.diagnostics.selected_primary_documents == ["api.health_endpoint"]
|
||||
assert result.diagnostics.primary_api_documents_after_filter == ["api.health_endpoint"]
|
||||
assert "api.control_actions_endpoint" in result.diagnostics.rejected_endpoint_candidates
|
||||
assert result.diagnostics.cross_endpoint_leakage_detected is True
|
||||
assert result.diagnostics.evidence_gate_target_api_spec_found is True
|
||||
assert "api.control_actions_endpoint" not in result.diagnostics.selected_doc_ids
|
||||
|
||||
|
||||
def test_api_method_explain_without_exact_target_returns_insufficiency() -> None:
|
||||
rows = [
|
||||
{
|
||||
"layer": "D1_DOCUMENT_CATALOG",
|
||||
"path": "docs/documentation/api/control-actions-endpoint.md",
|
||||
"title": "HTTP API /actions/{action}",
|
||||
"content": "Endpoint for controlling actions.",
|
||||
"metadata": {
|
||||
"document_id": "api.control_actions_endpoint",
|
||||
"type": "api_method",
|
||||
"endpoint": "/actions/{action}",
|
||||
},
|
||||
},
|
||||
{
|
||||
"layer": "D1_DOCUMENT_CATALOG",
|
||||
"path": "docs/documentation/api/send-endpoint.md",
|
||||
"title": "HTTP API /send",
|
||||
"content": "Endpoint for sending messages.",
|
||||
"metadata": {
|
||||
"document_id": "api.send_message_endpoint",
|
||||
"type": "api_method",
|
||||
"endpoint": "/send",
|
||||
},
|
||||
},
|
||||
]
|
||||
runner = DocsQAPipelineRunner(IntentRouterV2(), InMemoryDocsRetrievalAdapter(rows), repo_context=repo_context())
|
||||
|
||||
result = runner.run("Что делает метод health?", "docs-session", mode="pre_llm_only")
|
||||
|
||||
assert result.router_result.query_plan.sub_intent == "API_METHOD_EXPLAIN"
|
||||
assert result.diagnostics.target_endpoint_identity["normalized_doc_id"] == "api.health_endpoint"
|
||||
assert result.diagnostics.selected_primary_documents == []
|
||||
assert "api.control_actions_endpoint" in result.diagnostics.rejected_endpoint_candidates
|
||||
assert "api.send_message_endpoint" in result.diagnostics.rejected_endpoint_candidates
|
||||
assert result.diagnostics.target_api_spec_found_exact is False
|
||||
assert result.diagnostics.evidence_gate_target_api_spec_found is False
|
||||
assert result.diagnostics.gate_decision == "reject"
|
||||
assert result.answer_mode == "insufficient"
|
||||
assert "api.control_actions_endpoint" not in result.diagnostics.selected_doc_ids
|
||||
assert "api.send_message_endpoint" not in result.diagnostics.selected_doc_ids
|
||||
|
||||
|
||||
def test_api_method_explain_uses_indirect_mode_from_target_linked_docs() -> None:
|
||||
rows = [
|
||||
{
|
||||
"layer": "D1_DOCUMENT_CATALOG",
|
||||
"path": "docs/domain/runtime-health.md",
|
||||
"title": "Runtime health",
|
||||
"content": "Runtime health describes overall service state and component diagnostics.",
|
||||
"metadata": {
|
||||
"document_id": "domain.runtime_health",
|
||||
"type": "domain_entity",
|
||||
"links": [{"target": "api.health_endpoint", "type": "used_by"}],
|
||||
},
|
||||
},
|
||||
]
|
||||
runner = DocsQAPipelineRunner(IntentRouterV2(), InMemoryDocsRetrievalAdapter(rows), repo_context=repo_context())
|
||||
|
||||
result = runner.run("Как работает метод health?", "docs-session", mode="pre_llm_only")
|
||||
|
||||
assert result.answer_mode == "indirect"
|
||||
assert result.diagnostics.gate_decision == "allow_indirect"
|
||||
assert result.diagnostics.raw_retrieval_non_empty is True
|
||||
assert result.diagnostics.target_primary_context_non_empty is False
|
||||
assert result.diagnostics.indirect_target_context_non_empty is True
|
||||
assert result.diagnostics.graph_promotion_attempted is True
|
||||
assert result.diagnostics.graph_promotion_hits == ["api.health_endpoint"]
|
||||
assert result.diagnostics.promoted_target_loaded is False
|
||||
assert result.diagnostics.materialization_failure_reason == "materialized_rows_empty"
|
||||
assert result.diagnostics.final_primary_document_ids == []
|
||||
assert "domain.runtime_health" in result.diagnostics.final_secondary_document_ids
|
||||
|
||||
|
||||
def test_api_method_explain_skips_llm_when_no_exact_or_indirect_context() -> None:
|
||||
from tests.unit_tests.rag.test_docs_prompt_layer import FakeLlm
|
||||
|
||||
rows = [
|
||||
{
|
||||
"layer": "D1_DOCUMENT_CATALOG",
|
||||
"path": "docs/documentation/api/send-endpoint.md",
|
||||
"title": "HTTP API /send",
|
||||
"content": "Endpoint for sending messages.",
|
||||
"metadata": {
|
||||
"document_id": "api.send_message_endpoint",
|
||||
"type": "api_method",
|
||||
"endpoint": "/send",
|
||||
},
|
||||
}
|
||||
]
|
||||
llm = FakeLlm("should not be called")
|
||||
runner = DocsQAPipelineRunner(IntentRouterV2(), InMemoryDocsRetrievalAdapter(rows), repo_context=repo_context(), llm=llm)
|
||||
|
||||
result = runner.run("Что делает метод health?", "docs-session")
|
||||
|
||||
assert llm.calls == []
|
||||
assert result.answer_mode == "insufficient"
|
||||
assert result.diagnostics.llm_called is False
|
||||
assert result.diagnostics.llm_call_reason == "no_exact_or_indirect_target_context"
|
||||
assert result.diagnostics.gate_decision == "reject"
|
||||
|
||||
|
||||
def test_api_method_explain_materializes_promoted_target_into_primary_context() -> None:
|
||||
rows = [
|
||||
{
|
||||
"layer": "D1_DOCUMENT_CATALOG",
|
||||
"path": "docs/domain/runtime-health.md",
|
||||
"title": "Runtime health",
|
||||
"content": "Runtime health describes service state and component diagnostics.",
|
||||
"metadata": {
|
||||
"document_id": "domain.runtime_health",
|
||||
"type": "domain_entity",
|
||||
"links": [{"target": "api.health_endpoint", "type": "used_by"}],
|
||||
},
|
||||
}
|
||||
]
|
||||
materialized_rows = [
|
||||
{
|
||||
"layer": "D1_DOCUMENT_CATALOG",
|
||||
"path": "docs/api/health.md",
|
||||
"title": "GET /health",
|
||||
"content": "/health returns runtime and component statuses.",
|
||||
"metadata": {"document_id": "api.health_endpoint", "type": "api_method", "endpoint": "/health"},
|
||||
},
|
||||
{
|
||||
"layer": "D2_FACT_INDEX",
|
||||
"path": "docs/api/health.md",
|
||||
"title": "api.health_endpoint:response",
|
||||
"content": "Returns health summary and component diagnostics.",
|
||||
"metadata": {"subject_id": "api.health_endpoint", "type": "api_method"},
|
||||
},
|
||||
{
|
||||
"layer": "D0_DOC_CHUNKS",
|
||||
"path": "docs/api/health.md",
|
||||
"title": "api.health_endpoint:Overview",
|
||||
"content": "Endpoint /health returns overall runtime health.",
|
||||
"metadata": {"document_id": "api.health_endpoint", "type": "api_method", "endpoint": "/health"},
|
||||
},
|
||||
]
|
||||
runner = DocsQAPipelineRunner(
|
||||
IntentRouterV2(),
|
||||
InMemoryDocsRetrievalAdapter(rows, materialized_rows=materialized_rows),
|
||||
repo_context=repo_context(),
|
||||
)
|
||||
|
||||
result = runner.run("Как работает метод health?", "docs-session", mode="pre_llm_only")
|
||||
|
||||
assert result.answer_mode == "exact"
|
||||
assert result.diagnostics.graph_promotion_hits == ["api.health_endpoint"]
|
||||
assert result.diagnostics.graph_promotion_materialized == ["api.health_endpoint"]
|
||||
assert result.diagnostics.promoted_target_loaded is True
|
||||
assert result.diagnostics.promoted_target_chunks_loaded == 1
|
||||
assert result.diagnostics.promoted_target_facts_loaded == 1
|
||||
assert result.diagnostics.pinned_document_ids == ["api.health_endpoint"]
|
||||
assert result.diagnostics.final_primary_document_ids == ["api.health_endpoint"]
|
||||
assert "domain.runtime_health" in result.diagnostics.final_secondary_document_ids
|
||||
assert result.diagnostics.materialized_target_primary_context_non_empty is True
|
||||
assert result.diagnostics.gate_decision == "allow_exact"
|
||||
|
||||
|
||||
def test_entity_question_does_not_prefer_api_method_primary_doc() -> None:
|
||||
rows = [
|
||||
{
|
||||
"layer": "D1_DOCUMENT_CATALOG",
|
||||
"path": "docs/domain/runtime-health.md",
|
||||
"title": "Сущность runtime health",
|
||||
"content": "Runtime health describes service state.",
|
||||
"metadata": {"document_id": "domain.runtime_health", "type": "domain_entity"},
|
||||
},
|
||||
{
|
||||
"layer": "D1_DOCUMENT_CATALOG",
|
||||
"path": "docs/api/health.md",
|
||||
"title": "GET /health",
|
||||
"content": "/health returns runtime status.",
|
||||
"metadata": {"document_id": "api.health_endpoint", "type": "api_method", "endpoint": "/health"},
|
||||
},
|
||||
]
|
||||
runner = DocsQAPipelineRunner(IntentRouterV2(), InMemoryDocsRetrievalAdapter(rows), repo_context=repo_context())
|
||||
|
||||
result = runner.run("Что такое runtime health?", "docs-session", mode="pre_llm_only")
|
||||
|
||||
assert result.router_result.query_plan.sub_intent == "ENTITY_EXPLAIN"
|
||||
assert result.diagnostics.selected_primary_documents == []
|
||||
assert result.diagnostics.api_method_match_found is False
|
||||
assert result.answer == ""
|
||||
|
||||
|
||||
def test_openapi_method_with_only_path_is_rejected() -> None:
|
||||
|
||||
Reference in New Issue
Block a user