Files
lang/doc/ws-dpi-solution.md
T

168 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Обход 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=<blob.webm> | |
| 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 фрейм |