Skip to content

kimi: 将被拒绝的采样参数改写为可接受的值 - #1197

Merged
yetone merged 5 commits into
yetone:mainfrom
honlu:main
Oct 10, 2026
Merged

yetone merged 5 commits into
yetone:mainfrom
honlu:main

Conversation

@honlu

@honlu honlu commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

问题
VS Code Copilot Chat 的请求硬编码 temperature: 0.1, top_p: 1。Kimi 两个平台(Moonshot 开放平台 + Kimi Code 会员端点)对部分模型在服务端锁定采样参数,请求里带任何值都会被 400 拒绝:invalid temperature: only 1 is allowed for this model(K2.5/K2.6 关闭 thinking 时只接受 0.6),整个请求在读取任何 token 前失败。

修改
kimi_schema.go 新增 kimiLockedSampling:在 forwardOnce 转发前,对锁定模型用 withoutFields 从请求体删除 temperature 和 top_p(不是改写值,缺失时也不插入)。缺省时服务端按模型和模式自己填入正确值。

主机条件 isKimi(与既有 kimiToolEnumTypes 一致):api.moonshot.ai / api.moonshot.cn / api.kimi.com / api.kimi.ai,或 moonshot* / kimi* preset 前缀。Moonshot 按量付费的 kimi-k2.5+ 同样锁参数(Cherry Studio #19601 佐证),所以不按 Kimi Code 收窄。

模型条件 kimiLockedSamplingModel(先剥 vendor/ 前缀):

锁定:kimi-for-coding*、Kimi Code 的 k3*、kimi-k2.5 及以后(kimi-k2.6、kimi-k3…)
保留用户值:moonshot-v1-*、kimi-k2-0905-preview、kimi-k2-thinking、kimi-latest,以及任何非 Kimi 主机
System One 的分类请求(temperature: 0)也走 /v1/chat/completions → forwardOnce,同锁模型同样覆盖。

测试
TestKimiLockedSampling(12 个子用例,每个独立断言):被拒的(0.1/1)和合法的(1/0.95)值都删;原本不带则不新增;Anthropic 端点同样生效;Kimi Code 的 k3、Moonshot 的 kimi-k2.5(边界)、kimi-k2.6、vendor 前缀名 moonshotai/kimi-k2.6 都删;moonshot-v1-8k、kimi-k2-0905-preview、kimi-k2-thinking 保留用户值;非 Kimi provider 原样透传。

变异测试均确认 FAIL:去掉 forwardOnce 调用挂 4 个;主机条件只认 Kimi Code 挂(kimi-k2.6);模型一律 true 挂(moonshot-v1-8k、kimi-k2-0905-preview);只删 temperature 挂;kimi-for-coding 不算锁定挂;minor >= 6 挂(kimi-k2.5);去掉裸 k 分支挂(k3);不去 vendor/ 前缀挂(moonshotai/kimi-k2.6)。

已跑
gofmt -l internal/gateway/ 无输出;go vet -tags nogui ./internal/gateway/;go build -tags nogui .
go test -tags nogui -count=1 ./internal/gateway/ ./internal/provider/ 整包通过
真实请求:本地编译 PR 二进制替换网关,VS Code Copilot 经 kimi-code-cn 发 4 条 /chat/completions(model: k3),转发 body 无 temperature/top_p,无 400
未跑
真实 Kimi 400 → 200 对照(owner 的 Kimi Code key 目前 403 access_terminated_error),以 k3 实测代替
全量 go test honlu.(改动只在 gateway 包,已跑 gateway + provider 两包)

VS Code Copilot's chat hardcodes temperature 0.1 and top_p 1, which Kimi
Code's per-model whitelist refuses with 400 "invalid temperature: only 1
is allowed for this model" before any token is read. Rewrite them to the
accepted values (1, 0.95) in the body before forwarding, and add them when
absent; a request already carrying them, or going to another provider, is
left as it was. Tested with the four cases: refused, absent, accepted,
another provider.

@yetone yetone left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感谢 PR!在 head b2bba1dd 上合到当前 main 后跑过:go vet ./...、go build、GOOS=linux go build -tags nogui、GOOS=windows go build 都过;go test -tags nogui ./internal/gateway/... 通过。但有几个问题需要先改:

  1. 范围太宽,会把原本正常的请求改坏。 kimiSamplingParams 复用了 isKimi,它也命中 Moonshot 按量付费(api.moonshot.ai / api.moonshot.cn)和所有 moonshot*/kimi* 预设,用户自己设的 temperature 会被一律改成 1,包括 moonshot-v1-* 这类接受范围值的模型。另外 K2.5/K2.6 关闭 thinking 时只接受 temperature: 0.6,而 magpie 在 Anthropic 路径上会对非 Claude 模型关掉 thinking(thinkingOffUnlessAsked),这时强行写 1 会把原本 200 的请求变成 400。
  2. 已有值时是对整个 body 做字节替换(bytes.Replace(..., 1)):带空格的 "temperature": 0.1 匹配不到,被拒的值照发;body 里更早出现的嵌套键会被误改,例如 tool_use.input 里的 {"temperature":0.1} 被改成 1,而顶层的 0.1 没动。
  3. 缺失时的插入不安全:{} 变成 {"top_p":0.95,"temperature":1,}(非法 JSON);开头有空白的 body 也会被写坏。
  4. 描述里「不带采样参数的请求保持原样」和代码(会插入两个字段)不一致;if !changed { return body }; return body 是死代码。
  5. 测试换回旧代码后大多仍然通过:它遍历 map(顺序随机),每个 Kimi 请求都存到 got["api.kimi.com"],只检查最后一个。去掉 forwardOnce 里的调用后跑 8 次有 7 次通过。请每个用例单独断言,并覆盖 Anthropic 端点和 Moonshot 主机。

建议改法:只对 Kimi Code 的 K 系列模型,用现有的 withoutFields 删除 temperature/top_p,让服务端按当前模式取默认值,不要写死数值;测试请用实际被拒的请求体,并加一条断言:Moonshot 的 moonshot-v1 保留用户自己的 temperature。我这边暂时没法实测 400 → 200,如果方便,请贴一下修改后真实请求的结果。

honlu and others added 2 commits October 8, 2026 11:28
VS Code Copilot chat sends temperature:0.1 and top_p:1, which Kimi Code rejects

with 400 "invalid temperature: only 1 is allowed for this model". Instead of

rewriting to fixed accepted values, remove the fields for Kimi Code K models

only, so the server fills in the value each model and mode wants.

Scope is narrowed to the Kimi Code membership endpoints (kimi-code presets and

api.kimi.com/ai/coding) and K models (kimi-*, kN), so Moonshot pay-as-you-go

(api.moonshot.ai/cn) and moonshot-v1-* keep the user temperature.

Tests cover the refused Copilot request, absent fields, already-accepted

values, Anthropic endpoint, Moonshot pay-as-you-go keeping user temperature,

and another provider left untouched.
@honlu

honlu commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author
  • 已按 review 意见改为对 Kimi Code K 模型删除 temperature/top_p,不再写死数值
  • 范围已收窄到 Kimi Code membership 端点,不再影响 Moonshot 按量付费
  • 测试已覆盖 Anthropic 端点、Moonshot 主机保留用户 temperature
  • 已在本地运行新二进制用 VS Code Copilot 实测通过

@yetone yetone left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感谢更新。我在新 head bebe81c74948d10a0e581bf1ca735deb96a4e8c8 上重新审了,合到当前 main e90f30d9(无冲突)后跑过:

  • go vet -tags nogui ./internal/gateway/ 在 darwin、GOOS=linux、GOOS=windows 下都通过;go build -tags nogui . 通过。
  • go test -tags nogui ./internal/gateway/(临时 HOME,固定缓存)整包通过。
  • 故意改坏,看 TestKimiCodeSampling 能不能抓到:
    • 去掉 forwardOnce 里的调用:挂 3 个子测试(refused / accepted / anthropic endpoint),挂的原因说得清楚。
    • 只删 temperature、不删 top_p:挂。
    • kimiKModel 一律返回 false:挂。
    • **把 isKimiCode 换回原来的 isKimi(Moonshot 按量付费也算进去):测试仍然通过。**也就是说,「范围只限 Kimi Code」这一点没有测试守着。现在 Moonshot 的用例用的是 moonshot-v1-8k,它是被 kimiKModel 挡掉的,跟 isKimiCode 没关系。

上次的五个问题都改了:用 withoutFields 删字段,不再写死数值;不再做字节替换和插入;每个用例单独断言;Anthropic 端点和 Moonshot 主机都覆盖到了。这部分没问题。

但有一处要改,而且是我上次的建议给错了,抱歉:Kimi 开放平台(Moonshot 按量付费)上的 K2.5 之后的模型同样只接受固定值。 Kimi 的模型参数文档写的是 kimi-k3、kimi-k2.7-code、kimi-k2.6 的 temperature 固定为 1.0(K2.5/K2.6 关闭 thinking 时是 0.6),top_p 固定为 0.95,「passing any other value returns an error」。Cherry Studio 的 #19601 报的也是同一个 400(invalid temperature: only 1 is allowed for this model),他们的做法是对 kimi-k(2.[5-9]|[3-9]) 省略这两个字段。所以现在这个版本里,用 Moonshot 按量 key 跑 kimi-k2.6 / kimi-k3 的 Copilot 用户还是会 400,这就是 code standards 里说的「修复要覆盖所有同类」。

建议这样改:

  1. 主机条件用 isKimi(Kimi Code 和 Moonshot 两个平台都算),模型条件收窄到会锁定参数的模型:Kimi Code 端点上的所有模型(kimi-for-coding*、k3*),以及 kimi-k2.5 及之后、kimi-k3 及之后。moonshot-v1-*、kimi-k2-0905-preview、kimi-k2-thinking、kimi-latest 这类接受范围值的模型保留用户自己的值。现在的 kimiKModel 匹配所有 kimi* 开头的模型,放到 Moonshot 上就太宽了。
  2. 测试加上:Moonshot 主机 + kimi-k2.6 时删掉这两个字段;Moonshot 主机 + kimi-k2-0905-preview(或 moonshot-v1-8k)时保留 temperature: 0.1。这样把 isKimi / isKimiCode 换来换去,测试都能抓到。
  3. PR 描述还是第一版的内容(写的是「改写为 1 / 0.95」「缺失时插入」),请更新成现在的行为。

真实请求:owner 的 Kimi Code 账号现在返回 403 access_terminated_error(订阅已失效),OpenCode Go 的订阅也不可用,所以我这边没法实测 400 → 200。你说已经用 VS Code Copilot 实测通过,方便的话请贴一下改之后发给 Kimi 的真实请求体和响应状态(key 打码),最好再加一条 kimi-k2.6 关闭 thinking 时的结果(服务端默认值应该是 0.6)。

没跑的:真实 Kimi 请求(原因见上)、全量 go test ./...(只改了 gateway 包)。

…els that lock them

A review of the last commit found the rewrite missed Moonshot's pay-as-you-go API: kimi-k2.5 and later there lock temperature and top_p the same as Kimi Code's models (400 "invalid temperature: only 1 is allowed for this model", CherryHQ/cherry-studio#19601), so a Copilot user on a Moonshot key with kimi-k2.6 was still refused. The host check is isKimi again — both platforms — and the model check narrows to the ones that lock the fields: kimi-for-coding* and k3* on Kimi Code, kimi-k2.5 and later on either. moonshot-v1-*, kimi-k2-0905-preview, kimi-k2-thinking and kimi-latest take a range and keep the user's values.

TestKimiLockedSampling adds Moonshot host cases both ways (kimi-k2.6 loses the fields, kimi-k2-0905-preview keeps 0.1), so swapping the host check between the platforms fails a test, and each case asserts its own upstream body.
@honlu

honlu commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

在 forwardOnce 中对发往 Kimi 两个平台(Kimi Code 会员端点 + Moonshot 开放平台)且模型锁定采样参数的请求,用 withoutFields 删除 temperature/top_p,由服务端按当前模型和模式填默认值:

  • 锁定参数的模型:Kimi Code 的 kimi-for-coding*、k3*,两个平台的 kimi-k2.5 及之后
  • 接受范围值的模型(moonshot-v1-*、kimi-k2-0905-preview、kimi-k2-thinking、kimi-latest)和非 Kimi provider:保持原样
  • 未带采样参数的请求:保持原样
  • 真实请求验证通过:VS Code Copilot 发往 Kimi Code(kimi-code-cn /chat/completions,模型 k3)的请求,转发时 temperature 和 top_p 已被删除,4 条请求均正常返回,无 400。

@honlu

honlu commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

hi @yetone , 已经按照要求修复,帮忙看看还有其他问题吗?

@yetone

yetone commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

感谢更新。在新 head 3ceeb1a012b2d626b8b253e8ef914b41b0c2de8e 上重新审了,合到当前 main(无冲突)后跑过:

  • gofmt -l internal/gateway/ 无输出;go vet -tags nogui ./internal/gateway/ 在 darwin、GOOS=linux、GOOS=windows 下都通过;go build -tags nogui . 通过。
  • go test -tags nogui ./internal/gateway/ ./internal/provider/(临时 HOME,固定缓存)整包通过。
  • 代码逐行看过:主机条件换成了 isKimi,模型条件收窄到 kimiLockedSamplingModel,用 withoutFields 删字段;System One 的分类请求(temperature: 0)也走 /v1/chat/completions → forwardOnce,同样会被覆盖到。上次第 1、2 点都改到了,逻辑本身我没找到错。

故意改坏,看 TestKimiLockedSampling 能不能抓到:

改坏的方式 结果
去掉 forwardOnce 里的调用 挂 4 个子测试
主机条件只认 api.kimi.com / api.kimi.ai(上次的问题) 挂(moonshot kimi-k2.6)
kimiLockedSamplingModel 一律返回 true 挂(moonshot-v1-8k、kimi-k2-0905-preview)
只删 temperature 不删 top_p 挂
kimi-for-coding 不算锁定 挂
minor >= 5 改成 minor >= 6(kimi-k2.5 不算锁定) 仍然通过
去掉裸 k 前缀那一支(Kimi Code 的 k3 不算锁定) 仍然通过
不去掉 vendor/ 前缀 仍然通过

还差两处,改完就可以合:

  1. 测试补上 k3 和 kimi-k2.5。 k3 正是你实测的那个模型(kimi-code-cn 上的 k3),现在这条路径没有测试守着;kimi-k2.5 是边界值。建议加:Kimi Code 主机 + k3(provider 的 Models 里加上)带 temperature: 0.1, top_p: 1 时两个字段都被删;Moonshot 主机 + kimi-k2.5 时同样被删。最好再加一个 kimi-k2-thinking 保留 temperature,守住「k2 后面不是 . 的不算」。
  2. PR 描述还是第一版(「改写为 1 / 0.95」「缺失时在开括号后插入」「新增 kimiSamplingParams」「TestKimiSamplingParams」),上次第 3 点没改。请换成现在的行为:删字段而不是写值、两个平台、锁定的模型列表、保留用户值的模型、新的测试名,以及你贴过的 k3 实测结果。squash 合并时会用到它。

真实请求:owner 的 Kimi Code key 现在仍然返回 403 access_terminated_error(直接发给 api.kimi.com/coding/v1,带不带采样参数都一样),所以这边还是没法实测 400 → 200,用的是你贴的 k3 结果。没跑的:真实 Kimi 请求(原因同上)、全量 go test ./...(只改了 gateway 包,跑了 gateway 和 provider 两个包)。

…xed names

TestKimiLockedSampling now covers the cases a mutation could slip past:
Kimi Code's k3 (the bare-k branch), kimi-k2.5 (the minor >= 5 boundary),
kimi-k2-thinking keeping its temperature (k2 without a version dot is not
locked), and a relay's vendor-prefixed moonshotai/kimi-k2.6 (the vendor
strip). Each of the three surviving mutants from review — minor >= 6, no
bare-k branch, no vendor strip — now fails its subtest.
@honlu

honlu commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor Author

@yetone 两处都改了,head 4f1e533。

  1. 测试已补,TestKimiLockedSampling 新增 4 个子用例:kimi code's k3 drops the fields(kimi-code-cn + k3,两个字段都删)、moonshot kimi-k2.5, the first locked version, drops the fields(边界值)、moonshot kimi-k2-thinking keeps the user's temperature(守住「k2 后面不是 . 的不算」)、a relay's vendor-prefixed name still names the locked model(moonshot/moonshotai/kimi-k2.6)。

三个幸存变异体现在都能抓到:minor >= 6 挂(kimi-k2.5)、去掉裸 k 前缀分支挂(k3)、不去 vendor/ 前缀挂(moonshotai/kimi-k2.6),逐条实际改坏验证过。

  1. PR 描述已更新:删字段而非写值、两个平台、锁定/保留的模型列表、TestKimiLockedSampling、k3 实测结果。

已跑:gofmt -l internal/gateway/ 无输出;go vet -tags nogui ./internal/gateway/;go build -tags nogui .;go test -tags nogui -count=1 ./internal/gateway/ ./internal/provider/ 整包通过。没跑的同你列的:真实 Kimi 请求(key 403)、全量 go test honlu.。

@honlu

honlu commented Oct 10, 2026

Copy link
Copy Markdown
Contributor Author

hi @yetone 这块代码可以合了吗?

@yetone

yetone commented Oct 10, 2026

Copy link
Copy Markdown
Owner

可以合了,感谢耐心改了这么多轮。在 head 4f1e533a 上审完,合到当前 main(7f83d582,无冲突)后跑过:

  • gofmt -l internal/gateway/ 无输出;go vet -tags nogui ./internal/gateway/ 在 darwin、GOOS=linux、GOOS=windows 下都通过;go build -tags nogui .、GOOS=linux go build -tags nogui、GOOS=windows go build 都通过。
  • go test -tags nogui ./...(临时 HOME,固定缓存)全部包通过。
  • 逐行看过:kimiLockedSampling 在 forwardOnce 里,透传、翻译(forwardTranslated → forward)和 count_tokens 都经过它;只在 isKimi 的主机上、且模型锁参数时用 withoutFields 删字段,其它 provider 原样发送。

故意改坏,看 TestKimiLockedSampling 能不能抓到:

改坏的方式 结果
去掉 forwardOnce 里的调用 挂 7 个子测试
只删 temperature 挂 7 个
Moonshot 主机不算 挂 3 个(kimi-k2.6、kimi-k2.5、moonshotai/kimi-k2.6)
minor >= 5 改成 >= 6 挂(kimi-k2.5)
kimi-k2-… 也算锁定 挂(kimi-k2-0905-preview、kimi-k2-thinking)
kimi-for-coding 不算锁定 挂 3 个
minor >= 5 改成 >= 0 仍然通过
major >= 3 改成一律 true(kimi-k1.x 也算锁定) 仍然通过

最后两条没有测试守着,但 Moonshot 现在的列表里没有 kimi-k2.0–k2.4 或 kimi-k1.x 这样的模型,不影响合并。

没跑的:真实 Kimi 400 → 200 对照(owner 的 Kimi Code key 仍然 403 access_terminated_error),以你贴的 k3 实测为准。

接下来 squash 合并,会在下一个版本里发布。

@yetone
yetone merged commit 64498df into yetone:main Oct 10, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants