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:
@@ -165,8 +165,22 @@
|
||||
</div>
|
||||
|
||||
<hr class="flex-shrink-0">
|
||||
<h6 class="text-muted flex-shrink-0">Оригинальный пост</h6>
|
||||
<div class="flex-grow-1 overflow-auto" style="min-height:0;">
|
||||
<div class="d-flex align-items-center justify-content-between flex-shrink-0 mb-1">
|
||||
<ul class="nav nav-pills small mb-0" style="--bs-nav-link-padding-y:.15rem; --bs-nav-link-padding-x:.6rem;">
|
||||
<li class="nav-item"><button class="nav-link active" data-bs-toggle="pill" data-bs-target="#tab-orig-c" type="button">Оригинал</button></li>
|
||||
{% if selected.translated_text %}
|
||||
<li class="nav-item"><button class="nav-link" data-bs-toggle="pill" data-bs-target="#tab-tr-c" type="button">Перевод</button></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% if not selected.translated_text %}
|
||||
<form method="post" action="/posts/{{ selected.id }}/translate" class="mb-0">
|
||||
{{ hid }}
|
||||
<button class="btn btn-sm btn-outline-primary" type="submit" title="Перевести пост на русский локальной моделью (qwen3:8b)">🌐 Перевести</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="tab-content flex-grow-1 overflow-auto" style="min-height:0;">
|
||||
<div class="tab-pane fade show active" id="tab-orig-c">
|
||||
{% if selected.text %}
|
||||
<div class="post-text mt-1">{{ selected.text | safe_html }}</div>
|
||||
{% endif %}
|
||||
@@ -185,6 +199,12 @@
|
||||
{% endif %}
|
||||
{% if selected.summary %}<div class="mt-2"><span class="badge bg-light text-dark">резюме</span> {{ selected.summary }}</div>{% endif %}
|
||||
</div>
|
||||
{% if selected.translated_text %}
|
||||
<div class="tab-pane fade" id="tab-tr-c">
|
||||
<div class="post-text mt-1">{{ selected.translated_text | safe_html }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<div class="alert alert-info py-3">Выберите кандидата слева</div>
|
||||
@@ -230,6 +250,18 @@
|
||||
try { sessionStorage.removeItem(KEY); } catch(e) {}
|
||||
});
|
||||
upd();
|
||||
// Переключение закладок «Оригинал» / «Перевод» (без CDN, vanilla JS)
|
||||
document.querySelectorAll('button[data-bs-toggle="pill"]').forEach(function(btn){
|
||||
btn.addEventListener('click', function(){
|
||||
document.querySelectorAll('button[data-bs-toggle="pill"]').forEach(function(b){
|
||||
b.classList.remove('active');
|
||||
});
|
||||
this.classList.add('active');
|
||||
document.querySelectorAll('.tab-pane').forEach(function(p){ p.classList.remove('show','active'); });
|
||||
var t = document.querySelector(this.dataset.bsTarget);
|
||||
if (t) t.classList.add('show','active');
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user