Bundle
dsh-plugin-model-capability
Model Capability Manager for DSH Web: per-model thinking levels, context window, output caps, input modalities, gateway compat, route defaults, one-click presets, EN/中文 UI.
- Source
- yuioi666
- stars
- 4 stars
- License
- MIT
- Updated
- Updated 3 hours ago
Readme
# dsh-plugin-model-capability
[](https://www.npmjs.com/package/dsh-plugin-model-capability)
[](https://www.npmjs.com/package/dsh-plugin-model-capability)
[](https://github.com/yuioi666/dsh-plugin-model-capability/blob/main/LICENSE)
[](https://github.com/yuioi666/dsh-plugin-model-capability)
[](https://github.com/yuioi666/dsh-plugin-model-capability)
**Model Capability Manager** — manage the `llm-pi-ai` provider routes of DeepSeek Harness (DSH Web) from a dedicated **Model Capability** page in the in-app settings: per-model thinking levels, context window, output cap, input modalities, per-route defaults, gateway compatibility fields, one-click presets, and an EN/中文 switchable UI.
[简体中文说明](./docs/README-zh.md) · [Report Bug](https://github.com/yuioi666/dsh-plugin-model-capability/issues) · [Request Feature](https://github.com/yuioi666/dsh-plugin-model-capability/issues/new?template=feature_request.md)
---
## Table of Contents
- [Why this plugin exists](#why-this-plugin-exists)
- [Screenshots](#screenshots)
- [Features](#features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Uninstall](#uninstall)
- [FAQ / Troubleshooting](#faq--troubleshooting)
- [How it works](#how-it-works)
- [Development](#development)
- [Publishing](#publishing)
- [Contributing](#contributing)
- [License](#license)
---
## Why this plugin exists
DSH stores provider configuration in `llm-pi-ai.providers` inside `settings.yaml`. Editing it by hand is error-prone, and two classes of problems bite people often:
1. **Gateway incompatibility** — not every vendor accepts the same protocol dialect. For example Alibaba Cloud (DashScope) in `compatible-mode`, Moonshot/Kimi, Zhipu/BigModel, MiniMax, Volcengine Ark, SiliconFlow, Baidu Qianfan and other gateways may reject `developer` role messages or `reasoning_effort` echoes the way the OpenAI/Anthropic dialects expect. Turning `compat.supportsDeveloperRole` on against such a gateway produces 400-style errors.
2. **Thinking-level wiring** — the 7 levels (`off / minimal / low / medium / high / xhigh / max`) each need a wire value the upstream provider understands (e.g. `low` → `"low"` for one vendor, `"h3"` for another). Max-thinking configs and per-model `reasoningEfforts` are tedious to author by hand.
This plugin gives you a GUI for all of it, plus **one-click presets** that bake in dialect-safe configurations (see [Presets](#presets)).
## Screenshots
| Settings entry | Section overview (EN) |
| --- | --- |
|  |  |
| Model editor (EN) | Gateway compatibility fold (ZH) | Section overview (ZH) |
| --- | --- | --- |
|  |  |  |
## Features
- **Per-model editor** for every route:
- `name`, `contextWindow`, `maxTokens` — capacity fields accept plain numbers or `K`/`M` suffixes (`262144`, `256K`, `1M`).
- `input` modalities — `text` / `image` checkboxes with de-duplication.
- Thinking toggle — switch the whole model between reasoning off and the full 7-level matrix (`off/minimal/low/medium/high/xhigh/max`), each level with its own wire value. Empty non-`off` levels are prevented (the Host rejects them), and a one-click **fill all levels with the same value** button is included.
- **Apply field to all models** of the route (name / contextWindow / maxTokens / input / reasoningEfforts).
- Per-model `compat` editor (folded away by default).
- **Per-route editor**:
- `displayName`, `baseURL`, `api` (openai-completions / openai-responses / anthropic-messages).
- Defaults: `defaultContextWindow`, `defaultMaxTokens`, `defaultInput`, `reasoning`, `thinkingBudgets` (minimal/low/medium/high), `cacheRetention`, `transport`.
- Route-level `compat` editor and an **Advanced** fold: timeouts, max image bytes / pixel budget, `headers`, plus a read-only raw JSON view.
- **One-click presets** — 7 built-in recipes plus your own saved presets:
| Preset | What it does |
| --- | --- |
| Safe gateway | `compat.supportsDeveloperRole=false`, `supportsReasoningEffort=true` — for gateways that reject `developer` role messages (DashScope compatible-mode, Kimi/Moonshot, Zhipu, MiniMax, Ark, SiliconFlow, Qianfan, …) |
| OpenAI native | `developer` role + `reasoning_effort` + `thinkingFormat=openai` + `maxTokensField=max_completion_tokens` |
| DeepSeek dialect | `thinkingFormat=deepseek`, `developer` role on, `reasoning_effort` on |
| Qwen dialect | `thinkingFormat=qwen`, `developer` role off, `reasoning_effort` on |
| Max thinking (7 levels) | every model declares all 7 levels, `reasoning=high`, generous `thinkingBudgets` |
| Text only | `defaultInput=['text']` and per-model `input=['text']` |
| Image ready | `defaultInput=['text','image']` and per-model `input=['text','image']` |
- Apply any preset to a **selected subset of routes**. Save your current configuration as a custom preset; apply and delete them anytime. Custom presets are stored under `model-capability.customPresets` in `settings.yaml`.
- **Applying a custom preset replaces the whole `llm-pi-ai` user section** via `settings.replace`, not a merge. Any route that was added to the user section *after* the preset was saved will be **deleted**. This is not an additive recipe — treat the preset as a full snapshot.
- **Header credential protection** — credential-shaped header names (`authorization`, `api-key`, etc.) are **blocked** in the headers editor, and the `headers` dict is **stripped** from every provider route when saving a custom preset (credentials travel as `apiKeyEnv` reference names, never as literal header values). Existing presets that were saved before this safeguard are detected at startup and reported in the advisory checks.
- **Advisory checks** — the page shows diagnostics about your current setup: legacy-gateway lookalike URLs with `supportsDeveloperRole` on (hint: use Safe gateway), reasoning levels that map to no wire value, models without an explicit `contextWindow`, and routes without models.
- **Language switch** — the page follows the DSH UI language, and a select in the page header lets you pin **English / 中文 / follow DSH**. The choice persists into `settings.yaml` (`model-capability.language`), not just to the browser session.
All writes go through the DSH settings service with revision fencing (`expectedRevision`), the same pattern the built-in Models page uses; conflicting concurrent edits are retried via the live mirror. If the page is opened from a non-loopback origin (where writes are not allowed), every control is disabled with a hint.
## Installation
Requires a DSH installation with the web app (any profile that serves the browser UI), DSH ≥ 0.1.1-rc.2.
### Install the latest version
```bash
dsh plugin --profile web add dsh-plugin-model-capability # latest stable, or pin @<version>
```
Then **restart `dsh --profile web`** (the running Web UI is not hot-reloaded on plugin install). The **Model Capability** entry appears under **Settings**.
For other profiles, replace `web` with your profile name.
> **Pin the exact version** when you need a specific release — see [Getting the latest version](#getting-the-latest-version) below. Installing without a version may resolve to an older release cached locally or on the registry CDN.
### Getting the latest version (cache / publish-delay caveats)
A new release is only picked up when **all three caches** agree — the npm registry
CDN metadata, your local pnpm store, and the profile's lockfile. Any one of them
stale means `dsh plugin add dsh-plugin-model-capability` (no version) keeps
installing the old build. To guarantee you get the newest version:
1. **Check what the registry currently has:**
```bash
npm view dsh-plugin-model-capability version
```
If this does not show the version you expect, the registry CDN still serves
stale metadata — wait ~1–2 minutes and retry (npm publishes are usually
visible in seconds, but the `packument` metadata is cached per-TTL).
2. **Uninstall any previously installed copy first** (see [Uninstall](#uninstall)
below). The profile lockfile (`node_modules/.pnpm/lock.yaml` /
`pnpm-lock.yaml`) otherwise keeps the old version pinned.
3. **Install with the exact version** — this bypasses metadata resolution:
```bash
dsh plugin --profile web add dsh-plugin-model-capability@<version>
# e.g. dsh plugin --profile web add dsh-plugin-model-capability@1.1.1
```
4. **Clear stale local caches if the profile still reports an old version:**
```bash
pnpm store prune # remove unreferenced store packages
```
or, for the profile itself:
```bash
cd "$HOME/.dsh/profiles/web"
pnpm store prune
```
5. **Verify what actually got installed:**
```bash
grep -A2 '"dependencies"' "$HOME/.dsh/profiles/web/package.json"
```
(Windows PowerShell: `Select-String -Path "$HOME\.dsh\profiles\web\package.json" -Pattern "model-capability"`)
The version shown next to `dsh-plugin-model-capability` must match the version you intended to install.
6. **Restart the web UI** — the plugin is loaded at startup, never hot-reloaded:
```bash
dsh --profile web
```
Registry note: an already-published version can **never be overwritten**. If a bad
build got released under `0.1.2`, the fix is a new version (`0.1.3`, `1.1.1`, …),
not a re-publish — which is exactly why "install the latest" means **pin the
version**, not `npm update`.
### Uninstall
```bash
dsh plugin --profile web remove dsh-plugin-model-capability
```
If the command reports `no such dependency found` (a broken install whose
dependency entry is missing from `package.json`), remove it directly inside the
profile:
```bash
cd "$HOME/.dsh/profiles/web"
pnpm remove dsh-plugin-model-capability
```
After either step, **restart `dsh --profile web`**.
To verify the plugin is fully gone:
- `"$HOME/.dsh/profiles/web/package.json"` — no `dsh-plugin-model-capability`
entry under `dependencies`
- `"$HOME/.dsh/profiles/web/node_modules/dsh-plugin-model-capability"` — directory
no longer exists
- `"$HOME/.dsh/profiles/web/pnpm-lock.yaml"` — no `dsh-plugin-model-capability`
reference (0 hits)
> The host half also loads headlessly (it registers the settings schema); the settings UI itself needs the web app.
## Quick Start
After installing the plugin and restarting DSH, the **Model Capability** page is available under **Settings** in the sidebar. Here is how to get started in three steps:
### 1. Open the page
Navigate to **Settings → Model Capability**. You will see a list of all configured provider routes (e.g. `openai`, `anthropic`, `dashscope`, etc.), each with its models listed underneath.
### 2. Apply a preset (recommended first step)
The fastest way to get a working configuration is to use a **one-click preset**:
1. Click the **Presets** button in the page header.
2. Select a preset that matches your gateway type (e.g. **Safe gateway** for DashScope/Moonshot/Zhipu, **OpenAI native** for official OpenAI, **DeepSeek dialect** for DeepSeek API).
3. Choose the routes you want to apply it to (or leave all selected).
4. Click **Apply**.
The preset fills in the recommended `compat` fields, thinking levels, and defaults automatically.
### 3. Fine-tune individual models
Click any model row to expand its editor. From there you can:
- Set the **context window** and **max tokens** (supports `K`/`M` suffixes, e.g. `128K`, `1M`).
- Toggle **thinking** on/off and configure each of the 7 reasoning levels.
- Change **input modalities** (`text` / `image`).
- Open the **compat** fold to adjust gateway-specific fields per model.
All changes are saved immediately through the DSH settings service with revision fencing — no manual YAML editing required.
> **Tip:** If the page shows "Advisory checks" at the top, review them — they flag common misconfigurations like legacy gateways with `supportsDeveloperRole` enabled.
## How it works
One npm package with two halves, installed as a **profile bundle** by `dsh plugin add`:
- `lib/index.js` — the **host half**: registers the `model-capability` settings namespace (language + custom presets) with schemastery so the Host round-trips it like any native setting.
- `lib/client.js` — the **web client half**: a classic-script bundle registered with the web shell's module loader (`window.__ModuleLoader__.load({ id, factory })`), exactly like every shipped `@deepseek-ai` client bundle. It injects a section into the `settings.section` slot, binds both the `llm-pi-ai` and `model-capability` settings scopes, and drives all edits through `api.settings.mutate` with path ops and revision fencing.
- `cordis.patch.yml` — declares the bundle row, so `dsh plugin add` wires the whole thing automatically (no manual patch editing).
The `llm-pi-ai` schema itself is owned by DSH — this plugin only edits its *values*, so the Host keeps validating every write (`assertServiceable` etc.).
## Development
```bash
pnpm install
npm run build # esbuild → lib/client.js (loader-wrapped) + lib/index.js
```
Local testing: create a dev profile (e.g. `web-dev`), add the web app and the plugin, and restart the server on a separate port:
```bash
dsh plugin --profile web-dev add @deepseek-ai/dsh-web-app@0.1.1-rc.2
# add the local package, then note: `file:` dependencies are snapshotted —
# re-add after every rebuild, or replace the installed copy with a junction:
dsh plugin --profile web-dev add file:D:/path/to/dsh-plugin-model-capability
dsh --profile web-dev --port 3091 --no-open
```
Screenshots are captured with the included script (needs `playwright-core` and a local Chrome/Edge):
```bash
node scripts/screenshots.mjs [baseURL] [outDir]
node scripts/verify-dom.mjs [baseURL] # shadow-DOM-aware rendering checks
node scripts/e2e-write.mjs [baseURL] # end-to-end write smoke test (back up settings.yaml first!)
```
## FAQ / Troubleshooting
### Why are my changes not saved?
The DSH settings service only accepts writes from **loopback origins** (i.e. `http://127.0.0.1:3080` or `http://localhost:3080`). If you are accessing the web UI from a different IP address or through a reverse proxy, every control on the page is disabled automatically and a hint is shown. Connect via localhost to make changes.
### What does the "Safe gateway" preset do?
It sets `compat.supportsDeveloperRole=false` and `supportsReasoningEffort=true`. This is the safest choice for Chinese cloud gateways — DashScope (compatible-mode), Moonshot/Kimi, Zhipu/BigModel, MiniMax, Volcengine Ark, SiliconFlow, Baidu Qianfan, and others that do not accept the `developer` role message that OpenAI/Anthropic dialects send.
### Why do I see a credential warning?
The plugin detects credential-shaped header names (e.g. `authorization`, `api-key`) in the `headers` field of a provider route. This is a **safety advisory**, not a block — but credentials should be stored as `apiKeyEnv` environment variable references instead of literal header values, especially when saving custom presets (the preset system strips `headers` automatically).
### Can I add a new provider route?
The plugin edits existing routes in the `llm-pi-ai` providers section. To add a brand-new provider, you still need to edit `settings.yaml` manually or use the DSH CLI. Once added, this plugin will pick it up on the next page load.
### How do I reset a single model to defaults?
Click the model row to expand its editor and clear the fields you want to reset. The route-level defaults (set in the route editor) are used as fallbacks when a per-model value is empty.
### Why is the "Language" setting not persisting?
The language choice (`model-capability.language`) is stored in `settings.yaml` and survives restarts. If it keeps resetting, check that the settings file is writable and that no other process is overwriting it.
### Why does `thinkingBudgets` only have minimal/low/medium/high when there are 6 thinking levels?
This is not a bug — it is a deliberate schema design. DSH's thinking capability has two separate layers:
**1. Thinking Levels (6 levels)** — defined by `pi-ai` core:
`minimal | low | medium | high | xhigh | max`
All 6 levels can be configured with wire values in each model's `reasoningEfforts`.
**2. Thinking Budgets (4 levels)** — defined by `dsh-llm-pi-ai` schema:
`minimal | low | medium | high`
There is no `xhigh` or `max` budget field.
The `thinkingBudgets` controls custom token budgets for token-based providers (e.g. Anthropic/Bedrock). At runtime, when `xhigh` or `max` levels are used, the system falls back to default budgets:
```js
const budget = options.thinkingBudgets?.[level] ?? defaultBudgets[options.reasoning];
```
So if you set `thinkingBudgets.minimal/low/medium/high`, those levels use your custom values, while `xhigh` and `max` use the provider's default token allocation strategy — no custom budget is needed for those levels.
## Publishing
Full step-by-step instructions (including a post-release checklist) are in
[`PUBLISHING.md`](./PUBLISHING.md). Summary:
- `npm publish` — run after `npm run build` (the `prepublishOnly` hook rebuilds automatically). The package ships `lib/`, `cordis.patch.yml`, `img/`, license, the English README and the Chinese guide in `docs/`.
- GitHub — repository + releases; tag versions to match `package.json`.
## Contributing
Contributions are welcome! Here is how you can help:
- **Report bugs** — open an [issue](https://github.com/yuioi666/dsh-plugin-model-capability/issues) with a clear description and reproduction steps.
- **Suggest features** — use the [feature request template](https://github.com/yuioi666/dsh-plugin-model-capability/issues/new?template=feature_request.md).
- **Submit pull requests** — fork the repository, make your changes, and open a PR. Please follow the existing code style and include tests where applicable.
The plugin is built with esbuild; run `npm run build` after making changes to the `src/` files. See the [Development](#development) section for local testing instructions.
## License
[MIT](./LICENSE)Install
dsh plugin --profile web add github:yuioi666/dsh-plugin-model-capability
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-plugin-model-capability from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.