Przeglądaj źródła

fix: handle object-type arguments in Responses API stream

gpt-5.4 returns `arguments` as a JSON object for some tools (e.g.
apply_patch, tool_search), but ResponsesOutput.Arguments was typed as
string, causing json.Unmarshal to fail on every such SSE event.

The failure silently dropped all output_item events and the
response.completed event from the forwarded stream, so:
- the client never received tool call content
- usage/token counts could not be extracted (billed as 0)
- client reported "stream closed before response.completed"

Fix: change Arguments to json.RawMessage and add GetArguments() which
normalises both forms to a plain string (unescapes JSON strings,
returns raw bytes for objects).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
master
fengsilin 2 tygodni temu
rodzic
commit
6479883ddc
3 zmienionych plików z 20 dodań i 3 usunięć
  1. +18
    -1
      dto/openai_response.go
  2. +1
    -1
      relay/channel/openai/chat_via_responses.go
  3. +1
    -1
      service/openaicompat/responses_to_chat.go

+ 18
- 1
dto/openai_response.go Wyświetl plik

@@ -343,7 +343,24 @@ type ResponsesOutput struct {
Size string `json:"size"`
CallId string `json:"call_id,omitempty"`
Name string `json:"name,omitempty"`
Arguments string `json:"arguments,omitempty"`
// Arguments can be a JSON string or a JSON object (e.g. gpt-5.4 returns objects for some tools)
Arguments json.RawMessage `json:"arguments,omitempty"`
}

// GetArguments returns the arguments as a plain string.
// If the upstream sent a JSON string value, the inner string is returned (unescaped).
// If the upstream sent a JSON object, the raw JSON bytes are returned as a string.
func (r *ResponsesOutput) GetArguments() string {
if len(r.Arguments) == 0 {
return ""
}
if r.Arguments[0] == '"' {
var s string
if err := json.Unmarshal(r.Arguments, &s); err == nil {
return s
}
}
return string(r.Arguments)
}

type ResponsesOutputContent struct {


+ 1
- 1
relay/channel/openai/chat_via_responses.go Wyświetl plik

@@ -409,7 +409,7 @@ func OaiResponsesToChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo
toolCallNameByID[callID] = name
}

newArgs := streamResp.Item.Arguments
newArgs := streamResp.Item.GetArguments()
prevArgs := toolCallArgsByID[callID]
argsDelta := ""
if newArgs != "" {


+ 1
- 1
service/openaicompat/responses_to_chat.go Wyświetl plik

@@ -60,7 +60,7 @@ func ResponsesResponseToChatCompletionsResponse(resp *dto.OpenAIResponsesRespons
Type: "function",
Function: dto.FunctionResponse{
Name: name,
Arguments: out.Arguments,
Arguments: out.GetArguments(),
},
})
}


Ładowanie…
Anuluj
Zapisz