<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) Web UI の**表示層翻訳**プラグイン：**思考チェーン（Think 行）、タスクカード、回答本文**を選択した対象言語で表示します。原文は会話履歴に完全に残り、訳文は**モデルコンテキストに一切入りません**。

## ✨ 特徴

DeepSeek 系モデルは中国語で考えることが多く、あるいはたまたま思考に使う言語で考えます。dsh-think-translate は、モデルの思考に字幕を付けるように、Think 行・タスクカード・回答を*あなたの*言語でリアルタイム表示します。

- **🕵️ どんな思考チェーンも読める** — 推論・思考チェーン・タスクカード・回答をリアルタイムに翻訳し、バッチ単位でストリーミング表示
- **8 つの対象言語** — 中文 / English / 日本語 / 한국어 / Español / Français / Deutsch / Русский
- **単一言語 UI** — 設定パネル・思考行・タスクカードがすべて対象言語に追従（中英混在なし）、選択は永続化
- **ローカルモデル優先** — ローカル 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 行を自動検出し、ボタン1つで再スキャンしてまとめてチェーンに追加。キーは `.credentials.yaml` からリクエスト時に解決され、プラグインの設定には保存されません。harness 自身の既定ルートも対象です：`agent-default-model` が `deepseek-official` を指していれば、DeepSeek の公式 API も DSH 行として提供されます
- **⏱️ 失敗に強い** — host は3回バックオフ再試行 + ブラウザ直接フォールバック、行ごとのテストボタン、失敗結果はキャッシュしない

## 📦 インストール

```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. パッケージを profile の 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 のクライアントモジュールグラフ経由で読み込まれ、このグラフは**プロセス起動時に一度だけ**構成されます。失敗した構成は再起動までメモリに残ります。そのため、アップグレード直後は次の 3 つがよく起きます。

- **ソースからの起動が失敗する**：`client bundles not found; run \`pnpm run build\` before launch` —— 新しいクライアントパッケージは未ビルドです。harness のチェックアウトで `pnpm run build` を実行してから、`dsh web` を起動し直してください。
- **プラグインの UI が消える**（Think 行に訳文がない、設定に「思考チェーン翻訳」がない）—— **`dsh web` を一度再起動**してください。ページを再読み込みするだけでは足りないことがあります。
- **ローカルモデルの一覧が空** —— `ollama` サービスがそのモデルディレクトリを見ていません：`ollama list`（または `GET /api/tags`）と、実行中のサービスが実際に使っている `OLLAMA_MODELS` を確認してください。モデルが別ドライブにある場合は、ディレクトリジャンクションで既定ディレクトリをそちらへ向けられます。

プラグイン側の設定は不要です。DSH 内部パッケージの読み込み順に依存せず（`slots` サービスだけを利用し、`@deepseek-ai/dsh-client-ui-primitives` は任意）、旧バージョン（≤ 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 側の資格情報から解決されます）
   - キーは直接入力するほか、プリセットや DSH 行が持つ **`apiKeyEnv`** でも指定できます。その行には `env:NAME` バッジが付き、キーはリクエスト時に解決されて `config.json` には保存されません。編集フォームに環境変数の入力欄はありません（値はそのまま保持されます）が、フィールドを空にすると本当に削除されます（明示的な削除として送信）
   - チェックを外すとそのプロバイダーは使われません。詳細は [README.md](README.md)
4. メッセージを送信し、Think 行を展開して訳文を確認

## ⚙️ 仕組み

```
ブラウザ → POST /_xlate/translate（同一オリジン、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` はそのフィールドの削除を意味し、UI のクリアはこれを使っています
- **DSH の自動検出** は読み込み時に harness の `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 言語 UI、文単位バッチ翻訳、ストリーミング Think 行、localStorage 永続化
- 純表示層：原文は会話履歴とモデルコンテキストに完全保持

## 🛠 開発

- ビルド不要：`lib/client.js` はブラウザバンドル（ソース＝成果物）、`lib/index.js` は host ESM
- client 変更はページ更新で反映、host 変更は web 再起動が必要
- 8 言語の文言は `lib/client.js` の `UI_TEXT` ディクショナリにあり

## 📄 License

MIT
