GaoHu1997/dsh-model-think-level

dsh-model-think-level

为 DeepSeek Harness 中的第三方模型设置思考等级:在输入框菜单里选择服务商与模型,在官方模型页卡片上用一个开关控制思考等级,并可配置按路由的请求头。

Uncategorizeddeepseek-harness

DSH Model Think Level

DSH 的第三方模型补齐思考等级:在官方 Models 页每个模型行注入 Thinking 开关和强度/模态编辑器(插件编辑器所在的行会隐藏官方自带的「推理等级」编辑区,同一个字段只留一份控件),在输入框模型菜单加供应商→模型选择器、视觉徽章与等级行,并带搜索、拖拽排序、供应商启用开关;以及按路由接管请求头与 user-agent。

模型选择器

供应商配置

兼容字段与已有配置

1.1.4 起不再提供 thinkingTokenBudgetField、vllmPriority、supportsMaxOutputTokens 的专用编辑控件。已有值仍由 DSH 的 pi-ai 适配器使用;修改模型的思考档位或输入模态时,插件会保留这些值和其他手动兼容配置,避免覆盖供应商的定制行为。

需要调整旧值时,先备份当前 profile 的 cordis.patch.yml(位于 DSH 数据目录的 profiles//),找到 llm-pi-ai 对应供应商、模型行的 compat,仅修改或删除所需字段,保存后重新加载该 profile:

  • thinkingTokenBudgetField:令牌预算的请求字段名,可设为 thinking_token_budget、thinking_budget 或 thinking_budget_tokens。删除后使用默认字段;启用令牌预算的模型在后续适配或保存时可能重新补入 thinking_token_budget。若端点不支持令牌预算,应将 supportsThinkingTokenBudget 设为 false。
  • vllmPriority:删除后不再发送定制的 vLLM 优先级。
  • supportsMaxOutputTokens:删除后恢复适配器默认行为;保留 false 会继续关闭对应输出令牌参数。

不要删除整个供应商配置,也不要将包含 API key 的配置文件贴到公开 issue。

多密钥与按模型分流

同一个供应商可以登记多把密钥,请求进来时按「模型 → 密钥」自动分流:

  • 密钥按列表顺序排队。一个模型如果多把密钥都能访问,先走列表靠前的那把;某把密钥对某模型请求失败(401/403/408/429/5xx)时,自动换下一把能访问该模型的密钥重试;400/404/422 这类请求本身的问题不重试,直接返回。被拒的那把密钥会记住这次失败(时长取自 Retry-After,没有就按「太频繁」60 秒、「额度耗尽」30 分钟、「连接被拒」60 秒推算,重复失败翻倍并封顶),下次请求自动排到健康密钥后面;冷却只是排序,密钥永远不会被真正丢弃——全体都在冷却时仍按原顺序尝试。
  • 密钥能访问哪些模型可自动探测(密钥面板的「探测模型」按钮会逐一读取每把密钥各自的 /models 列表并记录),也可在「模型」里手动填写覆盖。探测按「这把密钥实际会被派到哪些协议/地址」进行,见下节。
  • 未探测过的密钥视为「通吃」,可以服务任何模型;探测后只服务列表内的模型。
  • 每把密钥行首有一个圆形对勾开关:亮起表示参与路由,灰色表示暂停使用。停用只是把密钥移出分流池,别名、模型清单与协议指定全部保留,重新点亮即恢复;最后一把启用中的密钥不允许停用(否则请求会回落到适配器自身的单密钥路径,仍然用刚被停用的那把签名)。
  • 插件自身元数据保存在 DSH 数据目录的 dsh-model-think-level/key-index.json,只记录引用、别名、模型清单、协议指定与启停状态,绝不保存密钥值(密钥值始终由 DSH 凭据库保管)。
  • 新增供应商的表单也是这套卡片式密钥列表(不再是单个密钥输入框):密钥先暂存在本页,点「创建」供应商落库后自动写入,第一把自动成为启用密钥;挂载前已敲进官方输入框的值会自动收进列表第一行。刷新页面会丢弃草稿,与表单自身行为一致;若 id 撞上已存在的供应商,草稿不会写入对方。

按密钥切换协议与多地址

有些供应商同一把密钥只在某一种线上协议下可用(例如同一家既有 OpenAI Chat Completions 端点、又有 Anthropic Messages 端点)。插件支持在密钥面板里给每把密钥单独指定协议:

  • 「这个密钥可用的协议」有「自动(按模型)」「Anthropic」「Chat Completions」「Responses」「Gemini」五档,默认「自动(按模型)」——每个请求按模型自己所属的协议族挑线路:claude 系模型走 Anthropic 地址、gemini 系走 Gemini 地址(前提是供应商配置了对应族的地址),其余模型沿用该供应商自身配置的协议。
  • 给某把密钥指定协议后,命中该密钥的请求会真正换用对应的线上协议(不只是换 URL/鉴权头:请求体也按该协议重新编码),由插件在 pi-ai 适配层重新派发到 @earendil-works/pi-ai 的对应协议实现。
  • 供应商可以按「地址家族」配置最多三个基础地址:一个 OpenAI 系(Chat Completions / Responses 共用)、一个 Anthropic 系、一个 Gemini 系。供应商卡片「API 地址」下方的「更多端点」会展开当前协议家族之外的其他家族地址行。密钥指定协议后,就用与之匹配的那个地址;「自动」档的密钥按模型所属族使用对应地址,模型不属任何已配地址的族时仍使用供应商自身的 baseURL。
  • 若密钥指定的协议跨了地址家族(如 OpenAI 系 ↔ Anthropic 系 ↔ Gemini 系)而供应商没有配置该家族的地址,这条指定不会生效:请求仍用供应商自身的协议和 baseURL,只由这把密钥签名。把 OpenAI 格式的请求体发到 Anthropic/Gemini 家族的地址只会换来网关的 404/400(模型根本不会被调用),所以宁可不换。这条在每次调用时都重新判定,供应商地址补上后同一个模型立刻换用该协议的密钥。「自动」档同理:某族没有地址就回落到供应商自身的协议。
  • 自动挑错线路有记忆:某族地址对一个模型返回了「还没产生内容就失败」(典型如网关 404)后,这个 (供应商, 模型) 组合的自动解析不再挑那一族,改用供应商自身协议,直到插件进程重启;成功过的线路也会被记住并优先使用。钉选协议的密钥不受这层记忆影响——钉选表达的是用户的明确意图,其失败只说明凭据问题。
  • 两把密钥指定了不同协议(或「自动」档解析出了不同协议)时,先试哪把不再取决于列表顺序,而是按「这个模型看起来属于哪一族」排:模型名(含 anthropic/、openai/、google/gemini-… 这类前缀或裸名,gemini/gemma 开头的名字属于 Gemini 族)与密钥协议的家族一致者优先,其次是以本密钥自身地址就能直接服务的,再次是不一致但能回落到供应商主地址的,最后是跨族且没有自己地址的。请求会从排第一的密钥的协议开始——切协议意味着换请求体形状,一次只切一把密钥,失败后按此顺序换下一把。
  • 同一协议(wire)的密钥会被当作一个池:先把这个池里的密钥都用完再换 wire,因为同一把密钥失败通常与协议无关;跨 wire 的切换排在池之后,且只有在上一次失败的请求还没开始产生内容时才会做。
  • 请求进行到一半再失败(已经开始流式输出、或调用方已经主动取消)不会触发换密钥——客户端已经消费了已经发出的内容,重放只会产生重复输出。
  • 密钥面板的「模型」选择改为可点选的真实模型清单(探测得到的模型做成多选胶囊),选中的模型才交给这把密钥;下面的「全部模型」表示不做限制。该项与其它操作按钮一样只在悬停时显示。
  • 「探测模型」现在按协议探测,而不是把每把密钥都按供应商默认协议问一遍:
  • 没指定协议的密钥,分别问供应商自身协议的地址、以及供应商配置了的其他族地址(配了几族就问几族),得到的模型取并集——这正是它实际会被派到的几条线。
  • 指定了协议的密钥只问它自己那条线:同族指定仍用供应商主地址,跨族指定必须用该族的基础地址。
  • 跨族指定但供应商没配该族地址的密钥会被跳过,原有模型清单保持不变(不会因为问不到就被清空)。
  • 供应商本身的协议既不是 Chat Completions / Responses 也不是 Anthropic Messages(插件说不出的协议)时不猜地址,直接提示无法探测;除非面板里配了某一族的基础地址,那就只问配了地址的那几族。
  • 探测结果在响应里逐条返回(协议、脱敏后的地址、HTTP 状态、模型数、失败原因),每把密钥的结果互不影响:一族被拒不影响另一族把模型写进清单。
  • 模型页供应商卡片的「获取模型」走同一套合并:按上面的规则问过每把密钥实际会被派到的几条线后取并集。单个密钥也一样——只要供应商配置了多个地址(或该密钥指定了协议),获取到的就是全部地址上模型的并集,而不是只有供应商自身协议那一份;供应商卡里原配的那把密钥(从未在密钥面板列出过的)同样按「自动(按模型)」参与这条合并。

Gemini 原生协议

  • 「Gemini」档指 Google 原生协议(gemini model: generateContent / streamGenerateContent,鉴权用 x-goog-api-key)。受 DSH 宿主适配层限制,供应商卡片的「API 协议」下拉里不会有 Gemini;原生访问一律通过「更多端点里的 Gemini 地址行 + 密钥指定 Gemini」完成,供应商自身的协议保持 OpenAI/Anthropic 三选一即可(纯 Gemini 网关可把主地址指向该网关任意可答路径)。
  • Gemini 地址需包含版本段(官方为 https://generativelanguage.googleapis.com/v1beta);只填裸域名时插件会自动补 /v1beta,路径非空则按网关自己的拼写使用。列表探测走原生 GET {地址}/models?pageSize=1000,模型名会剥掉 models/ 前缀,只保留支持 generateContent 的条目。
  • 思考等级沿用模型上声明的档位:pi-ai 会把 low/medium/high 翻译成 Gemini 的 thinkingLevel(Gemini 3 / Gemma 4)或 thinkingBudget(2.5 系);在插件的 Thinking 编辑器里为 gemini 模型声明等级后原生请求即会携带。

开发与验证

使用 Node.js ^22.19.0 || >=24。依次运行 npm ci、npm run typecheck、npm test、npm run build、npm run check:package。测试最多并行 4 个 worker,以控制 DOM 测试的内存占用;仍使用 Vitest 默认超时。

check:package 检查 npm 实际打包清单中的入口、截图和声明文件依赖,CI 与发布流程都会执行。类型声明所引用的 DSH 可选 peer 由宿主开发环境提供,不会自动安装额外宿主插件。

更新模型知识表后,可运行 npm run docs:models 生成可查阅的模型列表。node scripts/wire-repro.mjs 使用当前安装的 pi-ai,向本地回显服务发送测试请求,无需供应商凭据。