Transcriber
Services/Transcriber - Python CAN worker для live-транскрибации конференций. Он подключается к /can, принимает transcriber.start, входит в конференцию как сервисный receiver, подписывается на аудио renderer streams и отправляет обновления расшифровки в чат конференции.
Сервис ориентирован на Apple Silicon и mlx_whisper, потому что MLX дает практичный realtime path для локальной транскрибации.
Lifecycle
sequenceDiagram
participant Core
participant CAN as Transcriber CAN worker
participant WS as CommandLoop
participant RTP as RTP audio
participant MLX as mlx_whisper
participant Chat
CAN->>Core: /can hello(role=transcriber)
Core-->>CAN: job transcriber.start(auth.access_token, conference_tag)
CAN->>WS: connect_request(access_token)
CAN->>WS: connect_to_conference_request(tag)
WS-->>CAN: device_connect microphone renderer
CAN->>RTP: receiver_ssrc adjust packet
RTP-->>CAN: plain RTP Opus 48 kHz
CAN->>MLX: 3-5 sec PCM window
MLX-->>CAN: text
CAN->>Chat: delivery_messages transcript payload
Production job
{
"job_type": "transcriber.start",
"target_role": "transcriber",
"required_capability": "speech_to_text",
"payload": {
"server_url": "https://join.videograce.ru",
"conference_tag": "teamsink",
"source": {
"type": "conference_audio"
},
"model": "mlx-community/whisper-large-v3-turbo",
"language": "ru",
"window_sec": 4,
"max_windows": 0
}
}
Если payload.auth отсутствует, core пытается выпустить bearer JWT из service_accounts. По умолчанию используется service account с именем transcriber; альтернативное имя можно передать как payload.service_account.
После инъекции auth worker получает:
{
"auth": {
"type": "bearer",
"access_token": "eyJ..."
}
}
RTP assumptions
Текущий low-level media path повторяет путь recorder receiver:
- каждый renderer socket принимает один аудиопоток;
- поток приходит как plain RTP Opus 48 kHz;
- шифрование и mux на этом участке не используются;
receiver_ssrcоткрывает RTP path через adjust packet;author_ssrcсвязывает текст с конкретным remote microphone device;- окно транскрибации обычно 3-5 секунд.
Chat payload
Транскрибер отправляет структурированное сообщение:
{
"type": "transcript",
"version": 1,
"job_id": "transcriber-...",
"conference_tag": "teamsink",
"partial": false,
"speaker": {
"client_id": 228800001,
"device_id": 1071,
"ssrc": 1530,
"name": "Anton"
},
"text": "..."
}
GUID сообщения формируется стабильно:
transcript_{job_id}_{speaker_key}
Это позволяет клиенту обновлять один live transcript block, а не засыпать чат отдельными сообщениями каждые несколько секунд.
Локальная проверка
cd Services/Transcriber
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
CAN worker:
VG_CAN_URL=wss://join.videograce.ru/can \
VG_CAN_SERVICE_TOKEN=... \
python3 transcriber.py
Protocol-only self-test без MLX:
python3 transcriber.py --self-test \
--conference-tag teamsink \
--text "Тестовая расшифровка"
Инварианты
- Transcriber не публикует свои microphone/camera devices.
- Transcriber подключается к конференции как сервисный receiver и слушает remote microphone devices.
- Пароль сервисного пользователя не должен попадать в CAN job; production path - bearer JWT от
service_accounts. - CAN переносит только control plane, media идет напрямую по RTP.