mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-09-20 17:07:59 +00:00
fix(opencodego): inject empty reasoning_content on tool-calling turns
v1.5.4 stopped stripping reasoning_content, which fixes clients that send it — but most agent clients (pi included) never store or replay their reasoning, keeping only the tool call. OpenCode Go validates the field on any assistant turn that carries tool_calls and rejects the whole request: 400 invalid_request_error: The `reasoning_content` in the thinking mode must be passed back to the API. Verified against the live endpoint that an EMPTY string satisfies the check, so the adapter now fills in "" when a tool-calling assistant turn has no reasoning_content. Nothing is fabricated: the reasoning shown to the client is still exactly what the upstream returned for that turn. Measured: with a tool_call + tool_result history and no reasoning_content, all 25 configured Go models returned 400 before and all 25 answer correctly now. Test: TestOpenCodeGoVsZenReasoning also pins that a plain assistant turn (no tool calls) must NOT gain the field.
This commit is contained in:
@ -79,6 +79,17 @@ function adapter.transform_request(raw_body)
|
|||||||
-- 上游直接 400("The `reasoning_content` in the thinking mode must
|
-- 上游直接 400("The `reasoning_content` in the thinking mode must
|
||||||
-- be passed back to the API"),agent 的每一轮都会失败。
|
-- be passed back to the API"),agent 的每一轮都会失败。
|
||||||
-- 注意:与 opencodezen.lua 的行为**相反**,不要在这里剥。
|
-- 注意:与 opencodezen.lua 的行为**相反**,不要在这里剥。
|
||||||
|
--
|
||||||
|
-- 但绝大多数客户端(pi 等)根本不保存也不回传 reasoning,只保留
|
||||||
|
-- 工具调用本身。上游只在“带 tool_calls 的助手轮”上校验这个字段,
|
||||||
|
-- 实测**空串即可通过校验**,所以缺省时补空串:既满足上游的
|
||||||
|
-- 一致性要求,又不伪造任何推理内容(用户看到的 reasoning 仍然是
|
||||||
|
-- 上游本轮真实返回的)。
|
||||||
|
if msg.role == "assistant"
|
||||||
|
and type(msg.tool_calls) == "table" and #msg.tool_calls > 0
|
||||||
|
and msg.reasoning_content == nil then
|
||||||
|
msg.reasoning_content = ""
|
||||||
|
end
|
||||||
local drop = false
|
local drop = false
|
||||||
if type(msg.content) == "table" then
|
if type(msg.content) == "table" then
|
||||||
local parts = {}
|
local parts = {}
|
||||||
|
|||||||
@ -194,6 +194,32 @@ func TestOpenCodeGoVsZenReasoning(t *testing.T) {
|
|||||||
t.Errorf("opencodezen must still keep the tool call: %s", zout)
|
t.Errorf("opencodezen must still keep the tool call: %s", zout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An assistant turn that carries tool_calls but no reasoning_content must
|
||||||
|
// get an empty one injected: Go validates that field on tool-calling turns
|
||||||
|
// and 400s when it is absent, while most clients never send it.
|
||||||
|
// Verified against the live endpoint that an empty string passes.
|
||||||
|
bare := `{"model":"m","messages":[
|
||||||
|
{"role":"user","content":"go"},
|
||||||
|
{"role":"assistant","content":"","tool_calls":[{"id":"c1","type":"function","function":{"name":"read","arguments":"{}"}}]},
|
||||||
|
{"role":"tool","tool_call_id":"c1","content":"r"}
|
||||||
|
]}`
|
||||||
|
bout, err := vm.Transform("opencodego", "transform_request", bare)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("opencodego bare: %v", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(bout, `"reasoning_content":""`) {
|
||||||
|
t.Errorf("opencodego must inject an empty reasoning_content on a tool-calling turn: %s", bout)
|
||||||
|
}
|
||||||
|
// A plain assistant turn (no tool calls) must NOT gain the field.
|
||||||
|
plainAsst := `{"model":"m","messages":[{"role":"user","content":"hi"},{"role":"assistant","content":"hello"}]}`
|
||||||
|
pout, err := vm.Transform("opencodego", "transform_request", plainAsst)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if strings.Contains(pout, "reasoning_content") {
|
||||||
|
t.Errorf("opencodego must not inject reasoning_content on a plain assistant turn: %s", pout)
|
||||||
|
}
|
||||||
|
|
||||||
// Both must still omit stream_options on non-streaming requests.
|
// Both must still omit stream_options on non-streaming requests.
|
||||||
for _, a := range []string{"opencodego", "opencodezen"} {
|
for _, a := range []string{"opencodego", "opencodezen"} {
|
||||||
out, err := vm.Transform(a, "transform_request", `{"model":"m","messages":[{"role":"user","content":"hi"}]}`)
|
out, err := vm.Transform(a, "transform_request", `{"model":"m","messages":[{"role":"user","content":"hi"}]}`)
|
||||||
|
|||||||
Reference in New Issue
Block a user