Внешний (public-share) AI-чат расходится с внутренним: нет стриминга/typing — переиспользовать внутренний код #41

Closed
opened 2026-06-20 20:54:31 +03:00 by Ghost · 0 comments

Проблема

Внешний AI-чат на публичных шарах ощущается заметно хуже внутреннего: нет анимированного «печатает…» (typing) индикатора и ответ не выглядит стримящимся. Внутренний чат это уже умеет и отлажен — внешний переписан отдельным минимальным виджетом и расходится по UX.

Что в коде

Внутренний чатapps/client/src/features/ai-chat/ — богатый, отлаженный пайплайн:

  • стриминг через /ai-chat/stream (services/ai-chat-service.ts, queries/ai-chat-query.ts);
  • анимированный typing-индикатор: components/typing-indicator.tsx + логика showTypingIndicator() в components/message-list.tsx (мостит паузу до первых токенов);
  • инкрементальный рендер markdown, карточки tool-call (components/tool-call-card.tsx), message-item/message-list, история диалогов.

Внешний чатapps/client/src/features/share/components/share-ai-widget.tsx (~233 строки) — отдельная минимальная реализация:

  • использует useChat (@ai-sdk/react) + DefaultChatTransport на /api/shares/ai/stream — то есть транспорт стриминговый;
  • НО рендерит ответ как простой текст (getMessageText() + <Text style={{ whiteSpace: "pre-wrap" }}>), без markdown;
  • вместо анимированного индикатора показывает статичный текст «Thinking…» при isStreaming;
  • не переиспользует ни одного компонента из features/ai-chat (импортирует только useChat).

Итог расхождения: нет анимированного typing, ответ выглядит не-стримящимся (плоский текст без инкрементального markdown), нет tool-call карточек.

Почему так получилось

Похоже, public-share-assistant (PR #14) сделали отдельным тонким виджетом под анонимный/публичный контекст (другой эндпоинт /api/shares/ai/stream, credentials: "omit", без auth и без conversation-store), а UI написали с нуля вместо переиспользования внутреннего, уже отлаженного чата.

Предложение

Свести оба чата к общему отлаженному коду:

  1. Выделить из features/ai-chat переиспользуемые презентационные части (message-list, message-item, typing-indicator, markdown-рендер, tool-call-card) в общий слой, не завязанный на внутренний транспорт/auth.
  2. Параметризовать транспорт/эндпоинт: внутренний /ai-chat/stream vs публичный /api/shares/ai/stream, credentials, наличие/отсутствие conversation-store. Оба уже на AI SDK, так что общий рендер сообщений и typing-логика переиспользуемы.
  3. Переписать share-ai-widget.tsx поверх этого общего слоя — получить тот же стриминг / typing / markdown / tool-cards, что и внутри.
  4. При необходимости отрефакторить внутренний код для извлечения общих частей, не меняя внутреннего поведения.

Критерии готовности (DoD)

  • На публичном шаре ответ ассистента стримится инкрементально (как внутри), с анимированным typing-индикатором до первых токенов.
  • Markdown и tool-call карточки рендерятся одинаково внутри и снаружи.
  • Нет дублирующей бесхозной реализации рендера сообщений — общий код переиспользуется обоими чатами.

Затронутые файлы (ориентир)

  • apps/client/src/features/ai-chat/components/{message-list,message-item,typing-indicator,tool-call-card}.tsx
  • apps/client/src/features/ai-chat/utils/{chat-markdown,markdown,tool-parts}.*
  • apps/client/src/features/share/components/share-ai-widget.tsx
## Проблема Внешний AI-чат на публичных шарах ощущается заметно хуже внутреннего: **нет анимированного «печатает…» (typing) индикатора** и **ответ не выглядит стримящимся**. Внутренний чат это уже умеет и отлажен — внешний переписан отдельным минимальным виджетом и расходится по UX. ## Что в коде **Внутренний чат** — [`apps/client/src/features/ai-chat/`](apps/client/src/features/ai-chat/) — богатый, отлаженный пайплайн: - стриминг через `/ai-chat/stream` (`services/ai-chat-service.ts`, `queries/ai-chat-query.ts`); - анимированный typing-индикатор: `components/typing-indicator.tsx` + логика `showTypingIndicator()` в `components/message-list.tsx` (мостит паузу до первых токенов); - инкрементальный рендер markdown, карточки tool-call (`components/tool-call-card.tsx`), `message-item`/`message-list`, история диалогов. **Внешний чат** — [`apps/client/src/features/share/components/share-ai-widget.tsx`](apps/client/src/features/share/components/share-ai-widget.tsx) (~233 строки) — отдельная минимальная реализация: - использует `useChat` (`@ai-sdk/react`) + `DefaultChatTransport` на `/api/shares/ai/stream` — то есть транспорт **стриминговый**; - НО рендерит ответ как простой текст (`getMessageText()` + `<Text style={{ whiteSpace: "pre-wrap" }}>`), **без markdown**; - вместо анимированного индикатора показывает статичный текст **«Thinking…»** при `isStreaming`; - **не переиспользует ни одного** компонента из `features/ai-chat` (импортирует только `useChat`). Итог расхождения: нет анимированного typing, ответ выглядит не-стримящимся (плоский текст без инкрементального markdown), нет tool-call карточек. ## Почему так получилось Похоже, public-share-assistant (PR #14) сделали отдельным тонким виджетом под анонимный/публичный контекст (другой эндпоинт `/api/shares/ai/stream`, `credentials: "omit"`, без auth и без conversation-store), а UI написали с нуля вместо переиспользования внутреннего, уже отлаженного чата. ## Предложение Свести оба чата к общему отлаженному коду: 1. Выделить из `features/ai-chat` переиспользуемые **презентационные** части (`message-list`, `message-item`, `typing-indicator`, markdown-рендер, `tool-call-card`) в общий слой, не завязанный на внутренний транспорт/auth. 2. Параметризовать транспорт/эндпоинт: внутренний `/ai-chat/stream` vs публичный `/api/shares/ai/stream`, `credentials`, наличие/отсутствие conversation-store. Оба уже на AI SDK, так что общий рендер сообщений и typing-логика переиспользуемы. 3. Переписать `share-ai-widget.tsx` поверх этого общего слоя — получить тот же стриминг / typing / markdown / tool-cards, что и внутри. 4. При необходимости отрефакторить внутренний код для извлечения общих частей, **не меняя** внутреннего поведения. ## Критерии готовности (DoD) - На публичном шаре ответ ассистента стримится инкрементально (как внутри), с анимированным typing-индикатором до первых токенов. - Markdown и tool-call карточки рендерятся одинаково внутри и снаружи. - Нет дублирующей бесхозной реализации рендера сообщений — общий код переиспользуется обоими чатами. ## Затронутые файлы (ориентир) - `apps/client/src/features/ai-chat/components/{message-list,message-item,typing-indicator,tool-call-card}.tsx` - `apps/client/src/features/ai-chat/utils/{chat-markdown,markdown,tool-parts}.*` - `apps/client/src/features/share/components/share-ai-widget.tsx`
Ghost closed this issue 2026-06-21 01:29:18 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: vvzvlad/gitmost#41