Перейти к содержанию

Синхронное распознавание — STT v1

Синхронное распознавание преобразует аудио- или видеофайл в текст в рамках одного HTTP-запроса: вы отправляете запись — сервис возвращает распознанный текст.

Способ подходит для сравнительно коротких записей, когда результат нужен сразу. Для длинных файлов используйте асинхронное распознавание или асинхронный вариант этого метода.

Поддерживаются моно- и стереозаписи. В стереорежиме каждый канал считается отдельным говорящим, а реплики возвращаются в хронологическом порядке (диаризация по каналам).

HTTP-запрос

POST /api/stt/v1

Запрос отправляется в формате multipart/form-data и требует аутентификации — заголовок Authorization: Api-Key <ключ> (см. Аутентификация).

Параметры запроса

Параметр Тип Обязательный По умолчанию Описание
audio file да Аудио- или видеофайл с аудиодорожкой
lang string нет ru-RU Язык распознавания: Playground предлагает ru-RU, en-US, kk-KZ, ky-KG, uz-UZ и tg-TJ; GigaAM принимает только ru-RU, kk-KZ, ky-KG, uz-UZ. Передача locale не выбирает модель автоматически
topic string нет general Совместимое поле SpeechKit v1; сейчас принимается, но не влияет на маршрут или результат
format string нет oggopus Совместимое поле: lpcm, oggopus, amr. Для контейнеров формат определяется по файлу; загрузка headerless PCM в HTTP v1 пока не поддерживается
sampleRateHertz string нет 48000 Подсказка частоты: 8000, 16000, 48000; сама по себе не позволяет декодировать headerless PCM
channelMode string нет stereo Режим каналов: stereo — распознавание каждого канала отдельно (для mono-файла будет один канал); mono — сведение каналов в один
diarization boolean нет false Внешняя диаризация говорящих для mono. В stereo каналы уже считаются отдельными источниками, поэтому флаг не применяется; для ElevenLabs-Scribe-v2 отдельный внешний этап не запускается
maxSpeakers int нет Максимальное число говорящих при кластеризации во время диаризации (от 1 до 20)
llmCorrection boolean нет true LLM-коррекция распознанного текста. Включена по умолчанию — итоговый текст может отличаться от «сырого» результата ASR. Если LLM-бэкенд не настроен, возвращается исходный текст
entityExtraction boolean нет false Best-effort извлечение именованных сущностей из уже исправленного текста. Добавляет entities и entity_extraction_status
emotionAnalysis boolean нет false Параллельно с ASR запустить эмоциональную динамику DUSHA. Доступно только для русской речи
emotionIncludeSegments boolean нет false При включённом emotionAnalysis вернуть сглаженные окна в emotion_analysis.windows; эпизоды и session-метрики возвращаются всегда
emotionNegativeThreshold number нет серверный EMOTION_NEGATIVE_THRESHOLD, штатно 0.5 Порог 0..1 для сглаженной суммы P(angry) + P(sad) при выделении негативных эпизодов
summarizationPreset string нет Структурированная саммаризация окончательного текста. Доступны четырнадцать серверных пресетов. Если параметр не передан, LLM для саммаризации не вызывается
summarizationDetail string нет standard Подробность результата: brief, standard или detailed
summarizationOutputLanguage string нет язык записи BCP-47 tag языка сгенерированных полей саммаризации, например en-US. Не меняет транскрипт и учитывается только вместе с summarizationPreset
model string нет SpeechExpert-STT Модель распознавания
rawResults boolean нет false Запись чисел прописью (зарезервировано, пока не реализовано)

Query или форма

Параметры lang, channelMode, diarization, maxSpeakers, llmCorrection, entityExtraction, emotionAnalysis, emotionIncludeSegments, emotionNegativeThreshold, summarizationPreset, summarizationDetail и summarizationOutputLanguage можно передавать как в query-строке, так и в теле формы (multipart). Если параметр передан обоими способами, приоритет имеет значение из формы. Для emotion-параметров принимаются также snake_case-имена emotion_analysis, emotion_include_segments, emotion_negative_threshold.

Значение channelMode по умолчанию — stereo. Если нужен компактный mono-ответ с words/segments, передайте channelMode=mono явно, в том числе для физически одноканального файла.

Stereo — это независимые физические каналы

Значение по умолчанию stereo рассчитано на звонок, где собеседники записаны раздельно слева и справа. Если оба канала содержат один и тот же общий микс, автоматического удаления дублей сейчас нет и текст может повториться. Для обычной музыкальной, диктофонной или зеркальной стереозаписи передавайте channelMode=mono.

Модели распознавания

Идентификатор Описание
SpeechExpert-STT Рекомендуемая встроенная offline-модель SpeechExpert для телефонного качества
T-one T-One через Triton; для WebSocket и gRPC работает как настоящая потоковая модель, в HTTP v1 распознаёт готовый файл
GigaAM-v3-CTC Устаревший совместимый алиас GigaAM-Multilingual-Large-CTC; маршрутизация и тарификация явно используют multilingual replacement
GigaAM-Multilingual-Large-CTC Локальная 600M-модель ai-sage/GigaAM-Multilingual, проверенный snapshot ревизии large_ctc; публичный профиль для русского, казахского, кыргызского и узбекского с обязательной локальной оценкой kk/ky/uz
Whisper-Large-v3-Turbo Whisper Large v3 Turbo
ElevenLabs-Scribe-v2 ElevenLabs-Scribe-v2 через /v1/speech-to-text; в UI доступна для ru-RU, en-US, kk-KZ, ky-KG, uz-UZ, tg-TJ, используется ключ ELEVENLABS_API_KEY

Playground сначала предлагает выбрать язык, а затем показывает только совместимые модели. Та же матрица валидируется сервером для API: несовместимая пара отклоняется до распознавания, но API-клиент должен выбрать модель явно:

Язык Код Совместимые модели API и Playground
Русский ru-RU SpeechExpert-STT, T-One, Whisper, ElevenLabs, GigaAM Multilingual
Английский en-US Whisper, ElevenLabs
Казахский kk-KZ GigaAM Multilingual, ElevenLabs
Кыргызский ky-KG GigaAM Multilingual, ElevenLabs
Узбекский uz-UZ GigaAM Multilingual, ElevenLabs
Таджикский tg-TJ ElevenLabs

Английский декодер GigaAM существует, но в model card его качество обозначено как умеренное, поэтому он не включён в публичный профиль. Казахский, кыргызский и узбекский маршруты доступны, но требуют локальной оценки ASR и VAD на целевом домене, акцентах и акустике до production-включения: checkpoint MarbleNet не перечисляет kk, ky и uz среди языков обучения. Таджикский не добавлен в GigaAM: для tg нет готового large_ctc декодера и опубликованной приемлемой оценки. При этом официальный профиль ElevenLabs Scribe v2 поддерживает русский, английский, казахский, кыргызский, узбекский и таджикский; в Playground tg-TJ поэтому показывает только совместимую модель ElevenLabs. GigaAM выполняет синхронный GPU inference вне event loop в отдельном worker, а MarbleNet вызывается асинхронно по gRPC в уже существующем tone-triton. Конкурентность обоих этапов ограничивается независимыми семафорами API и GigaAM worker.

Locale сохраняется в последующих шагах: LLM-коррекция и извлечение сущностей получают выбранный код языка. Саммаризация по умолчанию формирует результат на основном языке расшифровки, но summarizationOutputLanguage независимо задаёт язык сгенерированных полей. Русские deterministic-нормализаторы чисел, ненормативной лексики и телефонов не запускаются для kk, ky и uz; русская модель восстановления пунктуации для multilingual-профиля отключена.

Миграция с Vosk

Vosk и его контейнеры удалены. Сохранённые идентификаторы Vosk 0.62, Vosk 0.56, Vosk 0.54, Zipformer2-RU, Vosk-Streaming-RU, Sherpa-Streaming-RU и SpeechExpert-STT-Streaming временно принимаются как устаревшие алиасы и маршрутизируются в T-One. Они также тарифицируются как T-one; отдельного тарифа Vosk больше нет, а исходное имя сохраняется только в истории ранее созданных записей и транзакций. Для новых интеграций указывайте T-one.

Сравнение моделей на телефонном датасете

Внутренний прогон моделей на датасете телефонного качества:

Модель WER edits RTFX
SpeechExpert-STT 20.871 % 7475 323.599
ElevenLabs-Scribe-v2 33.832 % 12117 8.108

WER и edits — чем меньше, тем лучше. RTFX — чем больше, тем быстрее.

Форматы аудио

Формат Расширение Описание
WAV .wav PCM 16 бит, любая частота дискретизации
Аудиоконтейнеры .mp3, .m4a, .ogg, .oga, .opus, .webm, .flac, .aac, .amr, .wma Конвертируются через ffmpeg
Видеоконтейнеры .mp4, .mov, .mkv, .avi, .mpeg, .mpg, .3gp, .3gpp, .webm Извлекается аудиодорожка и конвертируется через ffmpeg

Автоопределение формата

Если вы отправляете обычный аудио- или видеофайл, формат определяется автоматически и дополнительные параметры не нужны. Headerless PCM в HTTP v1 сейчас не декодируется, даже если переданы format=lpcm и sampleRateHertz; для сырого PCM используйте WebSocket или gRPC, либо заверните данные в WAV.

Ответ

Режим mono

В ответе всегда есть поле result — распознанный текст целиком. Для моделей, поддерживающих word offsets, дополнительно возвращаются words с нативными таймстемпами модели и segments, сгруппированные для интерфейса и экспорта SRT/VTT. Это относится и к Riva, и к GigaAM-Multilingual-Large-CTC:

{
  "result": "добрый день",
  "words": [
    {"text": "добрый", "start_ms": 340, "end_ms": 720, "speaker": null},
    {"text": "день", "start_ms": 760, "end_ms": 1080, "speaker": null}
  ],
  "segments": [
    {
      "text": "добрый день",
      "start_ms": 340,
      "end_ms": 1080,
      "speaker": null,
      "timing_source": "riva",
      "timing_estimated": false
    }
  ],
  "timing_source": "riva",
  "timing_estimated": false
}

Если речь не распознана, result содержит пустую строку.

Время задаётся в миллисекундах от начала исходного аудио, до VAD, интервалом [start_ms, end_ms). words остаются сырыми словами Riva; пунктуация и LLM-коррекция применяются к тексту segments, не меняя их реальные границы. Соседние слова объединяются в сегмент, пока пауза между ними не превышает 500 мс.

Для GigaAM Multilingual модель marblenet_vad в существующем tone-triton сначала строит интервалы речи с разрешением 20 мс и ограничивает каждую реплику 24 секундами. Публичный Triton wrapper принимает сырое mono-аудио 16 кГц, выполняет точный NeMo mel-препроцессинг и вызывает ONNX-core через BLS. Длинная запись обрабатывается блоками по 300 секунд с 2-секундным контекстом; после сшивания каждый исходный кадр присутствует ровно один раз. GigaAM возвращает нативные CTC-границы слов внутри реплики, после чего API добавляет начало VAD-интервала. Поэтому words[].start_ms/end_ms относятся к исходному файлу, segments соответствуют распознанным VAD-репликам, а верхнеуровневый timing_source равен gigaam-ctc+marblenet-vad и timing_estimatedfalse. Смежные части одной реплики, появившиеся только из-за 24-секундного лимита, распознаются общим GigaAM-запросом с внутренним overlap; нативные слова затем назначаются исходным VAD-сегментам по midpoint ровно один раз. Интервалы, разделённые реальной паузой, остаются отдельными запросами.

Успешный пустой ответ VAD означает строгую тишину: GigaAM не вызывается, result остаётся пустым, а временная разметка не публикуется (words, segments, timing_source и timing_estimated равны null). Если MarbleNet недоступен, сервис по умолчанию распознаёт весь файл целиком и сохраняет нативные CTC-таймкоды (timing_source=gigaam-ctc). При MARBLENET_VAD_REQUIRED=true такой сбой завершает запрос ошибкой вместо fallback.

Если выбранная модель не возвращает нативные offsets, поля words, segments, timing_source и timing_estimated не заполняются (null) — в mono сервис не подставляет искусственный диапазон 0..duration. Если Riva вернула полный текст, но только часть word offsets, result сохраняется целиком, words остаются доступными, а segments не публикуются, чтобы неполная разметка не обрезала текст и не включала ошибочный экспорт субтитров.

При channelMode=mono&diarization=true внешний diarization-пайплайн вместо words/segments возвращает utterances с кластерным номером speaker и границами реплики. Верхнеуровневый result в этом режиме имеет вид [0:00:03] Спикер 1: ...; maxSpeakers ограничивает число кластеров. Эти границы относятся к репликам диаризации и не выдаются за нативные word offsets ASR.

Режим stereo

Используется для стереозаписей, где в каждом канале — отдельный говорящий. Реплики выделяются, распознаются по каналам и возвращаются в хронологическом порядке.

Поле Тип Описание
result string Весь распознанный текст, склеенный в порядке реплик
speakers array Цельный текст каждого канала
utterances array Реплики каналов в хронологическом порядке
timing_source string riva, gigaam-ctc, marblenet-vad, gigaam-ctc+marblenet-vad, synthetic или mixed — источник границ реплик
timing_estimated boolean false только если все опубликованные границы нативные

Каждый элемент массива utterances:

Поле Тип Описание
speaker int Номер говорящего (канала): 0 — левый, 1 — правый
start_ms int Начало реплики в миллисекундах от начала файла
end_ms int Конец реплики в миллисекундах
text string Распознанный текст реплики

Пример ответа для стереозаписи:

{
  "result": "Привет Как дела Нормально",
  "utterances": [
    { "speaker": 0, "start_ms": 0, "end_ms": 520, "text": "Привет" },
    { "speaker": 1, "start_ms": 600, "end_ms": 1200, "text": "Как дела" },
    { "speaker": 0, "start_ms": 1250, "end_ms": 2100, "text": "Нормально" }
  ]
}

Диаризация по каналам

При channelMode=stereo каждый канал распознаётся отдельно (моно тоже допускается — тогда будет один канал). Каналы нумеруются с 0; для стерео это левый канал 0 и правый канал 1. В speakers возвращается цельный текст каждого канала, а в utterances — цельные реплики с таймингами в хронологическом порядке.

Если модель вернула полное выравнивание слов, границы реплик нативные и timing_source равен riva. Если выравнивания нет или оно неполное, для сохранения стереоконтракта текст канала получает оценочный диапазон на всю длительность канала (synthetic, timing_estimated: true). При сочетании источников возвращается mixed.

Матрица временной разметки файлового STT v1:

Маршрут Mono без диаризации Stereo
SpeechExpert-STT Нативные words и производные segments, если Riva вернула полное выравнивание Нативные границы реплик при полном выравнивании каждого канала
GigaAM-v3-CTC, GigaAM-Multilingual-Large-CTC Нативные CTC words; segments по MarbleNet VAD с абсолютными границами исходного файла utterances каждого канала получают абсолютные границы MarbleNet VAD; при недоступном VAD сохраняется совместимый диапазон канала
T-one, Whisper-Large-v3-Turbo, ElevenLabs-Scribe-v2 Текст без word offsets Реплики с оценочным диапазоном synthetic, если у маршрута нет выравнивания
Любая модель с channelMode=mono&diarization=true utterances с границами diarization-пайплайна вместо word offsets Не применяется

speakers[] содержит объекты {"channel": 0, "result": "..."}. Элементы words[] имеют text, start_ms, end_ms и необязательный speaker, а segments[] — те же текстовые и временные поля плюс timing_source и timing_estimated. Пользовательские подписи спикеров могут храниться в speaker_names; это поле не создаётся ASR и описано в разделе «Переименование спикеров».

Эмоциональная динамика

При emotionAnalysis=true сервис использует тот же канонический WAV, запускает DUSHA параллельно с ASR и возвращает слой аналитики рядом с транскриптом. Новая загрузка в отдельный Emotion API не нужна. После BCP-47-нормализации функция принимает только lang=ru и lang=ru-RU; например, сочетание lang=kk-KZ&emotionAnalysis=true отклоняется до inference.

{
  "result": "я уже третий раз объясняю одну и ту же проблему",
  "utterances": [
    {"speaker": 1, "start_ms": 814000, "end_ms": 823000, "text": "я уже третий раз объясняю одну и ту же проблему"}
  ],
  "emotion_analysis_status": "ready",
  "emotion_analysis": {
    "model": "dusha_emotion_ensemble_wav2vec_conformer_calibrated",
    "model_version": "1",
    "calibration_version": "sha256:...",
    "language": "ru-RU",
    "validated_languages": ["ru-RU"],
    "window_seconds": 3.0,
    "hop_seconds": 1.0,
    "negative_threshold": 0.5,
    "ema_alpha": 0.2835,
    "metadata": {
      "episode_min_windows": 2,
      "episode_min_duration_ms": 2000,
      "episode_merge_gap_ms": 1500,
      "uncertain_top1_threshold": 0.45,
      "uncertain_margin_threshold": 0.1,
      "positive_threshold": 0.4,
      "trend_window_share": 0.2,
      "trend_min_delta": 0.05
    },
    "session": {
      "duration_ms": 1200000,
      "window_count": 1198,
      "negative_share": 0.087,
      "positive_share": 0.14,
      "uncertain_share": 0.09,
      "mean_negative_score": 0.18,
      "peak_negative_score": 0.84,
      "peak_negative_raw_score": 0.89,
      "peak_negative_ema": 0.84,
      "peak_at_ms": 818000,
      "final_negative_ema": 0.12,
      "first_negative_mean": 0.43,
      "last_negative_mean": 0.12,
      "deescalation_score": 0.31,
      "deescalation_trend": "improved",
      "deescalated": true,
      "episode_count": 3,
      "probs": {
        "angry": 0.16,
        "neutral": 0.58,
        "other": 0.08,
        "positive": 0.09,
        "sad": 0.09
      }
    },
    "episodes": [
      {
        "label": "negative",
        "start_ms": 814000,
        "end_ms": 823000,
        "duration_ms": 9000,
        "peak_score": 0.84,
        "peak_at_ms": 818000,
        "mean_score": 0.72,
        "negative_score": 0.84,
        "confidence": 0.68,
        "mean_confidence": 0.68,
        "max_confidence": 0.76,
        "prediction": "angry",
        "window_count": 7,
        "speaker": 1,
        "utterance_indices": [0],
        "segment_indices": [],
        "transcript_excerpt": "я уже третий раз объясняю одну и ту же проблему",
        "probs": {
          "angry": 0.68,
          "neutral": 0.05,
          "other": 0.07,
          "positive": 0.04,
          "sad": 0.16
        }
      }
    ],
    "windows": null
  }
}

Числа в примере иллюстративны. windows заполняется только при emotionIncludeSegments=true; каждое окно содержит исходную prediction, confidence, все пять probabilities, raw и EMA negative-score, а также индексы пересекающихся реплик/сегментов. Эпизод возвращает как mean_confidence, так и max_confidence; поле confidence остаётся alias среднего значения.

Оконный negative_score равен P(angry) + P(sad) и сглаживается EMA. negative_share взвешивается по покрытию исходной временной шкалы. peak_negative_score и его явный alias peak_negative_ema — максимум сглаженного сигнала; несглаженный максимум хранится отдельно в peak_negative_raw_score. peak_at_ms указывает позицию сглаженного пика. positive_share и uncertain_share считаются как доля окон соответствующего производного состояния. deescalation_score — среднее сглаженное значение первых 20% окон минус среднее последних 20%; положительное значение означает снижение негативного сигнала, отрицательное — рост. Сервер объединяет короткие разрывы и отбрасывает одиночные/слишком короткие события; точные правила возвращаются в metadata.

Для интерфейса пять raw классов сводятся к negative, neutral, positive и uncertain. uncertain — производное состояние низкой уверенности или малого отрыва top-1 от top-2, а не шестой класс модели. other означает только другую или неопределённую акустическую окраску.

Связь со спикером публикуется лишь при однозначном пересечении с mono- диаризацией. Для channelMode=stereo текущий интегрированный анализ выполняется после downmix и намеренно не приписывается конкретному каналу; используйте utterance_indices для контекста, а не для автоматической оценки собеседника.

Emotion-обработка best-effort: её сбой не отменяет распознавание. Тогда emotion_analysis равен null, а emotion_analysis_statusfailed или unavailable. Результат вероятностный и предназначен для навигации к исходному аудио и ручной проверки, а не для автоматического решения о человеке. Полные ограничения и отдельный endpoint описаны в Emotion API.

Для ru-RU тот же флаг независимо запускает анализ слов финальной транскрипции. Он возвращается в text_emotion_analysis со своим text_emotion_analysis_status и не меняет акустические score, эпизоды или ranking. Текстовый model_score — sigmoid score модели, не калиброванная вероятность истины; provenance включает исходную цитату, clause, таймкоды, speaker и индексы utterance/segment.

Извлечение именованных сущностей

При entityExtraction=true обработка выполняется в порядке ASR → опциональная LLM-коррекция (если llmCorrection=true) → извлечение сущностей. Поэтому mention и символьные offsets относятся к тому тексту, который фактически возвращён клиенту, а не к сырому результату модели. Сбой извлечения не отменяет успешную транскрибацию.

Поддерживаются типы person, organization, location, product, event, date, time, money, phone, email, url, address, document и identifier. Одинаковые упоминания группируются по типу и нормализованному значению:

{
  "result": "Анна позвонила в ACME",
  "entities": [
    {
      "id": "person:1",
      "type": "person",
      "text": "Анна",
      "normalized": "Анна",
      "occurrences": [
        {
          "utterance_index": null,
          "mention": "Анна",
          "char_start": 0,
          "char_end": 4,
          "speaker": null,
          "start_ms": null,
          "end_ms": null
        }
      ]
    }
  ],
  "entity_extraction_status": "ready"
}

Offsets используют полуоткрытый интервал [char_start, char_end). utterance_index указывает индекс строки-источника: элемента utterances, если они есть, иначе элемента segments; при извлечении непосредственно из верхнеуровневого result он равен null. Временные поля в occurrence — это грубые границы исходной реплики/сегмента, а не отдельного слова.

Статус Значение
ready Все части текста обработаны, включая корректный пустой результат
partial Часть чанков или элементов не удалось проверить; валидные сущности сохранены
failed Извлечение завершилось ошибкой; транскрипт возвращён, entities пуст
unavailable Извлечение запрошено, но LLM-ключ не настроен

Если entityExtraction=false, оба поля сущностей не заполняются и равны null. Коррекция использует модель из LLM_CORRECTION_MODEL (штатно google/gemini-3.5-flash-minimal). Пустые LLM_ENTITY_EXTRACTION_* наследуют endpoint, модель и ключ коррекции; при необходимости NER можно настроить отдельно.

Саммаризация по пресетам

summarizationPreset запускается после коррекции и извлечения сущностей. То есть резюме, ссылки и цитаты относятся к окончательному тексту. Модель контролируется сервером через LLM_SUMMARIZATION_MODEL; значение по умолчанию — google/gemini-3.1-flash-lite.

Язык результата

Все сгенерированные человекочитаемые значения — заголовки, резюме, пункты, вопросы, ответы, задачи и элементы готовых наборов — по умолчанию возвращаются на преобладающем языке оригинальной речи. Язык определяется по тексту транскрипции; lang/locale служит только вспомогательной подсказкой. Явный summarizationOutputLanguage в распознавании или outputLanguage в JSON-методах саммаризации переопределяет это правило только для сгенерированных значений.

Технические поля (source_ref, таймкоды, спикеры и каналы), имена JSON-полей, серверные sources[].quote, ссылки, имена, идентификаторы и дословные фрагменты сохраняются в оригинале. Язык инструкции и англоязычная JSON Schema не подменяют выбранный язык. Для полного перевода транскрипта используйте отдельный POST /api/stt/v1:translate.

Пресет Назначение
short Резюме в 2–4 предложениях и до пяти ключевых пунктов
notes Тематический или хронологический конспект
conversation Цель, позиции участников, ключевые факты и итог разговора
action_items Решения, задачи, исполнители, сроки и открытые вопросы
topics_timestamps Темы в хронологическом порядке с переходом к началу каждой темы
lecture Учебный конспект: определения, аргументы, примеры и процедуры
sales_crm Потребности, квалификация, возражения, сигналы покупки и следующие шаги
contact_center Причина обращения, проблема, обработка, результат, эскалация и обещания
interview Вопросы или темы, содержательные ответы, мнения и уточнения
research_report Повторяющиеся темы, JTBD, возражения и доказательные выводы по одному или нескольким интервью
podcast_chapters Обзор выпуска и хронологические главы с таймкодами
voice_inbox Классификация голосовых мыслей на задачи, события, идеи, заметки и вопросы
study_pack Конспект, карточки, проверочные вопросы и словарь
creator_pack Тезисы, главы, фрагменты с таймкодами, полная статья, show notes и публикации

Параметр summarizationDetail не меняет сценарий, а регулирует объём: brief оставляет только самое важное, standard даёт сбалансированный результат, detailed сохраняет существенные детали, примеры, возражения и контекст.

Для research_report специализированные результаты находятся в summarization.workflow:

  • research_themes — повторяющиеся темы с названием, выводом и источниками;
  • jobs_to_be_done — JTBD-формулировка и отдельно подтверждённые ситуация, мотивация и ожидаемый результат;
  • objections — явно высказанные барьеры, сомнения и причины отказа;
  • доказательные выводы возвращаются в structured.key_points.

Сервер удаляет исследовательский элемент без существующего источника. Для повторяющейся темы нужны минимум две независимые реплики; в дайджесте нескольких интервью источники должны относиться минимум к двум разным записям. Точная доля респондентов не рассчитывается: источники подтверждают повторяемость, но не заменяют статистический анализ всей выборки.

Доказательная структура результата

При успехе ответ содержит summarization_status: "ready". Поля key_points, sections, decisions, action_items, open_questions и risks остаются массивами строк для обратной совместимости. Дополнительное поле structured содержит те же элементы со ссылками на исходный текст:

{
  "summarization_status": "ready",
  "summarization": {
    "schema_version": "2.0",
    "id": "4a518cc8-4f70-470d-b868-40e39420fba5",
    "created_at": "2026-07-15T12:00:00+00:00",
    "preset": "action_items",
    "detail_level": "standard",
    "output_language": "en-US",
    "title": "Project launch",
    "summary": "The team agreed on the launch.",
    "key_points": ["The launch is scheduled for Friday"],
    "sections": [],
    "decisions": ["Launch the project on Friday"],
    "action_items": ["Anna will prepare the release"],
    "open_questions": [],
    "risks": [],
    "structured": {
      "key_points": [],
      "sections": [],
      "decisions": [
        {
          "text": "Launch the project on Friday",
          "confirmation": "confirmed",
          "sources": [
            {
              "ref": "S0003",
              "anchor_id": "c26f6b0ee94de908b7106e21",
              "quote": "Да, запускаем в пятницу.",
              "start_ms": 42100,
              "end_ms": 43800,
              "speaker": 1,
              "speaker_name": "Руководитель",
              "utterance_index": 2,
              "segment_index": null,
              "timing_estimated": false
            }
          ]
        }
      ],
      "action_items": [
        {
          "text": "Anna will prepare the release",
          "assignee": "Анна",
          "due_date": "by Friday",
          "sources": [
            {
              "ref": "S0004",
              "quote": "Анна подготовит релиз до пятницы.",
              "start_ms": 44000,
              "end_ms": 45600,
              "speaker": 1,
              "utterance_index": 3,
              "timing_estimated": false
            }
          ]
        }
      ],
      "open_questions": [],
      "risks": []
    }
  }
}

summarization.output_language содержит нормализованный явно запрошенный tag; значение null означает автоматический выбор языка по транскрипции.

LLM возвращает только короткие source_ref, а сервер подставляет цитату, таймкод, спикера и индекс из исходного STTResponse. Неизвестные ссылки отбрасываются; утверждение или элемент workflow без хотя бы одного валидного источника не публикуется. Поэтому клиенту нельзя вычислять время из текста ответа LLM. Если у расшифровки нет utterances или segments, у источника остаётся цитата, но временные поля равны null. timing_estimated=true означает, что исходные границы были оценочными.

Для решений confirmation принимает confirmed, proposed, rejected или unclear. Поля задачи assignee и due_date заполняются только при явном упоминании. Отсутствующие данные представлены null, пустой строкой или массивом; предположение не превращается в подтверждённое решение.

Сбой провайдера не отменяет готовую транскрибацию: статус будет failed, а при отсутствующем LLM-ключе — unavailable; в обоих случаях summarization равен null.

Новый результат без повторного распознавания

Для уже готового текста используется только LLM-постобработка:

POST /api/stt/v1:summarize
Content-Type: application/json

Метод не читает аудиофайл, не запускает ASR и не списывает аудиоминуты. Нужно передать ровно один источник: полный response ещё не сохранённого результата или recordingId записи текущего пользователя. lang служит подсказкой о языке исходной речи. Необязательный outputLanguage независимо задаёт язык сгенерированных полей; без него используется преобладающий язык транскрипции. У сохранённой записи locale дополнительно берётся из её метаданных.

curl -X POST "https://api.speech.example.com/api/stt/v1:summarize" \
  -H "Authorization: Api-Key $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "preset": "topics_timestamps",
    "detailLevel": "detailed",
    "lang": "ru-RU",
    "outputLanguage": "en-US",
    "response": {
      "result": "Готовый распознанный текст",
      "segments": []
    }
  }'
curl -X POST "https://api.speech.example.com/api/stt/v1:summarize" \
  -H "Authorization: Api-Key $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "preset": "sales_crm",
    "detailLevel": "standard",
    "outputLanguage": "kk-KZ",
    "recordingId": "7ae1ee00-4fb3-4c43-83c3-53568ef06b0b"
  }'

Ответ содержит summarization, summarization_status, recordingId, recording_updated, summaryVersion и activeSummaryVersionId. Для сохранённой записи каждый успешный запуск создаёт неизменяемую версию и делает её активной. Активная версия также проецируется в прежние поля response.summarization и response.summarization_status, поэтому старые клиенты продолжают видеть один текущий результат. Ошибка LLM не создаёт версию и не удаляет предыдущую.

История результатов сохранённой записи

Методы /api/account/... в этом и следующих разделах требуют активную cookie- сессию портала и пользователя-владельца записи. Один API-ключ без portal-cookie для них недостаточен. Публичные методы /api/stt/... по-прежнему принимают обычную API-аутентификацию.

Метод Назначение
GET /api/account/recordings/{recording_id}/summaries Версии от новой к старой и activeSummaryVersionId
POST /api/account/recordings/{recording_id}/summaries/{summary_id}:activate Сделать версию активной
DELETE /api/account/recordings/{recording_id}/summaries/{summary_id} Удалить версию

Элемент списка содержит снимок summarization, пресет, подробность, выбранный output_language, модель, статус, время создания и is_active. Для результата, созданного собственным шаблоном, сохраняются template_id, имя и номер ревизии. При первом чтении истории старая запись с единственным response.summarization лениво получает одну legacy-версию. Если удалить активную версию, активируется самая новая из оставшихся; если версий больше нет, прежняя проекция очищается.

Собственные шаблоны

Шаблон задаёт дополнительный фокус, но не может отменить схему ответа, выбранный язык результата, сохранение исходных цитат, проверку источников и запрет на выдуманные факты:

Метод Назначение
GET /api/account/summary-templates Список шаблонов пользователя
POST /api/account/summary-templates Создать шаблон
PATCH /api/account/summary-templates/{template_id} Изменить и увеличить revision
DELETE /api/account/summary-templates/{template_id} Удалить шаблон
{
  "name": "Разбор исследования",
  "description": "Гипотезы, ограничения и следующие эксперименты",
  "instruction": "Отдельно перечисли подтверждения и опровержения гипотез.",
  "base_preset": "lecture",
  "default_detail": "detailed"
}

Чтобы применить шаблон, передайте templateId в POST /api/stt/v1:summarize. preset и detailLevel можно указать явно; иначе используются base_preset и default_detail шаблона. В готовой версии сохраняется снимок имени, инструкции и ревизии, поэтому дальнейшее изменение шаблона не меняет исторический результат.

Дайджест нескольких записей

POST /api/stt/v1:summarizeDigest
Content-Type: application/json

Метод принимает от 2 до 20 разных сохранённых записей текущего пользователя:

{
  "recordingIds": [
    "7ae1ee00-4fb3-4c43-83c3-53568ef06b0b",
    "4a518cc8-4f70-470d-b868-40e39420fba5"
  ],
  "preset": "research_report",
  "detailLevel": "detailed",
  "outputLanguage": "en-US",
  "title": "Исследование по 20 интервью"
}

В источниках структурированного результата дополнительно появляются recording_id и recording_name; ссылки имеют вид R01S0001. Ответ содержит digestId, summarization, статус и исходный список recordingIds. Успешный результат сохраняется и доступен через:

Метод Назначение
GET /api/account/summary-digests?offset=0&limit=50 Список дайджестов
GET /api/account/summary-digests/{digest_id} Один дайджест
DELETE /api/account/summary-digests/{digest_id} Удалить дайджест

Вместо preset можно передать templateId. Все записи и шаблон проверяются по владельцу; отсутствующий или чужой объект возвращается как 404. Для обычных пресетов суммарный контекст ограничен 200 000 символов. research_report при большем объёме автоматически переходит на доказательный map-reduce: каждая запись анализируется отдельно, затем сервер сводит только отобранные наблюдения и исходные реплики в общий отчёт. Общий лимит исследовательского дайджеста — 1 500 000 символов, то есть при 20 записях доступно в среднем до 75 000 символов на интервью без обрезания принятой расшифровки. При превышении соответствующего лимита возвращается 413.

Промежуточные выводы не получают синтетических ссылок: на обоих этапах используются исходные RxxSxxxx, а финальный reducer принимает только ссылки, которые реально попали в проверенный набор доказательств. Повторяющаяся тема обязана ссылаться минимум на две разные записи. Ограниченный набор доказательств не используется для заявлений о процентах, большинстве или частоте. Без outputLanguage язык дайджеста определяется по преобладающему объёму речи во всём объединённом контексте; locale отдельных записей и поле lang остаются подсказками. Явный BCP-47 tag задаёт язык сгенерированных выводов, но цитаты в источниках остаются на языках записей.

Готовые пользовательские наборы

Для сохранённой записи отдельный метод создаёт версионируемый результат без повторного ASR:

POST /api/stt/v1:derive
Content-Type: application/json
{
  "recordingId": "7ae1ee00-4fb3-4c43-83c3-53568ef06b0b",
  "kind": "study_pack",
  "detailLevel": "standard",
  "outputLanguage": "tg-TJ"
}

kind принимает voice_inbox, study_pack, action_center или creator_pack. Результат использует ту же контролируемую сервером модель (по умолчанию google/gemini-3.1-flash-lite) и те же проверенные сервером источники. В summarization.workflow находятся специализированные поля:

  • inbox_items — задача, событие, идея, заметка или вопрос;
  • flashcards, quiz, glossary — учебный набор;
  • clips — кандидаты фрагментов; таймкод берётся из проверенного источника;
  • content_drafts — полная статья, show notes, описание видео, рассылка и отдельные публикации для общего social-формата, Telegram и VK.

Creator Pack также заполняет доказательные тезисы в structured.key_points и хронологические главы в structured.sections. Вид article_outline сохраняется для совместимости, а полный материал имеет kind=article и сохраняет Markdown- заголовки и абзацы. Для встроенного long-form сценария сервер отдельно создаёт редакционный разбор, статью и набор публикаций, затем объединяет их и проверяет содержательность каждого формата. Недостающие части запрашиваются ещё один раз; неполный после повторной проверки long-form набор не получает статус ready. Короткие записи до 4000 символов и пользовательские шаблоны сохраняют прежний best-effort режим, чтобы полезный частичный результат не терялся. SRT и VTT формируются отдельно из STT-таймингов: если исходная модель не вернула временные границы, интерфейс не выдумывает их из ответа LLM.

Все сгенерированные текстовые поля workflow наследуют общий языковой контракт саммаризации и учитывают необязательный outputLanguage.

Ответ содержит resultId, versionId, sourceFingerprint, статус и payload. Повторный запуск добавляет неизменяемую версию к тому же логическому результату. Чтение и переключение версий:

Метод Назначение
GET /api/account/recordings/{recording_id}/derived-results Все виды и их активные версии
GET /api/account/recordings/{recording_id}/derived-results/{result_id}/versions История одного набора
POST /api/account/recordings/{recording_id}/derived-results/{result_id}/versions/{version_id}:select Сделать версию активной
DELETE /api/account/recordings/{recording_id}/derived-results/{result_id}/versions/{version_id} Удалить версию
DELETE /api/account/recordings/{recording_id}/derived-results/{result_id} Удалить логический результат со всеми версиями

У списка derived-results есть необязательный query-фильтр kind. После удаления активной версии сервер выбирает следующую доступную версию; если версий не осталось, удаляется и пустой логический результат.

Центр поручений

Задачи, решения и требующие проверки элементы Voice Inbox копируются из неизменяемого результата в отдельную очередь. Пользователь может подтвердить, исправить или отклонить кандидат, не меняя исходный ответ модели:

Метод Назначение
GET /api/account/recordings/{recording_id}/review-items Очередь подтверждения
PATCH /api/account/recordings/{recording_id}/review-items/{item_id} Статус, исправленное значение или заметка

Поддерживаются статусы pending, confirmed, corrected, dismissed и UI-алиасы accepted, edited, rejected. Поле anchors содержит серверные источники; sourceFingerprint связывает подтверждение с конкретной версией транскрипта. У GET доступны фильтры status и kind. Тело PATCH может содержать status, replacement и note; исходный LLM-результат при этом не изменяется.

Перевод готового транскрипта

Полный перевод — отдельная LLM-обработка, которая не запускает ASR, не меняет исходный транскрипт и не заменяет текущую саммаризацию:

POST /api/stt/v1:translate
Content-Type: application/json

Нужно передать ровно один источник: полный response или recordingId принадлежащей пользователю записи. targetLanguage обязателен, а sourceLanguage — необязательная подсказка. Языки задаются нормализуемыми BCP-47 tags:

{
  "sourceLanguage": "ru-RU",
  "targetLanguage": "en-US",
  "response": {
    "result": "Да, запускаем в пятницу.",
    "segments": [
      {"start_ms": 42100, "end_ms": 43800, "text": "Да, запускаем в пятницу."}
    ]
  }
}
{
  "recordingId": "7ae1ee00-4fb3-4c43-83c3-53568ef06b0b",
  "targetLanguage": "kk-KZ"
}

При успехе возвращается status: "ready" и отдельный объект translation:

{
  "status": "ready",
  "translation": {
    "schema_version": "1.0",
    "id": "da962058-770c-4d50-a5bb-77b75ea5a929",
    "created_at": "2026-07-16T12:00:00+00:00",
    "source_language": "ru-RU",
    "target_language": "en-US",
    "text": "Yes, we are launching on Friday.",
    "segments": [
      {
        "source_ref": "S0001",
        "source_text": "Да, запускаем в пятницу.",
        "text": "Yes, we are launching on Friday.",
        "source": {
          "ref": "S0001",
          "quote": "Да, запускаем в пятницу.",
          "start_ms": 42100,
          "end_ms": 43800,
          "segment_index": 0,
          "timing_estimated": false
        }
      }
    ]
  },
  "recordingId": null,
  "resultId": null,
  "versionId": null,
  "sourceFingerprint": "d3135a..."
}

Сервер переводит полные исходные фрагменты, проверяет сохранение всех source_ref и затем восстанавливает их порядок и серверные источники. Поля source_text, source.quote, таймкоды и спикеры относятся к оригиналу; новые word-level таймкоды для переведённых слов не выдумываются. Верхнее поле translation.text — соединённый по порядку текст переведённых фрагментов.

Для длинного текста сервер формирует пакеты не более 40 фрагментов и примерно 16 000 символов, запускает их асинхронно и пропускает запросы через общий LLM-семафор и pacing. Сам HTTP-метод возвращает ответ после завершения всех пакетов. Общий предел исходного текста — 500 000 символов; превышение даёт 413. Если LLM не настроена, возвращается status: "unavailable", при ошибке обработки — status: "failed"; в обоих случаях translation равен null.

Для POST /api/stt/v1:translate действует отдельный sliding-window rate limit. У аутентифицированного запроса лимит привязан к пользователю и общий для его сессии и API-ключей; если пользователь не определён, используется IP-адрес. По умолчанию разрешено 5 запросов за 60 секунд. Значения настраиваются через STT_TRANSLATION_RATE_LIMIT_MAX_REQUESTS и STT_TRANSLATION_RATE_LIMIT_WINDOW_SECONDS. При превышении сервер отвечает 429 Too Many Requests и передаёт число секунд до следующей попытки в заголовке Retry-After. Счётчики хранятся в памяти процесса, поэтому для строгого общего лимита при нескольких workers или replicas нужен дополнительный лимит на reverse proxy/API gateway либо общее хранилище.

Успешный перевод по recordingId сохраняется как производный результат kind=translation: отдельный целевой язык образует отдельный логический результат, а повторный запуск добавляет версию. Ответ тогда также содержит resultId, versionId и sourceFingerprint; перевод доступен через методы derived-results, перечисленные выше. Исходные поля записи и response.summarization при этом не изменяются. В интерфейсе Мои записи доступны русский, английский, казахский, кыргызский, узбекский и таджикский целевые языки; API принимает любой tag, прошедший BCP-47-валидацию.

Вопрос по архиву

POST /api/stt/v1:askArchive
Content-Type: application/json
{
  "question": "О каких сроках договорились и кто отвечает?",
  "recordingIds": [
    "7ae1ee00-4fb3-4c43-83c3-53568ef06b0b",
    "4a518cc8-4f70-470d-b868-40e39420fba5"
  ]
}

Можно передать 1–20 записей. Если recordingIds отсутствует, используются до 20 последних сохранённых расшифровок пользователя. Ответ строится только по архиву: каждый тезис имеет источники с записью, цитатой и таймкодом. Если подтверждений недостаточно, insufficient_evidence=true; внешние знания не подмешиваются.

Ответ и тезисы формируются на преобладающем языке речи выбранных записей, даже если вопрос задан на другом языке. Имена, термины и подтверждающие цитаты сохраняются в оригинале.

Приватный режим

Асинхронный метод принимает два дополнительных флага:

curl -X POST \
  "https://api.speech.example.com/api/stt/v1:recognizeAsync?privacyMode=true&externalAiConsent=false&llmCorrection=false&entityExtraction=false" \
  -H "Authorization: Api-Key $API_KEY" \
  -F "audio=@private.wav"

При privacyMode=true сервис:

  • не создаёт запись для файла или видео по ссылке;
  • не обновляет сохранённую запись при повторном распознавании;
  • помечает результат операции как временный; после копирования результата клиент должен вызвать POST /api/operations/{operationId}:purge;
  • запускает страховочную серверную очистку по deadline, который считается от момента запуска операции. По умолчанию это 15 минут (PRIVATE_OPERATION_TTL_MINUTES), а фоновая проверка выполняется раз в минуту.

Playground вызывает :purge автоматически после получения ответа. Обычный API-клиент должен сделать это сам: чтение GET /api/operations/{id} не удаляет содержимое. Если долгая задача завершилась уже после своего deadline, страховочная очистка может сработать почти сразу после завершения.

Это приватная серверная обработка, а не локальный STT и не сквозное шифрование. Без externalAiConsent=true блокируются LLM-коррекция, NER, саммаризация и ElevenLabs. Поскольку llmCorrection=true является общим значением по умолчанию, прямой приватный запрос без согласия должен явно передать llmCorrection=false (а также не включать NER/саммаризацию и не выбирать ElevenLabs), иначе сервер вернёт 400. Playground согласует эти переключатели автоматически. Явное согласие разрешает передачу текста или аудио настроенному внешнему провайдеру, но не включает постоянное хранение результата.

Хранение аудио отдельно от текста

У сохранённой записи доступны политики:

Политика Поведение
keep Хранить аудио без заданного срока
24h Назначить удаление через 24 часа от установки политики; worker выполняет его best-effort
delete_after_processing Пометить аудио к удалению; текущий account API синхронно пытается удалить доступное медиа, итог нужно проверить по media_status

Удаление медиа не удаляет транскрипт, источники, саммаризации, готовые наборы и подтверждения:

Метод Назначение
PUT /api/account/recordings/{recording_id}/retention Установить audio_retention
DELETE /api/account/recordings/{recording_id}/media Немедленно удалить только аудио
DELETE /api/account/recordings/{recording_id} Удалить запись и прямо связанные версии/производные результаты; подробности и edge cases — в API записей

media_status принимает available, purge_pending, purged или purge_failed. После успешного удаления media_available=false; при любом статусе кроме available метод GET .../media возвращает 410 Gone. Смена политики не восстанавливает уже удалённый файл.

Живые субтитры

Playground использует существующий WS /api/stt/v1/ws: браузер отправляет PCM16 mono 16 кГц, отображает partial/final-гипотезы крупным текстом и завершает поток сообщением {"event":"eof"}. Сеанс не создаёт запись и операцию. В UI есть изменение размера шрифта, контраст, автопрокрутка, копирование и локальное скачивание текста. Playground авторизуется cookie портала и не добавляет API-ключ в URL. Внешние клиенты могут использовать заголовок Authorization; legacy query ?api_key=... сохранён для совместимости. Перед началом потока проверяются доступные кредиты, после завершения фактическая длительность PCM записывается в usage и тарифицируется как STT.

Примеры использования

curl -X POST "https://api.speech.example.com/api/stt/v1?model=SpeechExpert-STT" \
  -H "Authorization: Api-Key $API_KEY" \
  -F "audio=@recording.wav" \
  -F "lang=ru-RU" \
  -F "channelMode=mono"
curl -X POST "https://api.speech.example.com/api/stt/v1" \
  -H "Authorization: Api-Key $API_KEY" \
  -F "audio=@recording.ogg" \
  -F "format=oggopus" \
  -F "sampleRateHertz=48000" \
  -F "channelMode=mono"
curl -X POST "https://api.speech.example.com/api/stt/v1" \
  -H "Authorization: Api-Key $API_KEY" \
  -F "audio=@recording.mp3" \
  -F "channelMode=mono"
curl -X POST "https://api.speech.example.com/api/stt/v1" \
  -H "Authorization: Api-Key $API_KEY" \
  -F "audio=@stereo.wav" \
  -F "lang=ru-RU" \
  -F "channelMode=stereo"

В ответе будут поля result и utterances — реплики по говорящим в хронологическом порядке.

curl -X POST "https://api.speech.example.com/api/stt/v1" \
  -H "Authorization: Api-Key $API_KEY" \
  -F "audio=@recording.wav" \
  -F "channelMode=mono" \
  -F "llmCorrection=true" \
  -F "entityExtraction=true"
import requests

with open("audio.wav", "rb") as f:
    response = requests.post(
        "https://api.speech.example.com/api/stt/v1",
        headers={"Authorization": "Api-Key <ваш-ключ>"},
        files={"audio": ("audio.wav", f, "audio/wav")},
        params={"lang": "ru-RU", "channelMode": "mono"},
    )

print(response.json()["result"])

Асинхронный вариант

Для длинных записей у метода есть фоновый вариант:

POST /api/stt/v1:recognizeAsync

Он сразу возвращает объект операции с полем id. Результат забирается методом GET /api/operations/{id}. Базовая часть response совпадает с синхронным STTResponse, но фоновые сценарии могут добавить служебные поля сохранённой записи.

Нужно передать ровно один источник:

Поле формы Поведение в обычном режиме
audio Временная загрузка; запись в Моих записях автоматически не создаётся
videoUrl Скачивается одна аудиодорожка, после успеха создаётся сохранённая запись и в ответ добавляется recording_id
recordingId Повторно используется медиа принадлежащей пользователю записи; её STT-ответ обновляется, а операция может вернуть recording_id или recording_deleted

Метод поддерживает те же настройки распознавания. lang, channelMode, diarization, maxSpeakers, llmCorrection, entityExtraction, summarizationPreset, summarizationDetail, summarizationOutputLanguage, privacyMode и externalAiConsent принимаются в query или форме; форма имеет приоритет. model, topic, rawResults, format и sampleRateHertz передаются только в query string. Значения по умолчанию: llmCorrection=true, entityExtraction=false, саммаризация и приватный режим выключены.

При privacyMode=true ни один источник не создаёт и не обновляет запись; правила согласия на внешние провайдеры и очистки операции описаны выше.

Распознавание видео по ссылке

Для videoUrl сервис с помощью yt-dlp скачивает одну лучшую аудиодорожку без плейлиста, после чего запускает обычный STT-пайплайн.

curl -X POST "https://api.speech.example.com/api/stt/v1:recognizeAsync?model=SpeechExpert-STT" \
  -H "Authorization: Api-Key $API_KEY" \
  -F "videoUrl=https://www.youtube.com/watch?v=VIDEO_ID" \
  -F "lang=ru-RU" \
  -F "channelMode=stereo"

Поддерживаются YouTube, VK Видео / VK Play, RuTube, Яндекс Видео, Дзен, Одноклассники, Vimeo, Dailymotion, Twitch, TikTok, SoundCloud и другие сайты с отдельным extractor в yt-dlp. Доступность зависит от конкретного ролика; некоторые ссылки требуют актуальных cookies или могут временно не работать из-за изменений на стороне площадки. Generic extractor отключён из соображений безопасности, прямые произвольные ссылки на файлы следует передавать через audio или STT v3 uri.

Размер скачанного аудио и длительность ролика ограничиваются настройками STT_YTDLP_MAX_DOWNLOAD_BYTES и STT_YTDLP_MAX_DURATION_SECONDS (по умолчанию 200 МБ и 4 часа). Плейлисты и прямые трансляции не загружаются.

Ошибки

Код Когда возникает
400 Пустой файл, неподдерживаемый формат, ошибка конвертации
401 Не аутентифицирован (отсутствует или неверный API-ключ)
402 Недостаточно кредитов для выбранной модели/длительности
404 Сохранённая запись, шаблон или другой принадлежащий пользователю ресурс не найден
413 Файл или транскрипт для LLM-перевода превышает допустимый размер
409 Операция или версия находится в состоянии, несовместимом с действием
422 Не прошли типы, диапазоны или взаимные ограничения параметров
500 Внутренняя ошибка
503 Сервис распознавания временно недоступен

Для :recognizeAsync исходный POST может успешно вернуть операцию, а ошибка загрузки, декодирования, ASR или LLM появится позже в Operation.error при опросе. Поэтому клиент должен проверять не только HTTP-код создания задачи, но и поля done, error и response завершённой операции.

См. также