docs(backlog): gate streaming dictation behind a feature toggle (default off)
Design entry: hide the silence-cut streaming dictation path behind a per- workspace settings.ai.dictationStreaming flag, default false, with batch dictation as the default and fallback. Reuses the existing STT model and /ai-chat/transcribe — no new provider/model/endpoint fields. Lists the server + client touch points, acceptance criteria, and edge cases. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
106
docs/backlog/streaming-dictation-feature-toggle.md
Normal file
106
docs/backlog/streaming-dictation-feature-toggle.md
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
# Стриминговая (по тишине) диктовка под фиче-тогглом, по умолчанию ВЫКЛ
|
||||||
|
|
||||||
|
Статус: **открыто.**
|
||||||
|
|
||||||
|
## Контекст
|
||||||
|
|
||||||
|
Стриминговая диктовка (нарезка по тишине через Silero VAD,
|
||||||
|
`@ricky0123/vad-web`) уже в `develop` и сейчас **жёстко включена**: `MicButton`
|
||||||
|
получает проп `streaming` литералом `true` в двух местах — редактор
|
||||||
|
([dictation-group.tsx](../../apps/client/src/features/editor/components/fixed-toolbar/groups/dictation-group.tsx))
|
||||||
|
и чат
|
||||||
|
([chat-input.tsx](../../apps/client/src/features/ai-chat/components/chat-input.tsx)).
|
||||||
|
Фича экспериментальная:
|
||||||
|
|
||||||
|
- тяжёлые ассеты (ONNX-модель + ORT-wasm, 13–26 МБ, грузятся в браузер при
|
||||||
|
первом использовании);
|
||||||
|
- задержка инициализации модели на первом клике (компиляция wasm + подъём
|
||||||
|
inference-сессии — повторяется на каждую загрузку страницы);
|
||||||
|
- много мелких запросов на `/ai-chat/transcribe` (по одному на сегмент речи)
|
||||||
|
вместо одного на запись.
|
||||||
|
|
||||||
|
Её нужно сделать **opt-in на воркспейс, по умолчанию выключенной**, с обычной
|
||||||
|
батч-диктовкой как дефолтом и фолбэком.
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Спрятать стриминговый путь за булевым флагом воркспейса
|
||||||
|
`settings.ai.dictationStreaming` (default `false`). Выкл → текущая стабильная
|
||||||
|
батч-диктовка. Вкл → стриминговая.
|
||||||
|
|
||||||
|
**Минимализм (явно):** один булев флаг, переиспользуем существующую STT-модель
|
||||||
|
и эндпоинт `/ai-chat/transcribe`, **без новых полей провайдера / модели /
|
||||||
|
эндпоинта / секретов** — осознанное требование после претензий к realtime-PR
|
||||||
|
(#118) за лишние поля настроек.
|
||||||
|
|
||||||
|
## Дизайн
|
||||||
|
|
||||||
|
### Сервер
|
||||||
|
|
||||||
|
- В типе AI-настроек
|
||||||
|
([integrations/ai/ai.types.ts](../../apps/server/src/integrations/ai/ai.types.ts))
|
||||||
|
и в
|
||||||
|
[dto/update-ai-settings.dto.ts](../../apps/server/src/integrations/ai/dto/update-ai-settings.dto.ts)
|
||||||
|
добавить `dictationStreaming?: boolean` рядом с уже существующим флагом
|
||||||
|
`dictation`. Проверить, валидируется ли апдейт настроек по whitelist
|
||||||
|
(`ai-settings.service.ts`) — если да, внести ключ; иначе passthrough.
|
||||||
|
- Это **только клиентский поведенческий флаг**: эндпоинт транскрипции и
|
||||||
|
STT-модель не меняются (стриминг переиспользует `/ai-chat/transcribe`).
|
||||||
|
Флаг просто отдаётся в составе `settings.ai`, который клиент уже читает.
|
||||||
|
|
||||||
|
### Клиент
|
||||||
|
|
||||||
|
- Тип
|
||||||
|
[features/workspace/types/workspace.types.ts](../../apps/client/src/features/workspace/types/workspace.types.ts)
|
||||||
|
(`settings.ai`, рядом с `dictation?: boolean`): добавить
|
||||||
|
`dictationStreaming?: boolean`.
|
||||||
|
- UI
|
||||||
|
[ai-provider-settings.tsx](../../apps/client/src/features/workspace/components/settings/components/ai-provider-settings.tsx):
|
||||||
|
добавить Switch «Streaming dictation (cut on pauses)» **внутри/рядом** с
|
||||||
|
тумблером «Voice dictation» — активен только когда `dictation` включена (это
|
||||||
|
под-режим диктовки). Оптимистичный апдейт по образцу `dictation`
|
||||||
|
(см. `handleDictationToggle` и запись `ai: { ...ai, dictation: value }`),
|
||||||
|
пишет `settings.ai.dictationStreaming`. Default off. Новый i18n-ключ.
|
||||||
|
- Гейтинг: в `dictation-group.tsx` и `chat-input.tsx` заменить жёсткий
|
||||||
|
`streaming` (литерал `true`) на `streaming={settings.ai.dictationStreaming === true}`.
|
||||||
|
Проп `streaming` у `MicButton` уже выбирает хук (`useStreamingDictation` vs
|
||||||
|
`useDictation`) — там менять ничего не нужно.
|
||||||
|
|
||||||
|
## Критерии приёмки
|
||||||
|
|
||||||
|
- Свежий воркспейс (флага нет) → mic-кнопка использует **батч**-диктовку;
|
||||||
|
ассеты VAD (ONNX/wasm) **не грузятся** (ленивый `import()` в
|
||||||
|
`useStreamingDictation.start()` срабатывает только при `streaming` и клике,
|
||||||
|
которого при выкл не будет — оба хука инертны до `start()`).
|
||||||
|
- Тоггл вкл → стриминговая диктовка работает и в редакторе, и в чате.
|
||||||
|
- Тоггл выкл → возврат к батчу; стриминговые ассеты не подгружаются.
|
||||||
|
- Нет новых полей модели / эндпоинта / секрета — переиспользуется
|
||||||
|
диктовочная STT-модель и `/ai-chat/transcribe`.
|
||||||
|
- Флаг персистится на воркспейс и гейтится как прочие `settings.ai.*`.
|
||||||
|
|
||||||
|
## Затрагиваемые файлы (указатели)
|
||||||
|
|
||||||
|
- **Сервер:** `integrations/ai/ai.types.ts`,
|
||||||
|
`integrations/ai/dto/update-ai-settings.dto.ts`,
|
||||||
|
`integrations/ai/ai-settings.service.ts` (если есть нормализация/whitelist).
|
||||||
|
- **Клиент:** `features/workspace/types/workspace.types.ts`,
|
||||||
|
`features/workspace/components/settings/components/ai-provider-settings.tsx`
|
||||||
|
(Switch + i18n), `features/editor/components/fixed-toolbar/groups/dictation-group.tsx`,
|
||||||
|
`features/ai-chat/components/chat-input.tsx`.
|
||||||
|
|
||||||
|
## Заметки / краевые случаи
|
||||||
|
|
||||||
|
- Батч-диктовка остаётся дефолтом и фолбэком (в т.ч. если стриминговая
|
||||||
|
инициализация падает).
|
||||||
|
- Подтвердить, что выкл-состояние не тянет ни одного VAD-байта: `MicButton`
|
||||||
|
хоть и вызывает оба хука безусловно (правило хуков), оба инертны до
|
||||||
|
`start()`, поэтому при `streaming=false` модель/wasm не запрашиваются.
|
||||||
|
- **Не** добавлять отдельные модель/эндпоинт под стриминг — переиспользовать
|
||||||
|
диктовочные (явное требование после realtime-PR).
|
||||||
|
|
||||||
|
## Вне scope
|
||||||
|
|
||||||
|
- Preload / мгновенный старт и латентность инициализации модели — отдельный
|
||||||
|
follow-up.
|
||||||
|
- Realtime-websocket путь (PR #118, [streaming-dictation-plan.md](../streaming-dictation-plan.md))
|
||||||
|
— не мержится.
|
||||||
Reference in New Issue
Block a user