feat(ui+docs): читаемые бейджи реплик + раздел документации

Бейджи в Песочнице:
- Каждый бейдж теперь с русским префиксом-меткой (ветка/шаг/роутер предложил/решение/тип ответа)
- Тег «многошаговая» на бейдже ветки при is_state_machine или наличии step_code
- Шаг: сначала русское название, потом код в скобках (Повод и специалист (qualify))
- get_thread_detail обогащает старые meta: подтягивает step_name и is_state_machine из БД

Документация:
- «Удержание в ветке» — пошаговый разбор sticky-механизма, явно что не второй роутер
- Новая карточка «Боковой вопрос (soft_insertion)» — откуда берётся, счётчик, nudge
- Блок под схемой «Что происходит на каждой реплике»: почему бейджи на ответах ассистента

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
AR 15 M4
2026-04-25 21:08:27 +05:00
parent 85c3ec0222
commit 45832e2b37
3 changed files with 132 additions and 46 deletions
+12
View File
@@ -508,6 +508,8 @@ async def send_message(
"router_intent_code": router_code,
"served_intent_code": served_code,
"step_code": snapshot.get("current_step_code"),
"step_name": current_step.name if current_step else None,
"is_state_machine": is_state_machine,
"events": events,
}
@@ -630,6 +632,10 @@ async def get_thread_detail(session: AsyncSession, thread_id: int) -> dict | Non
)
rows = (await session.execute(stmt)).all()
# Lookup для обогащения старых meta: (intent_id, step_code) -> step_name
step_rows = (await session.execute(select(IntentStep.intent_id, IntentStep.code, IntentStep.name))).all()
step_name_lookup: dict[tuple, str] = {(iid, sc): sn for iid, sc, sn in step_rows}
messages = []
for m, intent_code, intent_name in rows:
sources = []
@@ -644,6 +650,12 @@ async def get_thread_detail(session: AsyncSession, thread_id: int) -> dict | Non
meta = json.loads(m.meta_json)
except json.JSONDecodeError:
logger.warning("Bad meta_json for message %d", m.id)
# Обогащаем meta полями, которых не было в старых сообщениях
if meta and meta.get("step_code"):
if "step_name" not in meta and m.intent_id:
meta["step_name"] = step_name_lookup.get((m.intent_id, meta["step_code"]))
if "is_state_machine" not in meta:
meta["is_state_machine"] = True
messages.append({
"id": m.id,
"role": m.role,