diff --git a/docs/backlog/stt-providers-and-async.md b/docs/backlog/stt-providers-and-async.md deleted file mode 100644 index 58da2879..00000000 --- a/docs/backlog/stt-providers-and-async.md +++ /dev/null @@ -1,192 +0,0 @@ -# STT: дополнительные провайдеры и переход на асинхронную схему - -> Статус: беклог / план развития. Контекст — фича «голосовая диктовка» (STT, -> speech-to-text): кнопка-микрофон в чате агента и в редакторе, аудио -> распознаётся на сервере через AI-провайдер воркспейса. Документ фиксирует -> (1) какие ещё форматы STT-API имеет смысл поддержать и как, и (2) как в -> будущем перейти с текущей синхронной схемы (push-to-talk) на асинхронную. - -## 1. Где мы сейчас - -Распознавание построено как **синхронный запрос-ответ**: - -- Клиент пишет звук (`MediaRecorder`), POST-ит blob → сервер распознаёт → - возвращает `{ text }`, который вставляется в ввод. Никакого состояния задачи нет. -- Клиентская часть: `apps/client/src/features/dictation/` (`hooks/use-dictation.ts`, - `components/mic-button.tsx`, `services/dictation-service.ts`). -- Эндпоинт: `POST /ai-chat/transcribe` - (`apps/server/src/core/ai-chat/ai-chat.controller.ts`) — фича-гейт - `settings.ai.dictation`, throttle, лимит 25 МБ, whitelist mime, вывод реальной - ошибки провайдера (`describeProviderError`), формат контейнера выводится из mime. -- Тонкая обёртка: `apps/server/src/core/ai-chat/ai-transcription.service.ts` → - делегирует в `AiService.transcribe(workspaceId, audio, format)`. -- Выбор кодировки запроса — **явное** поле `sttApiStyle` - (`apps/server/src/integrations/ai/ai.types.ts`, `SttApiStyle`, - `STT_API_STYLES`): - - `multipart` — OpenAI-совместимый `POST /v1/audio/transcriptions` (form-data) - через AI SDK (`createOpenAI(...).transcription()` + `experimental_transcribe`); - - `json` — OpenRouter-стиль: `POST {baseURL}/audio/transcriptions`, - `Content-Type: application/json`, тело `{ model, input_audio: { data:, format } }`, - ответ `{ text }` (`AiService.transcribeJsonBase64`). -- Поле прокладывается как любой не-секрет: `resolve()` / `getMasked()` / - whitelist в `AiSettingsService.update` - (`apps/server/src/integrations/ai/ai-settings.service.ts`) **и** массив - `ALLOWED` в `WorkspaceRepo.updateAiProviderSettings` - (`apps/server/src/database/repos/workspace/workspace.repo.ts`). -- UI: селектор «Request format» на карточке Voice / STT - (`apps/client/.../settings/components/ai-provider-settings.tsx`) + - кнопка «Test endpoint» (бэкенд-проба — тихий WAV через тот же `transcribe`). - -**Важно:** `multipart` уже покрывает почти всю экосистему — её реализуют OpenAI, -Azure OpenAI (Whisper), Groq, Together, Fireworks, DeepInfra, vLLM, LM Studio, -whisper.cpp/llama.cpp server, `speaches`, `faster-whisper-server`, WhisperX. -Для них **новый формат не нужен**, достаточно base URL + модель + ключ. -`json` покрывает OpenRouter. Ось `sttApiStyle` — это абстракция над -*контрактом запроса/ответа*: каждый реально иной контракт = одно значение enum -+ одна ветка-энкодер. - -### Точки расширения для нового СИНХРОННОГО формата (чек-лист) - -1. `ai.types.ts` — добавить значение в `SttApiStyle` и `STT_API_STYLES`. -2. `dto/update-ai-settings.dto.ts` — `@IsIn(STT_API_STYLES)` подхватит автоматически. -3. `ai.service.ts` — ветка в `transcribe()` + приватный энкодер - (по образцу `transcribeJsonBase64`): сборка запроса, заголовок авторизации, - `!res.ok` → бросок со статусом+телом (без утечки ключа), парс ответа в `text`. -4. Клиент: `ai-settings-service.ts` (тип `SttApiStyle`), опция в `