fix(chat-completions): accept object content parts and absent content field from WorkBuddy - #14
Open
Windyztree wants to merge 2 commits into
Open
fix(chat-completions): accept object content parts and absent content field from WorkBuddy#14Windyztree wants to merge 2 commits into
Windyztree wants to merge 2 commits into
Conversation
…/array
Some OpenAI-compatible clients (e.g. WorkBuddy custom models) serialize a
message's `content` as a single content-part object (`{ type: "text", text:
"..." }`) instead of the spec-required string or array of parts. The proxy's
Zod schema only allowed string | array | null, so such requests failed with:
Request validation failed: messages.N.content: Invalid input
- Extend ChatCompletionsPayloadSchema to also accept a single content-part object.
- Normalize in the chat-completions handler before forwarding: a {type:"text"}
object becomes the plain string; any other object is wrapped in a one-element
array. Strings, arrays, and null are unchanged, so upstream semantics are
preserved and the Copilot /chat/completions endpoint receives a valid payload.
Adds unit + e2e tests covering the object-content shape.
…no content field WorkBuddy custom models emit assistant turns that carry tool_calls but omit the content field entirely. The chat-completions schema only allowed content as string | array | object | null, so absent content failed validation with 'messages.N.content: Invalid input'. OpenAI treats an absent content as null, so accept undefined/optional content and normalize it to null before forwarding.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
WorkBuddy's custom models (e.g. when proxying
claude-opus-5) serialize amessage's
contentinto shapes that fall outside the OpenAI spec, which onlyallows
contentto be astring, anarrayof parts, ornull. The proxy'sZod validation then rejects the request:
Two distinct shapes trigger this:
content: { type: 'text', text: '...' }instead of the spec-required
string/array.assistantturn that carriestool_callsbut omits
contententirely (undefined). OpenAI treats this asnull.Both appear in real multi-turn agentic tool-call conversations, and the error
only surfaces once the conversation accumulates enough messages that the
offending shape reaches the validator.
Fix
ChatCompletionsPayloadSchemato also accept a single content-partobject and an absent/undefined
contentfield.normalizeChatCompletionContentbefore forwarding (structureonly, semantics unchanged):
{ type: 'text', text }→ plain stringtext[object]content→nullChanges
src/lib/schemas.ts—contentunion addsz.undefined()and.optional()src/lib/chat-completions-content.ts— normalize absent content tonullsrc/routes/chat-completions/handler.ts— call normalizer after validationtests/chat-completions-content-normalization.test.ts— unit + e2e for bothshapes (9 tests, all passing)
Test plan
bun test tests/chat-completions-content-normalization.test.ts— 9 passclaude-opus-5multi-turn tool-call session:Invalid inputno longer occurs; completions return 200.Key diff
src/lib/schemas.tssrc/lib/chat-completions-content.tsfunction normalizeMessageContent(message: Message): Message { const content = message.content as unknown + // WorkBuddy: assistant with tool_calls but no content -> treat as null + if (content === undefined) + return { ...message, content: null } + if (content === null || typeof content === 'string' || Array.isArray(content)) return message