fix: rewrote provider for openai, handling schema and response#472
fix: rewrote provider for openai, handling schema and response#472hanneshapke wants to merge 2 commits into
Conversation
|
read through the full diff — clean approach, a few notes: reasoning model list overlap: conversion flow: masking on Chat Completions shape → converting to Responses shape → upstream → restoring on Responses shape tracks correctly. one question: tests cover the edge cases well — shape-detection table and NoOps suite are solid. |
franko-c
left a comment
There was a problem hiding this comment.
from what i can see this handles the main path cleanly — schema detection based on payload fields rather than a model allowlist is the right call, and the test matrix covers the edges well.
couple things i noticed:
reasoningModelFamilies has redundant entries — isReasoningModel matches on family + "-" prefix, so "o1" already catches "o1-mini", "o1-preview", "o1-pro" (and "o3" catches its sub-families). the specific entries never fire because the shorter prefix matches first. not a bug, just slightly confusing to read.
bigger question: when a client sends to /v1/chat/completions with gpt-5, the request converts but the response comes back in Responses API shape (output[] instead of choices[]). PR description flags this as out of scope — is there a tracking issue? any SDK or middleware downstream that reads choices[0].message.content will break on the converted path.
minor: mergeMask in createMaskedResponsesRequest takes masked string as first arg but immediately discards it (_ = masked).
Closes #461
Summary
gpt-5*,o1*,o3*,o4*) sent to/v1/chat/completionswere returning 400 from upstream — those modelsrequire the Responses API. The proxy now converts the request schema and
rewrites the path after PII masking, so masked content carries over.
(
output[].content[].text+ top-leveloutput_text) in addition toChat-Completions
choices[], so masked placeholders no longer leak backto the client when a
gpt-5request flows through/v1/responses./v1/responsestraffic (any model) is now masked on the way out:CreateMaskedRequestwalksinput(string or array, including contentparts) and
instructionswhenmessagesis absent.The split is based on reasoning vs non-reasoning model family, not
GPT-4 vs GPT-5. Schema detection happens by inspecting payload fields
(
messagesvsinput/instructions,choicesvsoutput/output_text)so the proxy doesn't bake in a version allow-list.
What changed
src/backend/providers/openai.goMaybeConvertOpenAIRequest: chat→responses for reasoning models,responses→chat for non-reasoning models. Folds
system/developermessages into
instructions, renamesmax_tokens→max_output_tokens, strips Chat-Completions-only fields(
frequency_penalty,presence_penalty,logit_bias,logprobs,top_logprobs,n,stop).isReasoningModel: matchesgpt-5*and the o-serieso[1-9]….CreateMaskedRequest/RestoreMaskedResponse/ExtractRequestText/ExtractResponseText: dispatch on payloadshape; new walkers for the Responses-API request and response
shapes. Proxy notice is appended only to
message-type outputitems so it doesn't get pinned to
reasoningitems.src/backend/providers/provider.go/v1/responsesas an OpenAI subpath inGetProviderFromPath.src/backend/proxy/handler.gocreateAndSendProxyRequestcallsMaybeConvertOpenAIRequestafterPII masking and threads the rewritten path through
buildTargetURL.buildTargetURLtakes the path as a parameterso callers can pass a rewritten value.
Out of scope
text/event-stream) — tracked separately.schema mapping.
for clients that called
/v1/chat/completions. Clients see theResponses shape on the wire.
Test plan
go test ./src/backend/...passesTestIsReasoningModel(table covering gpt-5/o-series/gpt-4/etc.)TestMaybeConvertOpenAIRequest_ChatToResponses(system→instructions,max_tokensrename, stripped fields, multi-system join)TestMaybeConvertOpenAIRequest_ResponsesToChatTestMaybeConvertOpenAIRequest_NoOps(invalid JSON, missing model,unrelated path)
TestOpenAIProvider_CreateMaskedRequest_ResponsesAPI(stringinput,instructions, array with string content, array withcontent parts, instructions-only)
TestOpenAIProvider_RestoreMaskedResponse_ResponsesAPI(output +output_text, ignoring
reasoningitems, proxy notice placement,output_text-only)
TestOpenAIProvider_ShapeDetectionmax_tokenson
/v1/chat/completions) and confirm 200 + correctly restored PII.