diff --git a/pkg/providers/openai_compat/provider.go b/pkg/providers/openai_compat/provider.go index 09bfddc1d..7471f8a46 100644 --- a/pkg/providers/openai_compat/provider.go +++ b/pkg/providers/openai_compat/provider.go @@ -691,6 +691,13 @@ func parseResponsesResponse(body io.Reader) (*LLMResponse, error) { if len(apiResponse.Output) == 0 { return nil, errors.New("openai responses returned terminal status with empty output") } + case "failed": + if apiResponse.Error != nil { + if msg := strings.TrimSpace(apiResponse.Error.Message); msg != "" { + return nil, errors.New(msg) + } + } + return nil, errors.New("openai responses request failed") default: return nil, fmt.Errorf("openai responses returned unexpected or non-terminal status: %q", status) } @@ -760,17 +767,10 @@ func parseResponsesResponse(body io.Reader) (*LLMResponse, error) { } } - if apiResponse.Status == "failed" { - if apiResponse.Error != nil && apiResponse.Error.Message != "" { - return nil, errors.New(apiResponse.Error.Message) - } - return nil, errors.New("openai responses request failed") - } - finishReason := "stop" if len(toolCalls) > 0 { finishReason = "tool_calls" - } else if apiResponse.Status == "incomplete" { + } else if status == "incomplete" { finishReason = "length" if apiResponse.IncompleteDetails != nil && apiResponse.IncompleteDetails.Reason != "" && apiResponse.IncompleteDetails.Reason != "max_output_tokens" { finishReason = apiResponse.IncompleteDetails.Reason diff --git a/pkg/providers/openai_compat/provider_test.go b/pkg/providers/openai_compat/provider_test.go index e88c0c845..b6ca73a44 100644 --- a/pkg/providers/openai_compat/provider_test.go +++ b/pkg/providers/openai_compat/provider_test.go @@ -556,6 +556,29 @@ func TestProviderChat_ParsesRefusalFromResponses(t *testing.T) { } } +func TestParseResponsesResponse_FailedStatusUsesServerMessage(t *testing.T) { + _, err := parseResponsesResponse(strings.NewReader(`{"status":" failed ","error":{"message":"responses failed"}}`)) + if err == nil { + t.Fatal("expected error, got nil") + } + if err.Error() != "responses failed" { + t.Fatalf("error = %q, want %q", err.Error(), "responses failed") + } +} + +func TestParseResponsesResponse_UsesNormalizedIncompleteStatus(t *testing.T) { + out, err := parseResponsesResponse(strings.NewReader(`{"status":" incomplete ","output":[{"type":"message","content":[{"type":"output_text","text":"partial answer"}]}],"incomplete_details":{"reason":"content_filter"}}`)) + if err != nil { + t.Fatalf("parseResponsesResponse() error = %v", err) + } + if out.Content != "partial answer" { + t.Fatalf("Content = %q, want %q", out.Content, "partial answer") + } + if out.FinishReason != "content_filter" { + t.Fatalf("FinishReason = %q, want %q", out.FinishReason, "content_filter") + } +} + func TestProviderChat_UsesMaxCompletionTokensForGLM(t *testing.T) { var requestBody map[string]any