From 28830bcebc3f8b8b27ed8791c61523476b797360 Mon Sep 17 00:00:00 2001 From: Pepelospalotes2005 <255143987+Pepelospalotes2005@users.noreply.github.com> Date: Thu, 9 Apr 2026 10:36:52 +0000 Subject: [PATCH] Fix Codex OAuth empty responses for gpt-5.4 --- pkg/gateway/channel_matrix.go | 2 +- pkg/providers/codex_provider.go | 42 ++++++++++++++++++- .../responses_common.go | 9 +++- 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/pkg/gateway/channel_matrix.go b/pkg/gateway/channel_matrix.go index a46addae1..37d9c4644 100644 --- a/pkg/gateway/channel_matrix.go +++ b/pkg/gateway/channel_matrix.go @@ -1,4 +1,4 @@ -//go:build !mipsle && !netbsd && !(freebsd && arm) +//go:build !mipsle && !netbsd && !(freebsd && arm) && !(linux && arm64) package gateway diff --git a/pkg/providers/codex_provider.go b/pkg/providers/codex_provider.go index d968215cc..4c7e91380 100644 --- a/pkg/providers/codex_provider.go +++ b/pkg/providers/codex_provider.go @@ -61,6 +61,8 @@ func (p *CodexProvider) Chat( var opts []option.RequestOption accountID := p.accountID resolvedModel, fallbackReason := resolveCodexModel(model) + var streamedText strings.Builder + var streamedReasoning strings.Builder if fallbackReason != "" { logger.WarnCF( "provider.codex", @@ -106,7 +108,19 @@ func (p *CodexProvider) Chat( var resp *responses.Response for stream.Next() { evt := stream.Current() - if evt.Type == "response.completed" || evt.Type == "response.failed" || evt.Type == "response.incomplete" { + switch string(evt.Type) { + case "response.output_text.delta": + delta := evt.AsResponseOutputTextDelta() + streamedText.WriteString(delta.Delta) + case "response.output_text.done": + done := evt.AsResponseOutputTextDone() + if strings.TrimSpace(done.Text) != "" && strings.TrimSpace(streamedText.String()) == "" { + streamedText.WriteString(done.Text) + } + case "response.reasoning_text.delta": + delta := evt.AsResponseReasoningTextDelta() + streamedReasoning.WriteString(delta.Delta) + case "response.completed", "response.failed", "response.incomplete": evtResp := evt.Response if evtResp.ID != "" { evtRespCopy := evtResp @@ -153,7 +167,31 @@ func (p *CodexProvider) Chat( return nil, fmt.Errorf("codex API call: stream ended without completed response") } - return orc.ParseResponseFromStruct(resp), nil + parsed := orc.ParseResponseFromStruct(resp) + if strings.TrimSpace(parsed.Content) == "" && strings.TrimSpace(streamedText.String()) != "" { + parsed.Content = strings.TrimSpace(streamedText.String()) + } + if strings.TrimSpace(parsed.ReasoningContent) == "" && strings.TrimSpace(streamedReasoning.String()) != "" { + parsed.ReasoningContent = strings.TrimSpace(streamedReasoning.String()) + } + itemTypes := make([]string, 0, len(resp.Output)) + for _, item := range resp.Output { + itemTypes = append(itemTypes, string(item.Type)) + } + logger.InfoCF("provider.codex", "Codex parsed response", map[string]any{ + "requested_model": model, + "resolved_model": resolvedModel, + "resp_status": resp.Status, + "output_items": len(resp.Output), + "output_types": itemTypes, + "streamed_text_len": len(streamedText.String()), + "streamed_reasoning_len": len(streamedReasoning.String()), + "content_len": len(parsed.Content), + "reasoning_len": len(parsed.ReasoningContent), + "tool_calls": len(parsed.ToolCalls), + }) + + return parsed, nil } func (p *CodexProvider) GetDefaultModel() string { diff --git a/pkg/providers/openai_responses_common/responses_common.go b/pkg/providers/openai_responses_common/responses_common.go index 839471f69..3fa95e2b9 100644 --- a/pkg/providers/openai_responses_common/responses_common.go +++ b/pkg/providers/openai_responses_common/responses_common.go @@ -286,8 +286,15 @@ func parseResponse(apiResp *responses.Response) *protocoltypes.LLMResponse { } } + contentStr := content.String() + if strings.TrimSpace(contentStr) == "" { + if fallback := strings.TrimSpace(apiResp.OutputText()); fallback != "" { + contentStr = fallback + } + } + return &protocoltypes.LLMResponse{ - Content: content.String(), + Content: contentStr, ReasoningContent: reasoningContent.String(), ToolCalls: toolCalls, FinishReason: finishReason,