|
| 1 | +package openai |
| 2 | + |
| 3 | +import ( |
| 4 | + "encoding/json" |
| 5 | + |
| 6 | + "github.com/fasthttp/router" |
| 7 | + bifrost "github.com/maximhq/bifrost/core" |
| 8 | + "github.com/maximhq/bifrost/transports/bifrost-http/lib" |
| 9 | + "github.com/valyala/fasthttp" |
| 10 | +) |
| 11 | + |
| 12 | +// OpenAIRouter holds route registrations for OpenAI endpoints. |
| 13 | +// It supports standard chat completions and image-enabled vision capabilities. |
| 14 | +// |
| 15 | +// Vision API Support: |
| 16 | +// The /openai/v1/chat/completions endpoint supports messages with mixed text and image content. |
| 17 | +// |
| 18 | +// Example Vision API Request: |
| 19 | +// |
| 20 | +// { |
| 21 | +// "model": "gpt-4o", |
| 22 | +// "messages": [{ |
| 23 | +// "role": "user", |
| 24 | +// "content": [ |
| 25 | +// {"type": "text", "text": "What's in this image? Keep the response short."}, |
| 26 | +// {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}} |
| 27 | +// ] |
| 28 | +// }] |
| 29 | +// } |
| 30 | +type OpenAIRouter struct { |
| 31 | + client *bifrost.Bifrost |
| 32 | +} |
| 33 | + |
| 34 | +// NewOpenAIRouter creates a new OpenAIRouter with the given bifrost client. |
| 35 | +func NewOpenAIRouter(client *bifrost.Bifrost) *OpenAIRouter { |
| 36 | + return &OpenAIRouter{client: client} |
| 37 | +} |
| 38 | + |
| 39 | +// RegisterRoutes registers all OpenAI routes on the given router. |
| 40 | +func (o *OpenAIRouter) RegisterRoutes(r *router.Router) { |
| 41 | + r.POST("/openai/v1/chat/completions", o.handleChatCompletion) |
| 42 | +} |
| 43 | + |
| 44 | +// handleChatCompletion handles POST /v1/chat/completions |
| 45 | +func (o *OpenAIRouter) handleChatCompletion(ctx *fasthttp.RequestCtx) { |
| 46 | + var req OpenAIChatRequest |
| 47 | + if err := json.Unmarshal(ctx.PostBody(), &req); err != nil { |
| 48 | + ctx.SetStatusCode(fasthttp.StatusBadRequest) |
| 49 | + errorResponse, _ := json.Marshal(map[string]string{"error": err.Error()}) |
| 50 | + ctx.SetBody(errorResponse) |
| 51 | + return |
| 52 | + } |
| 53 | + |
| 54 | + if req.Model == "" { |
| 55 | + ctx.SetStatusCode(fasthttp.StatusBadRequest) |
| 56 | + ctx.SetBodyString("Model parameter is required") |
| 57 | + return |
| 58 | + } |
| 59 | + |
| 60 | + bifrostReq := req.ConvertToBifrostRequest() |
| 61 | + |
| 62 | + bifrostCtx := lib.ConvertToBifrostContext(ctx) |
| 63 | + |
| 64 | + result, err := o.client.ChatCompletionRequest(*bifrostCtx, bifrostReq) |
| 65 | + if err != nil { |
| 66 | + ctx.SetStatusCode(fasthttp.StatusInternalServerError) |
| 67 | + errorResponse, _ := json.Marshal(err) |
| 68 | + ctx.SetBody(errorResponse) |
| 69 | + return |
| 70 | + } |
| 71 | + |
| 72 | + openaiResponse := DeriveOpenAIFromBifrostResponse(result) |
| 73 | + ctx.SetStatusCode(fasthttp.StatusOK) |
| 74 | + ctx.SetContentType("application/json") |
| 75 | + responseBody, _ := json.Marshal(openaiResponse) |
| 76 | + ctx.SetBody(responseBody) |
| 77 | +} |
0 commit comments