feat: public /v1/jobs API for Telemost transcribe service#2
Open
Savin99 wants to merge 4 commits into
Open
Conversation
Добавляет nullable-колонки для идемпотентности, payload'а спикеров, retention аудио и прогресса. Миграция делается тем же idempotent `ADD COLUMN` паттерном, что и предыдущие, плюс индекс по `session_id`. Статус-Literal расширяется новыми промежуточными значениями (`connecting`, `refining`, `cancelled`) — они нужны публичному state machine и не меняются для легаси `/join`-пути. Co-Authored-By: Claude <claude@anthropic.com>
- `POST /transcribe` принимает `speakers_hint`, `auto_enroll_unknown`, `initial_prompt` и возвращает `enrolled_voiceprints` + `speaker_roles`. - `identify_speakers` получает опциональный `allowed_names`, чтобы на конкретной встрече подбирать только голоса, заявленные клиентом. - Авто-энроллмент срабатывает ровно на «1 неопознанный кластер + 1 unenrolled-подсказка». В остальных конфигурациях — ничего не пишем. - `voice_bank_ids.json` — sidecar-реестр id ↔ имя с атомарной записью и транслитерацией (`Илья Савин → ilya-savin-<uuid8>`). VoiceBank остаётся адресованным по display_name, поэтому tg-bot/CLI не ломаются. - initial_prompt доходит до WhisperX через `transcribe_kwargs`. - Тесты: отдельный test_voice_bank_registry и 4 новых сценария в test_transcribe_pipeline (initial_prompt, allowed_names, auto-enroll success/skip). Co-Authored-By: Claude <claude@anthropic.com>
- `POST /v1/jobs` с payload'ом из спеки (source/metadata/speakers/options).
Идемпотентность по `metadata.session_id` → 409 с тем же job_id.
Concurrency-лимит (`MAX_CONCURRENT_JOBS`, default 4) → 429 + retry_after_sec.
- `GET /v1/jobs/{id}` отдаёт маппинг внутренних статусов в публичные
`queued/connecting/recording/transcribing/refining/done/error/cancelled`,
с `progress` на ходу и `result` в терминальном состоянии. `speaker_role`
вычисляется по speakers-payload + enrolled_voiceprints.
- `DELETE /v1/jobs/{id}` через тот же `_stop_session`, но пишет `cancelled`
(через новый флаг `cancel_terminal_status` в active_sessions), а не `error`.
- `GET /audio/{id}.wav` отдаёт запись поверх `PUBLIC_BASE_URL`. После истечения
`audio_retention_expires_at` возвращается 410 `audio_expired`; метаданные
при этом живут в `GET /v1/jobs/{id}`.
- Авторизация: `require_api_key` теперь path-aware — строгий Bearer с 401
для `/v1` и `/audio`, совместимость с X-API-Key на легаси-роутах
сохранена (Bearer на них тоже принимается).
- Новый `_bot_workflow_v1` идёт через `connecting → recording → transcribing
→ refining → done`, не зависит от легаси `_bot_workflow`. Drive-загрузка
— best-effort: упала — логируем и идём в done без `transcript_url`.
- Hourly `_audio_retention_cleanup_loop` удаляет протухшие файлы.
- Тесты: 10 новых сценариев (happy-path с speakers_hint/audio_url/roles,
drive-fail, retention 410, 401/404/409/429/cancelled).
Co-Authored-By: Claude <claude@anthropic.com>
PUBLIC_BASE_URL попадает в `result.audio_url`, остальные три — retention аудио, лимит параллельных джоб и интервал cleanup-петли. Defaults выставлены так, чтобы на чистом .env сервис стартовал без правок. Co-Authored-By: Claude <claude@anthropic.com>
Savin99
pushed a commit
that referenced
this pull request
Apr 21, 2026
- Кнопки теперь именованы 🎙 #1 / 🔗 #1, #2, … — видно какая кнопка к какой строке списка относится (раньше 10 одинаковых «Голоса» / «Drive» сливались в полосу). - Для встреч без длительности и без осмысленного имени показываем первые 16 символов meeting_id в <code> — хоть какая-то разница между строками, иначе пять раз подряд «📅 18.04 14:37 · ⏱ ?». Co-Authored-By: Claude <claude@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/v1/jobs(POST/GET/DELETE) на bot-service — идемпотентный поmetadata.session_id, с Bearer-auth (401), concurrency-лимитом (429), mapping'ом внутренних статусов вqueued → connecting → recording → transcribing → refining → done/error/cancelled.GET /audio/{id}.wavпод тем же Bearer'ом + hourly retention-cleanup; после истечения — 410audio_expired, метаданные вGET /v1/jobs/{id}остаются.speakers_hint/auto_enroll_unknown/initial_prompt; возвращаетenrolled_voiceprints({person_id, voice_bank_id}) +speaker_roles. Авто-энроллмент срабатывает ровно на «1 неопознанный кластер + 1 unenrolled подсказка».voice_bank_ids.json— sidecar-реестр opaque id ↔ display_name с атомарной записью и транслитерацией (Илья Савин → ilya-savin-<uuid8>). VoiceBank остаётся адресованным по имени, tg-bot/CLI не ломаются./join,/status,/leave,/transcripts,/meetings,speaker-review/*не тронуты — tg-bot по-прежнему ходит черезX-API-Key; на них теперь дополнительно принимается Bearer.Divergence from the spec
result.audio_urlотдаёт.wav, а не.mp3(без ffmpeg-транскода) — согласовано.options.diarize=falseсейчас силентно игнорируется (всегда диаризуем).options.llm_refine=falseвлияет только на показ статусаrefining, сам LLM-refinement управляется env-переменными сервиса. Клиент всегда шлётtrue/true, так что функционально не заметно./v1— best-effort: если падает, транскрипт всё равно едет вdoneи возвращается через JSON;transcript_urlостаётся NULL.Test plan
python -m unittest discover bot-service/tests— 19 passedpython -m unittest discover transcriber-service/tests— 49 passedpython -m unittest discover tg-bot/tests— 10 passed (регрессий нет)POST /v1/jobsс живым Telemost URL, прокликать через curl доdone, проверитьresult.audio_urlиenrolled_voiceprints.voice_bank_idиз прошлой сессии иenrolled: true— имя должно проставиться сразу.DELETE /v1/jobs/{id}во времяrecording→ через ~10сGETотдаётcancelled.🤖 Generated with Claude Code