mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-14 14:17:40 +00:00
fix(providers): return error on Codex response.failed instead of silent success (#888)
response.failed was lumped with response.completed/incomplete, causing silent empty responses with no retry. Split into its own case that returns a descriptive error from processSSEEvent, propagated up to ChatStream caller for proper error surface and retry logic. Added codexErrorDetail to codexAPIResponse to parse the error payload.
This commit is contained in:
1 parent
222ccfbc5b
commit
339b13e8f0
2 files changed
+30
-9
No files matched your search
@@ -3,6 +3,7 @@ package providers
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -148,7 +149,9 @@ func (p *CodexProvider) ChatStream(ctx context.Context, req ChatRequest, onChunk
|
||||
continue
|
||||
}
|
||||
|
||||
p.processSSEEvent(&event, result, toolCalls, streamState, onChunk, stripThinking)
|
||||
if err := p.processSSEEvent(&event, result, toolCalls, streamState, onChunk, stripThinking); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := sse.Err(); err != nil {
|
||||
@@ -189,7 +192,7 @@ func (p *CodexProvider) ChatStream(ctx context.Context, req ChatRequest, onChunk
|
||||
// processSSEEvent handles a single SSE event during streaming.
|
||||
// stripThinking drops reasoning summaries from user-visible output while
|
||||
// leaving billing counters (Usage.ThinkingTokens) untouched.
|
||||
func (p *CodexProvider) processSSEEvent(event *codexSSEEvent, result *ChatResponse, toolCalls map[string]*codexToolCallAcc, streamState *codexMessageStreamState, onChunk func(StreamChunk), stripThinking bool) {
|
||||
func (p *CodexProvider) processSSEEvent(event *codexSSEEvent, result *ChatResponse, toolCalls map[string]*codexToolCallAcc, streamState *codexMessageStreamState, onChunk func(StreamChunk), stripThinking bool) error {
|
||||
switch event.Type {
|
||||
case "response.output_item.added":
|
||||
if event.Item != nil {
|
||||
@@ -249,7 +252,7 @@ func (p *CodexProvider) processSSEEvent(event *codexSSEEvent, result *ChatRespon
|
||||
}
|
||||
}
|
||||
|
||||
case "response.completed", "response.incomplete", "response.failed":
|
||||
case "response.completed", "response.incomplete":
|
||||
if event.Response != nil {
|
||||
if result.Content == "" {
|
||||
streamState.ingestCompletedResponse(event.Response)
|
||||
@@ -271,5 +274,17 @@ func (p *CodexProvider) processSSEEvent(event *codexSSEEvent, result *ChatRespon
|
||||
result.FinishReason = "length"
|
||||
}
|
||||
}
|
||||
|
||||
case "response.failed":
|
||||
errMsg := "codex: response failed during generation"
|
||||
if event.Response != nil && event.Response.Error != nil {
|
||||
if event.Response.Error.Message != "" {
|
||||
errMsg = fmt.Sprintf("codex: response failed: %s", event.Response.Error.Message)
|
||||
} else if event.Response.Error.Code != "" {
|
||||
errMsg = fmt.Sprintf("codex: response failed: %s", event.Response.Error.Code)
|
||||
}
|
||||
}
|
||||
return errors.New(errMsg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -3,12 +3,18 @@ package providers
|
||||
// Wire types for the OpenAI Responses API (Codex flow).
|
||||
|
||||
type codexAPIResponse struct {
|
||||
ID string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
Model string `json:"model"`
|
||||
Output []codexItem `json:"output"`
|
||||
Usage *codexUsage `json:"usage,omitempty"`
|
||||
Status string `json:"status"`
|
||||
ID string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
Model string `json:"model"`
|
||||
Output []codexItem `json:"output"`
|
||||
Usage *codexUsage `json:"usage,omitempty"`
|
||||
Status string `json:"status"`
|
||||
Error *codexErrorDetail `json:"error,omitempty"` // set when status="failed"
|
||||
}
|
||||
|
||||
type codexErrorDetail struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type codexItem struct {
|
||||
|
||||
Reference in new issue
Block a user