Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 42 additions & 27 deletions transports/bifrost-http/integrations/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package integrations

import (
"encoding/json"
"fmt"
"log"
"strings"

Expand Down Expand Up @@ -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
}
}
Expand All @@ -137,56 +138,51 @@ 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
}
}

// 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
}

// Execute the request through Bifrost
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)
Expand All @@ -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.
Expand All @@ -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
}

Expand Down Expand Up @@ -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,
},
}
}
40 changes: 22 additions & 18 deletions transports/bifrost-http/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
}

Expand All @@ -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
}

Expand All @@ -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))
}
}