Files
grabber-ai/docs/integration.md
T

6.5 KiB
Raw Blame History

Интеграция из 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.

Клиентская постобработка (одинакова во всех языках):

  1. токенизировать дорожку (max_len 128, truncation);
  2. прогнать ONNX, взять argmax;
  3. спан: первый токен с меткой B, затем цепочка I; текст спана — по offsets токенизатора (или decode токенов спана);
  4. is_18plus = argmax(age_logits) == 1;
  5. канонизация автора по 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).