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
2 changes: 1 addition & 1 deletion .github/workflows/transport-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ jobs:

- name: Commit and push UI changes
run: |
git add .
git add transports/ui
if git diff --staged --quiet; then
echo "No changes to commit. UI build is already up to date."
else
Expand Down
7 changes: 6 additions & 1 deletion core/bifrost.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,12 @@ func (bifrost *Bifrost) GetMCPClients() ([]schemas.MCPClient, error) {
// })
func (bifrost *Bifrost) AddMCPClient(config schemas.MCPClientConfig) error {
if bifrost.mcpManager == nil {
return fmt.Errorf("MCP is not configured in this Bifrost instance")
manager := &MCPManager{
clientMap: make(map[string]*MCPClient),
logger: bifrost.logger,
}

bifrost.mcpManager = manager
}

return bifrost.mcpManager.AddClient(config)
Expand Down
2 changes: 2 additions & 0 deletions docs/benchmarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ _\*Bifrost's overhead is measured at 59 µs on t3.medium and 11 µs on t3.xlarge

**Note**: On the t3.xlarge, we tested with significantly larger response payloads (~10 KB average vs ~1 KB on t3.medium). Even so, response parsing time dropped dramatically thanks to better CPU throughput and Bifrost's optimized memory reuse.

**Disclaimer**: These metrics are measured without the UI enabled. When using the UI, there is no drop in performance - only memory usage increases due to the additional UI build being served.

Comment thread
Pratham-Mishra04 marked this conversation as resolved.
---

## 🎯 Key Performance Highlights
Expand Down
18 changes: 6 additions & 12 deletions transports/bifrost-http/handlers/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ func NewConfigHandler(client *bifrost.Bifrost, logger schemas.Logger, store *lib
func (h *ConfigHandler) RegisterRoutes(r *router.Router) {
r.GET("/api/config", h.GetConfig)
r.PUT("/api/config", h.handleUpdateConfig)
r.POST("/api/config/save", h.SaveConfig)
}

// GetConfig handles GET /config - Get the current configuration
Expand Down Expand Up @@ -74,27 +73,22 @@ func (h *ConfigHandler) handleUpdateConfig(ctx *fasthttp.RequestCtx) {
updatedConfig.InitialPoolSize = req.InitialPoolSize
}

if req.LogQueueSize != currentConfig.LogQueueSize {
updatedConfig.LogQueueSize = req.LogQueueSize
}

// Update the store with the new config
h.store.ClientConfig = updatedConfig

ctx.SetStatusCode(fasthttp.StatusOK)
SendJSON(ctx, map[string]any{
"status": "success",
"message": "Configuration updated successfully",
}, h.logger)
}

// SaveConfig handles POST /config/save - Persist current configuration to JSON file
func (h *ConfigHandler) SaveConfig(ctx *fasthttp.RequestCtx) {
// Save current configuration back to the original JSON file
if err := h.store.SaveConfig(); err != nil {
h.logger.Warn(fmt.Sprintf("Failed to save configuration: %v", err))
SendError(ctx, fasthttp.StatusInternalServerError, fmt.Sprintf("Failed to save configuration: %v", err), h.logger)
return
}

ctx.SetStatusCode(fasthttp.StatusOK)
SendJSON(ctx, map[string]any{
"status": "success",
"message": "Configuration saved successfully",
"message": "Configuration updated successfully",
}, h.logger)
}
9 changes: 8 additions & 1 deletion transports/bifrost-http/handlers/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ func NewLoggingHandler(logManager logging.LogManager, logger schemas.Logger) *Lo
func (h *LoggingHandler) RegisterRoutes(r *router.Router) {
// Log retrieval with filtering, search, and pagination
r.GET("/api/logs", h.GetLogs)
r.GET("/api/logs/dropped", h.GetDroppedRequests)
}

// GetLogs handles GET /v1/logs - Get logs with filtering, search, and pagination via query parameters
// GetLogs handles GET /api/logs - Get logs with filtering, search, and pagination via query parameters
func (h *LoggingHandler) GetLogs(ctx *fasthttp.RequestCtx) {
// Parse query parameters into filters
filters := &logging.SearchFilters{}
Expand Down Expand Up @@ -139,6 +140,12 @@ func (h *LoggingHandler) GetLogs(ctx *fasthttp.RequestCtx) {
SendJSON(ctx, result, h.logger)
}

// GetDroppedRequests handles GET /api/logs/dropped - Get the number of dropped requests
func (h *LoggingHandler) GetDroppedRequests(ctx *fasthttp.RequestCtx) {
droppedRequests := h.logManager.GetDroppedRequests()
SendJSON(ctx, map[string]int64{"dropped_requests": droppedRequests}, h.logger)
}

// Helper functions

// parseCommaSeparated splits a comma-separated string into a slice
Expand Down
28 changes: 23 additions & 5 deletions transports/bifrost-http/handlers/mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (h *MCPHandler) ExecuteTool(ctx *fasthttp.RequestCtx) {
SendJSON(ctx, resp, h.logger)
}

// GetMCPClients handles GET /mcp/clients - Get all MCP clients
// GetMCPClients handles GET /api/mcp/clients - Get all MCP clients
func (h *MCPHandler) GetMCPClients(ctx *fasthttp.RequestCtx) {
// Get clients from store config
configsInStore := h.store.MCPConfig
Expand Down Expand Up @@ -120,7 +120,7 @@ func (h *MCPHandler) GetMCPClients(ctx *fasthttp.RequestCtx) {
SendJSON(ctx, clients, h.logger)
}

// ReconnectMCPClient handles POST /mcp/client/{name}/reconnect - Reconnect an MCP client
// ReconnectMCPClient handles POST /api/mcp/client/{name}/reconnect - Reconnect an MCP client
func (h *MCPHandler) ReconnectMCPClient(ctx *fasthttp.RequestCtx) {
name, err := getNameFromCtx(ctx)
if err != nil {
Expand All @@ -139,7 +139,7 @@ func (h *MCPHandler) ReconnectMCPClient(ctx *fasthttp.RequestCtx) {
}, h.logger)
}

// AddMCPClient handles POST /mcp/client - Add a new MCP client
// AddMCPClient handles POST /api/mcp/client - Add a new MCP client
func (h *MCPHandler) AddMCPClient(ctx *fasthttp.RequestCtx) {
var req schemas.MCPClientConfig
if err := json.Unmarshal(ctx.PostBody(), &req); err != nil {
Expand All @@ -152,13 +152,19 @@ func (h *MCPHandler) AddMCPClient(ctx *fasthttp.RequestCtx) {
return
}

if err := h.store.SaveConfig(); err != nil {
h.logger.Warn(fmt.Sprintf("Failed to save configuration: %v", err))
SendError(ctx, fasthttp.StatusInternalServerError, fmt.Sprintf("Failed to save configuration: %v", err), h.logger)
return
}

SendJSON(ctx, map[string]any{
"status": "success",
"message": "MCP client added successfully",
}, h.logger)
}

// EditMCPClientTools handles PUT /mcp/client/{name} - Edit MCP client tools
// EditMCPClientTools handles PUT /api/mcp/client/{name} - Edit MCP client tools
func (h *MCPHandler) EditMCPClientTools(ctx *fasthttp.RequestCtx) {
name, err := getNameFromCtx(ctx)
if err != nil {
Expand All @@ -180,13 +186,19 @@ func (h *MCPHandler) EditMCPClientTools(ctx *fasthttp.RequestCtx) {
return
}

if err := h.store.SaveConfig(); err != nil {
h.logger.Warn(fmt.Sprintf("Failed to save configuration: %v", err))
SendError(ctx, fasthttp.StatusInternalServerError, fmt.Sprintf("Failed to save configuration: %v", err), h.logger)
return
}

SendJSON(ctx, map[string]any{
"status": "success",
"message": "MCP client tools edited successfully",
}, h.logger)
}

// RemoveMCPClient handles DELETE /mcp/client/{name} - Remove an MCP client
// RemoveMCPClient handles DELETE /api/mcp/client/{name} - Remove an MCP client
func (h *MCPHandler) RemoveMCPClient(ctx *fasthttp.RequestCtx) {
name, err := getNameFromCtx(ctx)
if err != nil {
Expand All @@ -199,6 +211,12 @@ func (h *MCPHandler) RemoveMCPClient(ctx *fasthttp.RequestCtx) {
return
}

if err := h.store.SaveConfig(); err != nil {
h.logger.Warn(fmt.Sprintf("Failed to save configuration: %v", err))
SendError(ctx, fasthttp.StatusInternalServerError, fmt.Sprintf("Failed to save configuration: %v", err), h.logger)
return
}

SendJSON(ctx, map[string]any{
"status": "success",
"message": "MCP client removed successfully",
Expand Down
28 changes: 23 additions & 5 deletions transports/bifrost-http/handlers/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func (h *ProviderHandler) RegisterRoutes(r *router.Router) {
r.DELETE("/api/providers/{provider}", h.DeleteProvider)
}

// ListProviders handles GET /providers - List all providers
// ListProviders handles GET /api/providers - List all providers
func (h *ProviderHandler) ListProviders(ctx *fasthttp.RequestCtx) {
providers, err := h.store.GetAllProviders()
if err != nil {
Expand Down Expand Up @@ -119,7 +119,7 @@ func (h *ProviderHandler) ListProviders(ctx *fasthttp.RequestCtx) {
SendJSON(ctx, response, h.logger)
}

// GetProvider handles GET /providers/{provider} - Get specific provider
// GetProvider handles GET /api/providers/{provider} - Get specific provider
func (h *ProviderHandler) GetProvider(ctx *fasthttp.RequestCtx) {
provider, err := getProviderFromCtx(ctx)
if err != nil {
Expand All @@ -138,7 +138,7 @@ func (h *ProviderHandler) GetProvider(ctx *fasthttp.RequestCtx) {
SendJSON(ctx, response, h.logger)
}

// AddProvider handles POST /providers - Add a new provider
// AddProvider handles POST /api/providers - Add a new provider
func (h *ProviderHandler) AddProvider(ctx *fasthttp.RequestCtx) {
var req AddProviderRequest
if err := json.Unmarshal(ctx.PostBody(), &req); err != nil {
Expand Down Expand Up @@ -200,14 +200,20 @@ func (h *ProviderHandler) AddProvider(ctx *fasthttp.RequestCtx) {
return
}

if err := h.store.SaveConfig(); err != nil {
h.logger.Warn(fmt.Sprintf("Failed to save configuration: %v", err))
SendError(ctx, fasthttp.StatusInternalServerError, fmt.Sprintf("Failed to save configuration: %v", err), h.logger)
return
}

h.logger.Info(fmt.Sprintf("Provider %s added successfully", req.Provider))

response := h.getProviderResponseFromConfig(req.Provider, config)

SendJSON(ctx, response, h.logger)
}

// UpdateProvider handles PUT /providers/{provider} - Update provider config
// UpdateProvider handles PUT /api/providers/{provider} - Update provider config
// NOTE: This endpoint expects ALL fields to be provided in the request body,
// including both edited and non-edited fields. Partial updates are not supported.
// The frontend should send the complete provider configuration.
Expand Down Expand Up @@ -340,6 +346,12 @@ func (h *ProviderHandler) UpdateProvider(ctx *fasthttp.RequestCtx) {
return
}

if err := h.store.SaveConfig(); err != nil {
h.logger.Warn(fmt.Sprintf("Failed to save configuration: %v", err))
SendError(ctx, fasthttp.StatusInternalServerError, fmt.Sprintf("Failed to save configuration: %v", err), h.logger)
return
}

if config.ConcurrencyAndBufferSize.Concurrency != oldConfigRaw.ConcurrencyAndBufferSize.Concurrency ||
config.ConcurrencyAndBufferSize.BufferSize != oldConfigRaw.ConcurrencyAndBufferSize.BufferSize {
// Update concurrency and queue configuration in Bifrost
Expand All @@ -354,7 +366,7 @@ func (h *ProviderHandler) UpdateProvider(ctx *fasthttp.RequestCtx) {
SendJSON(ctx, response, h.logger)
}

// DeleteProvider handles DELETE /providers/{provider} - Remove provider
// DeleteProvider handles DELETE /api/providers/{provider} - Remove provider
func (h *ProviderHandler) DeleteProvider(ctx *fasthttp.RequestCtx) {
provider, err := getProviderFromCtx(ctx)
if err != nil {
Expand All @@ -375,6 +387,12 @@ func (h *ProviderHandler) DeleteProvider(ctx *fasthttp.RequestCtx) {
return
}

if err := h.store.SaveConfig(); err != nil {
h.logger.Warn(fmt.Sprintf("Failed to save configuration: %v", err))
SendError(ctx, fasthttp.StatusInternalServerError, fmt.Sprintf("Failed to save configuration: %v", err), h.logger)
return
}

h.logger.Info(fmt.Sprintf("Provider %s removed successfully", provider))

response := ProviderResponse{
Expand Down
7 changes: 4 additions & 3 deletions transports/bifrost-http/lib/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import (
// ClientConfig represents the core configuration for Bifrost HTTP transport and the Bifrost Client.
// It includes settings for excess request handling, Prometheus metrics, and initial pool size.
type ClientConfig struct {
DropExcessRequests bool `json:"drop_excess_requests"`
InitialPoolSize int `json:"initial_pool_size"`
PrometheusLabels []string `json:"prometheus_labels"`
DropExcessRequests bool `json:"drop_excess_requests"` // Drop excess requests if the provider queue is full
InitialPoolSize int `json:"initial_pool_size"` // The initial pool size for the bifrost client
PrometheusLabels []string `json:"prometheus_labels"` // The labels to be used for prometheus metrics
LogQueueSize int `json:"log_queue_size"` // The size of the log queue, additional requests will be dropped (not saved for ui) if the queue is full
}

// ProviderConfig represents the configuration for a specific AI model provider.
Expand Down
Loading