feat(ai-chat): показывать reasoning live во время фазы «думания» (до начала ответа) #178
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Проблема
Reasoning («ход мысли» модели) уже стримится с сервера и рендерится клиентом, но текст размышления становится виден только после того, как начался ответный текст. Во время чистой фазы «думания» (а для reasoning-моделей вроде GLM/o-серии она бывает долгой) пользователь видит только индикатор
Thinking… · N tokens— сам ход мысли скрыт, пока модель не начнёт отвечать.Почему так сейчас
assistantMessageHasVisibleContentне считает reasoning-парт «видимым контентом» (видимы только непустой text, tool-парт, persisted error/aborted):А
MessageItemпри отсутствии видимого контента возвращаетnull, отдавая фазу «думания» отдельномуTypingIndicator:<ReasoningBlock>: apps/client/src/features/ai-chat/components/message-item.tsx:95-103Серверный форвардинг reasoning уже включён (
sendReasoning: true, ai-chat.service.ts:594; публичный share — на дефолте v6), так что дельты приходят на клиент в реальном времени — их просто не показывают как текст до начала ответа.Желаемое поведение
Показывать
ReasoningBlock(раскрытый и наполняемый live) сразу, как только появился непустой reasoning-парт — ещё до прихода ответного текста. По завершении turn'а блок можно автоматически сворачивать.Набросок реализации (точечные правки)
assistantMessageHasVisibleContent: считать сообщение видимым при наличии непустого reasoning-парта. Это единый источник истины, синхронизированный сtypingIndicatorShowsName(см. комментарии в файле) — менять аккуратно, чтобы во время фазы «думания» имя агента и лейаут держал ровно один элемент и не было прыжка/дубля.return null(стр. 80) для случая «только reasoning».Thinking…-индикатор не должен дублировать имя/строку (есть пропshowNameи логикаtypingIndicatorShowsName). Возможно, во время чистого reasoning индикатор «дотов» оставить внутри/рядом с блоком, а имя отдать баблу.Связанные изменения
reasoning_content, ради которого эта UX-доработка имеет смысл.includeUsage: trueуcreateOpenAICompatible(см. ревью PR #177) — живой счётчик из стрима работает и без него.Критерии приёмки
ReasoningBlock, наполняющийся в реальном времени.Заметка
Стейл-комментарий: message-item.tsx:41 гласит «reasoning … ignored for v1», хотя код ниже его рендерит — заодно поправить.
🤖 Generated with Claude Code