@deepseek-ai/dsh-client-ui-settings-models 0.2.1-alpha.3 → 0.2.1-alpha.4

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.i18n.yaml CHANGED
@@ -21,8 +21,8 @@
21
21
  en: f4817eb2e3537ed9
22
22
  zh: e589c193fa85473f
23
23
  /deepseek-ai-dsh-client-ui-settings-models/use-this-package/editing-a-provider:
24
- en: eac663a25c60adf9
25
- zh: 24450ef377a79e01
24
+ en: 3af73644655c8b7b
25
+ zh: d689eba772100c63
26
26
  /deepseek-ai-dsh-client-ui-settings-models/use-this-package/adding-and-deleting-providers:
27
27
  en: 9d533c65c82cd4a4
28
28
  zh: 0a5da334e127c035
package/README.md CHANGED
@@ -53,12 +53,14 @@ A blank key field offers a saved credential when one is detected, with no status
53
53
 
54
54
  An endpoint without a fixed catalog default shows the provider's `baseURLEnv` configuration names below the input. A profile endpoint remains the first placeholder choice.
55
55
 
56
- The collapsed 自定义设置 fold carries the curated extras: `baseURL` for both families (the placeholder shows the endpoint the profile resolves to, the public endpoint for deepseek, else the `defaultBaseURL` the configurable-provider directory reports for a pi-ai catalog route), each adapter's model catalog, and the **display name** and **API protocol** of a pi-ai route the adapter does not ship. Profile `headers` remain deployment configuration in `cordis.patch.yml` or Cordis config and have no Models-page editor. The Provider ID stays fixed: it is the settings key, the name every other namespace and every logged session references, and the stem of a credential reference the page cannot read back to move. Reasoning effort is deliberately not among the editable fields: it is a per-model capability, so a provider-scoped control could only be set to a value some models reject. Each model row edits `id`, optional display `name`, optional `contextWindow`/`maxTokens`, and input types; unrelated model fields survive edits.
56
+ The collapsed 自定义设置 fold carries the curated extras: `baseURL` for both families (the placeholder shows the endpoint the profile resolves to, the public endpoint for deepseek, else the `defaultBaseURL` the configurable-provider directory reports for a pi-ai catalog route), each adapter's model catalog, and the **display name** and **API protocol** of a pi-ai route the adapter does not ship. Profile `headers` remain deployment configuration in `cordis.patch.yml` or Cordis config and have no Models-page editor. The Provider ID stays fixed: it is the settings key, the name every other namespace and every logged session references, and the stem of a credential reference the page cannot read back to move. There is no provider-scoped reasoning-effort field: effort is a per-model capability, so a provider-scoped control could only be set to a value some models reject. Each model row edits `id`, optional display `name`, optional `contextWindow`/`maxTokens`, input types, and, on a hand-declared pi-ai route, reasoning levels; unrelated model fields survive edits.
57
57
 
58
58
  The DeepSeek card edits the `llm-deepseek` endpoint, credentials, and model catalog. It uses Messages, with the default endpoint placeholder `https://api.deepseek.com/anthropic`.
59
59
 
60
60
  Expand **Customized settings → Model options** to edit each model. Both provider families share the same row layout, labels, and icons: context window and max output tokens occupy two columns, and **Input types** occupies a separate row with **Text** and **Image** checkboxes. A row without an input declaration displays the installed model’s input types, then the provider default, then Text. Known pi-ai providers load their installed catalog without endpoint I/O; opening a row does not write an override. Explicit input selections take precedence, including text-only overrides of vision models. Checkbox edits save the selected types, with at least one type required. DeepSeek writes `inputModalities`; pi-ai writes `input`. Unchecking Image for DeepSeek also removes `imagePixelBudget` and `imageMaxBytes`, because the adapter rejects those limits without image input. Clearing the input field in `cordis.patch.yml` restores adapter inheritance; **Restore default models** resets the entire catalog override. Declare only input types the upstream model can actually process.
61
61
 
62
+ A model row on a hand-declared pi-ai route also edits its **Reasoning levels**, the `reasoningEfforts` the adapter already reads from `cordis.patch.yml`; the **?** beside the label explains the two columns. With the switch off the field is absent, so the model does not reason and the model picker offers no level; a stored `false` is kept until the switch is used. Turning it on always starts from rows for off, low, medium, and high; it does not restore levels removed earlier, and **Cancel** discards the edit instead. Each row pairs a level select — the keys `llm-pi-ai`'s configuration schema accepts in `reasoningEfforts`, read from the namespace schema like the protocol choices and today `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`, each usable once — with the **API value** sent for it, which starts as the level name; a field cleared while typing stores the level name and shows it once the field is left. Off only turns reasoning off, so its value field is disabled; a value already written for it in `cordis.patch.yml` is shown and kept. Changing a row's level resets a value still spelled as the old level; a custom value carries over only between two thinking levels, so changing a row to or from Off resets it. A stored key the schema does not list keeps its row and stays selectable there. Rows can be added and deleted and are saved in row order, while the model picker lists the levels in escalation order. The section refuses what the adapter refuses: no level besides Off — including a `reasoningEfforts:` left without a value, which shows the switch on with no rows — or a value that is missing on a level besides Off or empty on any level. A refused value marks its field invalid, and leaving that field fills in the level name. The rows keep no state of their own, so a model row reused after a deletion, a reset, or a fold shows only its own model. Routes served from the installed catalog and the DeepSeek card keep the adapter's levels and show no reasoning control.
63
+
62
64
  ### Adding and deleting providers
63
65
 
64
66
  **Add model provider** is the one entry to the add card, offered while any editor-backed settings namespace is mounted and enabled while either mode below can proceed. The card opens on a segmented mode switch with a one-line purpose beneath it: **Third-party model provider** carries the dormant-directory provider select over the provider editor — a bare-mounted `llm-pi-ai` offers its whole installed catalog before any route exists — and **Custom model API** carries the create form for a route pi-ai does not ship: a relay, a self-hosted server, or any other OpenAI- or Anthropic-compatible endpoint. A mode is offered only while its namespace is mounted; with one mode the card shows that form alone under the mode's name, and a mode whose namespace has nothing left to adopt or no protocol to declare is disabled with the reason as its hover text. A panel mounts the first time its mode is shown and stays mounted, hidden, while the card is open and its mode stays offered, so switching modes discards neither draft; the switch locks while either panel has a write or an endpoint interrogation in flight, since a switch underneath one would orphan its answer. Each tab controls its panel through ids, and closing either panel forgets the catalog draft's target. The custom form asks for a unique **Provider ID**, an endpoint, a protocol, and at least one uniquely-identified model, because nothing can default those. The protocol picker names each protocol by its product name — OpenAI Chat Completions, OpenAI Responses, Anthropic Messages — while the stored value stays the schema identifier; a protocol the adapter adds before this page names it shows that identifier. The endpoint placeholder follows the selected protocol: OpenAI Chat Completions and Responses show `https://gateway.example/v1`, while Anthropic Messages shows `https://gateway.example` because its SDK appends `/v1/messages`. Changing the protocol preserves any address already entered. The endpoint must be a parseable HTTP or HTTPS URL; localhost, IPv4 and IPv6 literals, and custom ports remain valid. A syntax error blocks both discovery and creation at the field, while a request failure remains a separate provider error. **Fetch available models** asks the `llm/discoverModels` Remote about the endpoint the form shows, so adding a provider is one pass instead of save-then-return; the reply opens a searchable picker rather than being written, and nothing is written until **Add selected**. Each candidate displays its original model id in a monospace font to distinguish models with the same name. Rows stay on one line; hovering reveals the model name, falling back to the full id when no name is supplied. Selection and configuration retain the original model id. Each selected candidate copies its id, display name, context window, output-token cap, and disclosed input types into the editable row when disclosed, while an existing row retains its user-tuned values. Search matches model ids and optional display names without clearing hidden selections. **Select all** adds the visible results, while **Deselect all** clears the entire selection so hidden results cannot be adopted accidentally. A row is deletable only when the user layer alone carries it (removal restores the composition base), and its confirmation dialog names the provider.
package/README.zh.md CHANGED
@@ -53,12 +53,14 @@ API 密钥输入框初始为空,并通过 `autocomplete="new-password"` 请求
53
53
 
54
54
  目录未提供固定默认端点时,输入框下方显示提供方的 `baseURLEnv` 配置变量名。Profile 中的端点仍是占位符的首选。
55
55
 
56
- 收起的「自定义设置」折叠区承载精选的额外字段:两个家族都有 `baseURL`(占位符显示 profile 解析出的端点,deepseek 即公共端点;没有时显示可配置提供商目录为 pi-ai 内置路由报告的 `defaultBaseURL`)、各适配器自己的模型目录,以及适配器未提供的 pi-ai 路由的**显示名称**与 **API 协议**。Profile `headers` 仍是 `cordis.patch.yml` 或 Cordis 配置中的部署配置,Models 页面不提供编辑器。Provider ID 保持固定:它是 settings 的键、其他每个 namespace 与每一条已记录会话引用的名字,也是页面读不回、因而搬不走的凭据引用词干。推理等级刻意不在可编辑字段之列:它是按模型的能力,提供商级的控件只可能被设成某些模型会拒绝的值。每个模型行可编辑 `id`、可选显示 `name`、可选 `contextWindow`/`maxTokens` 和输入类型;无关的模型字段在编辑后仍会保留。
56
+ 收起的「自定义设置」折叠区承载精选的额外字段:两个家族都有 `baseURL`(占位符显示 profile 解析出的端点,deepseek 即公共端点;没有时显示可配置提供商目录为 pi-ai 内置路由报告的 `defaultBaseURL`)、各适配器自己的模型目录,以及适配器未提供的 pi-ai 路由的**显示名称**与 **API 协议**。Profile `headers` 仍是 `cordis.patch.yml` 或 Cordis 配置中的部署配置,Models 页面不提供编辑器。Provider ID 保持固定:它是 settings 的键、其他每个 namespace 与每一条已记录会话引用的名字,也是页面读不回、因而搬不走的凭据引用词干。这里没有提供商级的推理等级字段:它是按模型的能力,提供商级的控件只可能被设成某些模型会拒绝的值。每个模型行可编辑 `id`、可选显示 `name`、可选 `contextWindow`/`maxTokens`、输入类型,以及手工声明的 pi-ai 路由上的推理等级;无关的模型字段在编辑后仍会保留。
57
57
 
58
58
  `llm-deepseek` 的 DeepSeek 卡片编辑端点、凭据和模型目录。它使用 Messages,默认端点占位符为 `https://api.deepseek.com/anthropic`。
59
59
 
60
60
  展开**自定义设置 → 模型选项**编辑模型。两个提供商家族共用相同的模型行布局、标签和图标:上下文窗口与最大输出 token 数分为两列,**输入类型**独占下一行,提供**文本**和**图片**复选框。未声明输入类型的模型行优先显示已安装模型的输入类型,其次是提供商默认值,最后回退为文本。已知 pi-ai 提供商会加载已安装目录,不向端点发送请求;打开模型行不会写入覆盖值。显式输入选择具有优先权,包括为视觉模型设置的仅文本覆盖。修改复选框会保存所选类型,且至少保留一种。DeepSeek 写入 `inputModalities`,pi-ai 写入 `input`。DeepSeek 取消勾选图片时,还会移除 `imagePixelBudget` 和 `imageMaxBytes`,因为适配器在没有图片输入时拒绝这些限制。在 `cordis.patch.yml` 中清除输入字段可恢复适配器继承;**恢复默认模型**会重置整个模型目录覆盖。仅声明上游模型实际能够处理的输入类型。
61
61
 
62
+ 手工声明的 pi-ai 路由上,模型行还会编辑**推理等级**,即适配器本来就从 `cordis.patch.yml` 读取的 `reasoningEfforts`;标签旁的 **?** 说明两列的含义。开关关闭时该字段不存在,模型不推理,模型选择器不提供任何档位;已存储的 `false` 在开关被操作前保持不变。打开开关总是从 off、low、medium 和 high 四行开始,不会恢复之前删掉的档位;要放弃修改请点**取消**。每行把一个档位选择框——可选值是 `llm-pi-ai` 配置 schema 允许作为 `reasoningEfforts` 键的档位,与协议选项一样从命名空间 schema 读取,目前为 `off`、`minimal`、`low`、`medium`、`high`、`xhigh`、`max`,每个档位只能用一次——与发送给它的 **API 参数值**配成一对,参数值初始为档位名;输入时清空的字段会存为档位名,离开字段后显示出来。Off 只表示关闭思考,因此它的参数值输入框是禁用的;`cordis.patch.yml` 中已为它写好的值会显示并保留。修改某行的档位时,仍是旧档位名的参数值会随之重置;自定义值只在两个思考档位之间保留,切到或切离 Off 都会重置。schema 未列出的已存键会保留自己的行,并可在该行中继续选择。行可以增删,并按行的顺序保存,模型选择器则按升级顺序列出档位。本节拒绝适配器会拒绝的配置:没有 Off 以外的档位(包括未写值的 `reasoningEfforts:`,此时开关显示为打开且没有任何行),或者 Off 以外的档位缺少参数值、任意档位的参数值为空字符串。被拒绝的值会把对应输入框标为无效,离开该输入框时会填入档位名。各行不保留自己的状态,因此删除、恢复默认或折叠之后被复用的模型行只显示它自己的模型。走已安装目录的路由和 DeepSeek 卡片沿用适配器的档位,不显示推理等级控件。
63
+
62
64
  ### 新增与删除提供商
63
65
 
64
66
  **添加模型提供商**是新增卡片的唯一入口:只要任一带编辑器的设置 namespace 已挂载就会出现,下述两种方式任一可继续时才可点击。卡片打开后顶部是分段式的方式切换,下方一行小字说明所选方式的用途:**第三方模型提供商**承载休眠目录提供商选择框与提供商编辑器——裸挂载的 `llm-pi-ai` 在任何路由存在之前就能提供其完整的已安装 catalog;**自定义模型 API**承载为 pi-ai 不提供的路由(中转站、自部署服务或其他兼容 OpenAI / Anthropic 协议的接口)准备的创建表单。某种方式只在其 namespace 已挂载时提供;只剩一种方式时卡片以该方式为标题直接显示表单;namespace 已无可采用的提供商或没有可声明的协议时,对应方式会被禁用并把原因作为悬停提示。面板在其方式首次显示时挂载,之后在卡片打开且该方式仍被提供期间保持挂载但隐藏,因此切换方式不会丢掉任何一边的草稿;任一面板有写入或端点探测进行中时滑块锁定,因为此时切换会让结果落到看不见的面板上。每个 tab 通过 id 控制其面板,关闭任一面板都会忘记目录草稿的目标。自定义表单会索要唯一的 **Provider ID**、端点、协议与至少一个可唯一识别的模型,因为没有东西能为它们兜底。协议选择框以产品名称显示各协议——OpenAI Chat Completions、OpenAI Responses、Anthropic Messages——存储的值仍是 schema 标识符;适配器新增而本页尚未命名的协议直接显示其标识符。端点占位示例随所选协议变化:OpenAI Chat Completions 与 Responses 显示 `https://gateway.example/v1`,Anthropic Messages 显示 `https://gateway.example`,因为其 SDK 会追加 `/v1/messages`。切换协议会保留已输入的地址。端点必须是可解析的 HTTP 或 HTTPS URL;localhost、IPv4 与 IPv6 字面地址以及自定义端口仍然有效。语法错误会在字段处阻止询问与创建,请求失败则继续作为独立的提供商错误显示。**获取可用模型**通过 `llm/discoverModels` Remote 查询表单显示的端点,因此新增提供商一次即可完成,而非先保存再返回;回复打开的是可搜索选择器而非直接写入,只有点击**添加所选**才会写入。候选项使用等宽字体显示原始模型 id,以区分同名模型。每行保持单行,悬停可查看模型名称,未提供名称时回退到完整 id。勾选与配置仍使用原始模型 id。每个选中候选会在提供商公布相应信息时,把 id、显示名、上下文窗口、最大输出 token 数和已公布的输入类型复制进可编辑行;已经存在的行保留用户调整过的值。搜索会匹配模型 id 与可选显示名称,且不会清除隐藏项的勾选状态。**全选**会加入可见结果,而**取消全选**会清空全部勾选,以免意外采用隐藏结果。只有用户层单独携带某行时,该行才可删除(删除会恢复组合基线),其确认对话框会指名该提供商。
package/lib/client.js CHANGED
@@ -55,7 +55,7 @@ window.__ModuleLoader__.load({
55
55
  }
56
56
  //#endregion
57
57
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-models/src/client/ModelsSection.module.css.mjs
58
- const css$3 = ".zGbnIq_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.zGbnIq_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.zGbnIq_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.zGbnIq_notice{color:var(--dsw-alias-state-warn-label);margin:0;font-size:12px;line-height:18px}.zGbnIq_savedNotice{color:var(--dsw-alias-state-success-primary);margin:0;font-size:12px;line-height:18px}.zGbnIq_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.zGbnIq_rowCard{border:.5px solid var(--dsw-alias-settings-card-stroke);background:var(--dsw-alias-settings-card-fill);border-radius:var(--dsw-radius-xl);flex-direction:column;gap:12px;padding:12px 14px;display:flex}.zGbnIq_rowHead{align-items:center;gap:10px;display:flex}.zGbnIq_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.zGbnIq_rowName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.zGbnIq_rowTag{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-xs);color:var(--dsw-alias-label-secondary);flex:none;padding:1px 6px;font-size:11px;line-height:16px}.zGbnIq_credentialDot{box-sizing:border-box;corner-shape:round;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.zGbnIq_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.zGbnIq_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.zGbnIq_accountStatus{color:var(--dsw-alias-state-success-primary);flex:none;align-items:center;gap:6px;font-size:13px;line-height:20px;display:inline-flex}.zGbnIq_rowActions{align-items:center;gap:4px;margin-left:auto;display:inline-flex}.zGbnIq_primaryButton,.zGbnIq_secondaryButton,.zGbnIq_addButton{box-sizing:border-box;border-radius:var(--dsw-radius-md);height:36px;font:inherit;cursor:pointer;border:none;justify-content:center;align-items:center;gap:4px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.zGbnIq_primaryButton{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.zGbnIq_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.zGbnIq_secondaryButton,.zGbnIq_addButton{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-primary);background:0 0}.zGbnIq_secondaryButton:hover:not(:disabled),.zGbnIq_addButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.zGbnIq_secondaryButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.zGbnIq_dangerButton{box-sizing:border-box;border-radius:var(--dsw-radius-md);height:36px;color:var(--dsw-alias-state-error-primary);font:inherit;cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.zGbnIq_dangerButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.zGbnIq_rowActions .zGbnIq_secondaryButton,.zGbnIq_rowActions .zGbnIq_dangerButton{border-radius:var(--dsw-radius-sm);height:28px;padding:0 10px;font-size:12px;line-height:18px}.zGbnIq_primaryButton:disabled,.zGbnIq_secondaryButton:disabled,.zGbnIq_dangerButton:disabled,.zGbnIq_addButton:disabled,.zGbnIq_linkButton:disabled,.zGbnIq_addModelButton:disabled{opacity:.4;cursor:default}.zGbnIq_primaryButton:focus-visible,.zGbnIq_secondaryButton:focus-visible,.zGbnIq_dangerButton:focus-visible,.zGbnIq_addButton:focus-visible,.zGbnIq_linkButton:focus-visible,.zGbnIq_addModelButton:focus-visible,.zGbnIq_iconButton:focus-visible,.zGbnIq_customizedSummary:focus-visible{box-shadow:0 0 0 2px var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline:none}.zGbnIq_editor{border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-bg-module-platform);flex-direction:column;gap:14px;padding:14px 16px;display:flex}.zGbnIq_editorHeader{align-items:baseline;gap:8px;display:flex}.zGbnIq_editorTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.zGbnIq_editorRoute{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.zGbnIq_field{flex-direction:column;gap:6px;display:flex}.zGbnIq_fieldLabel{color:var(--dsw-alias-label-secondary);align-items:center;gap:10px;font-size:12px;font-weight:500;line-height:18px;display:inline-flex}.zGbnIq_helpButton{border:.5px solid var(--dsw-alias-border-l3);corner-shape:round;width:16px;height:16px;color:var(--dsw-alias-label-tertiary);font:inherit;text-align:center;cursor:help;background:0 0;border-radius:50%;padding:0;font-size:11px;line-height:15px}.zGbnIq_helpButton:hover,.zGbnIq_helpButton:focus-visible{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-tertiary)}.zGbnIq_linkButton{box-sizing:border-box;border-radius:var(--dsw-radius-sm);height:28px;color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:none;align-items:center;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.zGbnIq_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.zGbnIq_advancedHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.zGbnIq_editorActions{justify-content:flex-end;gap:8px;display:flex}.zGbnIq_addBlock{flex-direction:column;gap:12px;display:flex}.zGbnIq_addActions{display:flex}.zGbnIq_addButton{border:1px dashed var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-lg);flex:1 1 0;gap:6px;min-width:180px;height:44px}.zGbnIq_addModes{flex-direction:column;align-items:flex-start;gap:8px;display:flex}.zGbnIq_addPanel{flex-direction:column;gap:14px;display:flex}.zGbnIq_addPanel[hidden]{display:none}.zGbnIq_addCard,.zGbnIq_setupCard{border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-bg-module-platform);flex-direction:column;gap:14px;padding:14px 16px;list-style:none;display:flex}.zGbnIq_addCard .zGbnIq_editor,.zGbnIq_setupCard .zGbnIq_editor{background:0 0;padding:0}.zGbnIq_customized{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:10px}.zGbnIq_customizedSummary{border-radius:var(--dsw-radius-sm);cursor:pointer;width:fit-content;color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.zGbnIq_customizedSummary::-webkit-details-marker{display:none}.zGbnIq_customizedSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.zGbnIq_customized[open]>.zGbnIq_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.zGbnIq_customizedSummary:hover{color:var(--dsw-alias-label-primary)}.zGbnIq_customizedBody{flex-direction:column;gap:12px;padding-top:12px;display:flex}.zGbnIq_modelCatalog{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding-top:12px;display:flex}.zGbnIq_modelCatalogHeading{flex-direction:column;gap:2px;display:flex}.zGbnIq_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.zGbnIq_modelCatalogMeta,.zGbnIq_modelEmpty{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.zGbnIq_modelList{flex-direction:column;gap:8px;display:flex}.zGbnIq_modelListHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.zGbnIq_modelEntry{border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-lg);padding:6px}.zGbnIq_modelRow{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) auto auto;align-items:center;gap:6px;display:grid}.zGbnIq_iconButton{box-sizing:border-box;border-radius:var(--dsw-radius-sm);width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;display:inline-flex}.zGbnIq_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.zGbnIq_iconButton:disabled{cursor:default;opacity:.4}.zGbnIq_iconButtonDanger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}.zGbnIq_modelAdvanced{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:8px 4px 2px;display:grid}.zGbnIq_modelField{flex-direction:column;gap:4px;min-width:0;display:flex}.zGbnIq_modelInputTypes{border:none;grid-column:1/-1;min-width:0;margin:0;padding:0}.zGbnIq_modelInputTypes legend{margin-bottom:4px;padding:0}.zGbnIq_modelInputChoices{flex-wrap:wrap;align-items:center;gap:16px;min-height:32px;display:flex}.zGbnIq_modelFieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.zGbnIq_modelEmpty{border:1px dashed var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-lg);text-align:center;padding:12px}.zGbnIq_addModelButton{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-sm);height:28px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;background:0 0;align-self:flex-start;align-items:center;gap:4px;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.zGbnIq_addModelButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.zGbnIq_input{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);width:100%;height:32px;font:inherit;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);padding:0 10px;font-size:14px;line-height:22px}select.zGbnIq_input{cursor:pointer;max-width:240px}.zGbnIq_input:focus{border-color:var(--dsw-alias-state-business-primary);outline:none}.zGbnIq_input::placeholder{color:var(--dsw-alias-label-dimmed)}.zGbnIq_input:disabled{opacity:.6;cursor:default}.zGbnIq_keyInput{position:relative}.zGbnIq_keyInputFilled{padding-right:40px}.zGbnIq_keyToggle{border-radius:var(--dsw-radius-md);width:32px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;padding:0;display:inline-flex;position:absolute;inset:1px 1px 1px auto}.zGbnIq_keyToggle:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.zGbnIq_keyToggle:focus-visible{outline:var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:-2px}.zGbnIq_keyToggle:disabled{opacity:.6;cursor:default}.zGbnIq_selectInput{appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");background-position:right 12px center;background-repeat:no-repeat;background-size:12px 12px;padding-right:32px}.zGbnIq_error{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}.zGbnIq_deleteDialog{width:min(480px,100%)}.zGbnIq_deleteConfirm:not(:disabled){border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.zGbnIq_deleteConfirm:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.zGbnIq_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}@media (prefers-reduced-motion:reduce){.zGbnIq_customizedSummary:before,.zGbnIq_switchThumb{transition:none}}.zGbnIq_fetchDialog{--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);max-width:520px}.zGbnIq_candidateToolbar{align-items:center;gap:8px;margin-bottom:6px;display:flex}.zGbnIq_candidateSearch{flex:240px;min-width:0}.zGbnIq_candidateList{flex-direction:column;gap:2px;max-height:320px;margin:0;padding:0;list-style:none;display:flex;overflow-y:auto}.zGbnIq_candidate{border-radius:var(--dsw-radius-md)}.zGbnIq_candidateLabel{cursor:pointer;align-items:center;gap:8px;padding:6px 8px;display:flex}.zGbnIq_candidateId{min-width:0;font-family:var(--ds-font-family-code);text-overflow:ellipsis;white-space:nowrap;flex:auto;font-size:13px;overflow:hidden}.zGbnIq_candidateEmpty{color:var(--dsw-alias-label-secondary);text-align:center;margin:24px 0;font-size:13px;line-height:20px}";
58
+ const css$3 = ".zGbnIq_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.zGbnIq_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.zGbnIq_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.zGbnIq_notice{color:var(--dsw-alias-state-warn-label);margin:0;font-size:12px;line-height:18px}.zGbnIq_savedNotice{color:var(--dsw-alias-state-success-primary);margin:0;font-size:12px;line-height:18px}.zGbnIq_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.zGbnIq_rowCard{border:.5px solid var(--dsw-alias-settings-card-stroke);background:var(--dsw-alias-settings-card-fill);border-radius:var(--dsw-radius-xl);flex-direction:column;gap:12px;padding:12px 14px;display:flex}.zGbnIq_rowHead{align-items:center;gap:10px;display:flex}.zGbnIq_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.zGbnIq_rowName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.zGbnIq_rowTag{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-xs);color:var(--dsw-alias-label-secondary);flex:none;padding:1px 6px;font-size:11px;line-height:16px}.zGbnIq_credentialDot{box-sizing:border-box;corner-shape:round;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.zGbnIq_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.zGbnIq_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.zGbnIq_accountStatus{color:var(--dsw-alias-state-success-primary);flex:none;align-items:center;gap:6px;font-size:13px;line-height:20px;display:inline-flex}.zGbnIq_rowActions{align-items:center;gap:4px;margin-left:auto;display:inline-flex}.zGbnIq_primaryButton,.zGbnIq_secondaryButton,.zGbnIq_addButton{box-sizing:border-box;border-radius:var(--dsw-radius-md);height:36px;font:inherit;cursor:pointer;border:none;justify-content:center;align-items:center;gap:4px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.zGbnIq_primaryButton{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.zGbnIq_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.zGbnIq_secondaryButton,.zGbnIq_addButton{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-primary);background:0 0}.zGbnIq_secondaryButton:hover:not(:disabled),.zGbnIq_addButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.zGbnIq_secondaryButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.zGbnIq_dangerButton{box-sizing:border-box;border-radius:var(--dsw-radius-md);height:36px;color:var(--dsw-alias-state-error-primary);font:inherit;cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.zGbnIq_dangerButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.zGbnIq_rowActions .zGbnIq_secondaryButton,.zGbnIq_rowActions .zGbnIq_dangerButton{border-radius:var(--dsw-radius-sm);height:28px;padding:0 10px;font-size:12px;line-height:18px}.zGbnIq_primaryButton:disabled,.zGbnIq_secondaryButton:disabled,.zGbnIq_dangerButton:disabled,.zGbnIq_addButton:disabled,.zGbnIq_linkButton:disabled,.zGbnIq_addModelButton:disabled{opacity:.4;cursor:default}.zGbnIq_primaryButton:focus-visible,.zGbnIq_secondaryButton:focus-visible,.zGbnIq_dangerButton:focus-visible,.zGbnIq_addButton:focus-visible,.zGbnIq_linkButton:focus-visible,.zGbnIq_addModelButton:focus-visible,.zGbnIq_iconButton:focus-visible,.zGbnIq_customizedSummary:focus-visible{box-shadow:0 0 0 2px var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline:none}.zGbnIq_editor{border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-bg-module-platform);flex-direction:column;gap:14px;padding:14px 16px;display:flex}.zGbnIq_editorHeader{align-items:baseline;gap:8px;display:flex}.zGbnIq_editorTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.zGbnIq_editorRoute{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.zGbnIq_field{flex-direction:column;gap:6px;display:flex}.zGbnIq_fieldLabel{color:var(--dsw-alias-label-secondary);align-items:center;gap:10px;font-size:12px;font-weight:500;line-height:18px;display:inline-flex}.zGbnIq_helpButton{border:.5px solid var(--dsw-alias-border-l3);corner-shape:round;width:16px;height:16px;color:var(--dsw-alias-label-tertiary);font:inherit;text-align:center;cursor:help;background:0 0;border-radius:50%;padding:0;font-size:11px;line-height:15px}.zGbnIq_helpButton:hover,.zGbnIq_helpButton:focus-visible{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-tertiary)}.zGbnIq_linkButton{box-sizing:border-box;border-radius:var(--dsw-radius-sm);height:28px;color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:none;align-items:center;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.zGbnIq_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.zGbnIq_advancedHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.zGbnIq_editorActions{justify-content:flex-end;gap:8px;display:flex}.zGbnIq_addBlock{flex-direction:column;gap:12px;display:flex}.zGbnIq_addActions{display:flex}.zGbnIq_addButton{border:1px dashed var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-lg);flex:1 1 0;gap:6px;min-width:180px;height:44px}.zGbnIq_addModes{flex-direction:column;align-items:flex-start;gap:8px;display:flex}.zGbnIq_addPanel{flex-direction:column;gap:14px;display:flex}.zGbnIq_addPanel[hidden]{display:none}.zGbnIq_addCard,.zGbnIq_setupCard{border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-bg-module-platform);flex-direction:column;gap:14px;padding:14px 16px;list-style:none;display:flex}.zGbnIq_addCard .zGbnIq_editor,.zGbnIq_setupCard .zGbnIq_editor{background:0 0;padding:0}.zGbnIq_customized{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:10px}.zGbnIq_customizedSummary{border-radius:var(--dsw-radius-sm);cursor:pointer;width:fit-content;color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.zGbnIq_customizedSummary::-webkit-details-marker{display:none}.zGbnIq_customizedSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.zGbnIq_customized[open]>.zGbnIq_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.zGbnIq_customizedSummary:hover{color:var(--dsw-alias-label-primary)}.zGbnIq_customizedBody{flex-direction:column;gap:12px;padding-top:12px;display:flex}.zGbnIq_modelCatalog{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding-top:12px;display:flex}.zGbnIq_modelCatalogHeading{flex-direction:column;gap:2px;display:flex}.zGbnIq_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.zGbnIq_modelCatalogMeta,.zGbnIq_modelEmpty{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.zGbnIq_modelList{flex-direction:column;gap:8px;display:flex}.zGbnIq_modelListHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.zGbnIq_modelEntry{border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-lg);padding:6px}.zGbnIq_modelRow{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) auto auto;align-items:center;gap:6px;display:grid}.zGbnIq_iconButton{box-sizing:border-box;border-radius:var(--dsw-radius-sm);width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;display:inline-flex}.zGbnIq_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.zGbnIq_iconButton:disabled{cursor:default;opacity:.4}.zGbnIq_iconButtonDanger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}.zGbnIq_modelAdvanced{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:8px 4px 2px;display:grid}.zGbnIq_modelField{flex-direction:column;gap:4px;min-width:0;display:flex}.zGbnIq_modelInputTypes{border:none;grid-column:1/-1;min-width:0;margin:0;padding:0}.zGbnIq_modelInputTypes legend{margin-bottom:4px;padding:0}.zGbnIq_modelInputChoices{flex-wrap:wrap;align-items:center;gap:16px;min-height:32px;display:flex}.zGbnIq_modelFieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.zGbnIq_modelReasoning{flex-direction:column;grid-column:1/-1;gap:6px;min-width:0;display:flex}.zGbnIq_modelReasoningLabel{align-items:center;gap:6px;display:inline-flex}.zGbnIq_modelReasoningHead{justify-content:space-between;align-items:center;gap:8px;display:flex}.zGbnIq_modelReasoningRow{grid-template-columns:minmax(0,1fr) minmax(0,1fr) auto;align-items:center;gap:6px;display:grid}.zGbnIq_modelReasoningRow .zGbnIq_selectInput{max-width:none}.zGbnIq_modelReasoningValueInput{font-family:var(--ds-font-family-code);font-size:13px}.zGbnIq_modelEmpty{border:1px dashed var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-lg);text-align:center;padding:12px}.zGbnIq_addModelButton{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-sm);height:28px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;background:0 0;align-self:flex-start;align-items:center;gap:4px;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.zGbnIq_addModelButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.zGbnIq_input{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);width:100%;height:32px;font:inherit;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);padding:0 10px;font-size:14px;line-height:22px}select.zGbnIq_input{cursor:pointer;max-width:240px}.zGbnIq_input:focus{border-color:var(--dsw-alias-state-business-primary);outline:none}.zGbnIq_input::placeholder{color:var(--dsw-alias-label-dimmed)}.zGbnIq_input:disabled{opacity:.6;cursor:default}.zGbnIq_keyInput{position:relative}.zGbnIq_keyInputFilled{padding-right:40px}.zGbnIq_keyToggle{border-radius:var(--dsw-radius-md);width:32px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;padding:0;display:inline-flex;position:absolute;inset:1px 1px 1px auto}.zGbnIq_keyToggle:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.zGbnIq_keyToggle:focus-visible{outline:var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:-2px}.zGbnIq_keyToggle:disabled{opacity:.6;cursor:default}.zGbnIq_selectInput{appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");background-position:right 12px center;background-repeat:no-repeat;background-size:12px 12px;padding-right:32px}.zGbnIq_error{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}.zGbnIq_deleteDialog{width:min(480px,100%)}.zGbnIq_deleteConfirm:not(:disabled){border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.zGbnIq_deleteConfirm:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.zGbnIq_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}@media (prefers-reduced-motion:reduce){.zGbnIq_customizedSummary:before,.zGbnIq_switchThumb{transition:none}}.zGbnIq_fetchDialog{--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);max-width:520px}.zGbnIq_candidateToolbar{align-items:center;gap:8px;margin-bottom:6px;display:flex}.zGbnIq_candidateSearch{flex:240px;min-width:0}.zGbnIq_candidateList{flex-direction:column;gap:2px;max-height:320px;margin:0;padding:0;list-style:none;display:flex;overflow-y:auto}.zGbnIq_candidate{border-radius:var(--dsw-radius-md)}.zGbnIq_candidateLabel{cursor:pointer;align-items:center;gap:8px;padding:6px 8px;display:flex}.zGbnIq_candidateId{min-width:0;font-family:var(--ds-font-family-code);text-overflow:ellipsis;white-space:nowrap;flex:auto;font-size:13px;overflow:hidden}.zGbnIq_candidateEmpty{color:var(--dsw-alias-label-secondary);text-align:center;margin:24px 0;font-size:13px;line-height:20px}";
59
59
  const tagId$3 = "@deepseek-ai/dsh-client-ui-settings-models/ModelsSection.module.css";
60
60
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
61
61
  const tag = document.createElement("style");
@@ -122,6 +122,11 @@ window.__ModuleLoader__.load({
122
122
  "modelInputTypes": "zGbnIq_modelInputTypes",
123
123
  "modelList": "zGbnIq_modelList",
124
124
  "modelListHead": "zGbnIq_modelListHead",
125
+ "modelReasoning": "zGbnIq_modelReasoning",
126
+ "modelReasoningHead": "zGbnIq_modelReasoningHead",
127
+ "modelReasoningLabel": "zGbnIq_modelReasoningLabel",
128
+ "modelReasoningRow": "zGbnIq_modelReasoningRow",
129
+ "modelReasoningValueInput": "zGbnIq_modelReasoningValueInput",
125
130
  "modelRow": "zGbnIq_modelRow",
126
131
  "notice": "zGbnIq_notice",
127
132
  "primaryButton": "zGbnIq_primaryButton",
@@ -254,6 +259,224 @@ window.__ModuleLoader__.load({
254
259
  });
255
260
  }
256
261
  //#endregion
262
+ //#region lib/types/client/ModelReasoningEfforts.js
263
+ /** Reasoning-level declarations for a model on a hand-declared pi-ai route. */
264
+ /** Levels a model offers when the switch is turned on, where the schema lists them: Off plus the common OpenAI-compatible trio. */
265
+ const INITIAL_LEVELS = [
266
+ "off",
267
+ "low",
268
+ "medium",
269
+ "high"
270
+ ];
271
+ /** The wire value a level sends unless its row says otherwise; `null` is the Off entry that sends nothing. */
272
+ function defaultValue(level) {
273
+ return level === "off" ? null : level;
274
+ }
275
+ /**
276
+ * Whether pi-ai accepts a stored value: a non-empty string, or nothing for Off.
277
+ * @param level - the declared level.
278
+ * @param wire - the value stored for it.
279
+ * @returns whether the adapter would resolve the entry.
280
+ */
281
+ function acceptedValue(level, wire) {
282
+ return typeof wire === "string" ? wire.length > 0 : wire === null && level === "off";
283
+ }
284
+ /**
285
+ * Read the declared levels from a row in their stored order, including a key
286
+ * the offered levels do not name, so an edit never drops it.
287
+ * @param model - model row as drafted.
288
+ * @returns the entries; empty for a valueless field, which pi-ai refuses, and
289
+ * `undefined` when the field is absent or `false`.
290
+ */
291
+ function readEfforts(model) {
292
+ const value = model["reasoningEfforts"];
293
+ if (value === null) return [];
294
+ if (typeof value !== "object" || Array.isArray(value)) return void 0;
295
+ return Object.entries(value);
296
+ }
297
+ /**
298
+ * Replace a row's declared levels, preserving every other field.
299
+ * @param model - model row as drafted.
300
+ * @param entries - the levels to write in row order, or `undefined` to remove the field.
301
+ * @returns the replacement row.
302
+ */
303
+ function withEfforts(model, entries) {
304
+ const next = { ...model };
305
+ if (entries === void 0) {
306
+ Reflect.deleteProperty(next, "reasoningEfforts");
307
+ return next;
308
+ }
309
+ next["reasoningEfforts"] = Object.fromEntries(entries);
310
+ return next;
311
+ }
312
+ /**
313
+ * Mirror the pi-ai refusals of a hand-declared `reasoningEfforts`, so a card
314
+ * names the row instead of failing at the write.
315
+ * @param value - drafted `models` value, or `undefined` while inherited.
316
+ * @returns the first refused row, or `undefined` when the adapter will accept every row.
317
+ */
318
+ function validateReasoningEfforts(value) {
319
+ for (const [index, model] of modelDrafts(value).entries()) {
320
+ const entries = readEfforts(model);
321
+ if (entries === void 0) continue;
322
+ if (entries.some(([level, wire]) => !acceptedValue(level, wire))) return {
323
+ index,
324
+ key: "modelReasoningValueRequired"
325
+ };
326
+ if (!entries.some(([level]) => level !== "off")) return {
327
+ index,
328
+ key: "modelReasoningNeedsLevel"
329
+ };
330
+ }
331
+ }
332
+ /**
333
+ * Edit the `reasoningEfforts` a hand-declared pi-ai model offers: a switch,
334
+ * then one row per level pairing the level with the value sent for it. Every
335
+ * row is read from the model, so a reused instance shows its new model.
336
+ * @param props - model declaration and row replacement action.
337
+ * @returns the reasoning-level group.
338
+ */
339
+ function ModelReasoningEfforts({ model, levels, position, disabled, t, onChange }) {
340
+ const entries = readEfforts(model);
341
+ const [typing, setTyping] = (0, react.useState)(void 0);
342
+ const label = `${t("modelReasoning")} ${String(position)}`;
343
+ const used = entries?.map(([level]) => level) ?? [];
344
+ const write = (next) => {
345
+ onChange(withEfforts(model, next));
346
+ };
347
+ const replaceAt = (current, index, entry) => current.map((existing, at) => at === index ? entry : existing);
348
+ const changeLevel = (current, index, from, wire, to) => {
349
+ write(replaceAt(current, index, [to, typeof wire === "string" && wire.length > 0 && wire !== from && from !== "off" && to !== "off" ? wire : defaultValue(to)]));
350
+ };
351
+ const addLevel = (current) => {
352
+ const highest = Math.max(-1, ...used.map((level) => levels.indexOf(level)));
353
+ const level = levels.find((candidate, index) => index > highest && !used.includes(candidate)) ?? levels.find((candidate) => !used.includes(candidate));
354
+ /* v8 ignore next -- the add button is disabled once every level has a row */
355
+ if (level === void 0) return;
356
+ write([...current, [level, defaultValue(level)]]);
357
+ };
358
+ const editValue = (current, index, level, text) => {
359
+ setTyping({
360
+ level,
361
+ text
362
+ });
363
+ const trimmed = text.trim();
364
+ write(replaceAt(current, index, [level, trimmed.length === 0 ? defaultValue(level) : trimmed]));
365
+ };
366
+ const leaveValue = (current, index, level, wire) => {
367
+ setTyping(void 0);
368
+ if (!acceptedValue(level, wire)) write(replaceAt(current, index, [level, defaultValue(level)]));
369
+ };
370
+ const choicesFor = (level) => [...levels.includes(level) ? [] : [level], ...levels.filter((choice) => choice === level || !used.includes(choice))];
371
+ const initialEntries = INITIAL_LEVELS.filter((level) => levels.includes(level)).map((level) => [level, defaultValue(level)]);
372
+ const refused = entries?.some(([level, wire]) => !acceptedValue(level, wire)) === true;
373
+ const missingLevel = entries !== void 0 && !used.some((level) => level !== "off");
374
+ return (0, react_jsx_runtime.jsxs)("div", {
375
+ className: ModelsSection_module_css_default["modelReasoning"],
376
+ role: "group",
377
+ "aria-label": label,
378
+ children: [(0, react_jsx_runtime.jsxs)("div", {
379
+ className: ModelsSection_module_css_default["modelReasoningHead"],
380
+ children: [(0, react_jsx_runtime.jsxs)("span", {
381
+ className: `${ModelsSection_module_css_default["modelFieldLabel"]} ${ModelsSection_module_css_default["modelReasoningLabel"]}`,
382
+ children: [t("modelReasoning"), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
383
+ label: t("modelReasoningHelpText"),
384
+ side: "right",
385
+ delayMs: 0,
386
+ focusDelayMs: 0,
387
+ portal: true,
388
+ openOnClick: true,
389
+ children: (0, react_jsx_runtime.jsx)("button", {
390
+ type: "button",
391
+ className: ModelsSection_module_css_default["helpButton"],
392
+ "aria-label": t("modelReasoningHelp"),
393
+ children: "?"
394
+ })
395
+ })]
396
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
397
+ checked: entries !== void 0,
398
+ label,
399
+ disabled,
400
+ onChange: (on) => {
401
+ write(on ? initialEntries : void 0);
402
+ }
403
+ })]
404
+ }), entries === void 0 ? null : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
405
+ entries.length > 0 ? (0, react_jsx_runtime.jsxs)("div", {
406
+ className: ModelsSection_module_css_default["modelReasoningRow"],
407
+ "aria-hidden": "true",
408
+ children: [(0, react_jsx_runtime.jsx)("span", {
409
+ className: ModelsSection_module_css_default["modelFieldLabel"],
410
+ children: t("modelReasoningLevel")
411
+ }), (0, react_jsx_runtime.jsx)("span", {
412
+ className: ModelsSection_module_css_default["modelFieldLabel"],
413
+ children: t("modelReasoningValue")
414
+ })]
415
+ }) : null,
416
+ entries.map(([level, wire], index) => (0, react_jsx_runtime.jsxs)("div", {
417
+ className: ModelsSection_module_css_default["modelReasoningRow"],
418
+ children: [
419
+ (0, react_jsx_runtime.jsx)("select", {
420
+ className: `${ModelsSection_module_css_default["input"]} ${ModelsSection_module_css_default["selectInput"]}`,
421
+ value: level,
422
+ "aria-label": `${t("modelReasoningLevel")} ${String(index + 1)}`,
423
+ disabled,
424
+ onChange: (event) => {
425
+ changeLevel(entries, index, level, wire, event.target.value);
426
+ },
427
+ children: choicesFor(level).map((choice) => (0, react_jsx_runtime.jsx)("option", {
428
+ value: choice,
429
+ children: choice
430
+ }, choice))
431
+ }),
432
+ (0, react_jsx_runtime.jsx)("input", {
433
+ className: `${ModelsSection_module_css_default["input"]} ${ModelsSection_module_css_default["modelReasoningValueInput"]}`,
434
+ type: "text",
435
+ value: typing?.level === level ? typing.text : typeof wire === "string" ? wire : "",
436
+ placeholder: level === "off" ? t("modelReasoningOffValuePlaceholder") : level,
437
+ "aria-label": `${level} ${t("modelReasoningValue")}`,
438
+ "aria-invalid": !acceptedValue(level, wire),
439
+ disabled: disabled || level === "off",
440
+ onChange: (event) => {
441
+ editValue(entries, index, level, event.target.value);
442
+ },
443
+ onBlur: () => {
444
+ leaveValue(entries, index, level, wire);
445
+ }
446
+ }),
447
+ (0, react_jsx_runtime.jsx)("button", {
448
+ type: "button",
449
+ className: `${ModelsSection_module_css_default["iconButton"]} ${ModelsSection_module_css_default["iconButtonDanger"]}`,
450
+ "aria-label": `${t("modelReasoningRemove")} ${level}`,
451
+ title: t("modelReasoningRemove"),
452
+ disabled,
453
+ onClick: () => {
454
+ write(entries.filter((_entry, at) => at !== index));
455
+ },
456
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutlineRegular, { size: 14 })
457
+ })
458
+ ]
459
+ }, index)),
460
+ refused ? (0, react_jsx_runtime.jsx)("p", {
461
+ className: ModelsSection_module_css_default["error"],
462
+ children: t("modelReasoningValueRequired")
463
+ }) : missingLevel ? (0, react_jsx_runtime.jsx)("p", {
464
+ className: ModelsSection_module_css_default["error"],
465
+ children: t("modelReasoningNeedsLevel")
466
+ }) : null,
467
+ (0, react_jsx_runtime.jsxs)("button", {
468
+ type: "button",
469
+ className: ModelsSection_module_css_default["addModelButton"],
470
+ disabled: disabled || levels.every((level) => used.includes(level)),
471
+ onClick: () => {
472
+ addLevel(entries);
473
+ },
474
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: 14 }), t("modelReasoningAdd")]
475
+ })
476
+ ] })]
477
+ });
478
+ }
479
+ //#endregion
257
480
  //#region lib/types/client/ModelRow.js
258
481
  /**
259
482
  * Render consistent model identity, capacity, and input-type controls.
@@ -301,33 +524,44 @@ window.__ModuleLoader__.load({
301
524
  ]
302
525
  }), props.expanded ? (0, react_jsx_runtime.jsxs)("div", {
303
526
  className: ModelsSection_module_css_default["modelAdvanced"],
304
- children: [["contextWindow", "maxTokens"].map((field) => (0, react_jsx_runtime.jsxs)("label", {
305
- className: ModelsSection_module_css_default["modelField"],
306
- children: [(0, react_jsx_runtime.jsx)("span", {
307
- className: ModelsSection_module_css_default["modelFieldLabel"],
308
- children: t(field)
309
- }), (0, react_jsx_runtime.jsx)("input", {
310
- className: ModelsSection_module_css_default["input"],
311
- type: "text",
312
- inputMode: "numeric",
313
- value: props[field].value,
314
- placeholder: props[field].placeholder,
315
- "aria-label": `${t(field)} ${String(position)}`,
527
+ children: [
528
+ ["contextWindow", "maxTokens"].map((field) => (0, react_jsx_runtime.jsxs)("label", {
529
+ className: ModelsSection_module_css_default["modelField"],
530
+ children: [(0, react_jsx_runtime.jsx)("span", {
531
+ className: ModelsSection_module_css_default["modelFieldLabel"],
532
+ children: t(field)
533
+ }), (0, react_jsx_runtime.jsx)("input", {
534
+ className: ModelsSection_module_css_default["input"],
535
+ type: "text",
536
+ inputMode: "numeric",
537
+ value: props[field].value,
538
+ placeholder: props[field].placeholder,
539
+ "aria-label": `${t(field)} ${String(position)}`,
540
+ disabled,
541
+ onChange: (event) => {
542
+ props[field].onChange(event.target.value);
543
+ },
544
+ onBlur: props[field].onBlur
545
+ })]
546
+ }, field)),
547
+ (0, react_jsx_runtime.jsx)(ModelInputTypes, {
548
+ model,
549
+ field: props.inputField,
550
+ position,
551
+ fallback: props.inputFallback,
552
+ disabled: disabled || props.inputLoading === true,
553
+ t,
554
+ onChange: props.onChange
555
+ }),
556
+ props.reasoningLevels !== void 0 && props.reasoningLevels.length > 0 ? (0, react_jsx_runtime.jsx)(ModelReasoningEfforts, {
557
+ model,
558
+ levels: props.reasoningLevels,
559
+ position,
316
560
  disabled,
317
- onChange: (event) => {
318
- props[field].onChange(event.target.value);
319
- },
320
- onBlur: props[field].onBlur
321
- })]
322
- }, field)), (0, react_jsx_runtime.jsx)(ModelInputTypes, {
323
- model,
324
- field: props.inputField,
325
- position,
326
- fallback: props.inputFallback,
327
- disabled: disabled || props.inputLoading === true,
328
- t,
329
- onChange: props.onChange
330
- })]
561
+ t,
562
+ onChange: props.onChange
563
+ }) : null
564
+ ]
331
565
  }) : null]
332
566
  });
333
567
  }
@@ -824,6 +1058,7 @@ window.__ModuleLoader__.load({
824
1058
  inputField: "input",
825
1059
  inputFallback: inputDefaults.get(textOf(model, "id")) ?? props.defaultInput,
826
1060
  inputLoading: catalogProvider !== void 0 && catalog === void 0,
1061
+ reasoningLevels: props.reasoningLevels,
827
1062
  expanded: expanded.has(index),
828
1063
  disabled,
829
1064
  t,
@@ -1014,6 +1249,26 @@ window.__ModuleLoader__.load({
1014
1249
  if (list?.type !== "union" || list.list === void 0) return [];
1015
1250
  return list.list.map((entry) => entry.value).filter((value) => typeof value === "string");
1016
1251
  }
1252
+ /**
1253
+ * The reasoning levels a hand-declared route's models may declare, read out of
1254
+ * the owning namespace's own schema for the same reason as
1255
+ * {@link protocolChoices}: the levels the page offers and the keys the
1256
+ * adapter accepts in `reasoningEfforts` come from the same `Config`.
1257
+ * @param namespace - the namespace view whose schema declares the profile shape.
1258
+ * @param schema - settings schema operations.
1259
+ * @returns the levels in the schema's escalation order, or an empty list when the schema has none.
1260
+ */
1261
+ function reasoningLevelChoices(namespace, schema) {
1262
+ if (namespace === void 0) return [];
1263
+ const path = [
1264
+ "providers",
1265
+ PROBE_ROUTE,
1266
+ "models",
1267
+ "0",
1268
+ "reasoningEfforts"
1269
+ ];
1270
+ return (((schema.nodeAtPath(schema.rehydrate(namespace.schema), path)?.list ?? []).find((branch) => branch.type === "dict")?.sKey)?.list ?? []).map((entry) => entry.value).filter((value) => typeof value === "string");
1271
+ }
1017
1272
  /** The credential reference a resolved profile names (its `apiKeyEnv` field). */
1018
1273
  function apiKeyEnvOf(namespace, path, schema) {
1019
1274
  if (namespace === void 0) return void 0;
@@ -1269,11 +1524,12 @@ window.__ModuleLoader__.load({
1269
1524
  * and at least one model — are required here rather than at load, so the
1270
1525
  * failure names the field while the user is still looking at it.
1271
1526
  *
1272
- * There is deliberately no reasoning-effort control, here or on the editor
1273
- * card: effort is a per-MODEL capability, and the models under one provider
1274
- * disagree about it, so a provider-scoped control can only be set to a value
1275
- * some of them reject. The composer's model picker offers each model its own
1276
- * levels instead.
1527
+ * There is deliberately no provider-scoped reasoning-effort control, here or
1528
+ * on the editor card: effort is a per-MODEL capability, and the models under
1529
+ * one provider disagree about it, so a provider-scoped control can only be set
1530
+ * to a value some of them reject. Each model row of a hand-declared route
1531
+ * declares its own `reasoningEfforts` instead, and the composer's model picker
1532
+ * offers each model those levels.
1277
1533
  */
1278
1534
  /** The settings namespace a hand-declared provider is written into. */
1279
1535
  const NS$1 = "llm-pi-ai";
@@ -1300,7 +1556,7 @@ window.__ModuleLoader__.load({
1300
1556
  * @returns the creation card.
1301
1557
  */
1302
1558
  function CustomProviderCard(props) {
1303
- const { taken, protocols, operations, t, onBusyChange } = props;
1559
+ const { taken, protocols, reasoningLevels, operations, t, onBusyChange } = props;
1304
1560
  const [openedAt] = (0, react.useState)(() => props.revision);
1305
1561
  const [route, setRoute] = (0, react.useState)("");
1306
1562
  const [displayName, setDisplayName] = (0, react.useState)("");
@@ -1331,7 +1587,7 @@ window.__ModuleLoader__.load({
1331
1587
  const routeTaken = taken.includes(route);
1332
1588
  const normalizedBaseURL = baseURL.trim();
1333
1589
  const baseUrlInvalid = baseURL.length > 0 && !isHttpUrl(normalizedBaseURL);
1334
- const modelFailure = validateDeepSeekModels(models);
1590
+ const modelFailure = validateDeepSeekModels(models) ?? validateReasoningEfforts(models);
1335
1591
  const keyFailure = apiKeyFailure(keyDraft);
1336
1592
  const keyValue = keyDraft.trim();
1337
1593
  const ready = route.length > 0 && !routeInvalid && !routeTaken && normalizedBaseURL.length > 0 && !baseUrlInvalid && models.length > 0 && modelFailure === void 0 && keyFailure === void 0;
@@ -1486,6 +1742,7 @@ window.__ModuleLoader__.load({
1486
1742
  (0, react_jsx_runtime.jsx)(ModelListEditor, {
1487
1743
  models,
1488
1744
  onChange: setModels,
1745
+ reasoningLevels,
1489
1746
  probe: {
1490
1747
  settingsNs: NS$1,
1491
1748
  baseURL: normalizedBaseURL,
@@ -1636,7 +1893,15 @@ window.__ModuleLoader__.load({
1636
1893
  const value = next === void 0 || next.trim().length === 0 ? void 0 : next;
1637
1894
  setDraft((current) => value === void 0 ? schema.deletePath(current, [key]) : schema.setPath(current, [key], value));
1638
1895
  };
1639
- const modelFailure = validateDeepSeekModels(schema.getPath(draft, ["models"]));
1896
+ const reasoningLevels = (0, react.useMemo)(() => layout === "pi-ai" && props.declared === true ? reasoningLevelChoices(namespace, schema) : [], [
1897
+ layout,
1898
+ props.declared,
1899
+ namespace,
1900
+ schema
1901
+ ]);
1902
+ const editsReasoning = reasoningLevels.length > 0;
1903
+ const validateModels = (value) => validateDeepSeekModels(value) ?? (editsReasoning ? validateReasoningEfforts(value) : void 0);
1904
+ const modelFailure = validateModels(schema.getPath(draft, ["models"]));
1640
1905
  const keyFailure = apiKeyFailure(keyDraft);
1641
1906
  const keyValue = keyDraft.trim();
1642
1907
  const shownKeyFailure = (props.credentialRequired === true && keyDraft.length > 0 && keyValue.length === 0 ? "keyRequired" : void 0) ?? keyFailure;
@@ -1659,7 +1924,7 @@ window.__ModuleLoader__.load({
1659
1924
  const ns = namespace.ns;
1660
1925
  const next = layout === "pi-ai" && stringAt(draft, "apiKeyEnv") === void 0 && stringAt(fallback, "apiKeyEnv") === void 0 && keyValue.length > 0 ? schema.setPath(draft, ["apiKeyEnv"], keyRef) : draft;
1661
1926
  if (props.credentialOnly !== true) {
1662
- const failure = validateDeepSeekModels(schema.getPath(next, ["models"]));
1927
+ const failure = validateModels(schema.getPath(next, ["models"]));
1663
1928
  /* v8 ignore next 3 -- unreachable from the card: the same failure disables submit */
1664
1929
  if (failure !== void 0) return `${t("model")} ${String(failure.index + 1)}: ${t(failure.key)}`;
1665
1930
  }
@@ -1888,6 +2153,7 @@ window.__ModuleLoader__.load({
1888
2153
  }) : (0, react_jsx_runtime.jsx)(ModelListEditor, {
1889
2154
  ...catalogProps,
1890
2155
  catalogProvider: props.declared === true ? void 0 : props.provider,
2156
+ reasoningLevels,
1891
2157
  defaultInput: Array.isArray(defaultInput) ? defaultInput : void 0,
1892
2158
  probe,
1893
2159
  probeBlocked: keyFailure,
@@ -2200,6 +2466,7 @@ window.__ModuleLoader__.load({
2200
2466
  });
2201
2467
  const piAi = state.namespaces.get("llm-pi-ai");
2202
2468
  const protocols = protocolChoices(piAi, schema);
2469
+ const reasoningLevels = reasoningLevelChoices(piAi, schema);
2203
2470
  const catalogOffered = configurable.length > 0;
2204
2471
  const catalogEnabled = addable.length > 0;
2205
2472
  const customOffered = piAi !== void 0;
@@ -2473,6 +2740,7 @@ window.__ModuleLoader__.load({
2473
2740
  children: (0, react_jsx_runtime.jsx)(CustomProviderCard, {
2474
2741
  taken: state.rows.map((row) => row.entry.provider),
2475
2742
  protocols,
2743
+ reasoningLevels,
2476
2744
  revision: piAi.revision,
2477
2745
  operations,
2478
2746
  t,
@@ -3028,6 +3296,16 @@ window.__ModuleLoader__.load({
3028
3296
  modelInputTypes: "Input types",
3029
3297
  modelInputText: "Text",
3030
3298
  modelInputImage: "Image",
3299
+ modelReasoning: "Reasoning levels",
3300
+ modelReasoningLevel: "Level",
3301
+ modelReasoningValue: "API value",
3302
+ modelReasoningHelp: "About reasoning levels",
3303
+ modelReasoningHelpText: "The API value is sent in the request's reasoning-effort field, such as OpenAI's reasoning_effort. It defaults to the level name; where the model provider's API documentation specifies a different value, follow it. Off turns reasoning off and needs no value; a value already written in the configuration is kept and sent.",
3304
+ modelReasoningNeedsLevel: "Add at least one level besides Off, or turn reasoning levels off.",
3305
+ modelReasoningValueRequired: "Every level besides Off needs an API value, and no value can be empty.",
3306
+ modelReasoningAdd: "Add level",
3307
+ modelReasoningRemove: "Delete level",
3308
+ modelReasoningOffValuePlaceholder: "Turns reasoning off",
3031
3309
  addModel: "Add model",
3032
3310
  removeModel: "Delete model",
3033
3311
  modelsEmpty: "No models will be shown in the selector. Unlisted IDs can still be sent directly.",
@@ -3155,6 +3433,16 @@ window.__ModuleLoader__.load({
3155
3433
  modelInputTypes: "输入类型",
3156
3434
  modelInputText: "文本",
3157
3435
  modelInputImage: "图片",
3436
+ modelReasoning: "推理等级",
3437
+ modelReasoningLevel: "档位",
3438
+ modelReasoningValue: "API 参数值",
3439
+ modelReasoningHelp: "推理等级说明",
3440
+ modelReasoningHelpText: "API 参数值会写入请求的推理强度字段(如 OpenAI 的 reasoning_effort),默认与档位名相同;如有不同,以模型提供商的 API 文档为准。Off 表示关闭思考,不需要填写参数值;配置中已写入的值会保留并发送。",
3441
+ modelReasoningNeedsLevel: "至少添加一个 Off 以外的档位;不需要思考请关闭推理等级。",
3442
+ modelReasoningValueRequired: "Off 以外的档位都需要填写 API 参数值,且参数值不能为空字符串。",
3443
+ modelReasoningAdd: "添加档位",
3444
+ modelReasoningRemove: "删除档位",
3445
+ modelReasoningOffValuePlaceholder: "关闭思考",
3158
3446
  addModel: "添加模型",
3159
3447
  removeModel: "删除模型",
3160
3448
  modelsEmpty: "模型选择器中将不显示任何模型;目录外 ID 仍可直接发送。",
@@ -17,11 +17,12 @@
17
17
  * and at least one model — are required here rather than at load, so the
18
18
  * failure names the field while the user is still looking at it.
19
19
  *
20
- * There is deliberately no reasoning-effort control, here or on the editor
21
- * card: effort is a per-MODEL capability, and the models under one provider
22
- * disagree about it, so a provider-scoped control can only be set to a value
23
- * some of them reject. The composer's model picker offers each model its own
24
- * levels instead.
20
+ * There is deliberately no provider-scoped reasoning-effort control, here or
21
+ * on the editor card: effort is a per-MODEL capability, and the models under
22
+ * one provider disagree about it, so a provider-scoped control can only be set
23
+ * to a value some of them reject. Each model row of a hand-declared route
24
+ * declares its own `reasoningEfforts` instead, and the composer's model picker
25
+ * offers each model those levels.
25
26
  */
26
27
  import type { ReactNode } from 'react';
27
28
  import type { ModelsOperations } from './operations.ts';
@@ -32,6 +33,8 @@ export interface CustomProviderCardProps {
32
33
  taken: readonly string[];
33
34
  /** Wire protocols the adapter can serve, in the order it reports them. */
34
35
  protocols: readonly string[];
36
+ /** Reasoning levels the adapter's schema accepts, in escalation order; empty shows no reasoning control. */
37
+ reasoningLevels: readonly string[];
35
38
  /**
36
39
  * Revision of the `llm-pi-ai` user section this card opened at, sent with
37
40
  * the create so a route another tab declared meanwhile is a refusal rather
@@ -45,6 +45,12 @@ export interface ModelListEditorProps {
45
45
  models: readonly ModelDraft[];
46
46
  /** Installed provider whose catalog supplies defaults without endpoint I/O. */
47
47
  catalogProvider?: string | undefined;
48
+ /**
49
+ * Reasoning levels a hand-declared route's rows may declare in
50
+ * `reasoningEfforts`, in escalation order; absent or empty shows no
51
+ * reasoning control, leaving a catalog model's levels to the catalog.
52
+ */
53
+ reasoningLevels?: readonly string[] | undefined;
48
54
  /** Route input types for models absent from the installed catalog. */
49
55
  defaultInput?: readonly string[] | undefined;
50
56
  /** Whether the user layer currently owns the whole array; absent on a create. */
@@ -0,0 +1,43 @@
1
+ /** Reasoning-level declarations for a model on a hand-declared pi-ai route. */
2
+ import type { ReactNode } from 'react';
3
+ import type { DeepSeekModelDraft } from './DeepSeekModelsEditor.tsx';
4
+ import type { ModelsKey } from './locales.ts';
5
+ /** A reasoning-level failure the Models cards name by row. */
6
+ export interface ReasoningEffortsFailure {
7
+ /** Zero-based model position. */
8
+ index: number;
9
+ /** Message key owned by the Models settings section. */
10
+ key: 'modelReasoningValueRequired' | 'modelReasoningNeedsLevel';
11
+ }
12
+ /**
13
+ * Mirror the pi-ai refusals of a hand-declared `reasoningEfforts`, so a card
14
+ * names the row instead of failing at the write.
15
+ * @param value - drafted `models` value, or `undefined` while inherited.
16
+ * @returns the first refused row, or `undefined` when the adapter will accept every row.
17
+ */
18
+ export declare function validateReasoningEfforts(value: unknown): ReasoningEffortsFailure | undefined;
19
+ /** Props of {@link ModelReasoningEfforts}. */
20
+ interface ModelReasoningEffortsProps {
21
+ /** Effective model row, including fields outside the curated editor. */
22
+ model: DeepSeekModelDraft;
23
+ /** Levels the adapter's schema accepts as `reasoningEfforts` keys, in escalation order. */
24
+ levels: readonly string[];
25
+ /** One-based row position for accessible names. */
26
+ position: number;
27
+ /** Prevent changes while read-only or saving. */
28
+ disabled: boolean;
29
+ /** Section copy. */
30
+ t: (key: ModelsKey) => string;
31
+ /** Replace this row, preserving unrelated configuration. */
32
+ onChange: (model: DeepSeekModelDraft) => void;
33
+ }
34
+ /**
35
+ * Edit the `reasoningEfforts` a hand-declared pi-ai model offers: a switch,
36
+ * then one row per level pairing the level with the value sent for it. Every
37
+ * row is read from the model, so a reused instance shows its new model.
38
+ * @param props - model declaration and row replacement action.
39
+ * @returns the reasoning-level group.
40
+ */
41
+ export declare function ModelReasoningEfforts({ model, levels, position, disabled, t, onChange }: ModelReasoningEffortsProps): ReactNode;
42
+ export {};
43
+ //# sourceMappingURL=ModelReasoningEfforts.d.ts.map
@@ -16,6 +16,8 @@ interface ModelRowProps {
16
16
  inputField: 'inputModalities' | 'input';
17
17
  inputFallback?: readonly string[] | undefined;
18
18
  inputLoading?: boolean;
19
+ /** Levels the row's `reasoningEfforts` may declare; absent or empty shows no reasoning control. */
20
+ reasoningLevels?: readonly string[] | undefined;
19
21
  expanded: boolean;
20
22
  disabled: boolean;
21
23
  t: (key: ModelsKey) => string;
@@ -71,6 +71,16 @@ export declare const en: {
71
71
  modelInputTypes: string;
72
72
  modelInputText: string;
73
73
  modelInputImage: string;
74
+ modelReasoning: string;
75
+ modelReasoningLevel: string;
76
+ modelReasoningValue: string;
77
+ modelReasoningHelp: string;
78
+ modelReasoningHelpText: string;
79
+ modelReasoningNeedsLevel: string;
80
+ modelReasoningValueRequired: string;
81
+ modelReasoningAdd: string;
82
+ modelReasoningRemove: string;
83
+ modelReasoningOffValuePlaceholder: string;
74
84
  addModel: string;
75
85
  removeModel: string;
76
86
  modelsEmpty: string;
@@ -87,6 +87,16 @@ export declare function deriveKeyRef(provider: string): string;
87
87
  * @returns the protocol identifiers, or an empty list when the schema has none.
88
88
  */
89
89
  export declare function protocolChoices(namespace: SettingsNamespaceView | undefined, schema: SettingsSchemaOperations): string[];
90
+ /**
91
+ * The reasoning levels a hand-declared route's models may declare, read out of
92
+ * the owning namespace's own schema for the same reason as
93
+ * {@link protocolChoices}: the levels the page offers and the keys the
94
+ * adapter accepts in `reasoningEfforts` come from the same `Config`.
95
+ * @param namespace - the namespace view whose schema declares the profile shape.
96
+ * @param schema - settings schema operations.
97
+ * @returns the levels in the schema's escalation order, or an empty list when the schema has none.
98
+ */
99
+ export declare function reasoningLevelChoices(namespace: SettingsNamespaceView | undefined, schema: SettingsSchemaOperations): string[];
90
100
  /** The models settings page controller (one per settings surface). */
91
101
  export declare class ModelsSettingsStore {
92
102
  private readonly ctx;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-settings-models",
3
3
  "description": "Models settings and shared product-onboarding dialogs over existing settings and credential joins",
4
- "version": "0.2.1-alpha.3",
4
+ "version": "0.2.1-alpha.4",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -41,20 +41,20 @@
41
41
  "@deepseek-ai/cordis": "~4.0.5-alpha.2"
42
42
  },
43
43
  "devDependencies": {
44
- "@deepseek-ai/dsh-host-webserver": "0.2.1-alpha.3",
45
- "@deepseek-ai/dsh-api-remotes": "0.2.1-alpha.3",
46
- "@deepseek-ai/dsh-client-locale": "0.2.1-alpha.3",
47
- "@deepseek-ai/dsh-client-store": "0.2.1-alpha.3",
48
- "@deepseek-ai/dsh-client-test-runtime": "0.2.1-alpha.3",
49
- "@deepseek-ai/dsh-client-ui-primitives": "0.2.1-alpha.3",
50
- "@deepseek-ai/dsh-client-ui-settings": "0.2.1-alpha.3",
51
- "@deepseek-ai/dsh-client-ui-slots": "0.2.1-alpha.3",
44
+ "@deepseek-ai/dsh-host-webserver": "0.2.1-alpha.4",
45
+ "@deepseek-ai/dsh-api-remotes": "0.2.1-alpha.4",
46
+ "@deepseek-ai/dsh-client-locale": "0.2.1-alpha.4",
47
+ "@deepseek-ai/dsh-client-store": "0.2.1-alpha.4",
48
+ "@deepseek-ai/dsh-client-test-runtime": "0.2.1-alpha.4",
49
+ "@deepseek-ai/dsh-client-ui-primitives": "0.2.1-alpha.4",
50
+ "@deepseek-ai/dsh-client-ui-settings": "0.2.1-alpha.4",
51
+ "@deepseek-ai/dsh-client-ui-slots": "0.2.1-alpha.4",
52
52
  "@deepseek-ai/cordis": "~4.0.5-alpha.2",
53
53
  "react": "^18.2.0",
54
- "@deepseek-ai/dsh-client-ui-renderer": "0.2.1-alpha.3",
55
- "@deepseek-ai/dsh-util-values": "0.2.1-alpha.3",
56
- "@deepseek-ai/dsh-remote-mock": "0.2.1-alpha.3",
57
- "@deepseek-ai/dsh-client-product-analytics": "0.2.1-alpha.3"
54
+ "@deepseek-ai/dsh-client-ui-renderer": "0.2.1-alpha.4",
55
+ "@deepseek-ai/dsh-util-values": "0.2.1-alpha.4",
56
+ "@deepseek-ai/dsh-remote-mock": "0.2.1-alpha.4",
57
+ "@deepseek-ai/dsh-client-product-analytics": "0.2.1-alpha.4"
58
58
  },
59
59
  "dependencies": {
60
60
  "@deepseek-ai/schemastery": "~3.18.5-alpha.2"