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>
6.9 KiB
Стриминговая (по тишине) диктовка под фиче-тогглом, по умолчанию ВЫКЛ
Статус: открыто.
Контекст
Стриминговая диктовка (нарезка по тишине через Silero VAD,
@ricky0123/vad-web) уже в develop и сейчас жёстко включена: MicButton
получает проп streaming литералом true в двух местах — редактор
(dictation-group.tsx)
и чат
(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)
и в
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
(
settings.ai, рядом сdictation?: boolean): добавитьdictationStreaming?: boolean. - UI
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уже выбирает хук (useStreamingDictationvsuseDictation) — там менять ничего не нужно.
Критерии приёмки
- Свежий воркспейс (флага нет) → 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) — не мержится.