Фиксация изменений

This commit is contained in:
2026-03-05 11:03:17 +03:00
parent 1ef0b4d68c
commit 417b8b6f72
261 changed files with 8215 additions and 332 deletions

View File

@@ -0,0 +1,102 @@
from __future__ import annotations
import re
from app.modules.rag.explain.models import ExplainHints, ExplainIntent
from app.modules.rag.retrieval.query_terms import extract_query_terms
class ExplainIntentBuilder:
_ROUTE_RE = re.compile(r"(/[A-Za-z0-9_./{}:-]+)")
_FILE_RE = re.compile(r"([A-Za-z0-9_./-]+\.py)")
_SYMBOL_RE = re.compile(r"\b([A-Z][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_]*|[A-Z][A-Za-z0-9_]{2,}|[a-z_][A-Za-z0-9_]{2,})\b")
_COMMAND_RE = re.compile(r"`([A-Za-z0-9:_-]+)`")
_TEST_KEYWORDS = (
"тест",
"tests",
"test ",
"unit-test",
"unit test",
"юнит-тест",
"pytest",
"spec",
"как покрыто тестами",
"как проверяется",
"how is it tested",
"how it's tested",
)
def build(self, user_query: str) -> ExplainIntent:
normalized = " ".join((user_query or "").split())
lowered = normalized.lower()
keywords = self._keywords(normalized)
hints = ExplainHints(
paths=self._dedupe(self._FILE_RE.findall(normalized)),
symbols=self._symbols(normalized),
endpoints=self._dedupe(self._ROUTE_RE.findall(normalized)),
commands=self._commands(normalized, lowered),
)
return ExplainIntent(
raw_query=user_query,
normalized_query=normalized,
keywords=keywords[:12],
hints=hints,
include_tests=self._include_tests(lowered),
expected_entry_types=self._entry_types(lowered, hints),
depth=self._depth(lowered),
)
def _keywords(self, text: str) -> list[str]:
keywords = extract_query_terms(text)
for token in self._symbols(text):
if token not in keywords:
keywords.append(token)
for token in self._ROUTE_RE.findall(text):
if token not in keywords:
keywords.append(token)
return self._dedupe(keywords)
def _symbols(self, text: str) -> list[str]:
values = []
for raw in self._SYMBOL_RE.findall(text):
token = raw.strip()
if len(token) < 3:
continue
if token.endswith(".py"):
continue
values.append(token)
return self._dedupe(values)
def _commands(self, text: str, lowered: str) -> list[str]:
values = list(self._COMMAND_RE.findall(text))
if " command " in f" {lowered} ":
values.extend(re.findall(r"command\s+([A-Za-z0-9:_-]+)", lowered))
if " cli " in f" {lowered} ":
values.extend(re.findall(r"cli\s+([A-Za-z0-9:_-]+)", lowered))
return self._dedupe(values)
def _entry_types(self, lowered: str, hints: ExplainHints) -> list[str]:
if hints.endpoints or any(token in lowered for token in ("endpoint", "route", "handler", "http", "api")):
return ["http"]
if hints.commands or any(token in lowered for token in ("cli", "command", "click", "typer")):
return ["cli"]
return ["http", "cli"]
def _depth(self, lowered: str) -> str:
if any(token in lowered for token in ("deep", "подроб", "деталь", "full flow", "trace")):
return "deep"
if any(token in lowered for token in ("high level", "overview", "кратко", "summary")):
return "high"
return "medium"
def _include_tests(self, lowered: str) -> bool:
normalized = f" {lowered} "
return any(token in normalized for token in self._TEST_KEYWORDS)
def _dedupe(self, values: list[str]) -> list[str]:
result: list[str] = []
for value in values:
item = value.strip()
if item and item not in result:
result.append(item)
return result