Шаги по схеме
CHAT — NATS чата. Панель подключается к нему по WSS. INFERENCE — NATS воркера. Мост меняет префикс subject при передаче между контурами. U — ID панели, R — ID квитанции, G — ID экземпляра Gateway.
1. Панель подключается и сообщает, что готова играть звук
- Chrome → Gateway. HTTPS POST
/auth/nats. Панель передаётsession_idиrole=ui, чтобы получить доступ к NATS. - Gateway → Chrome. HTTPS
200 OK. Gateway возвращает допуск,client_id,prefix,urlиspeech=1при доступной озвучке. Панель подписывается на свои каналы, включаяchat.ui.U.events.speech. - Chrome → Gateway. WSS/CHAT,
chat.ui.U.cmd.browser_context. Панель регистрирует контекст страницы, указываетprotocol=2и запрашиваетspeech=1. - Gateway → Chrome. WSS/CHAT,
chat.ui.U.reply.R. Квитанция200сdata.speech=1подтверждает, что озвучка доступна этому контексту. - Chrome → Gateway. WSS/CHAT,
chat.ui.U.cmd.speech_control. Командаreadyсcontext_idиconnection_idпривязывает готовый плеер к подключению панели. - Gateway → Chrome. WSS/CHAT,
chat.ui.U.reply.R. Квитанция200с пустым объектомdataподтверждает привязку.
2. Пользователь задаёт вопрос
- Chrome → Gateway. WSS/CHAT,
chat.ui.U.cmd.message. Панель передаёт вопрос, ID запроса, сессии и контекста, а такжеsource_page. - Gateway → Agent. NATS/CHAT,
chat.internal.agent.widget. Gateway передаётaction=submit, вопрос и проверенную привязку к браузеру. - Agent → Gateway. NATS/CHAT, reply inbox из запроса №8. Статус
202означает, что Agent принял вопрос в работу. - Gateway → Chrome. WSS/CHAT,
chat.ui.U.reply.R. Gateway подтверждает приём вопроса статусом202. Готовый ответ придёт событием.
3. Готовый ответ уходит в чат и на озвучку
- Agent → Gateway. NATS/CHAT,
nats.events.user123. Agent отправляет событиеwidget_stageсstage=done, текстом ответа и ID. - Gateway → Chrome. WSS/CHAT,
chat.ui.U.events.ui. Gateway пересылает исходное событие. Панель показывает текст. - Gateway → NATS-мост. Локальный NATS,
to.inference.tts.s2.command. Для готового плеера Gateway убирает разметку из текста, создаётspeech_idи публикуетaction=synthesize, текст и ID. - NATS-мост → Worker. NATS/INFERENCE,
inference.tts.s2.command. Мост доставляет то же JSON-тело. Worker проверяет текст и ID, отсекает повторы поspeech_idи резервирует место в своей очереди.
4. Worker берёт задание и запускает Qwen
- Worker → Gateway. NATS через мост,
inference.tts.s2.events.G→from.inference.tts.s2.events.G.queuedвX-Speechсообщает, что задание принято. Текст остаётся в очереди worker, тело события пустое. - Gateway → Chrome. WSS/CHAT,
chat.ui.U.events.speech. Плеер получаетqueued, создаёт состояние этой озвучки и запускает таймер обратной связи. - Worker → Gateway. NATS через мост,
inference.tts.s2.events.G→from.inference.tts.s2.events.G. Когда доходит очередь, worker отправляетgenerating, модель и время ожидания. - Gateway → Chrome. WSS/CHAT,
chat.ui.U.events.speech. Плеер получаетgenerating. Панель показывает, что идёт синтез. - Worker → Qwen. Локальный вызов Python
Qwen.pcm→generate_voice_clone_streaming. Worker преобразует время и десятичные числа по правилам русского произношения. Qwen получает текст, язык и настройки клонирования голоса Xenia. - Qwen → Worker. Локальный генератор Python возвращает порции PCM. Это несжатый монофонический звук 24 кГц,
float32little-endian.
5. Worker сжимает звук и отправляет его в Chrome
- Worker → Gateway. Диагностика. NATS через мост,
inference.tts.s2.events.G→from.inference.tts.s2.events.G. При появлении первого PCM worker отправляет замерfirst_pcm_ms. Gateway записывает его в журнал. - Worker → FFmpeg. Локальный pipe
stdin. Worker подаёт PCM порциями примерно по 100 мс звука. - FFmpeg → Worker. Локальный pipe
stdout. FFmpeg выдаёт поток WebM с Opus, моно 48 кГц, 32 кбит/с. - Worker → NATS-мост. NATS/INFERENCE,
inference.tts.s2.events.G. Worker берёт до 16 КиБ из потока FFmpeg, ставитtype=chunkи номерseqвX-Speechи отправляет байты в теле сообщения. - NATS-мост → Gateway. Локальный NATS,
from.inference.tts.s2.events.G. Мост передаёт заголовок и байты. Gateway проверяет ID и номер блока. - Gateway → Chrome. WSS/CHAT,
chat.ui.U.events.speech. Gateway пересылает тот же блок. Плеер сверяетseqи добавляет байты в буфер. - Worker → Gateway. Диагностика. NATS через мост,
inference.tts.s2.events.G→from.inference.tts.s2.events.G. После первой публикации блока worker отправляетfirst_block_msдля журнала.
6. Плеер начинает играть и сообщает позицию
Шаги №30–33 идут параллельно с передачей блоков №24–26, пока браузер ещё не получил end.
- Chrome → Gateway. Диагностика. WSS/CHAT,
chat.ui.U.cmd.speech_control. Обычно плеер запускает звук, накопив около 200 мс. При первом событии браузераplayingон отправляет одноимённую команду для записи времени старта. - Gateway → Chrome. Квитанция диагностики. WSS/CHAT,
chat.ui.U.reply.R. Ответ200подтверждаетplaying. Воспроизведение уже началось. - Chrome → Gateway. WSS/CHAT,
chat.ui.U.cmd.speech_control. Плеер посылаетaction=positionбез reply. Значениеplayed_msпоказывает, сколько звука уже сыграло. До старта позиция может быть нулевой. - Gateway → Worker. NATS через мост,
to.inference.tts.s2.command→inference.tts.s2.command. Gateway пересылает позицию. Worker ограничивает подачу PCM в FFmpeg примерно двумя секундами вперёд и ждёт следующего обновления, когда достигает предела.
7. Worker заканчивает отправку, Chrome доигрывает
- Worker → Gateway. NATS через мост,
inference.tts.s2.events.G→from.inference.tts.s2.events.G. После последнего блока worker отправляетendс числом блоков и итоговыми замерами. - Gateway → Chrome. WSS/CHAT,
chat.ui.U.events.speech. Плеер проверяет число блоков, прекращает отправлять позицию и доигрывает оставшийся звук. - Chrome → Gateway. WSS/CHAT,
chat.ui.U.cmd.speech_control. При окончании звука плеер отправляетended, освобождает ресурсы и показывает «Воспроизведено». Gateway закрывает состояние озвучки. - Gateway → Chrome. WSS/CHAT,
chat.ui.U.reply.R. Статус200подтверждает приёмended.
Передаваемые данные
- Команды панели · №3, 5, 7, 30, 32, 34
- JSON-конверт
{version:1, capability, data}. Уspeech_controlвнутриdataнаходятсяaction,context_id,connection_idи, для конкретной озвучки,request_idиspeech_id. Позиция добавляетplayed_ms— миллисекунды от начала аудио. - Задание и позиция для worker · №13–14, 33
- JSON без браузерного конверта.
action=synthesizeнесётtext,action=position—played_ms.gateway_instanceопределяет канал ответа воркера. - Идентификаторы задания и событий
session_id— сессия,client_id— панель,context_id— её контекст,connection_id— подключение плеера,request_id— вопрос,speech_id— конкретный синтез. Вместе они позволяют Gateway и плееру принять звук нужного запроса.- События worker · №15–18, 21, 24–29
- В заголовке NATS
X-Speechлежит JSON сversion=1,typeи шестью ID. Уchunkтело содержит до 16 КиБ WebM/Opus,seqнумерует блоки с нуля. Уendполеseqсодержит количество отправленных блоков. У статусов и замеров тело пустое. - Замеры и завершение
queue_ms— ожидание воркера,first_pcm_ms— время от начала синтеза до первой порции PCM,first_block_ms— до первой публикации WebM. Итог worker содержитbytes,audio_msиcompute_ms. Gateway измеряет время от финального ответа до получения командыplayingсвоим таймером. При остановкеabortсодержитreason.
Какие сообщения можно сократить
- №27. Замер
first_block_msуже приходит вendилиabort, если первый блок был отправлен. Отдельное событие можно убрать после переноса или проверки таймера ожидания в Gateway. - №21.
first_pcm_msможно добавить в итоговое событие и убрать отдельную публикацию. При этом нужны внутренняя проверка первого PCM у worker и учёт таймера Gateway, который обновляется при получении события. - №30–31. Если серверный замер фактического старта не требуется, можно убрать команду
playingи её квитанцию. - №35. Квитанцию можно убрать после перевода
endedна уведомление без reply. Само событие нужно Gateway для завершения состояния плеера.