from __future__ import annotations from datetime import datetime from pathlib import Path from app.modules.agent.code_qa_runtime import CodeQaRuntimeExecutor from app.modules.agent.llm import AgentLlmService from app.modules.agent.prompt_loader import PromptLoader from app.modules.shared.gigachat.client import GigaChatClient from app.modules.shared.gigachat.settings import GigaChatSettings from app.modules.shared.gigachat.token_provider import GigaChatTokenProvider from tests.pipeline_setup.suite_02_pipeline.pipeline_intent_rag.helpers.env_bootstrap import PipelineEnvLoader from tests.pipeline_setup.suite_02_pipeline.pipeline_intent_rag.helpers.models import PhraseCase, PipelineResult from tests.pipeline_setup.suite_02_pipeline.pipeline_intent_rag.helpers.pipeline_runner import ( IntentRouterRagPipelineRunner, RouterOnlyRunner, ) from tests.pipeline_setup_v2.core.models import ExecutionPayload, V2Case class RuntimeAdapter: def __init__(self) -> None: self._test_root = ( Path(__file__).resolve().parents[2] / "pipeline_setup" / "suite_02_pipeline" / "pipeline_intent_rag" ).resolve() PipelineEnvLoader(self._test_root).load() self._started_at = datetime.now() self._rag_adapter = None self._executor: CodeQaRuntimeExecutor | None = None def execute(self, case: V2Case, rag_session_id: str | None) -> ExecutionPayload: phrase = PhraseCase( case_id=case.case_id, text=case.query, rag_session_id=rag_session_id, expected_intent=case.expectations.router.intent, expect_non_empty_rag=bool(case.expectations.retrieval.non_empty), ) if case.mode == "router_only": result = RouterOnlyRunner(started_at=self._started_at).run_case(phrase) elif case.mode == "router_rag": result = IntentRouterRagPipelineRunner( started_at=self._started_at, rag_adapter=self._rag_components(), session_resolver=_StaticSessionResolver(), ).run_case(phrase) else: result = self._run_full_chain(phrase) record = result.to_record() return ExecutionPayload(actual=_actual_from_runtime_record(record), details=record) def _run_full_chain(self, phrase: PhraseCase) -> PipelineResult: result = self._executor_instance().execute(user_query=phrase.text, rag_session_id=phrase.rag_session_id or "") route = dict(result.diagnostics.router_result) retrieval = result.diagnostics.retrieval_request diagnostics = { "router_plan": { "sub_intent": route.get("sub_intent"), "graph_id": route.get("graph_id"), "path_scope": list(retrieval.get("path_scope") or []), "layers": list(retrieval.get("requested_layers") or []), "symbol_candidates": list(result.router_result.query_plan.symbol_candidates or []) if result.router_result else [], }, "timings_ms": dict(result.diagnostics.timings_ms or {}), "answer_policy": { "short_circuit": not result.llm_used, "answer_mode": _answer_status(result.answer_mode, result.llm_used), "failure_reason": ",".join(result.validation.reasons), }, } return PipelineResult( case=phrase, mode="full_chain", run_started_at=self._started_at, rag_session_id=phrase.rag_session_id, intent=str(route.get("intent") or ""), graph_id=str(route.get("graph_id") or ""), conversation_mode=str(route.get("conversation_mode") or ""), query=str(retrieval.get("query") or phrase.text), rag_rows=list(result.retrieval_result.raw_rows) if result.retrieval_result else [], symbol_resolution=result.router_result.symbol_resolution.model_dump() if result.router_result else {}, llm_answer=result.final_answer, diagnostics=diagnostics, ) def _rag_components(self): if self._rag_adapter is None: from app.modules.rag.persistence.repository import RagRepository from tests.pipeline_setup.suite_02_pipeline.pipeline_intent_rag.helpers.rag_db_adapter import ( RagDbAdapter, SessionEmbeddingDimensions, ) self._rag_adapter = RagDbAdapter( repository=RagRepository(), dim_resolver=SessionEmbeddingDimensions(), ) return self._rag_adapter def _executor_instance(self) -> CodeQaRuntimeExecutor: if self._executor is None: self._executor = CodeQaRuntimeExecutor(_build_llm()) return self._executor class _StaticSessionResolver: def resolve(self, case: PhraseCase) -> str: if not case.rag_session_id: raise ValueError(f"Case '{case.case_id}' requires rag_session_id or repo_path") return case.rag_session_id def _actual_from_runtime_record(record: dict) -> dict: diagnostics = dict(record.get("diagnostics") or {}) router_plan = dict(diagnostics.get("router_plan") or {}) summary = dict(record.get("summary") or {}) router_summary = dict(summary.get("router") or {}) return { "intent": record.get("actual_intent"), "sub_intent": router_summary.get("sub_intent") or router_plan.get("sub_intent"), "graph_id": record.get("graph_id"), "conversation_mode": record.get("conversation_mode"), "rag_count": int(record.get("rag_count") or 0), "llm_answer": str(record.get("llm_answer") or "").strip(), "answer_mode": dict(summary.get("llm") or {}).get("answer_status"), "path_scope": tuple(router_plan.get("path_scope") or []), "symbol_candidates": tuple(router_plan.get("symbol_candidates") or []), "layers": tuple(router_plan.get("layers") or []), } def _ms(started: float) -> int: return 0 def _sub_intent_from_result(result: PipelineResult) -> str | None: router_plan = dict(result.diagnostics.get("router_plan") or {}) value = str(router_plan.get("sub_intent") or "").strip() return value or None def _answer_status(answer_mode: str, llm_used: bool) -> str: if answer_mode == "normal" and llm_used: return "answered" return answer_mode def _build_llm() -> AgentLlmService: settings = GigaChatSettings.from_env() client = GigaChatClient(settings, GigaChatTokenProvider(settings)) return AgentLlmService(client=client, prompts=PromptLoader())