doc: сага с LLM — HTTP/2, ddos-guard, httpx→curl

Задокументированы все 4 попытки, хеш-анализ, находка с Node.js fetch,
хронология 12 коммитов за 14.06.2026.
This commit is contained in:
“Naeel”
2026-06-14 07:42:54 +04:00
parent 879e016754
commit e05af2b6ce
3 changed files with 355 additions and 1 deletions
+64 -1
View File
@@ -277,6 +277,69 @@ contracts-app/
"DB_PASS": "****",
"DB_PORT": "5432",
"DB_USER": "contracts",
"DB_SSLMODE": "disable"
"DB_SSLMODE": "disable",
"LLM_API_KEY": "sk-ucI...",
"LLM_API_URL": "https://api.aillm.ru/v1/chat/completions"
}
```
---
## Сага с LLM (aillm.ru + HTTP/2)
### Проблема
Python-библиотеки (`requests`, `httpx`) не держат HTTP/2 из коробки.
`api.aillm.ru` за ddos-guard, который **требует HTTP/2 от облачных IP**.
С локальной машины (81.200.23.210) даже HTTP/1.1 работает — для этого IP ddos-guard делает исключение.
### Хронология попыток
| # | Подход | Результат |
|---|---|---|
| 1 | `requests` (HTTP/1.1) | ❌ 401 token_not_found_in_db (ложная ошибка от ddos-guard) |
| 2 | `httpx` с `http2=True` | ❌ то же самое — `h2` пакет не установился в контейнере |
| 3 | `httpx[http2]` + `h2` в requirements | ❌ `h2` не ставится через pip в slim-контейнере |
| 4 | `subprocess.run(['curl', '-s', '--http2', ...])` | ⬜ ждёт редеплоя |
### Ключевые находки
1. **Хеш ключа не совпадал:** локально `5fad79ba...`, в облаке `c772c3e5...`.
Оказалось — не разные ключи, а `ddos-guard` возвращает ложный 401 при HTTP/1.1,
хешируя какой-то внутренний токен, а не наш ключ.
2. **Node.js `fetch()` работает** (проект `say`) — потому что undici (нативный fetch в Node)
поддерживает HTTP/2 из коробки. Python — нет.
3. **`curl --http2`** — единственный гарантированный способ получить HTTP/2 в Python-контейнере,
без дополнительных пакетов.
4. **Доступные модели:** `gpt-oss-120b`, `qwen3.6-27b-fp8`, `qwen3-6-27b-fp8-opt`, `whisper-large-v3-turbo`.
### Решение
`llm_client.py` использует `subprocess.run(['curl', '-s', '--http2', ...])` вместо Python HTTP-библиотек.
---
## Хронология коммитов (14.06.2026)
### DB слой
- `ba2e855` — `db.py`: `execute()` + `query_one()` для INSERT/UPDATE/DELETE и единичного SELECT
- `4635ed4` — `test_routes`: action `exec` через `db.execute()` (DDL без fetch)
- `9d01dba` — `schema.py`: `db.query → db.execute` для DDL
### Upload слой
- `8db35e3` — `upload.py`: Blueprint `/upload`, поток bytes→parser→textify→DB
- `db2f6cc` — fix MIME: приоритет расширения над content_type
### LLM слой
- `44e53ed` — `llm_client.py`: HTTP-клиент (сначала `requests`)
- `fe8a556` — `test_routes`: action `llm` для теста
- `c580c93` — fix: модель `gpt-oss-120b` (не `deepseek-chat`)
- `5904830` — fix: `httpx[http2]` вместо `requests`
- `c97f26d` — fix: `h2` явно в requirements
- `099edbc` — fix: `curl --http2` через `subprocess` вместо httpx
### Debug
- `cc3203a` — `test_routes`: action `debug` для просмотра env vars
- `0d0ba18` — debug показывает длину ключа
- `b457de4` — debug показывает `h2_available` и `httpx_version`