Skip to content

Commit 78eb82c

Browse files
refactor: core providers error handling standardization
1 parent d8a6076 commit 78eb82c

File tree

14 files changed

+155
-221
lines changed

14 files changed

+155
-221
lines changed

core/providers/anthropic/anthropic.go

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,7 @@ func (provider *AnthropicProvider) completeRequest(ctx context.Context, jsonData
150150
// Handle error response
151151
if resp.StatusCode() != fasthttp.StatusOK {
152152
provider.logger.Debug(fmt.Sprintf("error from %s provider: %s", provider.GetProviderKey(), string(resp.Body())))
153-
154-
var errorResp AnthropicError
155-
156-
bifrostErr := providerUtils.HandleProviderAPIError(resp, &errorResp)
157-
bifrostErr.Error.Type = &errorResp.Error.Type
158-
bifrostErr.Error.Message = errorResp.Error.Message
159-
160-
return nil, latency, bifrostErr
153+
return nil, latency, parseAnthropicError(resp)
161154
}
162155

163156
body, err := providerUtils.CheckAndDecodeBody(resp)
@@ -201,11 +194,7 @@ func (provider *AnthropicProvider) listModelsByKey(ctx context.Context, key sche
201194

202195
// Handle error response
203196
if resp.StatusCode() != fasthttp.StatusOK {
204-
var errorResp AnthropicError
205-
bifrostErr := providerUtils.HandleProviderAPIError(resp, &errorResp)
206-
bifrostErr.Error.Type = &errorResp.Error.Type
207-
bifrostErr.Error.Message = errorResp.Error.Message
208-
return nil, bifrostErr
197+
return nil, parseAnthropicError(resp)
209198
}
210199

211200
// Parse Anthropic's response
@@ -459,7 +448,7 @@ func HandleAnthropicChatCompletionStreaming(
459448
// Check for HTTP errors
460449
if resp.StatusCode() != fasthttp.StatusOK {
461450
defer providerUtils.ReleaseStreamingResponse(resp)
462-
return nil, parseStreamAnthropicError(resp, providerName)
451+
return nil, parseAnthropicError(resp)
463452
}
464453

465454
// Create response channel
@@ -762,7 +751,7 @@ func HandleAnthropicResponsesStream(
762751
// Check for HTTP errors
763752
if resp.StatusCode() != fasthttp.StatusOK {
764753
defer providerUtils.ReleaseStreamingResponse(resp)
765-
return nil, parseStreamAnthropicError(resp, providerName)
754+
return nil, parseAnthropicError(resp)
766755
}
767756

768757
// Create response channel
@@ -936,10 +925,3 @@ func (provider *AnthropicProvider) Transcription(ctx context.Context, key schema
936925
func (provider *AnthropicProvider) TranscriptionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, key schemas.Key, request *schemas.BifrostTranscriptionRequest) (chan *schemas.BifrostStream, *schemas.BifrostError) {
937926
return nil, providerUtils.NewUnsupportedOperationError(schemas.TranscriptionStreamRequest, provider.GetProviderKey())
938927
}
939-
940-
// parseStreamAnthropicError parses Anthropic streaming error responses.
941-
func parseStreamAnthropicError(resp *fasthttp.Response, providerType schemas.ModelProvider) *schemas.BifrostError {
942-
statusCode := resp.StatusCode()
943-
body := resp.Body()
944-
return providerUtils.NewProviderAPIError(string(body), nil, statusCode, providerType, nil, nil)
945-
}

core/providers/anthropic/errors.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package anthropic
2+
3+
import (
4+
providerUtils "github.com/maximhq/bifrost/core/providers/utils"
5+
schemas "github.com/maximhq/bifrost/core/schemas"
6+
"github.com/valyala/fasthttp"
7+
)
8+
9+
func parseAnthropicError(resp *fasthttp.Response) *schemas.BifrostError {
10+
var errorResp AnthropicError
11+
bifrostErr := providerUtils.HandleProviderAPIError(resp, &errorResp)
12+
if errorResp.Error != nil {
13+
if bifrostErr.Error == nil {
14+
bifrostErr.Error = &schemas.ErrorField{}
15+
}
16+
bifrostErr.Error.Type = &errorResp.Error.Type
17+
bifrostErr.Error.Message = errorResp.Error.Message
18+
}
19+
return bifrostErr
20+
}

core/providers/anthropic/types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,10 +386,10 @@ type AnthropicMessageErrorStruct struct {
386386
// AnthropicError represents the error response structure from Anthropic's API (legacy)
387387
type AnthropicError struct {
388388
Type string `json:"type"` // always "error"
389-
Error struct {
389+
Error *struct {
390390
Type string `json:"type"` // Error type
391391
Message string `json:"message"` // Error message
392-
} `json:"error"` // Error details
392+
} `json:"error,omitempty"` // Error details
393393
}
394394

395395
// AnthropicStreamError represents error events in the streaming response

core/providers/cohere/cohere.go

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -151,20 +151,7 @@ func (provider *CohereProvider) completeRequest(ctx context.Context, jsonData []
151151
// Handle error response
152152
if resp.StatusCode() != fasthttp.StatusOK {
153153
provider.logger.Debug(fmt.Sprintf("error from %s provider: %s", provider.GetProviderKey(), string(resp.Body())))
154-
155-
var errorResp CohereError
156-
157-
bifrostErr := providerUtils.HandleProviderAPIError(resp, &errorResp)
158-
bifrostErr.Type = &errorResp.Type
159-
if bifrostErr.Error == nil {
160-
bifrostErr.Error = &schemas.ErrorField{}
161-
}
162-
bifrostErr.Error.Message = errorResp.Message
163-
if errorResp.Code != nil {
164-
bifrostErr.Error.Code = errorResp.Code
165-
}
166-
167-
return nil, latency, bifrostErr
154+
return nil, latency, parseCohereError(resp)
168155
}
169156

170157
body, err := providerUtils.CheckAndDecodeBody(resp)
@@ -221,10 +208,7 @@ func (provider *CohereProvider) listModelsByKey(ctx context.Context, key schemas
221208

222209
// Handle error response
223210
if resp.StatusCode() != fasthttp.StatusOK {
224-
var errorResp CohereError
225-
bifrostErr := providerUtils.HandleProviderAPIError(resp, &errorResp)
226-
bifrostErr.Error.Message = errorResp.Message
227-
return nil, bifrostErr
211+
return nil, parseCohereError(resp)
228212
}
229213

230214
body, err := providerUtils.CheckAndDecodeBody(resp)
@@ -400,7 +384,7 @@ func (provider *CohereProvider) ChatCompletionStream(ctx context.Context, postHo
400384
// Check for HTTP errors
401385
if resp.StatusCode() != fasthttp.StatusOK {
402386
defer providerUtils.ReleaseStreamingResponse(resp)
403-
return nil, providerUtils.NewProviderAPIError(fmt.Sprintf("HTTP error from %s: %d", providerName, resp.StatusCode()), fmt.Errorf("%s", string(resp.Body())), resp.StatusCode(), providerName, nil, nil)
387+
return nil, parseCohereError(resp)
404388
}
405389

406390
// Create response channel
@@ -613,7 +597,7 @@ func (provider *CohereProvider) ResponsesStream(ctx context.Context, postHookRun
613597
// Check for HTTP errors
614598
if resp.StatusCode() != fasthttp.StatusOK {
615599
defer providerUtils.ReleaseStreamingResponse(resp)
616-
return nil, providerUtils.NewProviderAPIError(fmt.Sprintf("HTTP error from %s: %d", providerName, resp.StatusCode()), fmt.Errorf("%s", string(resp.Body())), resp.StatusCode(), providerName, nil, nil)
600+
return nil, parseCohereError(resp)
617601
}
618602

619603
// Create response channel

core/providers/cohere/errors.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package cohere
2+
3+
import (
4+
providerUtils "github.com/maximhq/bifrost/core/providers/utils"
5+
"github.com/maximhq/bifrost/core/schemas"
6+
"github.com/valyala/fasthttp"
7+
)
8+
9+
func parseCohereError(resp *fasthttp.Response) *schemas.BifrostError {
10+
var errorResp CohereError
11+
bifrostErr := providerUtils.HandleProviderAPIError(resp, &errorResp)
12+
bifrostErr.Type = &errorResp.Type
13+
if bifrostErr.Error == nil {
14+
bifrostErr.Error = &schemas.ErrorField{}
15+
}
16+
bifrostErr.Error.Message = errorResp.Message
17+
if errorResp.Code != nil {
18+
bifrostErr.Error.Code = errorResp.Code
19+
}
20+
return bifrostErr
21+
}

core/providers/elevenlabs/elevenlabs.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func (provider *ElevenlabsProvider) listModelsByKey(ctx context.Context, key sch
9494
return nil, bifrostErr
9595
}
9696
if resp.StatusCode() != fasthttp.StatusOK {
97-
return nil, parseElevenlabsError(providerName, resp)
97+
return nil, parseElevenlabsError(resp)
9898
}
9999

100100
var elevenlabsResponse ElevenlabsListModelsResponse
@@ -226,7 +226,7 @@ func (provider *ElevenlabsProvider) Speech(ctx context.Context, key schemas.Key,
226226
// Handle error response
227227
if resp.StatusCode() != fasthttp.StatusOK {
228228
provider.logger.Debug(fmt.Sprintf("error from %s provider: %s", providerName, string(resp.Body())))
229-
return nil, parseElevenlabsError(providerName, resp)
229+
return nil, parseElevenlabsError(resp)
230230
}
231231

232232
// Get the response body
@@ -346,7 +346,7 @@ func (provider *ElevenlabsProvider) SpeechStream(ctx context.Context, postHookRu
346346
// Check for HTTP errors
347347
if resp.StatusCode() != fasthttp.StatusOK {
348348
defer providerUtils.ReleaseStreamingResponse(resp)
349-
return nil, parseElevenlabsError(providerName, resp)
349+
return nil, parseElevenlabsError(resp)
350350
}
351351

352352
// Create response channel
@@ -483,7 +483,7 @@ func (provider *ElevenlabsProvider) Transcription(ctx context.Context, key schem
483483
}
484484
if resp.StatusCode() != fasthttp.StatusOK {
485485
provider.logger.Debug(fmt.Sprintf("error from %s provider: %s", providerName, string(resp.Body())))
486-
return nil, parseElevenlabsError(providerName, resp)
486+
return nil, parseElevenlabsError(resp)
487487
}
488488

489489
responseBody, err := providerUtils.CheckAndDecodeBody(resp)

core/providers/elevenlabs/errors.go

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
11
package elevenlabs
22

33
import (
4-
"fmt"
54
"strings"
65

7-
"github.com/bytedance/sonic"
86
"github.com/valyala/fasthttp"
97

108
providerUtils "github.com/maximhq/bifrost/core/providers/utils"
119
schemas "github.com/maximhq/bifrost/core/schemas"
1210
)
1311

14-
func parseElevenlabsError(providerName schemas.ModelProvider, resp *fasthttp.Response) *schemas.BifrostError {
15-
body := append([]byte(nil), resp.Body()...)
16-
17-
var message string
18-
// Try to parse as JSON first
12+
func parseElevenlabsError(resp *fasthttp.Response) *schemas.BifrostError {
1913
var errorResp ElevenlabsError
20-
if err := sonic.Unmarshal(body, &errorResp); err == nil {
14+
bifrostErr := providerUtils.HandleProviderAPIError(resp, &errorResp)
15+
if errorResp.Detail != nil {
16+
var message string
2117
// Handle validation errors (array format)
2218
if len(errorResp.Detail.ValidationErrors) > 0 {
2319
var messages []string
@@ -82,21 +78,12 @@ func parseElevenlabsError(providerName schemas.ModelProvider, resp *fasthttp.Res
8278
}
8379

8480
if message != "" {
85-
return &schemas.BifrostError{
86-
IsBifrostError: false,
87-
StatusCode: schemas.Ptr(resp.StatusCode()),
88-
Error: &schemas.ErrorField{
89-
Type: schemas.Ptr(errorType),
90-
Message: message,
91-
},
81+
if bifrostErr.Error == nil {
82+
bifrostErr.Error = &schemas.ErrorField{}
9283
}
84+
bifrostErr.Error.Type = schemas.Ptr(errorType)
85+
bifrostErr.Error.Message = message
9386
}
9487
}
95-
96-
var rawResponse map[string]interface{}
97-
if err := sonic.Unmarshal(body, &rawResponse); err != nil {
98-
return providerUtils.NewBifrostOperationError("failed to parse Elevenlabs error response", err, providerName)
99-
}
100-
101-
return providerUtils.NewBifrostOperationError(fmt.Sprintf("Elevenlabs error: %v", rawResponse), fmt.Errorf("HTTP %d", resp.StatusCode()), providerName)
88+
return bifrostErr
10289
}

core/providers/elevenlabs/types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ type ElevenlabsSpeechToTextWebhookResponse struct {
155155

156156
// ERROR TYPES
157157
type ElevenlabsError struct {
158-
Detail ElevenlabsErrorDetail `json:"detail"`
158+
Detail *ElevenlabsErrorDetail `json:"detail,omitempty"`
159159
}
160160

161161
// ElevenlabsErrorDetail handles both single object (non-validation errors) and

core/providers/gemini/errors.go

Lines changed: 27 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
package gemini
22

33
import (
4-
"fmt"
54
"strconv"
65

7-
"github.com/bytedance/sonic"
86
providerUtils "github.com/maximhq/bifrost/core/providers/utils"
97
"github.com/maximhq/bifrost/core/schemas"
108
"github.com/valyala/fasthttp"
119
)
1210

13-
// ToGeminiError derives a GeminiChatRequestError from a BifrostError
14-
func ToGeminiError(bifrostErr *schemas.BifrostError) *GeminiChatRequestError {
11+
// ToGeminiError derives a GeminiGenerationError from a BifrostError
12+
func ToGeminiError(bifrostErr *schemas.BifrostError) *GeminiGenerationError {
1513
if bifrostErr == nil {
1614
return nil
1715
}
@@ -27,64 +25,43 @@ func ToGeminiError(bifrostErr *schemas.BifrostError) *GeminiChatRequestError {
2725
if bifrostErr.StatusCode != nil {
2826
code = *bifrostErr.StatusCode
2927
}
30-
return &GeminiChatRequestError{
31-
Error: GeminiChatRequestErrorStruct{
28+
return &GeminiGenerationError{
29+
Error: &GeminiGenerationErrorStruct{
3230
Code: code,
3331
Message: message,
3432
Status: status,
3533
},
3634
}
3735
}
3836

39-
// parseStreamGeminiError parses Gemini streaming error responses
40-
func parseStreamGeminiError(providerName schemas.ModelProvider, resp *fasthttp.Response) *schemas.BifrostError {
41-
body := append([]byte(nil), resp.Body()...)
42-
43-
// Try to parse as JSON first
44-
var errorResp GeminiGenerationError
45-
if err := sonic.Unmarshal(body, &errorResp); err == nil {
46-
bifrostErr := &schemas.BifrostError{
47-
IsBifrostError: false,
48-
StatusCode: schemas.Ptr(int(resp.StatusCode())),
49-
Error: &schemas.ErrorField{
50-
Code: schemas.Ptr(strconv.Itoa(errorResp.Error.Code)),
51-
Message: errorResp.Error.Message,
52-
},
37+
// parseGeminiError parses Gemini error responses
38+
func parseGeminiError(resp *fasthttp.Response) *schemas.BifrostError {
39+
// Try to parse as []GeminiGenerationError
40+
var errorResps []GeminiGenerationError
41+
bifrostErr := providerUtils.HandleProviderAPIError(resp, &errorResps)
42+
if len(errorResps) > 0 {
43+
var message string
44+
for _, errorResp := range errorResps {
45+
if errorResp.Error != nil {
46+
message = message + errorResp.Error.Message + "\n"
47+
}
48+
}
49+
if bifrostErr.Error == nil {
50+
bifrostErr.Error = &schemas.ErrorField{}
5351
}
52+
bifrostErr.Error.Message = message
5453
return bifrostErr
5554
}
5655

57-
// If JSON parsing fails, use the raw response body
58-
var rawResponse interface{}
59-
if err := sonic.Unmarshal(body, &rawResponse); err != nil {
60-
return providerUtils.NewBifrostOperationError(schemas.ErrProviderResponseUnmarshal, err, providerName)
61-
}
62-
63-
return providerUtils.NewBifrostOperationError(fmt.Sprintf("Gemini streaming error (HTTP %d): %v", resp.StatusCode(), rawResponse), fmt.Errorf("HTTP %d", resp.StatusCode()), providerName)
64-
}
65-
66-
// parseGeminiError parses Gemini error responses
67-
func parseGeminiError(providerName schemas.ModelProvider, resp *fasthttp.Response) *schemas.BifrostError {
68-
body := append([]byte(nil), resp.Body()...)
69-
70-
// Try to parse as JSON first
56+
// Try to parse as GeminiGenerationError
7157
var errorResp GeminiGenerationError
72-
if err := sonic.Unmarshal(body, &errorResp); err == nil {
73-
bifrostErr := &schemas.BifrostError{
74-
IsBifrostError: false,
75-
StatusCode: schemas.Ptr(resp.StatusCode()),
76-
Error: &schemas.ErrorField{
77-
Code: schemas.Ptr(strconv.Itoa(errorResp.Error.Code)),
78-
Message: errorResp.Error.Message,
79-
},
58+
bifrostErr = providerUtils.HandleProviderAPIError(resp, &errorResp)
59+
if errorResp.Error != nil {
60+
if bifrostErr.Error == nil {
61+
bifrostErr.Error = &schemas.ErrorField{}
8062
}
81-
return bifrostErr
63+
bifrostErr.Error.Code = schemas.Ptr(strconv.Itoa(errorResp.Error.Code))
64+
bifrostErr.Error.Message = errorResp.Error.Message
8265
}
83-
84-
var rawResponse map[string]interface{}
85-
if err := sonic.Unmarshal(body, &rawResponse); err != nil {
86-
return providerUtils.NewBifrostOperationError("failed to parse error response", err, providerName)
87-
}
88-
89-
return providerUtils.NewBifrostOperationError(fmt.Sprintf("Gemini error: %v", rawResponse), fmt.Errorf("HTTP %d", resp.StatusCode()), providerName)
66+
return bifrostErr
9067
}

0 commit comments

Comments
 (0)