fix(opencode): strip prior reasoning items on Muse Spark Responses models

Strip prior-turn type: reasoning items and encrypted_content fields from body.input on Muse Spark Responses endpoints in OpenCode and OpenCode Go executors to avoid HTTP 400 errors across rotated proxy accounts.
This commit is contained in:
anojndr
2026-09-17 18:17:18 +07:00
parent c49efdf528
commit eafac37dcb
4 changed files with 167 additions and 2 deletions

View File

@@ -136,6 +136,28 @@ describe("OpenCodeGoExecutor routing + sanitization", () => {
expect(out.tools.find((t) => t.name === "bare").parameters).toEqual({ type: "object", properties: {} });
expect(out.tools.find((t) => t.name === "full").parameters).toEqual({ type: "object", properties: { a: { type: "string" } } });
});
it("strips prior-turn reasoning items carrying encrypted_content from input", () => {
const ex = new OpenCodeGoExecutor();
const body = {
model: MODEL,
input: [
{ type: "message", role: "user", content: [{ type: "input_text", text: "hi" }] },
{
type: "reasoning",
id: "rs_123",
encrypted_content: "ENC_BLOB_TURN_1",
summary: [{ type: "summary_text", text: "thinking text" }],
},
{ type: "function_call", call_id: "c1", name: "read", arguments: "{}" },
{ type: "function_call_output", call_id: "c1", output: "ok" },
],
};
const out = ex.transformRequest(MODEL, body, true, {});
expect(out.input.some((i) => i.type === "reasoning")).toBe(false);
expect(JSON.stringify(out.input)).not.toContain("ENC_BLOB_TURN_1");
expect(out.input.map((i) => i.type)).toEqual(["message", "function_call", "function_call_output"]);
});
});
describe("chat/claude clients translate to Responses without breaking tools", () => {

View File

@@ -165,4 +165,63 @@ describe("OpenCode Free Muse Spark thinking", () => {
expect(out.max_tokens).toBeUndefined();
}
});
it("strips prior-turn reasoning items carrying encrypted_content from input", () => {
const executor = new OpenCodeExecutor();
const model = "muse-spark-1.3-contributor-free";
const body = {
model,
input: [
{ type: "message", role: "user", content: [{ type: "input_text", text: "say hi" }] },
{
type: "reasoning",
id: "rs_123",
encrypted_content: "ENC_BLOB_TURN_1",
summary: [{ type: "summary_text", text: "thinking text" }],
},
{
type: "function_call",
id: "fc_1",
call_id: "call_1",
name: "shell",
arguments: JSON.stringify({ command: "echo hi" }),
},
{
type: "function_call_output",
call_id: "call_1",
output: "hi",
},
{ type: "message", role: "user", content: [{ type: "input_text", text: "now say bye" }] },
],
tools: [
{
type: "function",
function: {
name: "shell",
description: "Run shell command",
parameters: { type: "object" },
},
},
],
};
const out = executor.transformRequest(model, body, true, {});
expect(out.stream).toBe(true);
expect(out.store).toBe(false);
// Prior reasoning items stripped to prevent 400 "reasoning encrypted_content was not issued to this caller"
expect(out.input.some((item) => item.type === "reasoning")).toBe(false);
expect(JSON.stringify(out.input)).not.toContain("ENC_BLOB_TURN_1");
// User message, function_call, function_call_output, and next user message survive
const types = out.input.map((item) => item.type);
expect(types).toEqual(["message", "function_call", "function_call_output", "message"]);
// Tools flattened and empty properties added
expect(out.tools).toEqual([
{
type: "function",
name: "shell",
description: "Run shell command",
parameters: { type: "object", properties: {} },
},
]);
});
});