fix: send assistant content as string in openai-completions provider (#2008)
The OpenAI Chat Completions API standard format for assistant message
content is a plain string. Sending it as an array of
{type:"text", text:"..."} objects causes some models (notably DeepSeek
V3.2 via NVIDIA NIM) to mirror the content-block structure literally
in their output. This produces recursive nesting where each turn wraps
the previous content blocks deeper:
[{'type':'text','text':'[{\'type\':\'text\',\'text\':...}]'}]
The fix unifies the assistant content serialization to always use a
joined string — the same approach already used for the github-copilot
provider — for all openai-completions backends.
Affected models observed: deepseek-ai/deepseek-v3.2 (nvidia provider).
Models like GLM-5, GPT-4, Claude were unaffected as they tolerate
array content, but sending a standard string is safer for all.
Co-authored-by: geraldoaax <geraldoaax@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
15e0957b04
commit
23109b113d
@@ -559,15 +559,12 @@ export function convertMessages(
|
||||
// Filter out empty text blocks to avoid API validation errors
|
||||
const nonEmptyTextBlocks = textBlocks.filter((b) => b.text && b.text.trim().length > 0);
|
||||
if (nonEmptyTextBlocks.length > 0) {
|
||||
// GitHub Copilot requires assistant content as a string, not an array.
|
||||
// Sending as array causes Claude models to re-answer all previous prompts.
|
||||
if (model.provider === "github-copilot") {
|
||||
assistantMsg.content = nonEmptyTextBlocks.map((b) => sanitizeSurrogates(b.text)).join("");
|
||||
} else {
|
||||
assistantMsg.content = nonEmptyTextBlocks.map((b) => {
|
||||
return { type: "text", text: sanitizeSurrogates(b.text) };
|
||||
});
|
||||
}
|
||||
// Always send assistant content as a plain string (OpenAI Chat Completions
|
||||
// API standard format). Sending as an array of {type:"text", text:"..."}
|
||||
// objects is non-standard and causes some models (e.g. DeepSeek V3.2 via
|
||||
// NVIDIA NIM) to mirror the content-block structure literally in their
|
||||
// output, producing recursive nesting like [{'type':'text','text':'[{...}]'}].
|
||||
assistantMsg.content = nonEmptyTextBlocks.map((b) => sanitizeSurrogates(b.text)).join("");
|
||||
}
|
||||
|
||||
// Handle thinking blocks
|
||||
|
||||
Reference in New Issue
Block a user