diff --git a/transports/bifrost-http/integrations/utils.go b/transports/bifrost-http/integrations/utils.go index 40390167f7..de99c4024c 100644 --- a/transports/bifrost-http/integrations/utils.go +++ b/transports/bifrost-http/integrations/utils.go @@ -2,6 +2,7 @@ package integrations import ( "encoding/json" + "fmt" "log" "strings" @@ -126,7 +127,7 @@ func (g *GenericRouter) createHandler(config RouteConfig) fasthttp.RequestHandle body := ctx.Request.Body() if len(body) > 0 { if err := json.Unmarshal(body, req); err != nil { - g.sendError(ctx, fasthttp.StatusBadRequest, "Invalid JSON: "+err.Error()) + g.sendError(ctx, newBifrostError(err, "Invalid JSON")) return } } @@ -137,7 +138,7 @@ func (g *GenericRouter) createHandler(config RouteConfig) fasthttp.RequestHandle // or performing request-specific validation if config.PreCallback != nil { if err := config.PreCallback(ctx, req); err != nil { - g.sendError(ctx, fasthttp.StatusBadRequest, err.Error()) + g.sendError(ctx, newBifrostError(err, "failed to execute pre-request callback")) return } } @@ -145,15 +146,15 @@ func (g *GenericRouter) createHandler(config RouteConfig) fasthttp.RequestHandle // Convert the integration-specific request to Bifrost format bifrostReq, err := config.RequestConverter(req) if err != nil { - g.sendError(ctx, fasthttp.StatusBadRequest, err.Error()) + g.sendError(ctx, newBifrostError(err, "failed to convert request to Bifrost format")) return } if bifrostReq == nil { - g.sendError(ctx, fasthttp.StatusBadRequest, "Invalid request") + g.sendError(ctx, newBifrostError(nil, "Invalid request")) return } if bifrostReq.Model == "" { - g.sendError(ctx, fasthttp.StatusBadRequest, "Model parameter is required") + g.sendError(ctx, newBifrostError(nil, "Model parameter is required")) return } @@ -161,32 +162,27 @@ func (g *GenericRouter) createHandler(config RouteConfig) fasthttp.RequestHandle bifrostCtx := lib.ConvertToBifrostContext(ctx) result, bifrostErr := g.client.ChatCompletionRequest(*bifrostCtx, bifrostReq) if bifrostErr != nil { - g.sendError(ctx, func() int { - if bifrostErr.IsBifrostError { - return fasthttp.StatusInternalServerError - } - return fasthttp.StatusBadRequest - }(), err) + g.sendError(ctx, bifrostErr) return } // Execute post-request callback if configured // This is typically used for response modification or additional processing if config.PostCallback != nil { if err := config.PostCallback(ctx, req, result); err != nil { - g.sendError(ctx, fasthttp.StatusInternalServerError, err.Error()) + g.sendError(ctx, newBifrostError(err, "failed to execute post-request callback")) return } } if result == nil { - g.sendError(ctx, fasthttp.StatusInternalServerError, "Bifrost response is nil after post-request callback") + g.sendError(ctx, newBifrostError(nil, "Bifrost response is nil after post-request callback")) return } // Convert Bifrost response to integration-specific format and send response, err := config.ResponseConverter(result) if err != nil { - g.sendError(ctx, fasthttp.StatusInternalServerError, err.Error()) + g.sendError(ctx, newBifrostError(err, "failed to encode response")) return } g.sendSuccess(ctx, response) @@ -195,20 +191,18 @@ func (g *GenericRouter) createHandler(config RouteConfig) fasthttp.RequestHandle // sendError sends an error response with the appropriate status code and JSON body. // It handles different error types (string, error interface, or arbitrary objects). -func (g *GenericRouter) sendError(ctx *fasthttp.RequestCtx, statusCode int, err interface{}) { - ctx.SetStatusCode(statusCode) - ctx.SetContentType("application/json") +func (g *GenericRouter) sendError(ctx *fasthttp.RequestCtx, err *schemas.BifrostError) { + if err.StatusCode != nil { + ctx.SetStatusCode(*err.StatusCode) + } else { + ctx.SetStatusCode(fasthttp.StatusInternalServerError) + } - var errorBody []byte - switch e := err.(type) { - case string: - errorBody, _ = json.Marshal(map[string]string{"error": e}) - case error: - errorBody, _ = json.Marshal(map[string]string{"error": e.Error()}) - default: - errorBody, _ = json.Marshal(err) + ctx.SetContentType("application/json") + if encodeErr := json.NewEncoder(ctx).Encode(err); encodeErr != nil { + ctx.SetStatusCode(fasthttp.StatusInternalServerError) + ctx.SetBodyString(fmt.Sprintf("failed to encode error response: %v", encodeErr)) } - ctx.SetBody(errorBody) } // sendSuccess sends a successful response with HTTP 200 status and JSON body. @@ -218,7 +212,7 @@ func (g *GenericRouter) sendSuccess(ctx *fasthttp.RequestCtx, response interface responseBody, err := json.Marshal(response) if err != nil { - g.sendError(ctx, fasthttp.StatusInternalServerError, "failed to encode response: "+err.Error()) + g.sendError(ctx, newBifrostError(err, "failed to encode response")) return } @@ -346,3 +340,24 @@ func matchesAnyPattern(model string, patterns []string) bool { } return false } + +// newBifrostError wraps a standard error into a BifrostError with IsBifrostError set to false. +// This helper function reduces code duplication when handling non-Bifrost errors. +func newBifrostError(err error, message string) *schemas.BifrostError { + if err == nil { + return &schemas.BifrostError{ + IsBifrostError: false, + Error: schemas.ErrorField{ + Message: message, + }, + } + } + + return &schemas.BifrostError{ + IsBifrostError: false, + Error: schemas.ErrorField{ + Message: message, + Error: err, + }, + } +} diff --git a/transports/bifrost-http/main.go b/transports/bifrost-http/main.go index fa898f8113..8941203767 100644 --- a/transports/bifrost-http/main.go +++ b/transports/bifrost-http/main.go @@ -304,7 +304,7 @@ func handleCompletion(ctx *fasthttp.RequestCtx, client *bifrost.Bifrost, isChat bifrostCtx := lib.ConvertToBifrostContext(ctx) var resp *schemas.BifrostResponse - var err *schemas.BifrostError + var bifrostErr *schemas.BifrostError if bifrostCtx == nil { ctx.SetStatusCode(fasthttp.StatusInternalServerError) @@ -313,22 +313,13 @@ func handleCompletion(ctx *fasthttp.RequestCtx, client *bifrost.Bifrost, isChat } if isChat { - resp, err = client.ChatCompletionRequest(*bifrostCtx, bifrostReq) + resp, bifrostErr = client.ChatCompletionRequest(*bifrostCtx, bifrostReq) } else { - resp, err = client.TextCompletionRequest(*bifrostCtx, bifrostReq) + resp, bifrostErr = client.TextCompletionRequest(*bifrostCtx, bifrostReq) } - if err != nil { - if err.IsBifrostError { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - } else { - ctx.SetStatusCode(fasthttp.StatusBadRequest) - } - ctx.SetContentType("application/json") - if encodeErr := json.NewEncoder(ctx).Encode(err); encodeErr != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.SetBodyString(fmt.Sprintf("failed to encode error response: %v", encodeErr)) - } + if bifrostErr != nil { + handleBifrostError(ctx, bifrostErr) return } @@ -350,10 +341,9 @@ func handleMCPToolExecution(ctx *fasthttp.RequestCtx, client *bifrost.Bifrost) { bifrostCtx := lib.ConvertToBifrostContext(ctx) - resp, err := client.ExecuteMCPTool(*bifrostCtx, req) - if err != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.SetBodyString(fmt.Sprintf("failed to execute tool: %v", err)) + resp, bifrostErr := client.ExecuteMCPTool(*bifrostCtx, req) + if bifrostErr != nil { + handleBifrostError(ctx, bifrostErr) return } @@ -364,3 +354,17 @@ func handleMCPToolExecution(ctx *fasthttp.RequestCtx, client *bifrost.Bifrost) { ctx.SetBodyString(fmt.Sprintf("failed to encode response: %v", encodeErr)) } } + +func handleBifrostError(ctx *fasthttp.RequestCtx, bifrostErr *schemas.BifrostError) { + if bifrostErr.StatusCode != nil { + ctx.SetStatusCode(*bifrostErr.StatusCode) + } else { + ctx.SetStatusCode(fasthttp.StatusInternalServerError) + } + + ctx.SetContentType("application/json") + if encodeErr := json.NewEncoder(ctx).Encode(bifrostErr); encodeErr != nil { + ctx.SetStatusCode(fasthttp.StatusInternalServerError) + ctx.SetBodyString(fmt.Sprintf("failed to encode error response: %v", encodeErr)) + } +}