<div align="center">

# 🐋 dsh-think-translate

**Языки:** [English](README.md) · [中文](README.zh-CN.md) · [日本語](README.ja.md) · [한국어](README.ko.md) · [Español](README.es.md) · [Français](README.fr.md) · [Deutsch](README.de.md) · [Русский](README.ru.md)

[![npm version](https://img.shields.io/npm/v/dsh-think-translate?color=4D6BFE&label=npm)](https://www.npmjs.com/package/dsh-think-translate)
[![license](https://img.shields.io/npm/l/dsh-think-translate?color=4D6BFE)](LICENSE)
[![dsh](https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white)](https://github.com/deepseek-ai/deepseek-harness)

<img src="demo/demo.gif?v=2" width="46%" alt="dsh-think-translate demo" style="border:1px solid #4D6BFE;border-radius:8px;margin:4px" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="demo/demo2.gif?v=2" width="41%" alt="dsh-think-translate demo 2" style="border:1px solid #4D6BFE;border-radius:8px;margin:4px" />

</div>

---

Перевод на уровне отображения для веб-интерфейса [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): **цепочка размышлений (строка Think), карточки задач и текст ответа** отображаются на выбранном целевом языке, а оригиналы полностью сохраняются в истории; переведённый текст **никогда не попадает в контекст модели**.

## ✨ Возможности

Модели семейства DeepSeek часто рассуждают на китайском — или на том языке, на котором им случилось думать. dsh-think-translate показывает строку Think, карточки задач и ответ на *вашем* языке в реальном времени — как субтитры к размышлениям модели.

- **🕵️ Читайте любую цепочку рассуждений** — рассуждения, цепочка мыслей, карточки задач и ответы переводятся в реальном времени, потоком по батчам
- **8 целевых языков** — 中文 / English / 日本語 / 한국어 / Español / Français / Deutsch / Русский
- **Одноязычный интерфейс** — панель настроек, строки размышлений и карточки задач следуют целевому языку (без смешения zh/en); выбор сохраняется
- **Локальная модель в приоритете** — использует вашу локальную модель Ollama (qwen и др.): приватно, офлайн, бесплатно. При первом выборе **автоматически запускается загрузка** с индикатором прогресса; модель настраивается и активируется по завершении
- **🧠 Нулевая стоимость контекста** — чистый слой отображения: модель по-прежнему видит исходный текст, а переведённый текст никогда не расходует окно контекста
- **Запасной вариант Google / Bing** — автоматическое переключение, если локальная модель недоступна (google идёт через Node CONNECT-туннель с системным прокси)
- **Кодовые артефакты пропускаются** — пути, команды, URL, регулярные выражения и чистые строки кода не переводятся
- **Пакетный перевод по предложениям** — длинные цепочки переводятся небольшими пакетами, чтобы локальные малые модели сохраняли качество
- **🧩 Разбивка по абзацам и предложениям** — длинные цепочки делятся по пустым строкам (структура абзацев сохраняется) и дополнительно по предложениям, чтобы локальные малые модели сохраняли качество
- **Потоковый вывод** — переводы появляются пакет за пакетом во время размышления; разверните строку Think для сравнения с оригиналом
- **🎚️ Настраиваемый момент перевода** — переводить всё / ленивая загрузка старых цепочек (по умолчанию) / только при раскрытии
- **🔗 Динамическая цепочка провайдеров** — порядок списка и есть порядок обращения: перетаскивайте для сортировки, включайте и выключайте каждую строку. Встроенные google gtx / bing / локальный Ollama плюс любые свои эндпоинты
- **🔌 Свои провайдеры (OpenAI и Anthropic)** — из панели можно добавить любой OpenAI-совместимый эндпоинт (`/v1/chat/completions`) или **Anthropic Messages API** (Claude): тип, шаблон, базовый URL, ключ API, модель
- **🪄 Наследование провайдеров из DSH** — читает из `settings.yaml` (`llm-pi-ai.providers`) строки DSH только для чтения; одна кнопка заново сканирует и добавляет их все в цепочку. Ключ разрешается на каждый запрос из `.credentials.yaml` и никогда не попадает в конфиг плагина. Собственный маршрут харнесса тоже учитывается: если `agent-default-model` указывает на `deepseek-official`, официальный API DeepSeek появляется как ещё одна строка DSH
- **⏱️ Устойчивость** — 3 повтора с backoff + прямой запрос из браузера, кнопка проверки в каждой строке, ошибки не кэшируются

## 📦 Установка

```bash
# Способ 1: npm (рекомендуется)
dsh plugin --profile web add dsh-think-translate
# затем перезапустите web

# Способ 2: GitHub
dsh plugin --profile web add github:UncleK/dsh-think-translate

# Способ 3: вручную (junction + patch)
#  1. свяжите пакет с node_modules профиля
New-Item -ItemType Junction -Path "$HOME\.dsh\profiles\node_modules\dsh-think-translate" `
  -Target "<путь к репозиторию>"
#  2. добавьте в "$HOME\.dsh\profiles\web\cordis.patch.yml":
# - insert:
#     - id: dsh-think-translate
#       name: dsh-think-translate
#  3. перезапустите web
```

## 🧯 После обновления DSH

Сторонние клиентские плагины загружаются через граф клиентских модулей DSH, а этот граф собирается **один раз на процесс** — неудачная сборка остаётся в памяти до перезапуска. Поэтому сразу после обновления обычно случаются три вещи.

- **Запуск из исходного чекаута падает** с `client bundles not found; run \`pnpm run build\` before launch` —— новые клиентские пакеты не собраны: выполните `pnpm run build` в чекауте harness и снова запустите `dsh web`.
- **Интерфейс плагина пропал** (нет перевода в строке Think, нет раздела «Перевод цепочки размышлений» в настройках) —— **перезапустите `dsh web`**; простого обновления страницы иногда недостаточно.
- **Список локальных моделей пуст** —— служба `ollama` не использует этот каталог моделей: проверьте `ollama list` (или `GET /api/tags`) и `OLLAMA_MODELS`, который реально применяет запущенная служба. Если файлы моделей лежат на другом диске, каталог по умолчанию можно направить туда через junction.

На стороне плагина настраивать нечего: он не объявляет зависимостей от порядка загрузки внутренних пакетов DSH (использует только службу `slots` и, опционально, `@deepseek-ai/dsh-client-ui-primitives`), поэтому работает и на старых DSH (≤ 0.1.1-rc), и на текущей линии (≥ 0.1.2-alpha.1, включая 0.1.5-rc.1).

## 🚀 Использование

1. Откройте **Настройки → Перевод цепочки размышлений**
2. Выберите **целевой язык** (например, Русский) — панель, строки и карточки переключатся на этот язык
3. Управляйте **цепочкой провайдеров** (перетаскивание — порядок, галочка — включение):
   - Встроенные: **google gtx / bing** (бесплатно, сразу работает, системный прокси) и **локальная модель (Ollama)** (при первом выборе скачиваются 7b/14b или своя модель)
   - **Провайдеры DSH**: настроенные в `settings.yaml` эндпоинты появляются сами (только чтение; галочка добавляет их в цепочку). Кнопка **Импортировать из конфигурации DSH** сразу под списком заново сканирует конфигурацию и добавляет их все за один раз — ни baseURL, ни ключ вводить не нужно: ключ берётся из учётных данных DSH
   - Ключ вводится вручную или приходит как **`apiKeyEnv`** из шаблона либо строки DSH: у такой строки появляется значок `env:NAME`, а ключ разрешается на каждый запрос и никогда не пишется в `config.json`. В форме редактирования нет поля переменной окружения (значение сохраняется как есть), но очистка поля действительно его удаляет (отправляется как явное удаление)
   - Снятая галочка означает, что провайдер пропускается. Подробности — в [README.md](README.md)
4. Отправьте сообщение и разверните строку Think, чтобы увидеть перевод

## ⚙️ Как это работает

```
браузер → POST /_xlate/translate (тот же origin, без CORS)
  → цепочка провайдеров на host (fail-open, с изменяемым порядком):
      chain: [provider1, provider2, ...]   ← порядок перетаскиванием в настройках
        google / bing / OpenAI-совместимый / Anthropic
      fallback-цепочка (необязательно, по умолчанию выключена, включается в конфиге)
  → прямой запасной путь из браузера
```

- **Конфигурация провайдеров** живёт в `config.json` (создаётся во время работы, в gitignore): `chain` (упорядоченные id), `fallback` (enabled + chain, только в файле), `providers` (у каждого `type`/`enabled`/`baseURL`/`apiKey`/`apiKeyEnv`/`model`). Старые конфиги с `priority` мигрируют автоматически; провайдер с `apiKeyEnv` разрешает ключ из этой переменной на каждый запрос (литеральный `apiKey` остаётся запасным), и разрешённый ключ никогда не пишется обратно в `config.json`; `null` в патче удаляет поле — так интерфейс и очищает значения
- **Обнаружение DSH** при загрузке читает `settings.yaml` (`llm-pi-ai.providers`) и `.credentials.yaml` (`refs`) харнесса; найденные провайдеры помечаются `source: "dsh"`, разрешённые ключи остаются в памяти (никогда в `config.json`), а маршрут `/_xlate/dsh-scan` перечитывает их по запросу
- **Host-часть** (`lib/index.js`): адаптеры провайдеров, LRU-кэш (600), `/_xlate/models`, `/_xlate/model/pull` + `pull-status` (автонастройка по завершении)
- **Client-часть** (`lib/client.js`): интерфейс на 8 языках, пакетный перевод, потоковые строки Think, localStorage
- Чистый слой отображения: оригиналы остаются в истории и контексте модели

## 🛠 Разработка

- Без сборки: `lib/client.js` — это браузерный бандл (исходник = артефакт); `lib/index.js` — host ESM
- Изменения клиента применяются при обновлении страницы; изменения host требуют перезапуска web
- Строки на 8 языках находятся в словаре `UI_TEXT` в `lib/client.js`

## 📄 Лицензия

MIT
