Includes earlier uncommitted work: Tomie and Unity adapters, benchmark, structured output, call metrics. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
50 KiB
Local Translator
Локальный HTTP API для перевода игровых строк через LM Studio, OpenRouter и другие серверы с совместимым POST /chat/completions. Python 3.11+. По умолчанию перевод English → Russian, сервер слушает только 127.0.0.1:8787.
Запуск на Windows
- Установите Python 3.11+ и LM Studio. Скачайте модель, загрузите её и запустите сервер в Developer → Start Server.
- Дважды щёлкните
scripts/windows/start.bat. При первом запуске создаётся.venv, скачиваются зависимости из PyPI и копируетсяconfig.example.tomlвconfig.toml. - Документация с формами запросов: http://127.0.0.1:8787/docs. Состояние приложения: http://127.0.0.1:8787/health.
Все Windows-батники находятся в scripts/windows/: запуск API и бенчмарк — непосредственно в этой папке, подготовка/установка/откат Tomie — в scripts/windows/tomie/, внутренние общие команды — в scripts/windows/lib/. Они определяют корень проекта по своему расположению и работают при запуске из другого каталога.
После установки зависимостей локальный перевод не требует интернета. Чтобы обновить зависимости после изменения pyproject.toml, выполните .venv\Scripts\python.exe -m pip install -e ..
В config.toml задайте providers.lmstudio.model — точный ID из GET /providers/lmstudio/models, а не произвольное имя файла. Пустое значение допустимо, если /models возвращает ровно одну модель. При нескольких моделях приложение потребует явный выбор. Можно переопределить модель в отдельном проекте.
Автозапуск LM Studio
В config.toml, внутри [providers.lmstudio]:
autostart = true
startup_timeout = 90
# Необязательно: точное имя уже скачанной модели, известное `lms ls`.
load_model = ""
Нужен доступный в PATH официальный lms CLI. Если выбранный ID уже доступен через API, работающий сервер используется сразу. Иначе приложение выполняет lms daemon up, затем lms server start --port 1234 --bind 127.0.0.1, при заданном load_model — lms load ... --context-length 8192, с --identifier, если задан model. Окно загрузки меняется через load_context_length. После этого приложение ждёт доступности выбранного ID в /models до startup_timeout. GUI открывать не требуется. Это запуск локального движка; модель автоматически не скачивается. Внешний CLI запускается без видимого окна на Windows.
Если автозапуск не удался, API всё равно доступен для управления проектами. Причина видна в консоли и health.startup_errors (результат именно последней попытки запуска, а не текущая готовность провайдера). Запустите LM Studio вручную и повторите неудачное задание.
Официальные инструкции: сервер LM Studio, CLI, совместимый API.
Первый перевод из Python
В другой консоли из каталога проекта:
python examples/translate_game.py --input examples/strings.json --output data/result.json
Скрипт не требует сторонних библиотек. Создаёт проект с контекстом и небольшим примером словаря, отправляет строки пачками по 10 и сохраняет полный результат. Вход — JSON-массив:
[
{"id": "intro.001", "text": "Welcome, {player_name}!"},
{"id": "intro.002", "text": "Bring me the Moonstone."}
]
ID задаёт ваш скрипт извлечения; если их нет в игре, используйте порядковые номера в виде строк. ID уникальны внутри одного задания. Связь с файлами и обратную вставку обеспечивает клиентский скрипт.
Для создания проекта из своего JSON используйте --project-file examples/tomie_project.json. Существующий проект:
python examples/translate_game.py --project PROJECT_ID --input examples/strings.json --output data/result.json
Рядом с результатом сохраняется result.json.job.json с ID задания. Повтор той же команды продолжит наблюдать это задание после остановки клиента. --retry повторяет только неудачные строки. --new-job --project PROJECT_ID создаёт новое задание с текущими настройками проекта. Состояние предыдущего задания доступно в API. При создании нового проекта можно указать --context "описание игры" и --target Russian.
API
Полная схема: /docs и /openapi.json. Ключ модели не передаётся в запросах — используется переменная окружения, имя которой задано в конфигурации провайдера.
Создать проект
POST /projects
{
"name": "My RPG",
"source_language": "English",
"target_language": "Russian",
"context": "Мрачная фэнтези RPG. Реплики короткие, речь естественная.",
"instructions": "Сохраняй грубость и стиль оригинала. Обращение на ты.",
"glossary": {"Moonstone": "Лунный камень"},
"provider": "lmstudio",
"model": null,
"disabled_rules": [],
"protection_rules": []
}
Ответ содержит id и revision. Языки можно опустить: по умолчанию English → Russian. Контекст и словарь добавляются к каждой пачке. История разговоров между пачками не накапливается; словарь редактируется явно, автоматического извлечения терминов пока нет. Если вся строка в точности совпадает с ключом словаря (регистр учитывается), используется заданное значение без генерации моделью. Это обеспечивает точный перевод коротких кнопок и сокращений; защита синтаксиса проверяется и в этом случае. У таких строк attempts: 0. Для терминов внутри более длинных строк словарь служит инструкцией модели, без гарантии точного соблюдения.
GET /projects, GET /projects/{id} — просмотр. PUT /projects/{id} — полная замена настроек, без полей id и revision; для изменения одного значения сначала получите проект и отправьте все его настройки. Каждое обновление увеличивает revision.
Перевести одну строку или пачку
POST /projects/{id}/translate
{
"items": [
{"id": "1", "text": "Hello, {player}!"},
{"id": "2", "text": "You have %d seconds."}
],
"batch_size": 10,
"max_batch_chars": 12000,
"retries": 2,
"use_cache": true
}
Один элемент — перевод одной строки. Одна пачка — один запрос к модели с общим контекстом. Порядок результата соответствует входу, даже если модель переставила записи в JSON. retries: 2 означает максимум три попытки для каждой строки; успешно переведённые строки повторно не отправляются. Ответ модели проверяется до сохранения перевода.
Сервер ждёт результат до 30 секунд: 200 с готовым заданием или 202 с текущим состоянием и Location: /jobs/{id}. После 202 опрашивайте этот адрес. Завершение задания с ошибкой тоже возвращает объект задания — проверяйте status и состояние каждой строки, одного HTTP-кода недостаточно.
Вызов из браузера
Страница, открытая в браузере (например, библиотека игр), может работать с API напрямую, без посредника-сервера. Впишите её адрес в config.toml и перезапустите приложение:
allowed_origins = ["https://games.example", "http://localhost:5173"]
Допустимы только точные адреса вида схема://хост[:порт], без пути и без *: иначе любой сайт мог бы пользоваться вашей моделью и читать проекты. Для Chrome сервер отвечает на предварительный запрос с Access-Control-Request-Private-Network. Сам браузер при первом обращении https-страницы к 127.0.0.1 спросит разрешение на доступ к локальной сети. GET /health возвращает api_version (сейчас 2), по которой клиент проверяет совместимость.
Долговечные задания
POST /projects/{id}/jobs— сразу возвращает202и ID. Предпочтительный вариант для скриптов.GET /jobs/{id}— прогресс и результаты всех строк.GET /projects/{id}/jobs?limit=50&offset=0— история заданий, новые первыми.POST /jobs/{id}/retry— повторить незавершённые/неудачные строки после ошибки. Сбрасывает их счётчик попыток. Готовые строки сохраняются.POST /jobs/{id}/cancel— остановить задание в очереди или в работе: невыполненные строки получаютfailedс ошибкойCancelled, статус задания —cancelled; готовые строки сохраняются. Повторный запрос безвреден;retryвозобновляет остановленное.
Статусы задания: queued, running, completed, completed_with_errors, failed, cancelled. Статусы строк: pending, completed, failed. В items возвращаются id, source, translation, status, error, attempts, cached, position. При общей ошибке подключения/выбора модели задание получает failed, ещё не обработанные строки могут остаться pending.
Задание хранит снимок контекста, словаря, правил и настроек провайдера. Обновление проекта не меняет уже созданные задания. Повтор старого задания использует старые настройки; для перевода по новым настройкам создайте новое.
SQLite сохраняет каждый успешный результат. После перезапуска незавершённые задания возвращаются в очередь, готовые строки не теряются. Запрос, прерванный непосредственно во время генерации или до записи результата, может быть отправлен модели повторно; это не гарантия ровно одного вызова. Счётчик попыток также сохраняется. После исчерпания попыток нужен явный /retry.
Кэш разделён по проекту, его ревизии, настройкам провайдера, выбранной модели и версии промпта. Изменение настроек не удаляет историю, но прежний кэш больше не используется. use_cache: false принудительно выполняет новый перевод. Замена весов модели под тем же ID автоматически не распознаётся: обновите проект или отключите кэш.
Один процесс последовательно обрабатывает задания, чтобы не перегружать локальную GPU. Пачки группируются по числу строк и сумме символов, а не по точному числу токенов. Предел одной строки — 30 000 символов; она не разрезается даже при превышении max_batch_chars. На задание допускается 1–500 строк, суммарно до 1 000 000 символов; более крупные входы делите на задания. Размер реального контекста модели настраивается в LM Studio. При переполнении уменьшите пачку/контекст или увеличьте окно модели.
Плейсхолдеры и правила
GET /protection-rules возвращает имена и регулярные выражения встроенных правил:
braces:{player},{0},{{value}};dollar:${quest},$variable;printf:%s,%1$s,%02d,%NAME%,%%;tags:<color=red>,</color>, HTML/XML-подобные теги;square:[wait]и другие конструкции в квадратных скобках;escapes: текстовые\n,\t,\u1234и подобные последовательности;newlines: настоящие LF/CRLF/CR;entities:&,{;controls: табуляция и другие управляющие символы.
До отправки модели фрагменты заменяются уникальными маркерами. После ответа проверяются их количество и порядок, восстанавливаются исходные байт-в-байт значения строковых фрагментов, проверяется отсутствие новых распознаваемых конструкций. Повреждённый ответ не сохраняется как успешный перевод. Строгий порядок также запрещает переставлять плейсхолдеры ради грамматики.
В проекте можно отключать встроенные правила и добавлять свои Python-regex:
{
"disabled_rules": ["square"],
"protection_rules": [
{"name": "engine_codes", "pattern": "@[A-Z_]+@"},
{"name": "rpg_maker", "pattern": "\\\\[A-Za-z]+\\[[0-9]+\\]"}
]
}
Это часть полного объекта проекта. Пользовательские правила имеют приоритет над встроенными при совпадении начала фрагмента. При перекрытии защищается первое совпадение; вложенные языки/ICU/сложные шаблоны требуют правил для конкретного движка. square намеренно широкое: если в скобках обычный текст, отключите его. Универсального распознавания всех игровых форматов нет. Защита проверяет структуру, но не смысл или качество перевода.
HY-MT2 и выбор модели
По умолчанию локальный провайдер использует prompt_style = "hy_mt": один user-промпт с контекстом, словарём и JSON. temperature=0.7, top_p=0.6, top_k=20, repetition_penalty=1.05, max_tokens=4096 соответствуют рекомендациям Tencent для 7B. Для обычных instruction-моделей поставьте prompt_style = "generic"; неподдерживаемые необязательные параметры удалите из TOML. Параметры и инструкции — карточка Tencent.
Для RTX 3060 12 GB и 32 GB RAM разумно начать с HY-MT2-7B Q6_K, сравнив с Q4_K_M на своих строках. Файлы официальных квантовок: Q4_K_M — 4,62 GB, Q6_K — 6,16 GB, Q8_0 — 7,98 GB. Это размеры весов, не общий расход VRAM: контекст и runtime требуют дополнительную память. Стартовая проба — пачка 10 коротких строк и окно 8192, затем настройка по фактическому расходу LM Studio. Это ориентир, не измеренный тест на этой GPU. Официальные GGUF.
Tencent не обещает отсутствие цензуры в карточке HY-MT2. Промпт просит сохранять грубость и откровенность, однако отсутствие отказов и смягчений нужно проверять на репликах игры. Дополнительный кандидат для сравнения — Huihui Qwen3-8B abliterated, заявленный автором как модель с удалением отказов. Такая модификация сама по себе не доказывает качество EN→RU. Автоматической проверки отказов по смыслу в API нет.
Реальный перевод HY-MT2 через LM Studio пока не входит в автоматические проверки: тесты используют имитатор OpenAI-совместимого провайдера.
Пример: Tomie Wants to Get Married
Для версии Expansion-v1.3801-Win подготовлен отдельный экстрактор словаря интерфейса Naninovel DefaultUI, хранящегося в Unity TextAsset внутри TomieWGM_Data/data.unity3d.
.venv\Scripts\python.exe -m pip install -e ".[unity]"
.venv\Scripts\python.exe examples/extract_tomie_ui.py "D:\Torrents\Pgames\Tomie_Wants_to_Get_Married_Expansion-v1.3801-Win" --limit 15
.venv\Scripts\python.exe examples/translate_game.py --project-file examples/tomie_project.json --input data/tomie/input.json --output data/tomie/result.json
В словаре найдено 61 значение. По умолчанию извлекаются первые 15; --limit 0 извлекает все. В manifest рядом с входным JSON сохраняются исходные ключи, номера строк, ID Unity-объекта и хеш исходного текста. Экстрактор только читает файлы игры и запрещает вывод внутрь её каталога. Для обратной записи интерфейса есть отдельный тестовый адаптер ниже.
На этой машине выполнен реальный прогон первых 15 строк через HY-MT2-7B Q4_K_M в LM Studio на RTX 3060 с окном 8192: все строки обработаны, цветовые теги сохранены. Проверены повторное использование кэша и сохранность задания после перезапуска API через батник. Проект содержит контекст игры и словарь интерфейсных терминов. Качество диалогов и поведение модели на откровенных сценах этим тестом не оценивались.
Этот короткий пример проверяет только DefaultUI. Для сценариев есть отдельный массовый прогон ниже.
Установка тестового перевода в игру
scripts/windows/tomie/prepare_tomie_translation.bat— извлекает весьDefaultUIи выбранные статические надписи на кнопках, переводит через API и собирает пакет вdata/tomie/patches/. Если API не запущен, пытается запустить его сconfig.toml. При первом запуске создаётсяtomie.jsonизtomie.example.json; там можно изменить путь к игре. Игровые ресурсы на этом этапе не меняются.- Закройте игру и запустите
scripts/windows/tomie/install_tomie_translation.bat. Он устанавливает последний успешно собранный пакет, сохраняя оригиналы внутри.local-translator/backups/в каталоге игры. Затем запустите игру обычным способом. - Для отката закройте игру и запустите
scripts/windows/tomie/restore_tomie_original.bat. Сохранения игры эти инструменты не изменяют. Резервные копии после отката остаются на диске.
Тестовый адаптер меняет 61 запись словаря и выбранные статические UI-надписи в обоих наборах ресурсов: prefab-бандле и data.unity3d. Для Text-компонентов без сохранённой схемы используется проверяемая схема Unity UI Text из бандла той же игры. Переводятся также пять названий качества графики; порядок и значения самих графических настроек сохраняются. Короткие кнопки используют сокращения и подгонку размера шрифта. Декоративный шрифт без кириллицы заменяется встроенной Candara; статические TMPro SDF-атласы не перестраиваются. Сценарии, диалоги, текст на картинках и все остальные элементы игры этим патчем не переводятся. Отдельный пункт «Русский» в списке языков не добавляется: заменяются исходные записи интерфейса, а название исходного языка English остаётся корректным.
Названия качества добавляются как пять недостающих managed-text записей GameSettingsGraphicsDropdown.GraphicOption1..5: игра подставляет эти поля при открытии настроек. Для явной офлайн-пересборки можно передать --result <путь к translations.json> батнику подготовки. Такой файл должен полностью соответствовать текущему набору ID и исходных строк; неполные и несовместимые результаты отклоняются, а плейсхолдеры проверяются повторно. Если меняете контекст или словарь, выполняйте обычную подготовку без --result, чтобы получить новый перевод.
Пакет содержит translations.json, ui-preview.json, DefaultUI.ru.txt, manifest.json и готовые файлы payload/. После упаковки они повторно читаются через UnityPy: проверяются ID всех объектов и неизменность объектов, не относящихся к патчу. Для Addressables пересчитывается CRC несжатого бандла и обновляются размер и хеш его записи в каталоге с сохранением смещений бинарных данных.
Установка проверяет хеши исходников, пакета и резервных копий. При другой версии игры или наличии сторонних изменений она останавливается. Игра должна быть закрыта; параллельные установки блокируются. При ошибке частичной установки выполняется откат уже заменённых файлов, а журнал позволяет повторить восстановление после прерывания. Откат не перезаписывает неизвестные сторонние изменения. Новую сборку можно подготовить при установленном старом переводе: используются проверенные резервные копии оригиналов. Перед установкой новой сборки закройте игру, восстановите оригинал и запустите установку снова.
Размер тестового пакета — примерно 565 MiB; для резервных и временных копий при установке дополнительно требуется место на диске игры. Поддерживается конкретная локальная Windows-сборка Tomie, универсальной установки в другие Unity-игры пока нет. Проверка отображения и удобства в самой игре выполняется пользователем: проверьте настройки, загрузку/сохранение, русские буквы и помещаются ли надписи в кнопки.
Массовый перевод сценариев и динамических реплик
После подготовки UI-пакета запустите scripts/windows/tomie/translate_all_tomie.bat. Он извлекает все 108 скомпилированных сценариев этой сборки, реплики, варианты выбора, всплывающие сообщения, оставшийся статический UI, словари Tips, CharacterNames, Inventory и динамические шаблоны из StreamingAssets/text/. Изображения с надписями и строки, формируемые непосредственно C#-кодом, не извлекаются. Для этой сборки получено 34 274 сценарных записи и ещё 5 128 записей других ресурсов; после удаления повторов и разбиения длинных значений модели передаются 28 002 уникальных фрагмента.
Работа идёт последовательно через локальный API: части по 400 строк, внутри — до 20 строк за запрос, с ограничением 2 000 исходных символов. Настройки в tomie.json: full_output, full_part_size, full_batch_size, full_max_batch_chars. Размер частей ограничен 500 строками API; длинные значения разбиваются вне защищённых тегов и выражений. Общий контекст и словарь проекта берутся из examples/tomie_project.json. Между HTTP-запросами история чата модели не сохраняется; контекст передаётся заново с каждой пачкой.
scripts/windows/tomie/translation_status.bat показывает сохранённый прогресс. Результаты находятся в data/tomie/full-translation/: dataset.json, state.json, progress.json, parts/*.json; дополнительный набор — в подпапке extra/. При фоновом запуске логи пишутся в run.log и run-error.log. SQLite сохраняет каждую переведённую строку. Повторный запуск батника продолжает те же задания и повторяет ошибки; не меняйте входные файлы и параметры посреди прогона. Для новой модели или другого контекста выберите другой full_output. Если API остановлен, сначала снова запустите его; уже созданное задание сохраняется в БД.
После успешного завершения обоих наборов автоматически собирается общий пакет, а data/tomie/patches/latest.json указывает на него. Неполный перевод не устанавливается. Для замены ранее установленного UI-патча сначала закройте игру и выполните restore_tomie_original.bat, затем install_tomie_translation.bat из той же папки. Сам массовый прогон файлы игры не меняет и не запускает игру.
Сценарии разбираются по схемам из оригинальных DLL с поддержкой старого формата Unity SerializeReference. До перевода каждый объект проходит побайтный цикл чтения/записи; при упаковке сохраняются команды, условия, ссылки, номера строк и хеши исходных реплик. Упакованные сценарии повторно сравниваются целиком с ожидаемыми объектами; неизменяемые объекты и медиаресурсы тоже проверяются. Ключи словарей, разделители подсказок и таблицы синонимов сохраняются. Временные распакованные медиаданные хранятся на диске в data/tomie/tmp/, чтобы не создавать несколько больших копий в RAM; для сборки желательно иметь около 15 GiB свободного места на диске проекта. Поддержка кириллицы динамических реплик и естественность случайно комбинируемых шаблонов требуют проверки в игре.
Командная строка для диагностики: .venv\Scripts\python.exe -X utf8 -m local_translator.tomie --config tomie.json status. Чтобы повторно собрать перевод после изменения контекста/словаря, запустите scripts/windows/tomie/prepare_tomie_translation.bat; при ошибках API исходники и ID задания сохраняются в каталоге сборки.
Проверка скорости на разных моделях
Запустите scripts/windows/benchmark.bat. Он создаёт benchmark.json из benchmark.example.json, устанавливает недостающий UnityPy, извлекает первые 30 строк интерфейса Tomie и прогоняет их на всех скачанных текстовых моделях LM Studio. Embedding-модели исключаются; новые модели автоматически не скачиваются. Для другого компьютера измените путь game в JSON или задайте input с готовым массивом {id, text}. Нужны Python 3.11+, LM Studio и доступный lms CLI.
По умолчанию проверяются пачки 1, 5 и 10 строк, по 3 повтора. Перед замерами выполняется отдельный прогрев на двух строках. Порядок размеров пачек меняется между повторами, чтобы уменьшить влияние порядка. Пример сокращённого прогона:
.\scripts\windows\benchmark.bat --limit 20 --repeats 2 --batches 1 10
Выбор конкретных скачанных моделей (ключи и точные пути смотрите в lms ls --json):
.\scripts\windows\benchmark.bat --models hy-mt2-7b another-model
Либо задайте models в benchmark.json. Пустой список означает все скачанные LLM. Точный путь позволяет различать несколько квантовок одной модели. Пример одного элемента:
{
"model": "tencent/Hy-MT2-7B-GGUF/Hy-MT2-7B-Q4_K_M.gguf",
"label": "HY-MT2 Q4",
"prompt_style": "hy_mt",
"temperature": 0.7,
"top_p": 0.6,
"top_k": 20,
"repetition_penalty": 1.05
}
Для HY-MT автоматически выбирается профиль Tencent, для остальных — generic с температурой 0.2 и без специфичных параметров. В бенчмарке LM Studio по умолчанию включён structured_output: true; для отдельной модели можно явно отключить. Профиль можно переопределить в каждом элементе списка; фактические настройки сохраняются в отчёте. Сравнение разных профилей — сравнение готовых конфигураций перевода, а не строго одинаковых параметров декодирования.
Что измеряется
Бенчмарк запускает отдельный экземпляр нашего API на 127.0.0.1:8788 с собственной SQLite в каталоге результата. Основной API на 8787 продолжает работать, но во время замеров не отправляйте в него задания и не запускайте другие генерации LM Studio. Одинаковые строки, контекст проекта и размер окна используются для всех моделей. Итоговые переводы каждого прогона сохранены для ручного сравнения.
- Кэш переводов отключён. Из тестового словаря удаляются ключи, совпадающие с целыми входными строками, чтобы все строки реально проходили через модель. Остальные термины остаются в промпте. Рабочий проект игры не меняется.
- Время загрузки модели и прогрева записывается отдельно и не входит в замер перевода. Нативный prompt cache LM Studio не очищается: это измерение прогретого сервиса.
- В полное время входят отправка задания по HTTP, ожидание очереди, генерация, проверки, SQLite и повторные попытки. Опрос готовности идёт раз в 50 мс, поэтому время включает небольшую погрешность опроса.
- Скорость — количество успешно переведённых моделью строк или исходных Unicode-символов за полное время. Неуспешный прогон не участвует в медиане скорости успешных прогонов; число ошибок и повторов показано отдельно.
- API сохраняет число запросов провайдеру, их суммарное время и usage-токены, если сервер их сообщает. Отсутствующие токены обозначаются
null, а не нулём.completion_tokens_per_request_secondделит выходные токены на полное время запросов, включая обработку промпта; это не чистые decode tokens/s. Токенизаторы у моделей различаются, поэтому для сравнения полезнее символы/с. - Синтаксические проверки не доказывают качество перевода. Отдельно перечисляются строки, полностью совпавшие с оригиналом: часть из них может быть допустимыми именами/аббревиатурами. Отказы, смягчение смысла и качество русского требуют чтения результатов.
Модели переключаются последовательно. Занятые модели скрипт не выгружает и останавливается при обнаружении активной генерации. Для освобождения GPU он временно выгружает простаивающие LLM, оставляя embedding-модели. По умолчанию модель загружается заново с заданными context_length и gpu, без интерактивных вопросов, по modelKey установленного CLI; после загрузки проверяется путь фактически выбранного GGUF. reuse_loaded: true позволяет использовать уже загруженный экземпляр с нужным окном, но его offload скрипт не проверяет. После завершения или обычного Ctrl+C скрипт старается восстановить исходные ID моделей и размеры окон. Специальные настройки движка и offload исходных экземпляров полностью не воспроизводятся. Исходный список сохранён в original-models.json; если восстановление не удалось, это указано в отчёте. При принудительном завершении процесса восстановление не гарантируется.
Не запускайте несколько бенчмарков одновременно. Для одного каталога настроек действует файловая блокировка. Таймаут задания останавливает весь прогон, чтобы не переключать модель во время незавершённого запроса.
Результаты
Каждый запуск создаёт отдельный каталог data/benchmarks/<время>-<id>/:
report.html— обзор для браузера с медианами, ошибками и повторами;report.json— параметры, хеш набора, фактические модели/квантовки, usage и все замеры;runs.csv— замеры для Excel/анализа;jobs/— полные ответы API, исходники и переводы каждого прогона, включая прогрев;raw-responses/— исходные ответы модели до разбора JSON, для диагностики ошибок формата;dataset.json— точный использованный набор;translations.sqlite3— сохранённые проекты, задания и метрики;active-job.json— последнее созданное задание на случай прерывания.
При наличии nvidia-smi в отчёт также записываются название GPU, объём памяти и версия драйвера. Хеш исходников реализации позволяет отличать серии до и после изменений API. Диагностические файлы содержат переводы, а не только числовые показатели; весь каталог результатов исключён из Git.
Отчёт обновляется после каждого прогона. Ошибки моделей не скрываются: после обычной ошибки скрипт пытается проверить следующую модель и завершится с ненулевым кодом. Перезапуск создаёт новый замер, не переиспользует незавершённую серию. Для запуска без паузы батника:
.venv\Scripts\python.exe -X utf8 -m local_translator.benchmark --config benchmark.json
GET /jobs/{id} теперь также содержит metrics с накопленными метриками вызовов провайдера. При /retry они остаются накопительными за всю жизнь задания. Метрики появляются после перезапуска обновлённого API; у старых заданий ранее выполненные вызовы восстановить нельзя.
Другие провайдеры
Пример OpenRouter уже есть в config.example.toml. Задайте модель и OPENROUTER_API_KEY в окружении процесса, затем создайте проект с provider: "openrouter". В PowerShell:
$env:OPENROUTER_API_KEY = "ваш ключ"
.\scripts\windows\start.bat
Для другого сервера добавьте [providers.my_server] с base_url, model, при необходимости api_key_env. В base_url входит /v1 или другой префикс, но не /chat/completions. После изменения TOML перезапустите приложение. Сервис поддерживает HTTP OpenAI Chat Completions, а не произвольные нативные API всех поставщиков.
Для LM Studio включён structured_output = true: ответ ограничен JSON Schema через response_format, дополнительно проверяются ID и защищённые фрагменты. Модели передаются короткие внутренние ID вместо длинных ключей игры; клиент получает свои исходные ключи. Это уменьшает объём ответа и не позволяет превратить ключи в переводимый текст. Для сервера/модели без JSON Schema оставьте structured_output = false (по умолчанию для произвольного провайдера): тогда формат запрашивается только промптом, с последующей проверкой. Автоматического скрытого переключения между этими режимами нет. Structured output в LM Studio.
Linux и systemd
Для ручного запуска: bash start.sh (нужны Python 3.11+, модуль venv и доступ к PyPI при первой установке).
Для системного сервиса пример рассчитан на /opt/local-translator и пользователя translator. Подготовьте каталог с проектом и виртуальное окружение до запуска systemd:
sudo useradd --system --home-dir /opt/local-translator --shell /usr/sbin/nologin translator
sudo python3 -m venv /opt/local-translator/.venv
sudo /opt/local-translator/.venv/bin/python -m pip install /opt/local-translator
sudo cp /opt/local-translator/config.example.toml /opt/local-translator/config.toml
sudo install -d -o translator -g translator /opt/local-translator/data
sudo cp /opt/local-translator/deploy/local-translator.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now local-translator
journalctl -u local-translator -f
Если пользователь/каталог уже существуют, адаптируйте команды. Путь к БД должен оставаться внутри доступного сервису data/ либо нужно изменить ReadWritePaths. Ключи провайдеров можно положить в /etc/local-translator.env (OPENROUTER_API_KEY=..., права 600, владелец root).
Пример systemd предполагает уже работающий локальный сервер модели или внешний провайдер. LM Studio, запущенная под вашим desktop-пользователем, доступна сервису через loopback. Автозапуск lms из этого защищённого systemd unit требует отдельной настройки пользователя, HOME, PATH и прав на модели; для него удобнее пользовательский unit с собственным LM Studio/llmster. Системный unit сам это не настраивает.
Проверки и ограничения первой версии
.venv\Scripts\python.exe -m unittest discover -s tests -v
Проверяются обработка пачек, защита синтаксиса, пользовательские правила, словарь, кэш и ревизии, ошибки ответов модели, повтор только неудачных строк и восстановление очереди. Провайдер в тестах подменяется HTTP-имитатором. Windows-батник и работающая LM Studio проверены также в реальном прогоне. Установка systemd на Linux пока не проверялась.
Используйте один процесс приложения: файловая блокировка запрещает двум экземплярам обрабатывать одну БД. Сервер запускается на loopback, без отдельной авторизации. CORS выключен, пока в config.toml пуст allowed_origins (см. «Вызов из браузера»). В SQLite сохраняются исходные строки, контекст и переводы. Для резервного копирования остановите сервер и скопируйте data/ вместе с config.toml.
Пока нет веб-интерфейса управления, потоковой выдачи токенов, отмены заданий, автоматического пополнения словаря, точного подсчёта токенов и универсальных адаптеров игровых файлов. Адаптер Tomie поддерживает интерфейс и известные текстовые параметры скомпилированных сценариев этой сборки. Интерактивная /docs предназначена для работы с API.