# Обход DPI РКН для передачи аудио на Groq Whisper **Дата:** 2026-05-22 **Итог:** WebSocket — тупик. Рабочее решение — `fetch POST` с `multipart/form-data`. --- ## Результаты тестов DPI (Россия -> Германия, lang.kube5s.ru) | Метод | Размер | Результат | |-------|--------|-----------| | HTTP POST с JSON-телом | 40KB JSON | ✅ | | HTTP POST с JSON-телом | 133KB JSON | ✅ | | HTTP POST с JSON-телом | 200KB JSON | ⚠️ погранично | | HTTP POST multipart/form-data | 30-150KB blob | ✅ все | | WebSocket text-фрейм | <=45KB JSON | ✅ | | WebSocket text-фрейм | >45KB JSON | ❌ | | WebSocket чанки по 14KB + задержки | любой | ❌ | | WebSocket binary-фрейм | любой | ❌ | **Вывод:** DPI целенаправленно блокирует WebSocket-трафик. Обычный HTTPS POST проходит до ~130KB JSON или 150KB multipart. --- ## Рабочее решение (v62+) ### Архитектура ``` Браузер (РФ) lang.kube5s.ru (Германия) api.groq.com | | | | POST /openai/v1/audio/ | | | transcriptions | | | Content-Type: multipart/ | | | form-data | | | Authorization: Bearer KEY | | | file= | | | model=whisper-large-v3 | | | language=it ---------------->| | | | прокси пробрасывает ------>| | | Whisper | | <------- JSON {text} -----| | <------- JSON {text} --------| | ``` ### Ключевые моменты 1. Никаких WebSocket — DPI их палит 2. Никакого base64 — Blob идёт как есть в multipart 3. Никакого JSON для аудио — бинарные данные в FormData 4. Сервер НЕ менялся — универсальный Flask-прокси форвардит всё в Groq ### Клиент (index.html v62) ```javascript async function transcribe(blob) { const form = new FormData(); form.append('file', blob, 'audio.webm'); form.append('model', 'whisper-large-v3'); form.append('language', 'it'); const r = await fetch( 'https://lang.kube5s.ru/openai/v1/audio/transcriptions', { method: 'POST', headers: { 'Authorization': 'Bearer ' + GROQ_API_KEY }, body: form } ); const data = await r.json(); return { text: data.text || '', error: data.error ? data.error : null }; } ``` ### Сервер (proxy.py) — без изменений Универсальный прокси: любой метод, любой путь -> api.groq.com. Пробрасывает Authorization и Content-Type, стримит ответ. ### Nginx (groq-proxy.conf) ```nginx location / { proxy_pass http://127.0.0.1:8765; proxy_read_timeout 60s; proxy_send_timeout 60s; proxy_request_buffering off; } ``` --- ## Тупиковые подходы ### WebSocket + base64 (v45-v61) Идея: кодировать аудио в base64, отправлять текстовыми WS-фреймами. Почему провалилось: - Одиночный фрейм до ~45KB JSON проходил, больше — нет - Чанки по 14-30KB с задержками — DPI обрывал соединение на ~3-4 фрейме - Задержки между фреймами (150-200мс) не помогали - Результат хаотичный: 3 фрейма ОК -> 4 таймаут -> 5 ОК -> 8 таймаут - DPI детектит WebSocket-паттерн и режет независимо от содержимого --- ## Фонетический анализ: таймаут decodeAudioData decodeAudioData в Chrome может навсегда зависнуть на WebM/Opus >2с (баг Chrome, не DPI). ```javascript const phonTimeout = new Promise(resolve => setTimeout(() => resolve({ pitchHz:0, pitchStability:0, voiceClarity:0, phonScore:0 }), 15000) ); [whisperResult, phon] = await Promise.all([ transcribe(blob), Promise.race([phoneticAnalysis(blob), phonTimeout]) ]); ``` При таймауте — фонетика показывает нули, транскрипция работает. --- ## Файлы и деплой ``` Сервер (Vultr 95.179.252.111): /opt/groq-proxy/proxy.py — HTTP прокси (Flask/gunicorn :8765) ОСНОВНОЙ /opt/groq-proxy/ws_server.py — WS сервер (:8766) БОЛЬШЕ НЕ ИСПОЛЬЗУЕТСЯ /etc/nginx/conf.d/groq-proxy.conf Клиент (K8s lang.kube5s.ru): ~/lang/index.html — SPA ~/lang/deploy_lang.sh — деплой Команды: ssh -i ~/.ssh/vultr_openssh root@95.179.252.111 cd ~/lang && bash deploy_lang.sh systemctl restart groq-http ``` --- ## Хронология версий | Версия | Подход | Результат | |--------|--------|-----------| | v34-v44 | WAV->WebM, HTTP chunked | ❌ | | v45-v47 | WS binary, чанки base64 | ❌ | | v48 | WS один JSON base64 (~30KB) | ✅ короткие | | v49-v53 | Фиксы WS race condition | ❌ откачены | | v54-v59 | Чанки base64 в WS | ❌ | | v60-v61 | Чанки 14KB + задержки | ❌ | | **v62** | **fetch POST multipart/form-data** | **✅ РАБОТАЕТ** | --- ## Тестовые скрипты (в репозитории) | Файл | Что тестирует | |------|---------------| | test_http.py | HTTP POST JSON разных размеров | | test_multipart.py | HTTP POST multipart/form-data | | test_chunks.py | WS чанки с задержками | | test_threshold.py | Порог DPI для кол-ва WS фреймов | | test_hello.py | Минимальный WS: connect + 1 сообщение | | test_single.py | Один большой WS фрейм |