fix: подключить рекомендации модели к разбору, убрать лишнюю проверку грамматики ответа ДДС, задать настроение персоне worried

This commit is contained in:
gglamer 2026-09-26 17:12:29 +00:00
commit daa4f8c4f1
11 changed files with 63 additions and 34 deletions

View file

@ -359,3 +359,41 @@ def test_handoff_queue_mixes_trainee_card_then_selected_prepared_card(client):
assert state["score_auto"] < 100
finally:
control.__exit__(None, None, None)
def test_model_coaching_reaches_report_without_changing_score(client, monkeypatch):
from app.domain.events import AICoaching, AIRecommendation
seen: list[list[str]] = []
async def fake_coach(metrics):
failed = [item.key for item in metrics if not item.passed and item.weight > 0]
seen.append(failed)
return AICoaching(status="ready", model="test", recommendations=[
AIRecommendation(metric_key=failed[0], text="Перечитайте описание перед сдачей карточки."),
])
monkeypatch.setattr("app.session.finish.assess", rules_only_grammar)
monkeypatch.setattr("app.session.finish.coach", fake_coach)
session_id, control = start(client)
try:
with client.websocket_connect(f"/ws/call/{session_id}") as trainee:
read_until(trainee, "card.briefing")
trainee.send_json({"type": "kio.patch", "fields": {
"address": "улица Ленина, 14", "floor": "5", "incident_type": "fire",
"victims_count": 2, "description": "горит балкон",
"signs": ["жилой дом", "балкон", "открытое пламя"],
}})
wait_for(lambda: hub.get(session_id).kio.incident_code)
trainee.send_json({"type": "card.submit"})
read_until(trainee, "call.ended")
read_until(trainee, "score.ready")
score = wait_for(lambda: hub.get(session_id).score)
assert seen and "description_grammar" in seen[0]
coaching = score["full_report"]["ai_coaching"]
assert coaching["status"] == "ready"
assert coaching["recommendations"][0]["metric_key"] == seen[0][0]
assert score["score_auto"] < 100
finally:
control.__exit__(None, None, None)

View file

@ -6,6 +6,7 @@ from pathlib import Path
import pytest
from app.dialog.persona import BASE_MOOD
from app.scenarios.loader import ScenarioError, load_file, load_library
LIBRARY = Path(__file__).resolve().parents[2] / "scenarios"
@ -38,6 +39,13 @@ def test_library_loads():
assert all(s.ground_truth.dds for s in scenarios), "ДДС не выведен"
def test_library_personas_have_base_mood():
# base — свободная строка: неизвестное значение молча становится «спокойным»
# звонящим, и сценарий теряет задуманную подачу.
unknown = {s.id: s.persona.base for s in load_library(LIBRARY) if s.persona.base not in BASE_MOOD}
assert not unknown, f"персона без настроения в BASE_MOOD: {unknown}"
def test_extends_inherits_whole_checklist():
"""Общий чек-лист по классификатору наследуется целиком,
локальные пункты дополняют его."""

View file

@ -11,7 +11,6 @@ from app.domain.kio import KIO
from app.domain.statuses import PhoneCallPending, ServiceStatus
from app.domain.timers import TimerCode
from app.scenarios.loader import load_file
from app.scoring.grammar import basic_check
from app.session.checkpoint import dump_state, load_state
from app.session.dds import deliver_due_cards, prepare_handoff_queue, prepare_queue
from app.session.hub import LEASE_FENCED_MESSAGE, SessionHub
@ -50,7 +49,6 @@ def dds_state() -> SessionState:
service=service, crew=state.crew_selected, phase="dispatched"
)
state.reply_text = "Сообщение принято, бригада направлена."
state.reply_grammar = basic_check(state.reply_text)
state.reply_log.append((now_utc(), state.reply_text))
state.dds_log.append(("crew.select", now_utc(), state.crew_selected))
return state
@ -74,7 +72,6 @@ def test_active_dds_session_round_trips_without_losing_work():
assert restored.crew_assignments == before.crew_assignments
assert restored.phone_pending == before.phone_pending
assert restored.reply_text == before.reply_text
assert restored.reply_grammar == before.reply_grammar
assert restored.processed_station_commands == before.processed_station_commands
assert restored.dds_scenarios[0].id == before.scenario_id
# Время простоя backend входит в норматив, а не обнуляет таймер.