@jcy2387/dsh-models-input-modalities 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,29 +7,61 @@
7
7
 
8
8
  English | [简体中文](README.zh.md)
9
9
 
10
- A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH) Web client plugin that adds an **Input modalities** fold to every third-party (pi-ai) provider card on the **Settings → Models** page, declaring per model which inputs it accepts (whether images are allowed) — exactly the field the page's own forms do not expose. Built against DSH `0.1.5-alpha.1` (peer range `>=0.1.5-alpha.1 <0.2.0`).
10
+ A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH) Web client plugin that adds a **Model capabilities** fold to every third-party (pi-ai) provider card on the **Settings → Models** page, declaring per model which inputs it accepts (whether images are allowed) and which reasoning levels it offers — the two per-model fields the page's own forms deliberately do not expose. Built against DSH `0.1.5-alpha.1` (peer range `>=0.1.5-alpha.1 <0.2.0`).
11
11
 
12
12
  The providers themselves (provider ID, API base URL, protocol, API key, model list) are still created and edited entirely through the Models page forms; this plugin **pre-writes no provider configuration**.
13
13
 
14
14
  ## What it does
15
15
 
16
+ **Input modalities**
17
+
16
18
  - **A three-way choice per model** — *Provider default*, *Text only*, or *Text and image*.
17
19
  - **Exact adapter semantics** — *Text only* writes `input: [text]`, *Text and image* writes `input: [text, image]`, and *Provider default* removes the field so the row inherits the installed catalog's modalities, then the route's `defaultInput`.
20
+
21
+ **Reasoning levels**
22
+
23
+ - **A three-way choice per model** — *Catalog default*, *Not a reasoning model*, or *Declare levels*, which unfolds the seven levels (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`) as a tick box plus the wire spelling to send for it.
24
+ - **Exact adapter semantics** — a declared set writes the `reasoningEfforts` dict the adapter reads: every ticked level becomes a key whose value is the spelling dispatch sends, so `max: ultra` renames a level for a gateway with its own vocabulary, and an empty `off` stores a valueless `off:` — offered, and sent as no parameter at all. *Not a reasoning model* writes `reasoningEfforts: false`; *Catalog default* removes the field, keeping the installed catalog's capability, which for a hand-declared model is none.
25
+ - **A seed that assumes nothing about "off"** — switching a row to a declared set starts from `low`/`medium`/`high`, each spelled as its own name, with `off` left unticked: on the plain `reasoning_effort` wire an empty `off` is the very same request as naming no effort, so pre-offering it would promise a stop-thinking choice the endpoint may not honour. Tick `off` — and spell it — once the endpoint says how.
26
+ - **This is what puts levels in the picker** — the composer's model picker lists exactly the levels a model declares, so a gateway model added by hand gains its Off/Low/Medium/High choices here and nowhere else in the UI.
27
+ - **Refused before the write** — the adapter rejects a declared set with no level beyond `off`, and any level but `off` with no wire value; the card names the offending row and keeps **Save** closed rather than answering a rejected settings mutation.
28
+
29
+ **Both claims**
30
+
31
+ - **One fold, one write** — the two claims share the fold because they share the stored `models` array: two folds would each write it and fence each other into conflicts.
18
32
  - **Revision-fenced writes** — the whole `models` array is written back under the revision read when the fold was opened, with the same array semantics and conflict handling as the Models page's own cards: a concurrent edit surfaces a conflict notice and reloads instead of silently clobbering.
19
33
  - **Field preservation** — every other field of every row survives verbatim, including fields this card never shows.
20
- - **Follows the page live** — add or remove a model in the catalog above and the modality rows follow it in place: the fold listens for the pushed `settings/document-updated` invalidation of its own namespace, so nothing has to be closed and reopened. A commit that outruns a running read is caught by revision comparison and re-read until the fold is level. An unsaved draft is never overwritten, not even by a reopen — a change that lands mid-draft parks until the draft settles: revert the edits and the fold re-reads silently, or save and the revision fence refuses the raced write and reloads behind a conflict notice.
34
+ - **Follows the page live** — add or remove a model in the catalog above and the rows follow it in place: the fold listens for the pushed `settings/document-updated` invalidation of its own namespace, so nothing has to be closed and reopened. A commit that outruns a running read is caught by revision comparison and re-read until the fold is level. An unsaved draft is never overwritten, not even by a reopen — a change that lands mid-draft parks until the draft settles: revert the edits and the fold re-reads silently, or save and the revision fence refuses the raced write and reloads behind a conflict notice.
21
35
  - **Localized UI** — English and Chinese follow the Web UI's locale.
22
36
 
23
37
  ## Usage
24
38
 
25
39
  1. Install (see below) and restart `dsh web`.
26
40
  2. **Settings → Models**: create your gateway provider with **Add custom provider** as usual, or open an existing one.
27
- 3. An **Input modalities** fold appears under every third-party provider card; expand it and choose one of the three states per model.
41
+ 3. A **Model capabilities** fold appears under every third-party provider card; expand it and, per model, pick its input modalities and its reasoning state — for a model that reasons, choose *Declare levels* and tick the levels its endpoint serves.
28
42
  4. Click **Save**. The claim lands in the user layer of `$DSH_HOME/settings.yaml`; the adapter picks it up on its next request — no restart required.
29
43
 
44
+ A model declared here writes exactly this (other fields of the row untouched):
45
+
46
+ ```yaml
47
+ models:
48
+ - id: acme-think
49
+ contextWindow: 262144
50
+ maxTokens: 32768
51
+ input:
52
+ - text
53
+ - image
54
+ reasoningEfforts:
55
+ off: null # offered, and sent as no parameter at all
56
+ low: low
57
+ medium: medium
58
+ high: high
59
+ max: ultra # a gateway with its own vocabulary
60
+ ```
61
+
30
62
  ## How it works
31
63
 
32
- The plugin registers its component into the `settings.models.provider-card` extension seat exposed by the Models page (key `llm-pi-ai`, i.e. the cards of the whole pi-ai adapter family). On first expansion the fold reads the provider's stored `models` rows through the settings Remote, edits them locally, and writes the whole array back under the revision captured at read time — the same array semantics and conflict handling as the page's own cards (a concurrent edit prompts a conflict notice and a reload). All fields other than `input` are preserved verbatim in every row.
64
+ The plugin registers its component into the `settings.models.provider-card` extension seat exposed by the Models page (key `llm-pi-ai`, i.e. the cards of the whole pi-ai adapter family). On first expansion the fold reads the provider's stored `models` rows through the settings Remote, edits them locally, and writes the whole array back under the revision captured at read time — the same array semantics and conflict handling as the page's own cards (a concurrent edit prompts a conflict notice and a reload). All fields other than `input` and `reasoningEfforts` are preserved verbatim in every row.
33
65
 
34
66
  The fold also subscribes to the Host's forwarded `settings/document-updated` event and filters it to `llm-pi-ai`, so the page's own model-list writes reach it without a remount. An open, clean fold re-reads silently, and keeps re-reading until its data is level with the newest announced revision; a closed one parks the notice and re-reads on the next expansion; one holding an unsaved draft parks the notice until the draft settles, so a reopen never costs edits. The card's own write is recognised by the revision it just committed and does not echo back as a refresh.
35
67
 
@@ -105,11 +137,13 @@ Dependabot checks GitHub Actions dependencies weekly. npm version updates are in
105
137
  ```text
106
138
  .
107
139
  ├─ src/
108
- │ ├─ index.ts # Host half: an intentionally empty apply (browser-only plugin)
109
- │ ├─ image-input.ts # pure row helpers for the per-model input claim
110
- └─ client/ # Web half: the input-modality fold (controller, card, locales)
111
- ├─ tests/ # vitest suites over the pure row helpers
112
- ├─ build/ # tsdown preset for the self-contained client bundle
140
+ │ ├─ index.ts # Host half: an intentionally empty apply (browser-only plugin)
141
+ │ ├─ model-row.ts # the row vocabulary both claims share
142
+ ├─ image-input.ts # pure row helpers for the per-model input claim
143
+ ├─ reasoning-efforts.ts # pure row helpers for the per-model reasoning claim
144
+ │ └─ client/ # Web half: the capability fold (controller, card, locales)
145
+ ├─ tests/ # vitest suites over the pure row helpers
146
+ ├─ build/ # tsdown preset for the self-contained client bundle
113
147
  ├─ .github/workflows/ci.yml # validate + tarball audit + consumer smoke
114
148
  ├─ .github/workflows/release.yml # npm publish on GitHub Release
115
149
  ├─ cordis.patch.yml
@@ -119,9 +153,12 @@ Dependabot checks GitHub Actions dependencies weekly. npm version updates are in
119
153
  ## Known limitations
120
154
 
121
155
  - Dormant (not yet configured) provider cards do not render the fold; a freshly created custom provider appears **after** it is saved.
122
- - Route-level `defaultInput` and `modelOverrides` for built-in provider-catalog models are out of this plugin's scope set them directly in `$DSH_HOME/settings.yaml`.
156
+ - Route-level knobs — `defaultInput`, the default `reasoning` level, and the `compat` switches — plus `modelOverrides` for built-in provider-catalog models are out of this plugin's scope; set them directly in `$DSH_HOME/settings.yaml`.
157
+ - A declared level is a claim, not a check: nothing asks the gateway whether it serves that level or honors that spelling, and how the level travels on the wire (`reasoning_effort`, a thinking budget, chat-template kwargs) is `compat`'s job. A level the endpoint refuses is refused by the provider, mid-turn.
158
+ - A level unticked and ticked again restarts from its default spelling — its own name, empty for `off` — rather than reviving what it carried before.
123
159
  - In read-only settings deployments the fold is visible but cannot save.
124
160
 
161
+
125
162
  ## License
126
163
 
127
164
  [MIT](LICENSE) © jcy2387
package/README.zh.md CHANGED
@@ -7,35 +7,65 @@
7
7
 
8
8
  [English](README.md) | 简体中文
9
9
 
10
- 为 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)打造的 Web 客户端插件:给**设置 → 模型**页的每个第三方(pi-ai)提供方卡片补上一个**「输入模态」折叠区**,按模型声明它的输入模态列表(是否接受图片)——这正是模型页自带表单没有开放的那个字段。基于 DSH `0.1.5-alpha.1` 构建(peer 范围 `>=0.1.5-alpha.1 <0.2.0`)。
10
+ 为 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)打造的 Web 客户端插件:给**设置 → 模型**页的每个第三方(pi-ai)提供方卡片补上一个**「模型能力」折叠区**,按模型声明它接受哪些输入(是否允许图片),以及它提供哪些推理等级——这正是模型页自带表单刻意没有开放的两个 per-model 字段。基于 DSH `0.1.5-alpha.1` 构建(peer 范围 `>=0.1.5-alpha.1 <0.2.0`)。
11
11
 
12
12
  提供方本身(Provider ID、API 地址、协议、密钥、模型列表)仍然完全在模型页的表单里创建和编辑;本插件**不预写任何提供方配置**。
13
13
 
14
14
  ## 功能
15
15
 
16
+ **输入模态**
17
+
16
18
  - **按模型三选一** —— *提供方默认*、*仅文本*、*文本和图片*。
17
19
  - **与适配器语义一致** —— *仅文本*写入 `input: [text]`,*文本和图片*写入 `input: [text, image]`;*提供方默认*删除该字段,让这一行继承已安装目录的模态,再回退路由 `defaultInput`。
20
+
21
+ **推理等级**
22
+
23
+ - **按模型三选一** —— *目录默认*、*非推理模型*、*声明等级*;选*声明等级*会展开七个等级(`off`、`minimal`、`low`、`medium`、`high`、`xhigh`、`max`),每级一个勾选框加一个「发到网关的拼写」输入框。
24
+ - **与适配器语义一致** —— 声明写入适配器读取的 `reasoningEfforts` 字典:勾选的等级成为键,输入框里的值就是实际发送的拼写,所以 `max: ultra` 能给有自己的叫法的网关改名;`off` 留空则写成无值的 `off:`——提供该等级,但完全不发送参数。*非推理模型*写入 `reasoningEfforts: false`;*目录默认*删除该字段,保留已安装目录的能力,而手写模型的能力就是「没有」。
25
+ - **种子不替「off」做假设** —— 切到*声明等级*时从 `low`/`medium`/`high` 开始(拼写即等级名),`off` 不预勾:在纯 `reasoning_effort` 网关上,留空的 `off` 与「不指名任何等级」是同一个请求,预勾等于承诺一个网关未必兑现的「停止思考」。等网关说明了怎么表达「别思考」,再勾 `off` 并填上拼写。
26
+ - **等级就是这样进入选择器的** —— 对话输入框的模型选择器只列出模型自己声明的等级,因此手工添加的网关模型是在这里、也是在整个界面里唯一一处,获得它的 Off/Low/Medium/High 选项。
27
+ - **写入前就拦下** —— 适配器会拒绝「除 `off` 外没有任何等级」的声明,也会拒绝除 `off` 外缺少发送值的等级;卡片会指名是哪一行,并让**保存**保持禁用,而不是等一次被拒绝的设置写入回来。
28
+
29
+ **两者共同**
30
+
31
+ - **一个折叠区、一次写入** —— 两个声明合用一个折叠区,因为它们共用存储的 `models` 数组:拆成两个折叠区就会各写一次数组,互相被 revision 围栏判成冲突。
18
32
  - **revision 围栏写入** —— 整组 `models` 数组按折叠区打开时读到的 revision 写回,与模型页自身卡片相同的数组语义与冲突处理:他人同时改动时提示冲突并重新加载,而不是静默覆盖。
19
- - **字段保留** —— 行内除 `input` 外的所有字段原样保留,包括本卡片从不展示的字段。
20
- - **与模型页实时联动** —— 在上方模型目录里增删模型,模态行会就地跟上:折叠区订阅了本命名空间的 `settings/document-updated` 推送失效事件,无需关掉设置页再重开。读取途中被新提交反超时,按 revision 对比识别落差并自动追读,直到追平。未保存的草稿永不被覆盖,重新展开也不会——草稿期间落下的改动会一直挂起到草稿落定为止:撤回编辑,折叠区静默重读;直接保存,则由 revision 围栏拦下撞车的写入,提示冲突后重载。
33
+ - **字段保留** —— 行内除 `input` 与 `reasoningEfforts` 外的所有字段原样保留,包括本卡片从不展示的字段。
34
+ - **与模型页实时联动** —— 在上方模型目录里增删模型,这些行会就地跟上:折叠区订阅了本命名空间的 `settings/document-updated` 推送失效事件,无需关掉设置页再重开。读取途中被新提交反超时,按 revision 对比识别落差并自动追读,直到追平。未保存的草稿永不被覆盖,重新展开也不会——草稿期间落下的改动会一直挂起到草稿落定为止:撤回编辑,折叠区静默重读;直接保存,则由 revision 围栏拦下撞车的写入,提示冲突后重载。
21
35
  - **本地化界面** —— 中英文跟随 Web UI 语言。
22
36
 
23
37
  ## 使用
24
38
 
25
39
  1. 安装(见下),重启 `dsh web`。
26
40
  2. 设置 → 模型:用**添加自定义提供方**照常创建你的网关提供方(或打开已有的)。
27
- 3. 每张第三方提供方卡片下方出现**输入模态**折叠区,展开后按模型三选一:
28
- - **提供方默认** — 不写字段,继承已安装目录的模态,再回退路由 `defaultInput`
29
- - **仅文本** — 写入 `input: [text]`
30
- - **文本和图片** — 写入 `input: [text, image]`
41
+ 3. 每张第三方提供方卡片下方出现**模型能力**折叠区,展开后按模型选择输入模态与推理状态——会推理的模型选*声明等级*,勾上它的网关真正提供的等级。
31
42
  4. 点**保存**。写入 `$DSH_HOME/settings.yaml` 用户层,适配器在下一次请求时生效,无需重启。
32
43
 
44
+ 在这里声明一个模型,写出的正是这样(行内其他字段不动):
45
+
46
+ ```yaml
47
+ models:
48
+ - id: acme-think
49
+ contextWindow: 262144
50
+ maxTokens: 32768
51
+ input:
52
+ - text
53
+ - image
54
+ reasoningEfforts:
55
+ off: null # 提供该等级,但不发送任何参数
56
+ low: low
57
+ medium: medium
58
+ high: high
59
+ max: ultra # 网关有自己的叫法
60
+ ```
61
+
33
62
  ## 原理
34
63
 
35
- 插件把组件注册进模型页对外开放的 `settings.models.provider-card` 扩展位(key 为 `llm-pi-ai`,即整个 pi-ai 适配器家族的卡片)。折叠区首次展开时通过 settings Remote 读取该提供方存储的 `models` 行,本地编辑后按读取时的 revision 围栏整组写回——与模型页自身卡片相同的数组语义与冲突处理(他人同时改动时提示冲突并重新加载)。行内除 `input` 外的所有字段原样保留。
64
+ 插件把组件注册进模型页对外开放的 `settings.models.provider-card` 扩展位(key 为 `llm-pi-ai`,即整个 pi-ai 适配器家族的卡片)。折叠区首次展开时通过 settings Remote 读取该提供方存储的 `models` 行,本地编辑后按读取时的 revision 围栏整组写回——与模型页自身卡片相同的数组语义与冲突处理(他人同时改动时提示冲突并重新加载)。行内除 `input` 与 `reasoningEfforts` 外的所有字段原样保留。
36
65
 
37
66
  折叠区同时订阅 Host 转发的 `settings/document-updated` 事件,并只认 `llm-pi-ai`,所以模型页自己写模型列表时能无需重挂载地传到折叠区:已展开且没有草稿的折叠区静默重读,并一直追读到与最新通报的 revision 持平;收起的把通知挂起,等下次展开再重读;正握着未保存草稿的把通知挂起到草稿落定为止,因此重新展开永远不会丢掉编辑。卡片自己那次写入按刚提交的 revision 认出回声,不会再触发一次刷新。
38
67
 
68
+
39
69
  ## 安装
40
70
 
41
71
  前置条件:DeepSeek Harness(`dsh`)`>=0.1.5-alpha.1 <0.2.0`(装有 `web` profile)。
@@ -108,11 +138,13 @@ Dependabot 每周检查 GitHub Actions 依赖。npm 版本更新有意未启用
108
138
  ```text
109
139
  .
110
140
  ├─ src/
111
- │ ├─ index.ts # Host 半边:刻意留空的 apply(纯浏览器插件)
112
- │ ├─ image-input.ts # 每模型 input 声明的纯函数行助手
113
- └─ client/ # Web 半边:输入模态折叠区(controller、card、locales)
114
- ├─ tests/ # 纯函数行助手的 vitest 单测
115
- ├─ build/ # 自包含客户端 bundle 的 tsdown 预设
141
+ │ ├─ index.ts # Host 半边:刻意留空的 apply(纯浏览器插件)
142
+ │ ├─ model-row.ts # 两个声明共用的行词汇(ModelRow、rowId)
143
+ ├─ image-input.ts # 每模型 input 声明的纯函数行助手
144
+ ├─ reasoning-efforts.ts # 每模型 reasoningEfforts 声明的纯函数行助手
145
+ │ └─ client/ # Web 半边:模型能力折叠区(controller、card、locales)
146
+ ├─ tests/ # 纯函数行助手的 vitest 单测
147
+ ├─ build/ # 自包含客户端 bundle 的 tsdown 预设
116
148
  ├─ .github/workflows/ci.yml # 校验 + tarball 审计 + 消费者冒烟
117
149
  ├─ .github/workflows/release.yml # GitHub Release 触发 npm 发布
118
150
  ├─ cordis.patch.yml
@@ -122,9 +154,12 @@ Dependabot 每周检查 GitHub Actions 依赖。npm 版本更新有意未启用
122
154
  ## 已知边界
123
155
 
124
156
  - 休眠(尚未配置)的提供方卡片不渲染折叠区;新建的自定义提供方在**保存之后**才出现。
125
- - 路由级 `defaultInput` 与内置提供方目录模型的 `modelOverrides` 不在本插件范围内,仍直接在 `$DSH_HOME/settings.yaml` 中设置。
157
+ - 路由级开关——`defaultInput`、默认推理等级 `reasoning`、`compat` 系列——以及内置提供方目录模型的 `modelOverrides` 不在本插件范围内,仍直接在 `$DSH_HOME/settings.yaml` 中设置。
158
+ - 声明等级只是「声明」,不是「校验」:没有任何环节去问网关是否真的提供该等级、是否认这个拼写;等级究竟怎么上线(`reasoning_effort`、thinking budget、chat-template kwargs)由 `compat` 决定。网关不认的等级会在回合中途被提供方拒绝。
159
+ - 取消勾选再重新勾选某个等级,会从默认拼写重新开始(等级名本身,`off` 为空),不会恢复它之前带的值。
126
160
  - 只读设置部署中折叠区可见但不可保存。
127
161
 
162
+
128
163
  ## 许可
129
164
 
130
165
  [MIT](LICENSE) © jcy2387
package/cordis.patch.yml CHANGED
@@ -1,8 +1,9 @@
1
1
  # One installable row mounts the plugin. The Host half is an empty apply; the
2
2
  # Web client half (the package's dsh.client manifest) carries the whole
3
- # feature: a per-model input-modality selector inside every llm-pi-ai provider
4
- # card on the Models settings page. Provider routes themselves are NOT
5
- # pre-written here create and edit them entirely from the Models page.
3
+ # feature: a per-model capability fold input modalities and declared
4
+ # reasoning levels inside every llm-pi-ai provider card on the Models
5
+ # settings page. Provider routes themselves are NOT pre-written here create
6
+ # and edit them entirely from the Models page.
6
7
  - insert:
7
8
  - id: models-input-modalities
8
9
  name: '@jcy2387/dsh-models-input-modalities'