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

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,70 @@
import asyncio
from app.modules.chat.module import ChatModule
from app.modules.chat.task_store import TaskStore
from app.schemas.chat import ChatMessageRequest
from app.schemas.chat import TaskQueuedResponse
from app.modules.shared.event_bus import EventBus
from app.modules.shared.retry_executor import RetryExecutor
class _FakeRuntime:
async def run(self, **kwargs):
raise AssertionError("legacy runtime must not be called")
class _FakeDirectChat:
def __init__(self) -> None:
self.calls = 0
async def handle_message(self, request):
self.calls += 1
return TaskQueuedResponse(
task_id="task-1",
status="done",
)
class _FakeRagSessions:
def get(self, rag_session_id: str):
return {"rag_session_id": rag_session_id}
class _FakeRepository:
def create_dialog(self, dialog_session_id: str, rag_session_id: str) -> None:
return None
def get_dialog(self, dialog_session_id: str):
return None
def add_message(self, dialog_session_id: str, role: str, content: str, task_id: str | None = None, payload: dict | None = None) -> None:
return None
def test_chat_messages_endpoint_uses_direct_service(monkeypatch) -> None:
monkeypatch.setenv("SIMPLE_CODE_EXPLAIN_ONLY", "true")
direct_chat = _FakeDirectChat()
module = ChatModule(
agent_runner=_FakeRuntime(),
event_bus=EventBus(),
retry=RetryExecutor(),
rag_sessions=_FakeRagSessions(),
repository=_FakeRepository(),
direct_chat=direct_chat,
task_store=TaskStore(),
)
router = module.public_router()
endpoint = next(route.endpoint for route in router.routes if getattr(route, "path", "") == "/api/chat/messages")
response = asyncio.run(
endpoint(
ChatMessageRequest(
session_id="dialog-1",
project_id="rag-1",
message="Explain get_user",
),
None,
)
)
assert response.task_id == "task-1"
assert direct_chat.calls == 1

View File

@@ -0,0 +1,61 @@
import asyncio
from app.modules.chat.direct_service import CodeExplainChatService
from app.modules.chat.session_resolver import ChatSessionResolver
from app.modules.chat.task_store import TaskStore
from app.modules.rag.explain.models import ExplainIntent, ExplainPack
from app.schemas.chat import ChatFileContext, ChatMessageRequest
class _FakeRetriever:
def build_pack(self, rag_session_id: str, user_query: str, *, file_candidates: list[dict] | None = None) -> ExplainPack:
return ExplainPack(
intent=ExplainIntent(raw_query=user_query, normalized_query=user_query),
missing=["code_excerpts"],
)
class _FakeLlm:
def __init__(self) -> None:
self.calls = 0
def generate(self, prompt_name: str, user_input: str, *, log_context: str | None = None) -> str:
self.calls += 1
return "should not be called"
class _FakeDialogs:
def get(self, dialog_session_id: str):
return None
def test_direct_service_skips_llm_when_evidence_is_insufficient() -> None:
messages: list[tuple[str, str, str, str | None]] = []
llm = _FakeLlm()
task_store = TaskStore()
service = CodeExplainChatService(
retriever=_FakeRetriever(),
llm=llm,
session_resolver=ChatSessionResolver(_FakeDialogs(), lambda rag_session_id: rag_session_id == "rag-1"),
task_store=task_store,
message_sink=lambda dialog_session_id, role, content, task_id=None: messages.append((dialog_session_id, role, content, task_id)),
)
result = asyncio.run(
service.handle_message(
ChatMessageRequest(
session_id="dialog-1",
project_id="rag-1",
message="Explain get_user",
files=[ChatFileContext(path="app/api/users.py", content="", content_hash="x")],
)
)
)
task = task_store.get(result.task_id)
assert task is not None
assert task.answer is not None
assert "Недостаточно опоры в коде" in task.answer
assert result.status == "done"
assert llm.calls == 0
assert [item[1] for item in messages] == ["user", "assistant"]