py_faster_whisper: как звук проходит через систему
Статус: as-built по checkout, 16 сентября 2026 года.
Git revision: 21c85b0c7bc7a96497ce78ec2ead44b113f18db6 (main).
Основание: исходники, тесты, effective local configuration, Docker/DRS и CI этого репозитория.
Запущенные контейнеры, image digests, Swarm/Insight, production NATS-трафик и end-to-end работа внешних моделей в этом проходе не проверялись.
1. Что это за система
py_faster_whisper принимает речь и организует её обработку внешними моделями. Сам репозиторий не содержит модель Whisper: он принимает аудио, подготавливает его, вызывает LinTO ASR и сервисы диаризации, хранит состояние задания и возвращает текст с говорящими.
Работа разделена на три контура:
- Live bridge собирает короткие фразы из NATS-потока и возвращает текстовые события.
- Automatic file принимает целый файл и сам доводит его до готового результата.
- Staged file позволяет отдельно запускать подготовку, распознавание, диаризацию и объединение.
Все три контура запускаются из python -m src.stt_whisper_to_nats. Дальше они расходятся по разным моделям состояния и способам выдачи результата.
VERIFIED / SOURCE: src/stt_whisper_to_nats.py:1615-1688, src/webui/server.py, src/webui/staged_jobs.py.
2. Как читать доказательства
| Метка | Что она означает |
|---|---|
VERIFIED / SOURCE |
Связь подтверждена кодом, тестом или effective configuration этого checkout. |
VERIFIED / RUNTIME (local) |
Поведение наблюдалось при локальном запуске. Это не доказательство production runtime. |
INFERRED |
Вывод следует из нескольких источников, но не подтверждён прямым test/trace. |
UNKNOWN |
Доказательств недостаточно или нужный слой не проверялся. |
Исходники, pipeline, образ, desired stack, запущенный runtime и конкретный пользовательский сценарий — разные уровни доказательств. Успех на одном уровне не подтверждает следующий.
3. Где проходит граница репозитория
Исходный Mermaid
flowchart LR
Live[Live producer] -->|PCM16 через NATS| Repo[py_faster_whisper]
User[Человек / HTTP-клиент] -->|медиафайл| Repo
Repo -->|multipart HTTP| ASR[LinTO ASR]
Repo -->|аудио + metadata| Diar[Diarization backend]
Repo -->|state + artifacts| FS[(Local filesystem)]
Repo -->|progress + result| NATS[(NATS)]
Repo -->|HTTP snapshot| UserКак читать схему:
- Live producer принадлежит соседней системе и передаёт короткие PCM16-пакеты.
- Репозиторий управляет порядком действий: готовит аудио, вызывает модели и собирает результат.
- LinTO и diarization backend выполняют тяжёлую модельную работу за HTTP-границей.
- Filesystem хранит файловые задания; NATS переносит live-аудио и события.
- Человек или HTTP-клиент видит состояние задания и готовый текст через WebUI/API.
WebRTC ingress, NATS infrastructure, LinTO, внешние H100 routes, registry и Swarm находятся за этой границей. Поэтому /health = ok у WebUI подтверждает процесс, но не готовность моделей.
VERIFIED / SOURCE: src/stt_whisper_to_nats.py:83-145, src/webui/server.py:1075-1194, stack/stack.drs:12-70.
4. Один entrypoint, три контура работы
Исходный Mermaid
flowchart TB
Main[_main_async] --> Live[SttWhisperToNatsService]
Main --> WebUI[run_webui / create_app]
WebUI --> Mode{WEBUI_PAGE_MODE}
Mode -->|diarize / transcribe| Auto[FileJobWorkerService]
Mode -->|staged / summary / bench / solution / case| Staged[StagedFileJobService]
Live --> Text[короткие фразы в NATS]
Auto --> Result[готовый file job]
Staged --> Steps[повторяемые шаги и artifacts]Блоки схемы:
_main_asyncчитает конфигурацию и решает, запускать live bridge, WebUI или оба процесса.SttWhisperToNatsServiceвладеет буферами живого аудио и закрывает фразы по VAD/тайм-ауту.FileJobWorkerServiceвыполняет полный файловый процесс без ручного управления шагами.StagedFileJobServiceработает с уже сохранёнными промежуточными результатами.WEBUI_PAGE_MODEвыбирает нужный контур на HTTP-границе.
Эти контуры используют общие адаптеры LinTO, диаризации и transport, но по-разному хранят состояние. Поэтому их полезно разбирать отдельно.
VERIFIED / SOURCE: src/stt_whisper_to_nats.py:83-145,586-745,1615-1688, src/webui/server.py:1075-1194.
5. Контур 1: Live bridge
Роль
Live bridge превращает непрерывный поток PCM16 в отдельные текстовые фразы. Он нужен, когда результат должен появляться во время разговора, а не после загрузки целого файла.
Исходный Mermaid
sequenceDiagram
participant P as Live producer
participant N as NATS
participant B as Live bridge
participant V as Silero VAD
participant L as LinTO
participant C as Consumer
P->>N: stream.{token}: PCM16 frame
N->>B: binary wire packet
B->>V: накопленный buffer 16 kHz
V-->>B: speech timestamps
B->>N: pending + phrase_id
B->>L: temporary WAV
L-->>B: text
B->>N: result / error
N-->>C: phrase eventЧто происходит по блокам:
- Producer отправляет metadata и PCM16 samples в
inference.whisper.stream.*. - Bridge ведёт отдельный
PhraseStreamStateдля каждого output subject. - Silero VAD находит участки речи и помогает определить момент закрытия фразы.
- LinTO получает временный WAV и возвращает текст.
- NATS consumer получает
pending, затемresultилиerrorс тем жеphrase_id.
Ограничение
Буфер живёт в памяти процесса. После restart незавершённая фраза не восстанавливается. Временный WAV удаляется в finally после обращения к LinTO.
VERIFIED / SOURCE: src/stt_whisper_to_nats.py:83-438, src/utils/wire_packet.py, src/utils/silero_onnx_vad.py.
6. Контур 2: Automatic file
Роль
Automatic file нужен для простого сценария: человек загружает файл и ждёт готовый текст. Runner сам проходит подготовку, ASR, диаризацию, объединение и cleanup.
Исходный Mermaid
sequenceDiagram
participant U as Browser / API
participant W as WebUI
participant R as File runner
participant S as FileJobStore
participant L as LinTO
participant D as Diarization
U->>W: POST /api/file-transcribe
W->>R: start_job(job_id, session_id)
R->>S: source + starting state
par Распознавание
R->>L: chunks /transcribe
L-->>R: words + timestamps
and Разделение говорящих
R->>D: audio artifact /diarize
D-->>R: speaker segments
end
R->>R: words + segments → turns
R->>S: transcript + combined result
U->>W: GET /api/file-job
W-->>U: state + resultЧто происходит по блокам:
- WebUI проверяет вход, создаёт
job_idи передаёт работу runner. - Runner нормализует звук в mono PCM WAV 16 kHz и режет длинную запись на перекрывающиеся chunks.
- LinTO возвращает слова с метками времени; без них нельзя надёжно назначить говорящих.
- Diarization backend возвращает интервалы говорящих на той же временной шкале.
- Runner сопоставляет слова и интервалы, строит turns и сохраняет результат в
FileJobStore.
Ветка ошибки
Если пользователь запросил диаризацию, а backend недоступен, WebUI может продолжить транскрипцию без говорящих. Причина сохраняется в diarization_fallback_reason; отсутствие speakers не выдаётся за полный результат.
VERIFIED / SOURCE: src/webui/server.py:603-793, src/stt_whisper_to_nats.py:668-1542, src/webui/file_mode.py:839-1253.
7. Контур 3: Staged file
Роль
Staged file разделяет дорогую обработку на повторяемые шаги. Это полезно для сравнения моделей и настроек на одном аудио: файл не нужно загружать и нормализовать заново.
Исходный Mermaid
flowchart LR
Upload[Upload] --> Prepare[Prepare]
Prepare --> T[Transcribe]
Prepare --> D[Diarize]
T --> TT[(transcription.json)]
D --> DD[(diarization.json)]
TT --> C[Combine]
DD --> C
C --> R[(combined.json)]Что происходит по блокам:
- Prepare сохраняет исходник и создаёт рабочее аудио.
- Transcribe получает слова с timestamps и записывает
transcription.json. - Diarize получает speaker segments и записывает
diarization.json. - Combine читает оба сохранённых результата и не вызывает модели повторно.
combined.jsonстановится итогом, который WebUI возвращает через job snapshot.
Combine остаётся заблокирован, пока не готовы обе ветки. Один активный step защищён локальной блокировкой; optional HTTP lock добавляет lease между процессами и страницами.
VERIFIED / SOURCE: src/webui/staged_jobs.py:146-540, src/webui/job_lock.py, tests/test_diarization.py.
8. Как живут данные файлового задания
Исходный Mermaid
flowchart LR
Input[input.ext] --> Normalize[normalized.wav]
Normalize --> Chunks[chunks/*.wav]
Chunks --> Words[transcription.json]
Input --> Audio[diarization_input.m4a]
Audio --> Segments[diarization.json]
Words --> Merge[Attribution + turns]
Segments --> Merge
Merge --> Combined[combined.json]
State[state.json] -. tracks .-> Normalize
State -. tracks .-> Words
State -. tracks .-> Segments
State -. tracks .-> CombinedРазбор данных:
input.ext— исходный upload; он определяет идентичность задания.normalized.wavиchunks/— рабочие данные для ASR.diarization_input.m4aили WAV — аудио, подготовленное для backend диаризации.transcription.jsonиdiarization.json— две независимые ветки результата.combined.jsonсоединяет текст и говорящих;state.jsonхранит progress и признаки готовности.
Корневой каталог по умолчанию: локальный источник<job_id>/. Automatic job планирует удаление примерно через 30 секунд после terminal completion. Сохранность staged artifacts зависит от runtime mount и cleanup policy; production retention остаётся UNKNOWN.
VERIFIED / SOURCE: src/webui/file_mode.py:631-837, src/stt_whisper_to_nats.py:1491-1510.
9. Где искать реализацию
src/
├── stt_whisper_to_nats.py # entrypoint, live bridge, automatic file runner
├── webui/
│ ├── server.py # HTTP routes и выбор runner
│ ├── file_mode.py # store, audio, chunks, LinTO client
│ ├── staged_jobs.py # Prepare/Transcribe/Diarize/Combine
│ ├── job_lock.py # optional shared lock
│ ├── diagnostics.py # dependency checks
│ └── static/ # browser UI
├── diarization.py # backend client, attribution, turns
├── model_transport.py # direct/CONNECT transport
├── *_diarization_server.py # pyannote, sherpa, Sortformer
└── utils/ # wire packet, VAD, audio helpers
tests/ # contracts и orchestration branches
docker/ # runtime images и local composition
stack/ # desired legacy CPU deployment
Эта карта отвечает на вопрос «куда идти». server.py владеет HTTP-границей, runners — порядком действий, file_mode.py — рабочими файлами и LinTO client, diarization.py — назначением говорящих, model_transport.py — способом HTTP-доставки.
10. Два выборочных call tree
Call tree показывает реализацию ветки. Временной порядок участников уже показан sequence diagrams выше.
Live path
main
└── _main_async
└── SttWhisperToNatsService.run
└── _handle_message # NATS callback
└── _process_message
└── _check_stream_buffer
├── _get_timestamps # Silero VAD
└── _transcribe_and_publish
├── transcribe_http_text
└── _publish_json # NATS result
File path
run_webui
└── create_app
└── POST /api/file-transcribe
└── _file_http_handler
└── FileJobWorkerService.start_job
└── _process_job
├── write_normalized_audio_wav
├── transcribe_http_json
├── diarize_http_json
├── attribute_words / build_speaker_turns
└── FileJobStore writes + NATS events
Обе ветки сходятся на HTTP-адаптерах моделей, но расходятся по состоянию: live хранит буфер в памяти, file пишет каталог job.
11. Контракты, которые нельзя менять локально
| Граница | Контракт | Что проверить при изменении |
|---|---|---|
| Browser → WebUI | POST /api/file-transcribe, /file-prepare, /file-{step}-step |
form fields, page mode, reset, snapshot |
| WebUI polling | GET /api/file-job |
terminal/error states и rendering |
| Producer → live bridge | inference.whisper.stream.*, binary wire packet |
producer и parser |
| Bridge → consumer | inference.whisper.text.*, pending/error/result JSON |
correlation и terminal event |
| Runner → observers | *.file.event.*, *.file.diar* |
job/session fanout |
| Runner → LinTO | multipart file; file mode ждёт word timestamps |
parser, chunk merge, attribution |
| Runner → diarizer | audio + job metadata → segments | progress и domain mapping |
HTTP связывает caller и response во времени. NATS разделяет producer и consumer границей сообщения. Файлы связывают повторные шаги через сохранённое состояние. Похожая ошибка в UI требует разных проверок для каждой границы.
VERIFIED / SOURCE: src/webui/server.py:1153-1178, src/webui/file_mode.py:285-366,1150-1253, src/model_transport.py.
12. Три backend диаризации
| Backend | Что получает | Что отдаёт | Особенность runtime |
|---|---|---|---|
| pyannote | multipart audio + hints | segments, progress | HF access, lazy load, CPU route в desired stack |
| sherpa-onnx | multipart audio | speaker segments | CPU pipeline, ffmpeg normalization, model cache |
| Sortformer | multipart audio | stitched segments | NeMo/Triton, окна для длинного аудио |
Внешний HTTP shape похож, но backend нельзя считать взаимозаменяемым только по наличию /diarize. Desired stack/stack.drs направляет bridge на локальный pyannote; sherpa и Sortformer объявлены как отдельные сервисы без автоматического переключения.
VERIFIED / SOURCE: src/*_diarization_server.py, src/diarization.py, stack/stack.drs:53-70.
13. Что происходит при сбое
| Сбой | Наблюдаемый результат | Где искать |
|---|---|---|
| NATS недоступен | live bridge не стартует; file HTTP может работать без live events | entrypoint, NATS connection |
| LinTO не отвечает | transcription получает error; готового текста нет | file_mode.py, live publish path |
| Diarization недоступна | возможен transcript-only результат с явной причиной | server.py, runner state |
| Proxy route настроен, но не работает | запрос не падает скрыто на direct transport | model_transport.py |
| Job lock недоступен | остаётся только local one-active-task guard | job_lock.py, runners |
| Процесс перезапущен | live buffer теряется; file recovery зависит от сохранённых artifacts | live state, FileJobStore |
Здесь нет общего fallback-механизма. Каждый контур сохраняет собственную семантику ошибки, поэтому диагностика начинается с границы, на которой пропал результат.
14. Как исходники доходят до работающей системы
Исходный Mermaid
flowchart LR
S[Source] --> P[Pipeline]
P --> I[Image]
I --> D[Desired DRS]
D --> R[Running tasks]
R --> B[Named behavior]Состояние ступеней:
- Source — entrypoints, contracts и tests проверены в
21c85b0. - Pipeline — build/push описан; job с тестами закомментирован.
- Image — digest конкретной сборки не проверялся.
- Desired DRS — proxy, bridge и три sidecar описаны в
stack/stack.drs. - Running tasks — Swarm health/update state не проверялись.
- Named behavior — production live/file smoke не выполнялся.
Desired stack подключается к внешней сети web-rtc-komaroff_default. Deploy wrapper объединяет otlp.drs + stack.drs и вызывает Dry Stack с --prune; это описание намерения deployment, а не доказательство текущего runtime.
VERIFIED / SOURCE: .gitlab-ci.yml, docker/build-images.sh, stack/stack.drs, stack/deploy.sh. Остальные слои помечены выше.
15. Что защищают тесты
16 сентября 2026 года выполнено:
.venv/bin/python -m unittest discover -s tests -v
Результат: Ran 115 tests in 0.943s — OK. Предупреждения aiohttp.NotAppKeyWarning не привели к падениям.
Проверены transport, WebUI routes/modes, file state, staged steps, locks, diarization parsing/attribution, diagnostics, pyannote helpers и Sortformer stitching. Этот прогон не подтверждает реальные модели, production NATS routing, image/deployment state, cold/warm readiness и путь browser → model → result.
VERIFIED / RUNTIME (local): локальный unittest run. CI сам по себе эти тесты сейчас не запускает — job в .gitlab-ci.yml закомментирован.
16. Риски и область влияния
| Причина | Последствие | Где проявится |
|---|---|---|
README.md называет stack/stack.drs пустым, хотя в нём 5 сервисов |
разработчик неверно определит deployment ownership | onboarding, delivery |
.version=0.0.20, src/.version=0.0.19 |
разные части могут сообщать разные версии | image labels, metadata |
| CI tests job выключен | build/deploy не обязан выполнить 115 тестов | regression confidence |
Job state по умолчанию в /tmp |
сохранность зависит от mount | restart, staged jobs |
| Один active job/step на процесс | масштабирование требует общей coordination model | concurrent files |
| Оркестрационные модули занимают 1000–1688 строк | изменение контракта затрагивает несколько задач | review, regression surface |
/health проверяет только процесс |
зелёный route не означает готовую модель | readiness, diagnostics |
Таблица не требует немедленного рефакторинга. Она показывает, где локальная правка нуждается в более широкой проверке.
17. Если нужно изменить систему
| Задача | Начать здесь | Проверить до завершения |
|---|---|---|
| WebUI layout или текст | src/webui/static/, server.py |
page modes и browser rendering |
| Upload/job API | src/webui/server.py |
snapshot, reset, automatic/staged tests |
| Chunks или timestamps | src/webui/file_mode.py |
overlap merge и LinTO JSON |
| Speaker attribution | src/diarization.py |
midpoint/overlap и turns tests |
| Staged workflow | src/webui/staged_jobs.py |
artifacts, invalidation, lock, cancel |
| Live phrase behavior | SttWhisperToNatsService |
wire packet, VAD, events, consumer |
| Proxy/model route | model_transport.py, bench.py |
fail-closed tests и diagnostics |
| Deployment | stack/*.drs, .gitlab-ci.yml |
image → desired → running → smoke |
Сначала назовите наблюдаемый эффект и изменяемый контракт. Затем ищите файл. Такой порядок не даёт ограничиться одной функцией там, где меняются producer, consumer или runtime route.
18. Что остаётся неизвестным
- Какие image digests реально запущены?
- Совпадают ли runtime env, subjects и routes с DRS?
- Какие backend диаризации доступны и прогреты сейчас?
- Какой storage mount используется для file jobs и какова retention policy?
- Есть ли production job lock service?
- Каковы latency/error profile для live phrase и больших файлов?
- Какая version file считается canonical?
Эти вопросы нельзя закрыть чтением исходников. Для ответа нужны CI/image/Insight/NATS/browser evidence.
19. Короткий маршрут нового разработчика
- Начните с разделов 1–4 и выберите один из трёх контуров.
- Пройдите его sequence diagram от входа до наблюдаемого результата.
- Найдите соответствующий call tree и implementation anchors.
- Выпишите контракт, который изменится: HTTP, NATS, artifact или env.
- Проверьте tests обеих сторон границы.
- Для delivery отдельно докажите image, desired DRS, running state и named behavior.
20. Claim ledger
| Утверждение | Статус | Evidence |
|---|---|---|
| Один entrypoint запускает live bridge и/или WebUI | VERIFIED / SOURCE |
src/stt_whisper_to_nats.py:1615-1688 |
| Live path использует NATS → VAD → LinTO → NATS | VERIFIED / SOURCE |
src/stt_whisper_to_nats.py:83-438 |
| File path создаёт normalized WAV, chunks и timed transcript | VERIFIED / SOURCE |
src/webui/file_mode.py:839-1253 |
| ASR и diarization могут выполняться параллельно | VERIFIED / SOURCE |
src/stt_whisper_to_nats.py:914-947 |
| Staged Combine не вызывает модели повторно | VERIFIED / SOURCE + tests |
src/webui/staged_jobs.py, tests/test_diarization.py |
| Proxy path не делает silent direct fallback | VERIFIED / SOURCE + tests |
src/model_transport.py, tests/test_model_transport.py |
| Desired stack использует local pyannote CPU | VERIFIED / SOURCE |
stack/stack.drs:53-64 |
| Production соответствует desired stack | UNKNOWN |
runtime evidence не собиралось |
| End-to-end обработка реального файла сейчас работает | UNKNOWN |
named smoke не выполнялся |