WebRTC Trickle ICE: приёмка 05.10.2026
В той же Chrome-сессии медиана local SDP → connected сократилась с 16,108 с до 1,245 с. Все пять штатных подключений установили DTLS через TURN relay; после подключения росли Opus/RTP-счётчики. Синтетическая речь прошла через Core PCM, streaming ASR/NATS и вернулась в чат как partial, final и ответ. Это измерение подключения в текущей сети, а не скорости модели распознавания.
Исходники, CI и фактические контейнеры
- Реализация: 95595f2, pipeline 4899.
- Совместимость с TCP host-кандидатами Chrome: a925c28, pipeline 4900.
- В обоих pipeline Python, Frontend, Ruby checks и
build streaming consumersзавершились успешно.build pushиdeployостались manual; полный DRS deploy не запускался. Состав стэков, NATS, TURN и ASR mode не менялись. - Core сначала обновлён на 95595f2, затем Frontend на 95595f2; после проверки Core обновлён на a925c28. Listener выполнил rollout, повторное U не запускалось.
Insight подтвердил running, 1/1, UpdateStatus=completed и OCI revision.
Image ID совпал с writing image соответствующего CI build:
| Контейнер во время приёмки | OCI revision | Image ID / SHA-256 конфигурации образа |
|---|---|---|
| Core | a925c28c21e33dc83424704c7bdf4f5407a7fe29 | 021629b24ffa13231a5ae819cfca5fadd400d4bd611d7c98bbb53628e8487c21 |
| Frontend | 95595f22ad0c84a9140e3a564b9eb8e6afc744ac | 067e526f5ee85dee59590f3eb381dc5155c4d859a17684f560db590b3f587430 |
Это content hash образа контейнера, не registry manifest digest: Insight inspect не выдаёт RepoDigests, а сохранённый Docker SSH context не имеет доступа. Позднейшая поставка этого отчёта может иметь другую Frontend revision.
Замеры в одной сети и браузере
Контроль до изменения: local SDP → POST занимал 10002 мс; POST → answer — 5224/5291 мс; answer → connected — 868/828 мс. Итог: 16094/16121 мс. Время разрешения микрофона исключено. Во всех новых замерах source — синтетический MediaStream; физический микрофон и его permission latency не измерялись.
| Попытка | Local SDP → connected, мс | POST → answer, мс | Answer → connected, мс | RTP за следующую секунду |
|---|---|---|---|---|
| 1 | 1363 | 149 | 1214 | 0 → 50 packets |
| 2 | 1229 | 128 | 1101 | 0 → 49 packets |
| 3 | 1245 | 126 | 1118 | 0 → 50 packets |
| 4 | 1287 | 125 | 1162 | 0 → 50 packets |
| 5 | 1243 | 120 | 1123 | 0 → 50 packets |
Во всех пяти: connected, DTLS connected, remote relay, local srflx.
Серверный end ещё не получен в момент connected: медленное gathering Core
не задерживает media. Клиентское gathering в этой сети уже успевало завершиться.
Тест с зависшим STUN на обеих сторонах выполнен отдельно на точных версиях
библиотек: реальные ICE/DTLS/Opus/RTP/decoded PCM работают до его завершения.
Пользовательские и негативные сценарии
- WAV
webrtc-control-2plus2-20261002.wav: PCM16 mono 48 kHz, 3,866125 с, SHA-256513c1066858c3b6052cb1f648cdce18825edd29c47a3a546f149097f9e6fd419. После WAV продолжалась тишина, чтобы не прерывать PCM-поток. - NATS: пять partial, затем один final «Сколько будет? 2 плюс 2. Ответь коротко.»;
затем
SHOW_MESSAGE, в UI «2 плюс 2 будет 4!». - Отдельный текстовый запрос «Сколько будет 3 плюс 4?» получил ответ «3 плюс 4 будет 7!».
В
/core/historyсохранены четыре user/assistant сообщения; после reload они восстановлены в UI. Тесты использовали отдельную сессию. - Legacy offer без
trickleна актуальном Core: DTLS connected,prflx → relay, RTP 0 → 49 packets; local SDP → connected 16092 мс. Для вызова frontend fallback только в тестовой вкладке capability заменялась на 404. - Дополнительный Trickle NAT-тест: из исходного SDP убраны кандидаты, два отправляемых srflx-кандидата имеют намеренно изменённый публичный порт; все пять UDP host-кандидатов содержат приватные адреса. Core обнаружил рабочий NAT-путь: connected за 1936 мс, DTLS и RTP 0 → 50. Это проверка несовпадения сообщённого и фактического порта; browser stats по-прежнему называют локальный кандидат srflx. Реальный prflx явно виден в отдельном legacy-сценарии выше.
- Без OAuth GET capabilities, DELETE и WebSocket Upgrade возвращают 302 на вход, защищённые данные не выданы. С OAuth handshake работает.
- Неверный connection token: WS закрыт с 1008, ни одного сообщения с данными; действующее соединение владельца остаётся connected.
- После закрытия ICE-канала уже connected peer остаётся connected: DTLS работает,
RTP растёт 1950 → 2000 packets; UI предупреждает
audio remains connected. - Явная остановка закрыла все девять созданных тестовых peer. HTTP DELETE после одновременного peer close может получить 404: запись уже удалена; UI cleanup идемпотентен. Изолированные HTTP/WS tests подтверждают token DELETE 204, освобождение registry, auth timeout, лимиты и разрыв до connected.
Все браузерные подмены getUserMedia/RTCPeerConnection/fetch/WebSocket, временный file input и AudioContext удалены после проверки; голос отключён.
Найденная несовместимость и исправление
Первый Trickle rollout отвергал валидные TCP host-кандидаты Chrome и закрывал
попытку с Invalid ICE signaling message. UDP-кандидаты уже были пригодны для
соединения. handle_ice.py теперь проверяет
принадлежность MID/ufrag и общий лимит, затем пропускает TCP-кандидат без отмены
UDP/TURN. Регрессионный тест отправляет
Chrome TCP active, дубликат, UDP и end через настоящий aiohttp WebSocket.
Пять успешных замеров выполнены после этого исправления.
Границы результата
Точная длительность до первого PCM до изменения не наблюдалась в доступной production телеметрии; сравнения этого времени нет. Декодирование подтверждено реальными pinned aiortc tests и production ASR-результатом. WER/DER, физический микрофон, ICE restart и auto-reconnect не проверялись и не добавлялись. Дополнительное полное отключение клиентского STUN не установило соединение; поддерживаемая конфигурация сохраняет STUN/TURN, как в принятом плане. Числа не являются обещанием для других сетей.
Приёмка полного состава chat/voice от 04.10 остаётся отдельным датированным снимком. Сегодня повторно проверен изменённый путь WebRTC → PCM → ASR/NATS → чат/история; файловые voice UI и CPU benchmark не менялись и не объявляются заново проверенными этим отчётом.