[中文](README.md) · **English** # dsh-model-profile · Model capability sync (reasoning levels / image support / capacity) [](https://github.com/topics/dsh-plugin) > **Since 0.6.0 this plugin does exactly one thing: the 「Sync model capabilities」 button on a provider row.** > The official model row now ships its own **reasoning-level** editor (the current DSH build of > `dsh-client-ui-settings-models` bundles `readEfforts` / `withEfforts`: one row per offered level with its > wire value, validating that every non-`off` level carries a non-empty value) — just like it has shipped its > own **input type** (text / image) checkboxes since 0.2.0-rc.1. So the plugin no longer injects a control into > any model row, and no longer defends those fields across saves; only the bulk models.dev button remains. > The build baseline stays `@deepseek-ai/dsh-*` `0.2.0-rc.2` (peer range `^0.2.0-rc.1`, covering `0.2.x`). ## The only entry point: 「Sync model capabilities」 Every provider row whose model list **you** own gains a **「Sync model capabilities」** button to the left of the official 「Edit」 button (it copies that button's class, so it renders at exactly the same size; when a React repaint wipes it the observer re-injects it, and repeated sweeps never create a second one). Clicking it looks up **every model** of that provider on [models.dev](https://models.dev) by model ID: | Field | Behavior | | --- | --- | | Reasoning levels `reasoningEfforts` | Written on a match (the `reasoning_options` enum, `none` → `off` with a `null` wire value) | | Image support `input` | Written on a match (`modalities.input` containing `image` → `['text','image']`, otherwise `['text']`) | | Context window `contextWindow` / max output `maxTokens` | **Gap-filled only** (`limit.context` / `limit.output`): an existing value is never overwritten | The button reports progress and outcome in place (「Syncing…」 → 「Synced N fields」); hover for the match count, the unlisted model IDs, and any write failures. Models models.dev does not list are left untouched. It appears as soon as the row card renders — **no need to expand the model catalog**. **Every write is a one-shot seed**: once a field lands, the official row's own reasoning-level and input-type controls are the only authority — the plugin never re-applies a value on a later save or reload, so an edit made in the official control always stands. ## Model ID matching Gateway / catalog routes usually prefix a model ID, so prefixes are peeled off before the lookup (every peeled form is a candidate, most specific first): 1. **Namespace prefixes**: both separators are peeled — `/` and `:` — and they nest, so `cn:deepseek-v4.1-flash`, `ag/gemini-3.7-flash-high` and `openrouter/anthropic:claude-x-high` all resolve to `deepseek-v4.1-flash` / `gemini-3.7-flash` / `claude-x`; 2. **Reasoning-tier suffixes**: one more peel per level — `-high` / `-medium` / `-low` / `-extra-low` / `-thinking` / `-none`; 3. Same-id **first-party** entries (deepseek / google / openai / zai …) beat aggregator aliases. The raw ID always comes first, so a genuinely prefixed entry in models.dev is never shadowed by the stripped form. ## 0.6.0 changes | Area | 0.5.0 and earlier | 0.6.0 | | --- | --- | --- | | Reasoning levels (`reasoningEfforts`) | Plugin injected an inline 「inherit / no reasoning / custom levels」 select plus the seven-level grid | The **official row ships** the reasoning-level editor; the plugin injects no per-model control at all | | Per-model 「Preset from models.dev」 button | Present (staged, landed with the official card's save) | **Removed**: per-model editing belongs to the official control, bulk filling to 「Sync model capabilities」 | | Capacity auto-fill | Filled empty capacity inputs when a row expanded | **Removed**: capacity is gap-filled only when you click 「Sync model capabilities」 | | Write lifetime | `reasoningEfforts` was **sticky-restored** (nothing official could edit it, so the plugin defended it) | **Seed once**: the official control is authoritative, the plugin never restores | | Model ID matching | Peelled `/` route prefixes only | Also peels `:` namespace prefixes (`cn:` …), including nested chains | | Injected DOM | One control block per model row | One button per provider row | Values already stored in settings are **not** touched by this upgrade — the official row simply renders them as its initial state. ## What it solves The official model row can edit reasoning levels and input types level by level, but doing that **per model by hand** is still tedious, and the level enum, its wire spellings, and the `contextWindow` / `maxTokens` numbers are all already published on models.dev. This plugin turns "align one provider in one click" into a button that fills exactly those fields of the model catalog editor. ## How it works - **Host side**: no behavior (pure browser plugin). - **Browser side**: - `controller.ts` reuses the official Models page join (`remote.llm.listConfigurableProviders` + `remote.settings.describe`) and only picks providers under the `llm-pi-ai` namespace whose model lists are held by the **user** layer (lists inherited from the built-in catalog are never materialized). - `enhance.ts` uses a MutationObserver for **language-agnostic structural detection**: a provider row is an `