mirror of
https://gitverse.ru/kpa39l/vesti.git
synced 2026-09-29 09:55:03 +00:00
feat(web): перевод поста на русский локальной моделью (openspec post-translate)
Кнопка «🌐 Перевести» в карточках кандидатов и отобранных переводит пост
локальной моделью (qwen3:8b-nothink), результат в posts.translated_text
(идемпотентный ALTER при старте). Метка «Оригинальный пост» стала закладкой,
при готовом переводе рядом появляется закладка «Перевод» (активна по умолчанию),
переключение на vanilla JS. Повторный клик не тратит токены: перевод в БД.
call_ollama из classifier не подошла (парсит ответ как JSON) — прямой запрос
к Ollama /v1/chat/completions. POST /posts/{id}/translate, лимит 6000 символов.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
# post-translate
|
||||
|
||||
Перевод поста на русский локальной моделью (кнопка «Перевести» в карточках кандидатов и отобранных).
|
||||
|
||||
## Why
|
||||
|
||||
Многие посты в кандидатах приходят на английском (LWN, bleepingcomputer, omarchy и т.п.).
|
||||
Чтобы прочитать и отобрать новость, модератору нужен перевод на русский прямо в карточке.
|
||||
Сейчас поста нет: есть только оригинал на языке источника.
|
||||
|
||||
Ограничение: перевод должен быть ПО ЗАПРОСУ (кнопка), потому что массовый перевод
|
||||
всех кандидатов будет жечь токены локальной модели без пользы (модератор смотрит
|
||||
далеко не все посты). Это соответствует существующему паттерну: классификация и
|
||||
пересказ тоже запускаются только по кнопке.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **БД**: колонка `posts.translated_text TEXT` (через ALTER TABLE при старте, идемпотентно,
|
||||
как это делает classifier/classify.py для direction/summary).
|
||||
- **web/app.py**:
|
||||
- helper `_translate_text(text)` → переводит текст через qwen3:8b-nothink
|
||||
(Ollama /v1, та же `call_ollama`, что у rewrite/reclassify). Текст обрезается
|
||||
до 6000 символов (перевод длинного текста + ответ упирается в контекст локальной
|
||||
модели быстрее, чем классификация). Возвращает str, не падает — при ошибке кидает исключение.
|
||||
- POST `/posts/{post_id}/translate` (src, group_by, direction, own, q — как у rewrite):
|
||||
читает пост, переводит, сохраняет в `translated_text` БД, редирект обратно с `selected=post_id`.
|
||||
Если `translated_text` уже есть — не тратит токены, сразу редирект (повторный клик — бесплатный).
|
||||
- render candidates/selected — уже передаёт `selected` (sqlite row), колонка подтянется
|
||||
автоматически (SELECT *).
|
||||
- **web/templates/candidates.html и selected.html**:
|
||||
- Заголовок «Оригинальный пост» → становится кликабельной закладкой/аккордеоном
|
||||
(bootstrap collapse, без JS-файлов — как остальной проект).
|
||||
- Рядом с ним кнопка «Перевести» — POST-форма на `/posts/{id}/translate`.
|
||||
- Если `translated_text` есть — вторая закладка «Перевод» с текстом перевода.
|
||||
- Активная закладка по умолчанию: «Перевод», если перевод уже есть, иначе «Оригинал».
|
||||
|
||||
## Why Not
|
||||
|
||||
- Не переводим автоматически при крауле — жечь токены на все посты (включая неотобранные).
|
||||
- Не делаем отдельную страницу/ход в БД «переводы» — достаточно одной колонки,
|
||||
перевод — атрибут поста, а не сущность.
|
||||
- Не используем внешний API перевода (DeepL и т.п.) — локальная модель уже есть,
|
||||
бесплатна и подходит для новостного текста.
|
||||
|
||||
## Impact
|
||||
|
||||
- Веб-морда: в карточках кандидатов и отобранных появляется перевод на русский по кнопке.
|
||||
- Рестарт vesti-web.service после выката (колонка добавляется идемпотентно).
|
||||
- Никаких изменений в pipeline/publisher/краулере — перевод чисто для модерации,
|
||||
не для публикации.
|
||||
Reference in New Issue
Block a user