Fix/swe family tool dialect - #252
Conversation
3126255 to
898af0f
Compare
|
Review: the premise is plausible — What I ranhead
The dialect table itself does what you say it does — I drove it: So the routing works. What it does downstream is the problem. B1 (blocker) — tool calls stop being extracted on the streaming path
Both failing tests are this: The second one matters more than its name suggests. It was added with the #241/#243
Why non-stream survives, and why that is not reassuring. It is not that the and then That is a streaming-vs-non-streaming fork on one B2 (blocker) — the prefix match is the mistake its neighbour already madeI nearly filed the failing tests as the whole finding, then checked the neighbours — if (normalizedProvider === 'moonshot' || normalizedModelKey.startsWith('kimi')) {
// The Kimi K2 vLLM dialect is verified working only against the original
// `kimi-k2` and `kimi-k2-thinking` SKUs. Newer Moonshot SKUs … are served by a
// different upstream runtime that rejects vLLM markup.
if (normalizedModelKey === 'kimi-k2' || normalizedModelKey === 'kimi-k2-thinking') return 'kimi_k2';
return 'openai_json_xml';
}The The history is sharper than the comment. On 2026-05-07, Related, from the #238 ledger entry on this same model family:
The claim I cannot check, and what would settle it// SWE-1.X use the same <|tool_call_begin|> / <|tool_call_end|> dialect as
// Kimi K2 — observed in production traffic 2026-08-07.I have no way to verify that from here, and I am not asserting it is wrong — if What would make this mergeable:
One more to think about before the next round: The dialect-per-family idea is sound and the code is in the right function. It needs the |
|
评审:方言这条观察我认为是对的,但 先说一句与你无关的:这个 PR 的 CI 从 08-07 起一直卡在 我实跑过什么head
两条失败: 第二条的措辞值得注意:原始工具标记直接当可见答案发给客户端了。 M1(blocker)—
|
| spec | 实测 master | spec 声明 | 合本 PR 后 |
|---|---|---|---|
bash-prefix-repair-boundary.json |
94 | 94 ✅ | 95 |
schema-ref-fanout-budget.json |
72 | 72 ✅ | 73 |
scripts/mutate-verify.mjs:208 在不匹配时是 die(),而测试套件不跑 spec,所以三个绿灯(你的 CI、本地 npm test、合并后门禁)都看不见这条。改 M1 时如果测试数又变,这两个数请一并按实测重量,不要从 94+1 推。
其余
方言判定放在 pickToolDialect 里、跟着 normalizedModelKey 走,位置是对的;改动只有 5 行加一条测试,范围克制。这个 PR 的价值在观察本身 —— SWE 系列跟 Kimi K2 共用 section-token 格式这件事,不看生产流量是想不到的。收窄之后我直接合。
改完 ping 我,我重跑门禁 + 那两个 spec。
- src/handlers/tool-emulation.js: constrain observed SWE routing, preserve native Kimi history continuity, and parse native plus legacy streamed forms (PR dwgx#252). - src/runtime-config.js, src/windsurf.js: suppress only contradictory tool syntax reinforcement (PR dwgx#252). - scripts/mutate-verify.mjs: read Node 22 and Node 24 TAP summary counters so the required mutation gate is portable (R2). - test/: pin stream/non-stream parity, history serialization, wire evidence, and measured mutation baselines (R2).
|
Thanks @dwgx for your review! I narrowed this back to the evidence from real SWE/Kimi traffic. The final scope is deliberately small:
I removed the unverified wrapperless-parser branch and the unrelated Node TAP reporter change, rather than broadening the PR without production evidence. Validation:
As a result, "unlimited" use of SWE model with external harness 😎 |
- src/handlers/tool-emulation.js: keep swe-1-7-lightning on openai_json_xml after the live probe showed narrative calls rather than Kimi section tokens (PR dwgx#252). - test/: pin the lightning exclusion and keep wrapperless grammar coverage bound to the actual kimi_k2 dialect.
- Remove wrapperless parser paths and generic prompt protocol detection without production evidence (PR dwgx#252). - Retain section/XML compatibility, narrow SWE routing, and Kimi history continuity. - Revert unrelated Node TAP reporter handling from this PR.
hasField21 在整个 metadata buffer 里搜 0xaa(21<<3|2 的 tag 字节)。但 Metadata 里有个既存的随机字段,任意字段 VALUE 的任意一字节都可能是 0xaa。 tag 字节只有落在字段边界上才是 tag。 实测 2000 次:86 次 plain metadata 里出现游离 0xaa = 4.3% 假失败率。 这条是 3c1a173 引入的,一直概率性通过,合并 #249/#252 后的全量恰好掀到。 不是那两个 PR 的问题。 改成 parseFields + getAllFields 判定,和紧邻的下一条测试写法一致 —— 仓库自己早就总结过「断言 wire 不变要用『字段不存在』,不能比字节」, 这条踩的是同一个坑的变体。 验证:修后连跑 30 次 0 失败;把 writeStringField(21) 改成 22,测试变红。
#252 想修 swe-* 的工具方言冲突,做了两件事: 1. windsurf.js 加 conflictsWithKimiPreamble(),在 Kimi 前导下跳过 XML 强化段 2. runtime-config.js 把 systemPrompts.toolReinforcement 默认值换成一句泛泛的 「Use them when they can help answer the request」 问题是第 1 步的检测条件就是「强化段里含 <tool_call>」,而第 2 步把这个 marker 删掉了。实测:合并后 conflictsWithKimiPreamble 在默认配置下永远返回 false,那段隔离代码永远不执行。 所以 PR 真正生效的机制是「把冲突文本从全局默认值里删干净」,不是它声称的 「按方言跳过」。而 toolReinforcement 在 windsurf.js 是无条件拼给所有带工具的 Cascade 请求的,没有方言分流 —— 代价是所有非 Kimi 方言的模型一起失去了 <tool_call> 格式示范。这不是 #252 想改的东西。 还原默认值后实测:隔离函数开始真正触发,#252 自己的 137 个测试仍然全过 —— 它要修的 SWE 问题照旧被修,只是走它设计的那条路径。 顺带补上 #252 缺的那条断言。原有测试是 setSystemPrompts 注入一段含 <tool_call> 的文本来验证抑制,证明了抑制能工作,但没证明它会被触发 —— 默认值不带 marker 时抑制静默变成 no-op,那条测试照样绿。新测试用真实默认值 跑端到端,并钉住「默认值必须含 marker」这个前提。 突变验证:重新施加 #252 的泛化默认值 -> 变红;把抑制调用短路成 false -> 变红。
台账停在 08-07 第十五轮,08-09 和 08-10 两轮的发现没进去。台账是这个仓库 记审计发现的正式载体,漏两轮等于那些教训只活在 commit message 里。 第十五轮的教训是"跨会话的数字要重测"。这一轮推进一步:重测用的工具可能 和产生错误的工具是同一个,那样重测只会确认错误。五个发现都是这一类: - 一个开关计数和执行它的守卫出自同一条正则,于是互相印证而不是印证代码。 "86/86 全有文档"两半都错,真实 156 个、47 个没文档,含全部 5 个 wire 坐标。 同一盲点在默认开台账里是第四次(前三次 a98a572 3af3f63 c374ec7)。 - 我犯了它的反面:搜环境变量名得出"没有关闭路径测试",实际测试走的是运行时 字段名。据此写完了一整个重复测试文件才发现。 - #252 的两处改动互相抵消:检测条件是"含 <tool_call>",而同一个 PR 把这个 marker 删了。一个开不了火的守卫和一个通过了的守卫看起来一模一样。 - #249 带进第五种读取形式(常量间接),守卫四种模式全看不见,绿着却少算一个。 - getuserjwt 一条测试里两个随机失败(4.3% + 3.3%)。台账第十一轮记过"不能比 字节相等",但比长度、扫单字节是同一个坑的变体,规则那样写没拦住。 判别动作那节新增六条,核心是:信任一次扫描之前先证明它能看见 —— 往真实文件 里追加已知坏样本,每种形态各一个,要求守卫逐个变红,近似合法样本保持绿。 顺带修了导航:第 19 行写死"第十一轮那张",追加后过时了。这份文件自己就记过 "写死行号会腐烂"的教训,轮次号同样会。改成说清判据是"最后一个命中"。 开头"十五轮追加"改十六。
改了什么 / What changed
pickToolDialect in src/handlers/tool-emulation.js so that every model key starting with
swe-is routed to thekimi_k2tool-call dialect.为什么 / Why
SWE-family models use the same
� ... �section-token tool-call format as Kimi K2. the new rule makes the dialect dispatch model-agnostic for the whole SWE family, preventing "invalid tool call" errors while using external harness.测试 / Testing
node --test test/tool-emulation.test.js— 54/54 pass, including the newroutes all SWE family models to Kimi K2 vLLM dialecttest.POST /v1/chat/completionswithtool_choice=requiredto every SWE model served by the local instance:Checklist