Skip to content

[Bug] DeepSeek 模型下发上游不支持的 reasoning_effort(supportsReasoningEffort 排除列表漏配,所有 DeepSeek 中转渠道均受影响) #1664

Closed

Description

@wxyhaiy

What happened? / 问题描述

DeepSeek 模型(无论官方端点还是 DeepSeek 系中转站)在开启思考后,请求里会被塞入上游不接受的 reasoning_effort 字段,导致 400 / invalid request。

关键点:detectCompat() 里 supportsReasoningEffort 的排除列表漏掉了 DeepSeek,其他已知不支持的厂商(Grok / Zai / Moonshot / Together / Cloudflare AI Gateway / Nvidia / AntLing)都排除了。

// out/main/chunks/openai-completions-C0fxjjC3.js:1133
// out/main/chunks/openai-completions-BNZB_AlW.js:1154
supportsReasoningEffort: !isGrok && !isZai && !isMoonshot && !isTogether && !isCloudflareAiGateway && !isNvidia && !isAntLing,
//                         ↑ 缺 !isDeepSeek

影响面比看起来大:不只是 api.deepseek.com,所有 DeepSeek 模型中转渠道都会中招,因为 thinkingFormat 的判定是按 base_url 是否含 deepseek.com 来的:

const isDeepSeek = provider === "deepseek" || baseUrl.toLowerCase().includes("deepseek.com");

中转站(new-api / one-api 之类,base_url 是自有域名)会落到 thinkingFormat: "openai" 分支,然后命中通用的 reasoning_effort 下发分支——即使 DeepSeek 上游本身不支持这个字段。

顺带一提,这里有个不一致:deepseek 分支里 thinking 和 reasoning_effort 是分开处理的(thinking 走 params.thinking,reasoning_effort 单独受 supportsReasoningEffort 控制),说明设计上本来就打算让某些端点只开思考、不传 effort——只是 DeepSeek 漏配了。

Steps to reproduce / 复现步骤

  1. 添加一个 openai_compatible 供应商,api_style = chat_completions,base_url 指向一个 DeepSeek 系中转站(非 deepseek.com,例如 https://<relay>/v1)
  2. 模型填 deepseek-v4.1-flash(或任意 DeepSeek 模型 id)
  3. 把该模型的思考等级设为任意非 off 档位(low / medium / high),例如 thinkingLevels: ["off","low","medium","high"]
  4. 发送任意消息 → 上游拒绝 reasoning_effort

对照组:思考等级保持 off 时正常——因为 reasoningEffort 为空时不会下发该字段,所以这个 bug 会一直藏着,直到用户打开思考。

Expected behavior / 预期行为

DeepSeek 系模型不应下发 reasoning_effort;开启思考时仅通过端点自身机制生效(或按端点能力下发 thinking),不影响其他厂商在同一中转站上的行为。

Actual behavior / 实际行为

请求体中携带 reasoning_effort,上游返回 400 / invalid request。

疑似根因 / Suspected root cause(代码级,已用打包代码验证)

说明:下面是我对 0.20.1 打包后的 app.asar 做的静态分析 + 直接驱动被 patch 前后的真实 buildParams() 得到的 payload 对比。没有抓真实线上请求体(本地日志不记录该字段),所以请以维护者侧的请求日志为准。

1. 判定漏掉 DeepSeek

见上面的 supportsReasoningEffort 表达式。

2. 中转渠道走通用分支

detectCompat() 的 thinkingFormat 取值:

thinkingFormat: isDeepSeek ? "deepseek" : isZai ? "zai" : isTogether ? "together" : isAntLing ? "ant-ling" : isOpenRouter ? "openrouter" : "openai",

isDeepSeek 只看 provider 与 base_url,所以中转站拿不到 "deepseek" 分支。

3. 我做的验证

我从打包代码里抽出 detectCompat / getCompat / buildParams(brace-match 抽取 + stub 掉无关 helper),直接调用真实的 buildParams(model, ctx, { maxTokens, reasoningEffort }, getCompat(model)),观察实际 payload:

场景 修补前 修补后
deepseek-v4.1-flash + 中转站 URL(thinkingFormat: "openai") reasoning_effort: "medium" 字段不下发
官方 api.deepseek.com(thinkingFormat: "deepseek") reasoning_effort: "high" 不下发,且 thinking: {"type":"enabled"} 保留
同中转站的 glm-5.2 / grok-4.7 下发 仍下发(未被误伤)

4. 验证有效的最小改动

把 DeepSeek 识别从「只看 base_url」扩展到「也看模型 id / family」,然后加进排除列表:

const isDeepSeekModel = isDeepSeek || /deepseek/i.test(String(model.id ?? "")) || /deepseek/i.test(String(model.family ?? ""));
// ...
supportsReasoningEffort: !isDeepSeekModel && !isGrok && !isZai && !isMoonshot && !isTogether && !isCloudflareAiGateway && !isNvidia && !isAntLing,

要注意只加 !isDeepSeekModel,不要直接放宽 isDeepSeek 本身——isDeepSeek 还参与 useMaxTokens / isNonStandard / supportsStore / requiresReasoningContentOnAssistantMessages,直接改会顺带改变中转站上 max_tokens 字段名等无关的线上行为。

5. 为什么没法用配置绕过

这点也许值得记一笔,因为对用户不直观:目前没有任何 UI 或供应商配置能关掉这个字段。

  • config_json.models[] 由 bindingFromGenericModel()(out/main/index.js)解析,它只回填固定字段(contextWindow / maxTokens / nativeWebSearch …),会丢弃 compat
  • 用户供应商的目录来自 modelConfigFromModelsDev()(chunks/index-*.js),对非 models.dev 记录不设置 compat
  • modelConfigWithBinding() 只把 binding.thinkingProtocol 映射到 compat.forceAdaptiveThinking,不 gate reasoning_effort

也就是说 compat.supportsReasoningEffort 目前只能来自内置的 models.dev 条目,用户侧无法表达。建议至少放开其一(允许用户 per-model 覆盖该 compat 位),这样中转站用户不必改包。

Environment / 环境

  • App 版本:0.20.1
  • OS:Windows 11 专业版(AMD64)
  • 安装方式:NSIS 安装版(D:\Program Files\PI-Desktop,含 Uninstall PI-Desktop.exe;app-update.yml → provider: github, repo: vastsa/PI-Desktop)
  • 供应商类型:openai_compatible + api_style = chat_completions
  • 复现模型:deepseek-v4.1-flash(中转站),另见 agentrouter 渠道的 deepseek-v4-flash(thinkingLevels: ["medium","high"])同样会下发

Logs / 日志

本地日志中未记录请求体,无法附上该字段的直接证据;provider.log 只有同期的 model discovery 401。已用上面的「驱动 buildParams()」方式替代取证。

备注 / Notes

我不是在要求上游支持 reasoning_effort——恰恰相反,诉求是不要下发它。如果维护者认为更合适的做法是「按 models.dev 的 reasoning_options 决定是否下发 effort」,那也能一并覆盖 Grok/Zai 那类已经硬编码排除的厂商,可能比继续往这个列表里加名字更稳。这条由你们判断。

Activity

  1. vastsa commented on Oct 14, 2026

    @vastsa
    Owner

    感谢反馈,我们看看

  2. muzimu217 commented on Oct 14, 2026

    @muzimu217
    Contributor

    补充一条对修复有用的调研结论,供参考(如果维护者已在处理可以忽略):

    这个问题在 PI-Desktop 侧有一处现成的修复通路,不需要动 pi-ai。 核实链:

    1. pi-ai 1.1.0 openai-completions.js:1265 的排除列表确实缺 DeepSeek(:1236 的 isDeepSeek 只匹配 provider === "deepseek" 或 base_url 含 deepseek.com,所以所有中转站都落到 thinkingFormat:"openai" 的兜底分支,reasoning_effort 由 :714 下发);
    2. 但 :1317 是 model.compat.supportsReasoningEffort ?? detected.supportsReasoningEffort——模型级 compat 可以整体覆盖,且本仓库的 pi-ai patch 没有改这一行;
    3. PI-Desktop 已有现成的家族判定函数 deepseekRequestCompat(packages/shared/src/provider-presets.ts:497,按 vendorKey/URL/modelId/family 四路匹配,正是为了解决"PI-Desktop 存 UUID 作 provider 导致 pi-ai 探测不到"的同类问题),它返回的 compat 在 provider-binding.ts:408 展开进 model.compat。

    所以最小修复是给 DeepSeekRequestCompat 加 supportsReasoningEffort: false(官方端点与中转两分支都带):false 不是 undefined,在 ?? 合并下会正确压过自动探测值,阻断 :714/:624/:634/:675/:701 全部下发点;thinking 字段照常走(官方端点思考不受影响),中转站则不再发不支持的字段。不影响其他厂商在同一中转站上的行为(判定按模型家族,gpt/glm 等不命中)。

    我本地已按这个方向起草了修复 + 回归测试(官方/中转/无关模型三分支断言,先红后绿验证过)。如果维护者还没动手,我可以整理成 PR 提交;如果你们已按别的方式处理,这些分析就当佐证。

  3. vastsa commented on Oct 16, 2026

    @vastsa
    Owner

    已在 PR #1701 修复并合入 main(合并提交 )。DeepSeek 官方端点和中转模型现在会显式关闭通用 OpenAI 的 字段,同时保留原有 thinkingFormat 与 reasoning replay;回归测试已验证实际 Completions 请求不再携带该字段。

  4. vastsa commented on Oct 16, 2026

    @vastsa
    Owner

    补充说明:上条评论中的代码标记因命令行转义被去掉,正确信息如下。

    已在 PR #1701 修复并合入 main(合并提交 7403d76)。DeepSeek 官方端点和中转模型现在会显式关闭通用 OpenAI 的 reasoning_effort 字段,同时保留原有 thinkingFormat 与 reasoning replay;回归测试已验证实际 Completions 请求不再携带该字段。

  5. added a commit that references this issue on Oct 16, 2026
    9fea8a3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions