[feature][ai-chat] Реалтайм-счётчик токенов (включая токены размышления), как в Claude Code #151
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/thinking) — как в Claude Code: строка вида
Thinking… · 60 tokensрядом с индикатором размышления, а не только итог после ответа.Сейчас токены учитываются только пост-фактум: в
onFinishпишутсяusage/contextTokens, а бейдж в шапке окна обновляется лишь при открытии/переключении чата и мид-стрим не тикает (помечено как ограничение v1). Reasoning не запрашивается и не отображается.Что уже есть (опора для фичи)
Чат на Vercel AI SDK v6 (сервер и клиент).
streamText(...)→result.pipeUIMessageStreamToResponse(...)вapps/server/src/core/ai-chat/ai-chat.service.ts. КолбэкmessageMetadataуже задействован (вешаетchatIdна частьstart, см.chatStreamStartMetadata) — готовая точка для проброса usage в реальном времени.onFinish(totalUsage/usage), пишутся вmetadataстроки (usage,contextTokens).useChat(chat-thread.tsx) на каждую streamed-дельту подменяет объект сообщения → компоненты, читающиеmessage.parts, перерисовываются на каждый тик. Это и есть готовый «реалтайм-движок».TypingIndicator(typing-indicator.tsx) висит именно в фазе размышления (reasoning-парты сейчас не считаются «видимым контентом» —message-content.ts, и игнорируются вmessage-item.tsx). Идеальное место дляThinking… · N tokens.formatTokens()(1.2M/3.4k/950), бейдж контекста в шапке,contextTokensиз метаданных (ai-chat-window.tsx).ai-chat.types.ts): естьusage.inputTokens/outputTokens/totalTokens,contextTokens; нетreasoningTokens.Ключевой факт про источники данных (AI SDK v6)
Три канала, разные по точности/гранулярности:
reasoning-парты в стриме (part.type==='reasoning')messageMetadataнаfinish-step→part.usagereasoningTokensonFinish→totalUsage/usageСледствия:
.chat(), — только числоreasoningTokensв конце шага, без текста).Предлагаемая архитектура — гибрид «живая оценка + авторитетная сверка»
Почему так:
useChat).Точки изменений
Сервер (
apps/server/src/core/ai-chat/ai-chat.service.ts):chatStreamStartMetadata(или ввестиchatStreamMetadata): наfinish-stepиfinishвозвращать{ usage: part.usage }(включаетreasoningTokens). Точка вызова — опцииpipeUIMessageStreamToResponse. Чистая функция → юнит-тест.sendReasoning: true(в v6 по умолчанию true).providerOptionsдля включения reasoning у поддерживающих провайдеров (geminithinkingConfig.includeThoughts, openai-совместимые шлюзы) — аккуратно/конфигурируемо.onFinishдобавитьreasoningTokensв сохраняемыйmetadata.usage.Клиент:
count-stream-tokens.ts: чистыеestimateTokens(text)иliveTurnTokens(message)→{ reasoning, output, authoritative? }(отдаёт авторитет, если пришёлmetadata.usage, иначе оценку). Юнит-тестируемо.typing-indicator.tsx: пропthinkingTokens?→ дорисовать· {{count}} tokens; считать вmessage-list.tsxиз tail-сообщения.message-item.tsx/message-content.ts(слой 2): рендерreasoningкак сворачиваемого блока «Размышление» с его счётчиком.ai-chat-window.tsx: живой бейдж токенов хода мид-стрим (throttle ~5–10 Гц, чтобы не ререндерить окно на каждую дельту); по завершении — обратно к persistedcontextTokens.ai-chat.types.ts: добавитьreasoningTokens?: numberвusage; синхронно — экспорт в Markdown (chat-markdown.ts)."Thinking… · {{count}} tokens".План (поэтапно)
Thinking… · N tokensв индикаторе. Сервер не трогаем. Быстро закрывает референс.usageнаfinish-step/finish; клиент сверяет оценку с фактом; живой бейдж в шапке; блок «Размышление»;reasoningTokensв типах/persist/экспорте.Util
liveTurnTokensспроектирован так, что B встраивается без переделки A.Краевые случаи
reasoningTokensиз usage по приходу шага (B); на A — только output-оценку.usage(как в Claude Code).MAX_AGENT_STEPS=20): копим reasoning/output по всем шагам.reasoningTokensопционально.Definition of Done
Thinking… · N tokens, число тикает в реальном времени.usage.reasoningTokens).reasoningTokensсохраняется в метаданных и попадает в Markdown-экспорт.liveTurnTokens/estimateTokens, расширенныйchatStreamMetadata).Ghost referenced this issue2026-06-24 12:45:05 +03:00
Ghost referenced this issue2026-06-24 13:05:44 +03:00