Skip to content

dsh-plugin-model-tuning

Verified

dsh-plugin-model-tuning · v2.0.2 · MIT · Web UI

「设置 → 模型调参」页:模型参数(推理档位/上下文/最大输出/输入模态)、「用途」页(标题生成独立路由,替换宿主标题行)、「绘图」页(绘图模型配置 + generate_image 工具 + 直接绘图面板)。v2 合并了原 dsh-plugin-title-model 与 dsh-plugin-image-generation。

Install

dsh plugin add dsh-plugin-model-tuning

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Published to npm without a public repository. Inspect the package contents before installing.

Tags

Readme

dsh-plugin-model-tuning

「设置 → 模型调参」页面插件。页面分为**「模型参数」「用途」「绘图」**三个页签。

v2.0 合并说明:本插件现已并入原 dsh-plugin-title-model(标题生成独立路由) 与原 dsh-plugin-image-generation(绘图模型配置、generate_image 工具、直接绘图面板)。 三个曾经的包现在是一个包、一个设置命名空间(model-tuning)、一个 Config schema。 原 title-model 与 image-generation 两个包已从本仓库移除;合并动机与决策记录见文末。

功能

模型参数

  • 按 provider 分组展示当前模型目录;
  • 为每个模型配置:
    • 可选推理档位(仅 llm-pi-ai 可编辑,其他 provider 只读展示);
    • 上下文窗口;
    • 最大输出 tokens;
    • 输入模态三态开关(见下);
    • 系统提示角色兼容开关:自动 / system / developer(仅 llm-pi-ai, 写入模型条目的 compat.supportsDeveloperRole;遇到 kimi/DeepSeek 等 报 role 'developer' is not allowed 时选 system);
    • 思考格式兼容开关:自动 / 自适应 thinking(仅 llm-pi-ai 且路由 api: anthropic-messages, 写入模型条目的 compat.forceAdaptiveThinking;见下节「Anthropic 思考格式」。 该模型一个推理档位都没勾选时不显示——那时请求里根本没有 thinking);
    • 生图模型勾选(把该模型写入「绘图」页签的模型列表,见「绘图页签」);
  • 顶部搜索框按模型名 / ID / 描述 / provider 过滤;
  • 修改即时保存(remote.settings.mutate;模型目录来自 remote.session.modelCatalog())。

用途(标题生成)

按「用途」组织的配置。目前含标题生成一项:

  • 默认继承当前会话已记录的 provider / model(与内置标题生成一致);
  • 也可指定独立的 provider / model,选项全部来自真实模型目录(不提供自由输入框,避免臆造 ID);
  • 标题请求预算也在本页配置(目标词数 / 汉字数、输入字节上限、输出 token 上限、超时): 这些字段是 volatile 配置,写入后按需覆盖插件行的默认值;清空某一项即回到默认;
  • 每次生成都重新读取路由与预算,保存后下一次标题生成即生效,不需要重启宿主;
  • 生效范围是本 profile 之后生成的标题;已有标题、手动重命名、会话的对话模型都不受影响;
  • 不提供推理档位控件:标题策略不向标题请求传递 reasoningEffort,加一个不生效的控件会误导。 但这意味着是否思考由所选模型自己决定——推理模型会先思考再作答,需要更大的 「输出上限」(例如 1024)才能在预算内产出标题;直接作答的模型用默认的 64 就够。 例外:内置 DeepSeek 适配器对 purpose === "session-title" 强制关闭思考 (dsh-llm-deepseek 里 options.purpose === "session-title" ? "off"),所以 deepseek-official 的模型在默认预算下即可直接出标题;
  • 「宿主行未激活」提示只在标题 provider 注册失败(插件半区未启动)时出现。

绘图页签

独立配置绘图模型、generate_image / list_image_models 工具与「直接绘图」面板。 绘图模型不进入聊天模型列表,不改动现有会话存储或对话 provider。

  • 添加模型:填供应商 API 地址(默认 https://api.openai.com/v1)与 API Key, 点「获取模型列表」搜索并选择(只请求目录、不生成、不保存草稿 Key;模型 ID 可手填);
  • 也可在「模型参数」页勾选某模型的「生图模型」,自动按该 provider 的 baseURL + /images/generations 与 apiKeyEnv 推导出一条配置(带 source 溯源标记);
  • 首个模型自动成为默认项;尺寸、质量、超时等高级参数按模型配置;
  • 直接绘图面板:短请求启动任务 + 轮询状态 + 取消,不经过聊天模型;
  • 协议:OpenAI Images(POST,解析 data[].b64_json 或 data[].url,1–4 张)。

配置结构(单一命名空间)

profile 条目 id model-tuning 的 Config 字段(volatile,热更新即时生效):

- id: model-tuning
  name: dsh-plugin-model-tuning
  config:
    # ── 绘图(原 image-generation 命名空间字段原样保留) ──
    models:
      - id: my-art
        name: 我的绘图模型
        model: your-model-id
        api: openai-images
        endpoint: https://api.openai.com/v1/images/generations
        apiKeyEnv: IMAGE_API_KEY
        timeoutSeconds: 300
    defaultModel: my-art
    # ── 标题生成(原 title-model 的 mode/provider/model 加 title 前缀) ──
    titleMode: inherit        # inherit | custom
    # titleProvider: tofu-gpt # custom 时必填
    # titleModel: gpt-5       # custom 时必填
    # ── 标题预算(volatile:可在「用途」页改;这里是未改时的默认值) ──
    targetWords: 5
    targetCjkCharacters: 10
    maxInputBytes: 4096
    maxOutputTokens: 64
    timeoutMs: 60000

它替换了什么(标题行)

宿主自带一行标题生成 provider(@deepseek-ai/dsh-session-title-first-prompt-llm)。 ctx.sessionTitle.register() 只允许一个 provider,第二次注册直接抛错,所以本包的 cordis.patch.yml 显式替换该行:

- id: session-title-llm
  disabled: true          # 必须先摘掉原行,否则 boot 时重复注册会失败
- insert:
    - id: model-tuning
      name: dsh-plugin-model-tuning
      config: { targetWords: 5, targetCjkCharacters: 10, maxInputBytes: 4096, maxOutputTokens: 64, timeoutMs: 60000 }
  • 顺序即语义:disabled 必须排在 insert 之前,否则两行会同时注册。
  • config 与 base 的数值逐项相同:未在「用途」页改动预算时,删掉本包的 config: 块也不改变标题行为; 页面写入的用户层值会覆盖这些默认值。
  • 注册时沿用原 provider id(session-title-llm)与原 cadence(first-prompt),会话日志里的标题归因不变。
  • 标题请求的提示词组装、取消与结果校验沿用宿主共享策略 (@deepseek-ai/dsh-session-title-llm 的 generateSessionTitleWithLlm); 本插件决定路由与预算(提示词目标词数 / 汉字数、输入字节上限、输出 token 上限、超时)。
  • 卸载本 bundle 会同时移除 disabled 与 insert 两条 patch,宿主自带 provider 自动恢复。

模块解析(重要)

@deepseek-ai/dsh-session-title-llm 由运行本插件的那个部署提供,而不是从本包目录解析。 原因:本包在工作区里是 link: 依赖,若在包内安装该依赖,会连带把 @deepseek-ai/dsh-llm 等 peer 装成与部署不同的版本,形成重复模块图。index.js 用宿主自身入口锚点 createRequire(process.argv[1]) 定位并动态 import() 该包。

输入模态

harness 的请求模态是闭合集合 text | image:dsh-llm 的 ModelModalityMap 与 dsh-llm-pi-ai 的 MODALITY_GATE 都只有这两个成员。视频没有配置项可开。

选项 写入值 生效语义
自动 删除该字段 交由模型条目 → 已安装目录条目 → 路由 defaultInput(默认 [text])解析。空数组 [] 与「不存在」等价。
纯文本 ["text"] 显式声明。附图在发送前被拒,read_image 同样拒绝。
文本 + 图像 ["text", "image"] 贴图、read_image、MCP 结果图全部可用。
  • 字段名按 namespace 区分:llm-pi-ai 写模型条目的 input,llm-deepseek 写 inputModalities;
  • 内置目录 provider 走 modelOverrides.<id>.input;
  • llm-deepseek 从「文本 + 图像」切走会连 imagePixelBudget / imageMaxBytes / imageDetail 一起删除;
  • 保存即生效,无需重启 dsh web。

注意:网关是否真收图像无法从配置读出。开「文本 + 图像」前建议先对端点实测一次。 「系统提示角色」开关依赖 DSH 适配层 dsh-llm-pi-ai 透传 compat.supportsDeveloperRole (0.1.0-rc.6 默认不透传,需按本仓库 deepseek-harness 的 catalog.ts 补丁同步,或等上游合入)。

Anthropic 思考格式(自适应 vs 预算式)

「模型参数」页的思考格式开关解决的是这一条报错:

400 {"error":{"message":"claude-opus-5-5 requires adaptive thinking; omit thinking or use
thinking.type=adaptive and output_config.effort","type":"invalid_request_error"}}
  • 原因:pi-ai 只有在模型的 compat.forceAdaptiveThinking === true 时才发 thinking: {type:"adaptive"} + output_config: {effort:"…"};否则发旧的预算式 thinking: {type:"enabled", budget_tokens:N}。只接受自适应思考的模型(Claude Opus 4.6 及更新、 Sonnet 5、Fable 5、Kimi K3 等)会直接 400 拒掉预算式请求。
  • 为什么手写路由会中招:pi-ai 内置目录给它自己带的模型 ID 标了这个标记, 而 dsh-llm-pi-ai 只在「模型 ID 命中该 provider 自己的目录条目」时才继承目录 compat (resolveModelCompat 要求 base.api === api)。手写路由(例如 providers.ironman-claude 这种目录里不存在的 key)没有任何目录条目可继承,claude-opus-5-5 这类目录里其实认识、 但挂在 anthropic 名下的 ID 也一样继承不到——于是请求退化成预算式。
  • 怎么修:在该模型的「思考格式」里选自适应 thinking,页面把 compat.forceAdaptiveThinking: true 写进模型条目(与 系统提示角色 同一套写法), 下一次请求即生效,无需重启。Claude 4.5 及更早只认预算式,保持「自动」。
  • 另一条路(不动插件):把这条路由改成目录里已有的 key(例如 providers.anthropic 只覆盖 baseURL / apiKeyEnv,用 models: 收窄到要用的几个 ID),模型 ID 因此命中目录条目, 自适应标记、supportsTemperature 等兼容位都会自动继承。代价是 provider ID 变了, 老会话里记录的路由名会对不上,所以通常仍是逐个模型打勾更省事。

已知边界:思考强度选 off 时,dsh-llm-pi-ai 会把声明里 off: null 的那一项移出 thinkingLevelMap,pi-ai 因此发 thinking: {type:"disabled"}(只有把 off 钉成 null 才会 整个省略该参数——那是内置目录条目的写法),本开关管不到这一段。若网关连 disabled 也拒绝, 把该模型的「可选推理档位」全部取消(写成 reasoningEfforts: false,即视为无推理能力), 请求里就完全不会出现 thinking。

耦合点:字段名 forceAdaptiveThinking 与「只在该协议的白名单内」都由上游决定 ——dsh-llm-pi-ai 的 anthropic-messages compat 门(0.1.7-alpha.2 起已含该字段, 884f7b9c41 于 2026-08-18 引入)会拒收白名单外的字段,pi-ai 的 dist/api/anthropic-messages.js 是唯一读它的地方。tests/adaptive-thinking.test.mjs 在有 DSH_TEST_DEPLOY_ROOT 时直接问该部署的 pi-ai 要请求体,上游改名会在这里失败而不是在会话里变成 400。

与 DSH 版本的耦合点(0.1.7-alpha.2)

  • provider 目录必须取 llm.listConfigurableProviders()(返回 LlmConfigurableProvider 含 settingsNs / settingsPath / declared)。llm.listProviders() 返回的 LlmProviderInfo { id, name } 没有这些字段,用错会让整页控件被禁用。
  • 绘图 Remote 面板走 ctx.connection.rpc.call('/api', 'imageGeneration/<method>', { args }) (浏览器半区)× TypertRemoteService(宿主半区)。
  • 标题 helper @deepseek-ai/dsh-session-title-llm 需 >=0.1.6-alpha.2; @deepseek-ai/schemastery 需 ^3.18.4(volatile() 链式方法 3.18.4 才有)。

测试

node --test tests/*.test.mjs                      # 全部(title-provider 需要 DSH_TEST_DEPLOY_ROOT)
# 或分开:
node --test tests/title-route.test.mjs            # 标题路由解析与 Config schema(纯函数)
node --test tests/title-purpose.test.mjs          # 用途页读写规划 + locale 回归
node --test tests/image-link.test.mjs             # 生图链接写入规划(纯函数)
node --test tests/image.test.mjs                  # 绘图传输/工具/Remote(本地 HTTP,不外联)
node --test tests/adaptive-thinking.test.mjs      # 思考格式开关:白名单 + 写入规划(+ 部署耦合守卫)
DSH_TEST_DEPLOY_ROOT=<隔离部署> node --test tests/title-provider.test.mjs   # 真 helper + 假 ctx
  • title-provider.test.mjs 用假 ctx + 真共享策略(只 mock ctx.llm.stream)验证唯一注册、 继承/指定路由、首条消息选择、辅助请求记录、失败与取消路径。tests/_host.mjs 要求 DSH_TEST_DEPLOY_ROOT 指向隔离的 0.1.7-alpha.2 部署。
  • adaptive-thinking.test.mjs 的前半段是纯函数(开关只出现在 anthropic-messages 路由、 写入落在模型条目的 compat 上,目录路由落到 modelOverrides);后半段只在有 DSH_TEST_DEPLOY_ROOT 时运行,把该部署的 pi-ai 拦在假 fetch 前,直接读出请求体, 证明「打勾 → thinking.type=adaptive + output_config.effort,不打勾 → 预算式」。
  • 这些测试不是实机验证。实机验收:标题从会话日志读 session/title-llm-request 的 route 与最终 session/title 的 source.model;绘图确认「模型参数」勾选 → 绘图页出现条目 → 直接绘图面板出图;思考格式在模型下拉里选一档后看会话日志里那次请求的请求体。

安装 / 卸载

dsh plugin --profile web add "link:D:\program\dsh-plugins\dsh-plugin-model-tuning"
dsh plugin --profile web remove dsh-plugin-model-tuning

装/卸后重启 profile 生效(composition 在进程启动时组树;patch 层 live 重载只对 config 变更生效)。 卸载后:宿主自带标题 provider 恢复;绘图配置在用户 settings 里的段落成为未注册命名空间的普通文本, 不再被读取;已有图片附件与共享凭据不会被删除。

移动端

「容量字段」的「下拉框 + 自定义输入」在所有视口都上下堆叠(1.1.1 起)。

v2 合并决策记录(2026-10)

合并了什么:dsh-plugin-title-model(宿主侧标题路由)与 dsh-plugin-image-generation (绘图工具/面板)并入本包。9 包 → 7 包。

为什么:

  1. UI 早已同居。「用途」页与「生图模型」勾选本来就在本页,旧结构下同一个页面要跨三个包、 两个外部命名空间(title-model、image-generation)读写,任何一处 schema 变更都要双包同步, 还有专门的跨包契约测试守着同步——这是分布式单体,不是解耦。
  2. 卸载粒度论点不成立。单 profile 部署下不存在「只要绘图不要调参」的场景;且旧结构下卸载 image-generation 会让调参页的生图勾选静默降级,粒度本来就是假的。
  3. 数据迁移可控。绘图 models/defaultModel 字段名原样保留;标题 mode/provider/model 改为 titleMode/titleProvider/titleModel(一次性迁移 cordis.patch.yml 中的配置块)。

没合并谁:model-search(占输入栏 slot,崩溃自动回退内置菜单,独立价值明确)、 computer-use(原生运行时)、read-image-preview(通用图像卡片,刻意为任何图像工具设计)、 mcp-ui / shell-selector(功能域不同)保持独立。

迁移指引(从旧结构升级时):

  1. cordis.patch.yml 里原 - id: image-generation 块的 config: 内容(models、defaultModel) 原样搬进 - id: model-tuning 块;
  2. 原用户 settings 里 title-model: 段的 mode/provider/model 若为 custom,改写到 model-tuning: 段的 titleMode/titleProvider/titleModel;
  3. 从 profile 移除 dsh-plugin-image-generation 与 dsh-plugin-title-model 两个 bundle;
  4. 重启 profile。