from app.modules.agent.engine.orchestrator.actions.explain_actions import ExplainActions from app.modules.agent.engine.orchestrator.execution_context import ExecutionContext from app.modules.agent.engine.orchestrator.models import ( ExecutionPlan, OutputContract, RoutingMeta, Scenario, TaskConstraints, TaskSpec, ) def _ctx(rag_items: list[dict]) -> ExecutionContext: task = TaskSpec( task_id="task-1", dialog_session_id="dialog-1", rag_session_id="rag-1", user_message="Объясни по коду как работает task_processor", scenario=Scenario.EXPLAIN_PART, routing=RoutingMeta(domain_id="project", process_id="qa", confidence=0.9, reason="test"), constraints=TaskConstraints(), output_contract=OutputContract(result_type="answer"), metadata={ "rag_items": rag_items, "rag_context": "", "confluence_context": "", "files_map": {}, }, ) plan = ExecutionPlan( plan_id="plan-1", task_id="task-1", scenario=Scenario.EXPLAIN_PART, template_id="tpl", template_version="1", steps=[], ) return ExecutionContext(task=task, plan=plan, graph_resolver=lambda *_: None, graph_invoker=lambda *_: {}) def test_explain_actions_switch_to_code_profile_when_code_layers_present() -> None: ctx = _ctx( [ { "source": "app/task_processor.py", "layer": "C1_SYMBOL_CATALOG", "title": "task_processor.process_task", "content": "function task_processor.process_task(task)", "metadata": {"qname": "task_processor.process_task", "kind": "function"}, }, { "source": "app/task_processor.py", "layer": "C2_DEPENDENCY_GRAPH", "title": "task_processor.process_task:calls", "content": "task_processor.process_task calls queue.publish", "metadata": {"edge_type": "calls"}, }, ] ) actions = ExplainActions() actions.collect_sources(ctx) actions.extract_logic(ctx) actions.summarize(ctx) sources = ctx.artifacts.get_content("sources", {}) assert sources["source_profile"] == "code" answer = str(ctx.artifacts.get_content("final_answer", "")) assert "кодовых слоев индекса" not in answer assert "CodeRAG" not in answer assert "app/task_processor.py" in answer assert "requirements/docs context" not in answer def test_explain_actions_add_code_details_block() -> None: ctx = _ctx( [ { "source": "src/config_manager/__init__.py", "layer": "C1_SYMBOL_CATALOG", "title": "ConfigManager", "content": "const ConfigManager\nConfigManager = config_manager.v2.ConfigManagerV2", "metadata": { "qname": "ConfigManager", "kind": "const", "lang_payload": {"imported_from": "v2.ConfigManagerV2", "import_alias": True}, }, }, { "source": "src/config_manager/v2/control/base.py", "layer": "C1_SYMBOL_CATALOG", "title": "ControlChannel", "content": "class ControlChannel\nControlChannel(ABC)", "metadata": {"qname": "ControlChannel", "kind": "class"}, }, { "source": "src/config_manager/v2/core/control_bridge.py", "layer": "C1_SYMBOL_CATALOG", "title": "ControlChannelBridge", "content": "class ControlChannelBridge\nПредоставляет halt и status как обработчики start/stop/status", "metadata": {"qname": "ControlChannelBridge", "kind": "class"}, }, { "source": "src/config_manager/v2/core/control_bridge.py", "layer": "C2_DEPENDENCY_GRAPH", "title": "ControlChannelBridge.on_start:calls", "content": "ControlChannelBridge.on_start calls self._start_runtime", "metadata": {"src_qname": "ControlChannelBridge.on_start", "dst_ref": "self._start_runtime"}, }, { "source": "src/config_manager/v2/__init__.py", "layer": "C0_SOURCE_CHUNKS", "title": "src/config_manager/v2/__init__.py:1-6", "content": '"""Контракт: управление через API (config.yaml, секция management)."""', "metadata": {}, }, ] ) actions = ExplainActions() actions.collect_sources(ctx) actions.extract_logic(ctx) actions.summarize(ctx) answer = str(ctx.artifacts.get_content("final_answer", "")) assert "### Что видно по коду" in answer assert "ConfigManager` в проекте доступен как alias" in answer assert "ControlChannelBridge.on_start" in answer assert "### Где смотреть в проекте" in answer assert "В индексе нет точного символа" not in answer assert "отдельный интерфейс управления" in answer