fix(translator): set status:completed on Responses input items to satisfy strict upstream validators (#8083) (#8507)

Co-authored-by: ikelvingo <im.kelvinwong@gmail.com>
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-07-25 04:56:33 -03:00
committed by GitHub
parent 7a8f9156da
commit e0aef4deb9
4 changed files with 68 additions and 0 deletions

View File

@@ -0,0 +1 @@
- fix(translator): set `status: "completed"` on translated OpenAI Responses `input` items so strict Responses-compatible upstreams stop rejecting them with 400 MissingParameter input.status (#8083)

View File

@@ -120,6 +120,7 @@ export function openaiToOpenAIResponsesRequest(
type: "message",
role: "developer",
content: buildResponsesTextParts(msg.content),
status: "completed",
});
continue;
}
@@ -185,6 +186,7 @@ export function openaiToOpenAIResponsesRequest(
type: "message",
role: "user",
content,
status: "completed",
});
}
@@ -223,6 +225,7 @@ export function openaiToOpenAIResponsesRequest(
type: "message",
role: "assistant",
content: outputContent,
status: "completed",
});
}
@@ -241,6 +244,7 @@ export function openaiToOpenAIResponsesRequest(
call_id: clampCallId(toString(toolCall.id).trim() || generateToolCallId()),
name: fnName,
arguments: toString(fn.arguments, "{}"),
status: "completed",
});
}
}
@@ -255,6 +259,7 @@ export function openaiToOpenAIResponsesRequest(
call_id: clampCallId(`call_${fnName}`),
name: fnName,
arguments: toString(fc.arguments, "{}"),
status: "completed",
});
}
}
@@ -276,6 +281,7 @@ export function openaiToOpenAIResponsesRequest(
return c;
})
: String(msg.content ?? ""),
status: "completed",
});
}
@@ -285,6 +291,7 @@ export function openaiToOpenAIResponsesRequest(
type: "function_call_output",
call_id: clampCallId(`call_${toString(msg.name)}`),
output: typeof msg.content === "string" ? msg.content : String(msg.content ?? ""),
status: "completed",
});
}
}

View File

@@ -0,0 +1,55 @@
import test from "node:test";
import assert from "node:assert/strict";
// Kept in its own file rather than growing the frozen
// tests/unit/translator-openai-responses-req.test.ts (file-size ratchet).
const { openaiToOpenAIResponsesRequest } = await import(
"../../open-sse/translator/request/openai-responses.ts"
);
// --- Issue #8083: strict Responses-compatible upstreams reject items whose
// `type` is set without an accompanying `status`, returning
// 400 MissingParameter input.status. `status` is optional per OpenAI's own
// schema, so setting it to "completed" on translated history items (which by
// construction always represent an already-completed prior turn) is a safe
// superset fix that also satisfies stricter third-party validators.
test("Chat -> Responses sets status:completed on every input item (#8083)", () => {
const result = openaiToOpenAIResponsesRequest(
"gpt-4o",
{
messages: [
{ role: "system", content: "Rules" },
// Mid-conversation developer turn -> developer-role message item.
{ role: "developer", content: "Follow up instruction" },
{ role: "user", content: "Hello" },
{
role: "assistant",
content: "Done",
tool_calls: [
{ id: "call_1", type: "function", function: { name: "read_file", arguments: "{}" } },
],
},
{ role: "tool", tool_call_id: "call_1", content: "ok" },
// Deprecated function_call / function role pair.
{
role: "assistant",
function_call: { name: "legacy_fn", arguments: "{}" },
},
{ role: "function", name: "legacy_fn", content: "legacy ok" },
],
},
false,
null
) as Record<string, unknown>;
const input = result.input as Array<Record<string, unknown>>;
assert.ok(input.length > 0, "input array must not be empty");
for (const item of input) {
assert.equal(
item.status,
"completed",
`input item of type "${item.type}" is missing status:"completed" — strict Responses-compatible ` +
`upstreams reject this with 400 MissingParameter input.status (#8083)`
);
}
});

View File

@@ -360,22 +360,26 @@ test("Chat -> Responses converts messages, tool calls, tool outputs, tools and p
{ type: "input_image", image_url: "https://example.com/cat.png", detail: "high" },
{ type: "input_file", file_data: "abc", filename: "doc.txt" },
],
status: "completed",
},
{
type: "message",
role: "assistant",
content: [{ type: "output_text", text: "Done" }],
status: "completed",
},
{
type: "function_call",
call_id: "call_1",
name: "read_file",
arguments: '{"path":"/tmp/a"}',
status: "completed",
},
{
type: "function_call_output",
call_id: "call_1",
output: [{ type: "input_text", text: "ok" }],
status: "completed",
},
]);
assert.deepEqual((result as any).tools, [
@@ -520,6 +524,7 @@ test("Chat -> Responses converts assistant image_url history parts to output_tex
{ type: "output_text", text: "I inspected the screenshot." },
{ type: "output_text", text: "[Image: https://example.com/scope.png]" },
],
status: "completed",
},
]);
assert.equal(JSON.stringify(result).includes('"image_url"'), false);