Фикс состояния

This commit is contained in:
2026-03-12 16:55:23 +03:00
parent 417b8b6f72
commit 6ba0a18ac9
1445 changed files with 620025 additions and 885 deletions

View File

@@ -0,0 +1 @@
"""Runtime-specific executors for pipeline_setup_v2."""

View File

@@ -0,0 +1,56 @@
from __future__ import annotations
from app.modules.agent.code_qa_runtime import CodeQaRuntimeExecutor
from app.modules.rag.contracts.enums import RagLayer
from app.modules.rag.intent_router_v2 import RepoContext
from tests.pipeline_setup_v2.core.models import ExecutionPayload, V2Case
class CodeQaEvalAdapter:
def __init__(self) -> None:
self._executor = CodeQaRuntimeExecutor(llm=None)
def execute(self, case: V2Case, rag_session_id: str | None) -> ExecutionPayload:
if not rag_session_id:
raise ValueError(f"Case '{case.case_id}' requires rag_session_id or repo_path")
result = self._executor.execute(user_query=case.query, rag_session_id=rag_session_id)
route_result = result.router_result
route = result.diagnostics.router_result
details = {
"case_id": case.case_id,
"query": case.query,
"rag_session_id": rag_session_id,
"actual": {
"intent": route.get("intent"),
"sub_intent": route.get("sub_intent"),
"graph_id": route.get("graph_id"),
"conversation_mode": route.get("conversation_mode"),
"rag_count": len(result.retrieval_result.code_chunks) if result.retrieval_result else 0,
"llm_answer": "",
"answer_mode": result.answer_mode,
"path_scope": tuple(route.get("path_scope") or []),
"symbol_candidates": tuple(route_result.query_plan.symbol_candidates or []) if route_result else (),
"layers": tuple(route.get("layers") or []),
},
"timings_ms": result.diagnostics.timings_ms,
}
actual = dict(details["actual"])
return ExecutionPayload(actual=actual, details=details)
def _repo_context() -> RepoContext:
return RepoContext(
languages=["python"],
available_domains=["CODE", "DOCS"],
available_layers=[
RagLayer.CODE_ENTRYPOINTS,
RagLayer.CODE_SYMBOL_CATALOG,
RagLayer.CODE_DEPENDENCY_GRAPH,
RagLayer.CODE_SEMANTIC_ROLES,
RagLayer.CODE_SOURCE_CHUNKS,
RagLayer.DOCS_MODULE_CATALOG,
RagLayer.DOCS_FACT_INDEX,
RagLayer.DOCS_SECTION_INDEX,
RagLayer.DOCS_POLICY_INDEX,
],
)

View File

@@ -0,0 +1,154 @@
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())