6.5 KiB
Интеграция из Rust / TypeScript
Два пути. А (HTTP) — самый простой: Python-процесс один раз загружает модель, любой язык ходит по REST. Б (ONNX) — без Python в рантайме: модель запускается локально в вашем процессе.
| Путь А: HTTP-сервис | Путь Б: ONNX | |
|---|---|---|
| Зависимости рантайма | запущенный Python-процесс | onnxruntime (+ токенизатор) в вашем языке |
| Сегментация HTML | да, на стороне сервиса | нет — в примерах обрабатывается одна дорожка; сегментацию нужно вызвать отдельно или портировать (html_extract.py ~60 строк) |
| Канонизация имён | да, на стороне сервиса | по aliases.json (простой словарь) |
| Скорость | сеть + инференс (~мс) | локальный инференс (~мс) |
| Кому подходит | сервис/бэкенд | CLI/десктоп/edge, где Python нельзя |
Путь А: HTTP-сервис
Запуск (из корня проекта):
python -m src.service --model models/best --port 8000
# или открыто наружу: python -m src.service --host 0.0.0.0 --port 8000
API:
GET /health→{"status": "ok", "model": "...", "device": "...", "classes": [...]}POST /predict— ровно одно из полей:{"text": "Перевод: Дублированный | MovieDalen"}→ одна дорожка;{"html": "<html>…вся страница…"}→ все дорожки релиза.
Ответ: {"tracks": [ …контракт из docs/inference.md… ]}.
curl:
curl -X POST http://127.0.0.1:8000/predict -H "Content-Type: application/json" \
-d '{"text": "Перевод: Дублированный | MovieDalen"}'
Windows/Git Bash: кириллица в аргументе
-dможет искажаться кодировкой консоли — используйте--data-binary @req.jsonс файлом в UTF-8.
Rust (examples/rust/http_client)
cd examples/rust/http_client
cargo run -- --text "Перевод: Дублированный | MovieDalen"
cargo run -- --html page.html
DUB_URL=http://host:8000 cargo run -- --text "..."
Ядро клиента:
let resp: PredictResponse = client
.post("http://127.0.0.1:8000/predict")
.json(&serde_json::json!({ "html": page_html }))
.send().await?
.json().await?;
for t in &resp.tracks {
println!("{} — {}{}", t.translation_type,
t.studio.clone().unwrap_or_else(|| "—".into()),
if t.is_18plus { " 18+" } else { "" });
}
TypeScript / Node.js (examples/typescript)
Без зависимостей (Node >= 18, глобальный fetch):
node examples/typescript/predict.mjs --text "Одноголосый закадровый — Дмитрий Есарев"
node examples/typescript/predict.mjs --html page.html
const res = await fetch("http://127.0.0.1:8000/predict", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text: "Одноголосый закадровый — Дмитрий Есарев" }),
});
const { tracks } = await res.json();
Типизированная версия — examples/typescript/predict.ts (запуск: npx tsx predict.ts --text "...").
Путь Б: ONNX без Python
Экспорт (из корня проекта, после обучения):
python -m src.export_onnx # -> models/best-onnx/ + reports/onnx_parity.md
Состав models/best-onnx/: model.onnx (входы input_ids,
attention_mask, token_type_ids int64 [B, seq]; выходы type_logits
[B, 14], bio_logits [B, seq, 3], age_logits [B, 2]), tokenizer.json
(HF fast-токенизатор), label_maps.json (классы), aliases.json
(нормализация имён).
Эквивалентность PyTorch проверяется на всём held-out test (совпадение
типа/спана/18+ ≥ 99%) — отчёт в reports/onnx_parity.md.
Клиентская постобработка (одинакова во всех языках):
- токенизировать дорожку (max_len 128, truncation);
- прогнать ONNX, взять
argmax; - спан: первый токен с меткой B, затем цепочка I; текст спана — по offsets
токенизатора (или
decodeтокенов спана); is_18plus = argmax(age_logits) == 1;- канонизация автора по
aliases.json.
Rust (examples/rust/onnx_local)
cd examples/rust/onnx_local && cargo build
ORT_DYLIB_PATH="$(find ../../.venv -name onnxruntime.dll | head -1)" \
./target/debug/dub-ai-onnx-local.exe ../../models/best-onnx \
"Перевод: Дублированный | MovieDalen"
Крейты: ort 2.0.0-rc.15 + tokenizers (читает tokenizer.json как есть).
Библиотека onnxruntime грузится динамически — DLL можно взять из pip-пакета
onnxruntime (уже есть в .venv проекта) или из официального релиза;
проверено: тип, спан автора и 18+ совпадают с Python-инференсом.
TypeScript (examples/typescript/onnx_local.mjs)
cd examples/typescript
npm init -y && npm i onnxruntime-node @huggingface/transformers
node onnx_local.mjs "../../models/best-onnx" "Перевод: Дублированный | MovieDalen"
@huggingface/transformers используется только как токенизатор;
инференс — onnxruntime-node.
Замечание про сегментацию страниц в пути Б
Сервис (путь А) сам находит все дорожки в HTML. В пути Б примеры принимают
одну дорожку; для полной обработки страницы нужно сначала получить сегменты
(например, один вызов лёгкого HTTP-эндпоинта или порт html_extract.py —
это регулярки без зависимостей, кроме BeautifulSoup для HTML).