Repository navigation
Description
Description *
- Started pi with billion-context (plugin native) and the model mistral/deepseek-v4-1-flash-1017 hosted by Mistral.
- After the conversation reached the first tool call, the follow-up request carrying the assistant tool_calls message failed. Plain-text turns work.
- Saw:
Mistral API error (422): {"detail":[{"type":"extra_forbidden","loc":["body","messages",5,"assistant","reasoning_content"],"msg":"Extra inputs are not permitted","input":""}]}
Expected: the request reaches Mistral without a reasoning_content field. pi's Mistral converter never emits reasoning_content (it sends reasoning as thinking content chunks), and input: "" matches the blank value injected by normalizeStrictEchoReasoning.
Cause: isStrictReasoningEcho also returns true when the body model id matches /deepseek/i (#1027 / #1028):
function isStrictReasoningEcho(session, upstreamOrigin, model) {
if (session.metadata.strictReasoningEcho === true) return true;
if (upstreamOrigin !== void 0 && /deepseek/i.test(upstreamOrigin)) return true;
return typeof model === "string" && model.length > 0 && /deepseek/i.test(model);
}That assumes every gateway serving a DeepSeek model accepts or requires reasoning_content on assistant messages. Mistral's API uses its own message schema and forbids extra fields, so reasoning_content: "" on each assistant tool-call message makes it reject the body.
Suggested fix: an explicit per-provider/per-model opt-out that takes precedence over the static detection, for example:
{ "providers": { "https://api.mistral.ai": { "strictReasoningEcho": false } } }bili version *
0.1.209
Usage mode *
Plugin native (bili plugin install …, runs inside the host process)
Client and version *
pi 1.4.0
Operating system *
NixOS on WSL2
Upstream and model *
api.mistral.ai · deepseek-v4-1-flash-1017 · 1M (1048576)
Compression-related config (optional)
(default config, no overrides)Log excerpt (strongly recommended)
From ~/.local/state/billion-context/bili.log. The model is deepseek-v4-1-flash-1017 until 17:40:16, when the same session switches to mistral-large-4 with the same history there is no strict-echo injection and the request succeeds.
17:39:55.748 [info] strict-echo: injected blank reasoning_content on 1 assistant tool-call message(s) (#762)
17:39:55.750 [info] forward POST → https://api.mistral.ai/v1/chat/completions
17:39:56.036 [warn] ← upstream 422: {"detail":[{"type":"extra_forbidden","loc":["body","messages",5,"assistant","reasoning_content"],"msg":"Extra inputs are not permitted","input":""}]}
17:40:09.444 [info] strict-echo: injected blank reasoning_content on 1 assistant tool-call message(s) (#762)
17:40:09.446 [info] forward POST → https://api.mistral.ai/v1/chat/completions
17:40:10.260 [warn] ← upstream 422: {"detail":[{"type":"extra_forbidden","loc":["body","messages",5,"assistant","reasoning_content"],"msg":"Extra inputs are not permitted","input":""}]}
17:40:16.559 [info] [window-change] deepseek-v4-1-flash-1017/plugin/1048576 -> mistral-large-4/plugin/1048576
17:40:16.564 [info] request: 9 msgs, inbound=60.0KiB, outbound=71.4KiB, local=11ms
17:40:16.565 [info] forward POST → https://api.mistral.ai/v1/chat/completions
Activity
[-][Bug][pi] strict-echo model-id detection injects reasoning_content on Mistral API -> 422 extra_forbidden after the first tool call[/-][+]strict-echo injects reasoning_content on Mistral API for DeepSeek models -> 422 extra_forbidden[/+]wiechapeter commentedon Nov 4, 2026
Second report, different stack, same cause — confirms it isn't NixOS- or pi-version-specific.
bili 0.1.212 / pi 1.5.0 / Ubuntu 24.04, model deepseek-v4-1-flash-1017 on api.mistral.ai: every tool round 422s with extra_forbidden on ["body","messages",N,"assistant","reasoning_content"], input:"". bili's log shows strict-echo: injected blank reasoning_content … (#762) immediately before the forward. Confirmed in 0.1.212's dist: isStrictReasoningEcho's model-id /deepseek/i clause fires with upstreamOrigin=api.mistral.ai, and both self-heal learners still key on status===400, so the 422 never teaches the session (no strictReasoningEchoVeto code in 0.1.212 — before #5280).
Direct curl proof the field alone is the trigger: same body with reasoning_content:"" → 422, without → 200.
We built #5280 on top of the v0.1.212 tag and it resolves it here: first request 422s, the veto is learned, requests 2 and 3 in the same session return 200 with the correct completion. The PR's own test file passes 5/5. Two nits on the PR: mergeable: false against current master, and its Closes #5260 points at the first-byte-stall issue, not this one.
Interim workaround: a 15-line pi extension setting x-bili-plugin-bypass: 1 on before_provider_headers for the matched model (raw passthrough, verified 422→200 through the proxy). It costs compression on those requests, which is why we'd prefer #5280 merged.
+1 on that PR.
ranxianglei commentedon Nov 4, 2026
(ai: vllm-qwen/qwen3.8-27b)
感谢第二份独立报告——不同栈(Ubuntu 24.04 / bili 0.1.212 / pi 1.5.0)+ 直接 curl 对照,进一步钉死了「字段本身即触发点」的诊断。你指出的两个 nit 都已核实并处理(均独立验证过,不是照单全收):
1. Closes 指向错误 —— 已修正。 我开 PR 时用的是内部镜像编号 5260,但上游 GitHub 的编号是 5257(GitHub 上的 #5260 是另一个 first-byte-stall 条目)。已把 PR #5280 正文的引用改为
Closes #5257。2. mergeable: false —— 已解决。 冲突真实存在:master 侧 #5495 在两个学习点把 demand 正则扩到了
content[].thinking(DeepSeek Anthropic-wire 拼写),与本 PR 在同一代码块重构了 veto 分支。已把 master 合入分支并按「意图并集」解冲突——veto 分支保留在前,demand 学习器采用 #5495 的扩展正则,两侧语义都不丢。分支上此前追加的 review 修复 commit(veto 清除独立成 transition,0a3d0da0f)也完整保留。合并后本地定向验证全绿:typecheck clean;strict-echo 三件套 67/67(含 review 新增的 demand→forbid→re-demand→accept 翻转回归);wire-contract + loop-strict-echo + loop-core + reasoning-drop 共 72/72;unused-exports gate clean。已推送(merge commit 8df9308)。
当前状态:PR #5280
mergeable: true,mergeable_state: blocked= 仅剩 CI 未跑完(非冲突)。你的 pi 扩展临时绕行方案在 #5280 合入后就不需要了。合并由 owner 执行。Aruir commentedon Nov 6, 2026
第三份报告,这次来自 DSH 桌面版:DSH 0.2.2、bili 0.1.212、Windows,
api.mistral.ai上的deepseek-v4-1-flash-1017,原因相同。同一个会话换成 Mistral 上的
zai-glm-5-3后,不再注入这个字段,也没有再报 422。有一点请 #5280 确认:在 DSH 上,这个 422 让这一轮直接结束(
PI_AI_ERROR)。如果要等第一次 422 才学会不发这个字段,DSH 用户可能要手动再发一次。我没有在 DSH 上测试 #5280。希望 #5280 能合入。
ranxianglei commentedon Nov 6, 2026
(ai: vllm-qwen/qwen3.8-27b)
第三份报告(DSH 桌面版 / Windows / bili 0.1.212)与诊断完全吻合——同样的失败签名,同样的「同会话换非 deepseek 命名模型即恢复」证据。至此 pi/Linux、pi/Ubuntu、dsh/Windows 三个栈都指向同一机制。
关于你提的 DSH 代价问题——如实回答:是的,这正是设计取舍。
PI_AI_ERROR结束,需要手动重发一次。重发的请求会干净地出去并成功——因为 veto 在错误返回前就已学习并持久化(随会话元数据落盘),即使宿主在失败与重发之间重启也照样生效;此后该会话永不再注入该字段(除非出现反向的「索要字段」证据)。宿主若对 422 自动重试,则用户完全无感。PR #5280 当前状态:分支已 rebase 到当前 master(线性历史,head
15d86b429),冲突解法我已逐行核对保留完整(veto 分支 + #5495 扩展正则 + review 的独立清除 transition);CI 全绿(26 pass / 4 skip / 0 fail)。已具备合并条件,合并由 owner 执行。XelestiaIXIX commentedon Nov 10, 2026
补充一份 Windows / DSH 桌面版的复现信息:使用 Mistral 托管的
deepseek-v4-1-flash-1017,遇到了同样的问题。在
billion-context 0.1.212上,实际日志显示:更新到
0.1.213后,检查本机安装的dist/index.js,并提取其中的isStrictReasoningEcho和normalizeStrictEchoReasoning做了离线验证:传入upstreamOrigin=https://api.mistral.ai、model=deepseek-v4-1-flash-1017和一条没有reasoning_content的 assistant 工具调用消息,仍会开启 strict echo 并注入reasoning_content: ""。说明:
0.1.212有真实请求失败日志;0.1.213目前只做了上述代码检查和离线验证,尚未重新发起真实 API 请求。期待 #5280 修复合入并发布,谢谢!ranxianglei commentedon Nov 10, 2026
(ai: vllm-qwen/qwen3.8-27b)
谢谢第四份报告(Windows / DSH)。你的离线验证结果与预期完全一致,说明两点:
/deepseek/i检测与注入行为,所以按你描述的方式离线检查dist/index.js,必然看到「仍会开启 strict echo 并注入reasoning_content: ""」。这不是验证方法问题,是版本还没到。loc里的消息下标随历史长度不同(5、8)——诊断无分歧。解决路径与升级后的行为:#5280 合入 → 随下一个发版发布 → 升级到新版本后,每个受影响的会话第一次带工具调用的请求仍会失败一次(422),bili 在错误返回前就学会并持久化「该网关禁止此字段」,重发即成功,此后该会话永不再注入(宿主若对 422 自动重试则用户无感)。这是设计取舍:网关自己的 schema 判决是唯一可靠信号,不做按网关名字的硬编码名单。
当前状态:PR #5280 开启中,head
15d86b429的 CI 全绿(26 pass / 4 skip / 0 fail),可合并(相对最新 master 略有 behind,不阻塞合并)。合并与发版均为 owner 操作——我不会自行切版;如需催办,请在 #5280 上向 owner 提出。