Repository navigation
fix(responses): map mid-conversation system turns to developer role (#6954) - #7056
Conversation
There was a problem hiding this comment.
Code Review
This pull request ensures that mid-conversation system and developer turns are preserved as developer-role input items in the Responses API, rather than being dropped or misattributed. It also adds a unit test to verify this behavior. The review feedback suggests enhancing the robustness of the new buildResponsesTextParts helper function by handling raw string elements within content arrays to prevent potential data loss.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if (Array.isArray(content)) { | ||
| const parts: unknown[] = []; | ||
| for (const partValue of content) { | ||
| const part = toRecord(partValue); | ||
| if (part.type === "text" || typeof part.text === "string") { | ||
| parts.push({ type: "input_text", text: toString(part.text) }); | ||
| } | ||
| } | ||
| return parts.length > 0 ? parts : [{ type: "input_text", text: "" }]; | ||
| } |
There was a problem hiding this comment.
If content is an array containing raw strings (e.g., ["instruction part"]), toRecord(partValue) will return {} and the string will be silently ignored. Handling raw string elements in the array makes the helper more robust and prevents potential bugs where system instructions are lost.
if (Array.isArray(content)) {
const parts: unknown[] = [];
for (const partValue of content) {
if (typeof partValue === "string") {
parts.push({ type: "input_text", text: partValue });
} else {
const part = toRecord(partValue);
if (part.type === "text" || typeof part.text === "string") {
parts.push({ type: "input_text", text: toString(part.text) });
}
}
}
return parts.length > 0 ? parts : [{ type: "input_text", text: "" }];
}|
Thanks for the precise root-cause analysis and the clean fix. I ran the full openai-responses translator regression suite against this branch (72 tests across the surrounding files) plus your 2 new tests — everything passes with no regressions, and ESLint is clean. One thing worth flagging so you're not surprised: this is one of two bugs feeding the same #6954 report. There's a second, upstream bug in |
ff89a3d
into
cheaper-inference:release/v3.8.49
|
Thanks @xier2012! Merged into release/v3.8.49 — validated in the combined merge-train (all gates green). Clean, surgical series; much appreciated. |
…heaper-inference#6954) (cheaper-inference#7056) * fix(responses): map mid-conversation system turns to developer role (cheaper-inference#6954) * test(responses): add cheaper-inference#6954 mid-conversation system -> developer regression * fix(6954): keep bare-string content parts in buildResponsesTextParts * test(6954): cover array-form system content with bare string
…12207) The leading system message was read as `typeof content === "string" ? content : ""`, so a Chat-Completions content-part array — valid for `system`, and what every prompt-caching client sends — collapsed the whole system prompt into an empty `instructions`. Upstream accepted the request and reported a normal prompt_tokens count, so the model answered with no instructions at all and nothing in the response said so. Mid-conversation system turns already handled the array shape (#7056); only the first one did not. Reuses buildResponsesTextParts() and joins the text parts, since `instructions` is a string rather than a part array. Co-authored-by: Vadim Zhyvylo <zhyvylo@involve.software>
…heaper-inference#6954) (cheaper-inference#7056) * fix(responses): map mid-conversation system turns to developer role (cheaper-inference#6954) * test(responses): add cheaper-inference#6954 mid-conversation system -> developer regression * fix(6954): keep bare-string content parts in buildResponsesTextParts * test(6954): cover array-form system content with bare string
…heaper-inference#12207) The leading system message was read as `typeof content === "string" ? content : ""`, so a Chat-Completions content-part array — valid for `system`, and what every prompt-caching client sends — collapsed the whole system prompt into an empty `instructions`. Upstream accepted the request and reported a normal prompt_tokens count, so the model answered with no instructions at all and nothing in the response said so. Mid-conversation system turns already handled the array shape (cheaper-inference#7056); only the first one did not. Reuses buildResponsesTextParts() and joins the text parts, since `instructions` is a string rather than a part array. Co-authored-by: Vadim Zhyvylo <zhyvylo@involve.software>
Problem (#6954)
When translating an Anthropic/OpenAI Messages-format request to the OpenAI Responses API (
openaiToOpenAIResponsesRequestinopen-sse/translator/request/openai-responses/toResponses.ts), the firstrole: "system"(orrole: "developer") message is extracted intoinstructions, but every subsequent mid-conversation system turn is silently dropped (continue).Clients such as Claude Code v2.1.207 inject harness reminders as mid-stream
role: "system"turns. Dropping them loses harness instructions entirely; mapping them toassistant(as older revisions did) misattributes harness-authored content as model output. The correct target is the Responses APIdeveloperrole, which exists for exactly this purpose.Root cause
In
toResponses.ts, the system/developer branch only handled the first message:Fix
After the first system turn (which still becomes
instructions), subsequentsystem/developerturns are now emitted asdeveloper-role message input items, preserving their content:A small
buildResponsesTextPartshelper converts a string or text-part array into the Responsesinput_textpart shape. The downstream Codex executor'sconvertSystemToDeveloperRolealready converts any residualsystemitems in-place, so emittingdeveloperdirectly is compatible.Test
Added
mid-conversation system turns are preserved as developer-role input items (#6954)totests/unit/openai-responses-request-split.test.ts:[system, user, system, assistant, system]history.instructions.developer-role input items are produced with the correct text.assistantprose.The case passes on the patched code and would fail (zero developer items / dropped content) on the unpatched
main.Fixes #6954.