feat: browser access (allowed_origins, CORS with Private Network Access), job cancel, Tomie/Unity adapters, benchmark

Includes earlier uncommitted work: Tomie and Unity adapters, benchmark, structured output, call metrics.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nidandClaude Sonnet 5.5 committed 2026-10-25 18:59:28 +03:00
1 parent 13492929c6
commit f49ee90477
32 files changed
+2799 -51

No files matched your search

+123 -8
View File
@@ -5,9 +5,11 @@
## Запуск на Windows
1. Установите Python 3.11+ и LM Studio. Скачайте модель, загрузите её и запустите сервер в Developer → Start Server.
2. Дважды щёлкните `start.bat`. При первом запуске создаётся `.venv`, скачиваются зависимости из PyPI и копируется `config.example.toml` в `config.toml`.
2. Дважды щёлкните `scripts/windows/start.bat`. При первом запуске создаётся `.venv`, скачиваются зависимости из PyPI и копируется `config.example.toml` в `config.toml`.
3. Документация с формами запросов: <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` возвращает ровно одну модель. При нескольких моделях приложение потребует явный выбор. Можно переопределить модель в отдельном проекте.
@@ -104,14 +106,25 @@ python examples/translate_game.py --project PROJECT_ID --input examples/strings.
Сервер ждёт результат до 30 секунд: `200` с готовым заданием или `202` с текущим состоянием и `Location: /jobs/{id}`. После `202` опрашивайте этот адрес. Завершение задания с ошибкой тоже возвращает объект задания — проверяйте `status` и состояние каждой строки, одного HTTP-кода недостаточно.
### Вызов из браузера
Страница, открытая в браузере (например, библиотека игр), может работать с API напрямую, без посредника-сервера. Впишите её адрес в `config.toml` и перезапустите приложение:
```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`. Статусы строк: `pending`, `completed`, `failed`. В `items` возвращаются `id`, `source`, `translation`, `status`, `error`, `attempts`, `cached`, `position`. При общей ошибке подключения/выбора модели задание получает `failed`, ещё не обработанные строки могут остаться `pending`.
Статусы задания: `queued`, `running`, `completed`, `completed_with_errors`, `failed`, `cancelled`. Статусы строк: `pending`, `completed`, `failed`. В `items` возвращаются `id`, `source`, `translation`, `status`, `error`, `attempts`, `cached`, `position`. При общей ошибке подключения/выбора модели задание получает `failed`, ещё не обработанные строки могут остаться `pending`.
Задание хранит снимок контекста, словаря, правил и настроек провайдера. Обновление проекта не меняет уже созданные задания. Повтор старого задания использует старые настройки; для перевода по новым настройкам создайте новое.
@@ -171,11 +184,111 @@ Tencent не обещает отсутствие цензуры в карточ
.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-объекта и хеш исходного текста. Экстрактор только читает файлы игры и запрещает вывод внутрь её каталога. Обратная упаковка и замена шрифтов пока не реализованы.
В словаре найдено 61 значение. По умолчанию извлекаются первые 15; `--limit 0` извлекает все. В manifest рядом с входным JSON сохраняются исходные ключи, номера строк, ID Unity-объекта и хеш исходного текста. Экстрактор только читает файлы игры и запрещает вывод внутрь её каталога. Для обратной записи интерфейса есть отдельный тестовый адаптер ниже.
На этой машине выполнен реальный прогон первых 15 строк через HY-MT2-7B Q4_K_M в LM Studio на RTX 3060 с окном 8192: все строки обработаны, цветовые теги сохранены. Проверены повторное использование кэша и сохранность задания после перезапуска API через батник. Проект содержит контекст игры и словарь интерфейсных терминов. Качество диалогов и поведение модели на откровенных сценах этим тестом не оценивались.
Здесь проверяется только `DefaultUI`: тексты сценариев и динамические реплики не охвачены. Основной контент использует Unity/Naninovel, для полноценной локализации понадобится отдельный разбор сценариев, пользовательского интерфейса и поддержки кириллицы шрифтами.
Этот короткий пример проверяет только `DefaultUI`. Для сценариев есть отдельный массовый прогон ниже.
### Установка тестового перевода в игру
1. **`scripts/windows/tomie/prepare_tomie_translation.bat`** — извлекает весь `DefaultUI` и выбранные статические надписи на кнопках, переводит через API и собирает пакет в `data/tomie/patches/`. Если API не запущен, пытается запустить его с `config.toml`. При первом запуске создаётся `tomie.json` из `tomie.example.json`; там можно изменить путь к игре. Игровые ресурсы на этом этапе не меняются.
2. Закройте игру и запустите **`scripts/windows/tomie/install_tomie_translation.bat`**. Он устанавливает последний успешно собранный пакет, сохраняя оригиналы внутри `.local-translator/backups/` в каталоге игры. Затем запустите игру обычным способом.
3. Для отката закройте игру и запустите **`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 повтора**. Перед замерами выполняется отдельный прогрев на двух строках. Порядок размеров пачек меняется между повторами, чтобы уменьшить влияние порядка. Пример сокращённого прогона:
```powershell
.\scripts\windows\benchmark.bat --limit 20 --repeats 2 --batches 1 10
```
Выбор конкретных скачанных моделей (ключи и точные пути смотрите в `lms ls --json`):
```powershell
.\scripts\windows\benchmark.bat --models hy-mt2-7b another-model
```
Либо задайте `models` в `benchmark.json`. Пустой список означает все скачанные LLM. Точный путь позволяет различать несколько квантовок одной модели. Пример одного элемента:
```json
{
"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.
Отчёт обновляется после каждого прогона. Ошибки моделей не скрываются: после обычной ошибки скрипт пытается проверить следующую модель и завершится с ненулевым кодом. Перезапуск создаёт новый замер, не переиспользует незавершённую серию. Для запуска без паузы батника:
```powershell
.venv\Scripts\python.exe -X utf8 -m local_translator.benchmark --config benchmark.json
```
`GET /jobs/{id}` теперь также содержит `metrics` с накопленными метриками вызовов провайдера. При `/retry` они остаются накопительными за всю жизнь задания. Метрики появляются после перезапуска обновлённого API; у старых заданий ранее выполненные вызовы восстановить нельзя.
## Другие провайдеры
@@ -183,10 +296,12 @@ Tencent не обещает отсутствие цензуры в карточ
```powershell
$env:OPENROUTER_API_KEY = "ваш ключ"
.\start.bat
.\scripts\windows\start.bat
```
Для другого сервера добавьте `[providers.my_server]` с `base_url`, `model`, при необходимости `api_key_env`. В `base_url` входит `/v1` или другой префикс, но не `/chat/completions`. После изменения TOML перезапустите приложение. Сервис поддерживает HTTP OpenAI Chat Completions, а не произвольные нативные API всех поставщиков. Структурированный ответ запрашивается промптом, без обязательного `response_format`, чтобы работать с разными серверами.
Для другого сервера добавьте `[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](https://lmstudio.ai/docs/developer/openai-compat/structured-output).
## Linux и systemd
@@ -218,6 +333,6 @@ journalctl -u local-translator -f
Проверяются обработка пачек, защита синтаксиса, пользовательские правила, словарь, кэш и ревизии, ошибки ответов модели, повтор только неудачных строк и восстановление очереди. Провайдер в тестах подменяется HTTP-имитатором. Windows-батник и работающая LM Studio проверены также в реальном прогоне. Установка systemd на Linux пока не проверялась.
Используйте один процесс приложения: файловая блокировка запрещает двум экземплярам обрабатывать одну БД. Сервер запускается на loopback, без отдельной авторизации и CORS. В SQLite сохраняются исходные строки, контекст и переводы. Для резервного копирования остановите сервер и скопируйте `data/` вместе с `config.toml`.
Используйте один процесс приложения: файловая блокировка запрещает двум экземплярам обрабатывать одну БД. Сервер запускается на loopback, без отдельной авторизации. CORS выключен, пока в `config.toml` пуст `allowed_origins` (см. «Вызов из браузера»). В SQLite сохраняются исходные строки, контекст и переводы. Для резервного копирования остановите сервер и скопируйте `data/` вместе с `config.toml`.
Пока нет веб-интерфейса управления, потоковой выдачи токенов, отмены заданий, автоматического пополнения словаря, точного подсчёта токенов и адаптеров игровых файлов. Интерактивная `/docs` предназначена для работы с API.
Пока нет веб-интерфейса управления, потоковой выдачи токенов, отмены заданий, автоматического пополнения словаря, точного подсчёта токенов и универсальных адаптеров игровых файлов. Адаптер Tomie поддерживает интерфейс и известные текстовые параметры скомпилированных сценариев этой сборки. Интерактивная `/docs` предназначена для работы с API.