Skip to content

Commit 725f355

Browse files
adds dynamic ui
1 parent b478ede commit 725f355

File tree

47 files changed

+1532
-841
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1532
-841
lines changed

core/schemas/plugin.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,24 @@ type PluginShortCircuit struct {
1111
Error *BifrostError // If set, short-circuit with this error (can set AllowFallbacks field)
1212
}
1313

14+
// PluginStatus constants
15+
const (
16+
PluginStatusActive = "active"
17+
PluginStatusError = "error"
18+
PluginStatusDisabled = "disabled"
19+
PluginStatusLoading = "loading"
20+
PluginStatusUninitialized = "uninitialized"
21+
PluginStatusUnloaded = "unloaded"
22+
PluginStatusLoaded = "loaded"
23+
)
24+
25+
// PluginStatus represents the status of a plugin.
26+
type PluginStatus struct {
27+
Name string `json:"name"`
28+
Status string `json:"status"`
29+
Logs []string `json:"logs"`
30+
}
31+
1432
// Plugin defines the interface for Bifrost plugins.
1533
// Plugins can intercept and modify requests and responses at different stages
1634
// of the processing pipeline.

framework/configstore/migrations.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,22 +699,31 @@ func migrationAddProviderConfigBudgetRateLimit(ctx context.Context, db *gorm.DB)
699699
// migrationAddPluginPathColumn adds the path column to the plugin table
700700
func migrationAddPluginPathColumn(ctx context.Context, db *gorm.DB) error {
701701
m := migrator.New(db, migrator.DefaultOptions, []*migrator.Migration{{
702-
ID: "add_plugin_path_column",
702+
ID: "update_plugins_table_for_custom_plugins",
703703
Migrate: func(tx *gorm.DB) error {
704704
tx = tx.WithContext(ctx)
705705
migrator := tx.Migrator()
706706
if !migrator.HasColumn(&tables.TablePlugin{}, "path") {
707707
if err := migrator.AddColumn(&tables.TablePlugin{}, "path"); err != nil {
708708
return err
709+
}
710+
}
711+
if !migrator.HasColumn(&tables.TablePlugin{}, "is_custom") {
712+
if err := migrator.AddColumn(&tables.TablePlugin{}, "is_custom"); err != nil {
713+
return err
709714
}
710715
}
716+
return nil
711717
},
712718
Rollback: func(tx *gorm.DB) error {
713719
tx = tx.WithContext(ctx)
714720
migrator := tx.Migrator()
715721
if err := migrator.DropColumn(&tables.TablePlugin{}, "path"); err != nil {
716722
return err
717723
}
724+
if err := migrator.DropColumn(&tables.TablePlugin{}, "is_custom"); err != nil {
725+
return err
726+
}
718727
return nil
719728
},
720729
}})

framework/configstore/rdb.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -826,12 +826,12 @@ func (s *RDBConfigStore) DeleteModelPrices(ctx context.Context, tx ...*gorm.DB)
826826

827827
// PLUGINS METHODS
828828

829-
func (s *RDBConfigStore) GetPlugins(ctx context.Context) ([]tables.TablePlugin, error) {
830-
var plugins []tables.TablePlugin
829+
func (s *RDBConfigStore) GetPlugins(ctx context.Context) ([]*tables.TablePlugin, error) {
830+
var plugins []*tables.TablePlugin
831831
if err := s.db.WithContext(ctx).Find(&plugins).Error; err != nil {
832832
return nil, err
833833
}
834-
return plugins, nil
834+
return plugins, nil
835835
}
836836

837837
func (s *RDBConfigStore) GetPlugin(ctx context.Context, name string) (*tables.TablePlugin, error) {
@@ -852,6 +852,10 @@ func (s *RDBConfigStore) CreatePlugin(ctx context.Context, plugin *tables.TableP
852852
} else {
853853
txDB = s.db
854854
}
855+
// Mark plugin as custom if path is not empty
856+
if plugin.Path != nil && *plugin.Path != "" {
857+
plugin.IsCustom = true
858+
}
855859
return txDB.WithContext(ctx).Create(plugin).Error
856860
}
857861

@@ -867,6 +871,11 @@ func (s *RDBConfigStore) UpdatePlugin(ctx context.Context, plugin *tables.TableP
867871
localTx = true
868872
}
869873

874+
// Mark plugin as custom if path is not empty
875+
if plugin.Path != nil && *plugin.Path != "" {
876+
plugin.IsCustom = true
877+
}
878+
870879
if err := txDB.WithContext(ctx).Delete(&tables.TablePlugin{}, "name = ?", plugin.Name).Error; err != nil {
871880
if localTx {
872881
txDB.Rollback()

framework/configstore/store.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ type ConfigStore interface {
5757
UpdateConfig(ctx context.Context, config *tables.TableConfig, tx ...*gorm.DB) error
5858

5959
// Plugins CRUD
60-
GetPlugins(ctx context.Context) ([]tables.TablePlugin, error)
60+
GetPlugins(ctx context.Context) ([]*tables.TablePlugin, error)
6161
GetPlugin(ctx context.Context, name string) (*tables.TablePlugin, error)
6262
CreatePlugin(ctx context.Context, plugin *tables.TablePlugin, tx ...*gorm.DB) error
6363
UpdatePlugin(ctx context.Context, plugin *tables.TablePlugin, tx ...*gorm.DB) error

framework/configstore/tables/plugin.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type TablePlugin struct {
1717
ConfigJSON string `gorm:"type:text" json:"-"` // JSON serialized plugin.Config
1818
CreatedAt time.Time `gorm:"index;not null" json:"created_at"`
1919
UpdatedAt time.Time `gorm:"index;not null" json:"updated_at"`
20+
IsCustom bool `gorm:"default:false" json:"isCustom"`
2021

2122
// Virtual fields for runtime use (not stored in DB)
2223
Config any `gorm:"-" json:"config,omitempty"`

plugins/otel/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ configs:
140140
content: |
141141
server:
142142
http_listen_port: 3200
143-
grpc_listen_port: 3200
143+
grpc_listen_port: 3201
144144
log_level: info
145145
146146
distributor:

transports/bifrost-http/handlers/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type ConfigManager interface {
2424
ReloadClientConfigFromConfigStore() error
2525
ReloadPricingManager() error
2626
UpdateDropExcessRequests(value bool)
27-
ReloadPlugin(ctx context.Context, name string, pluginConfig any) error
27+
ReloadPlugin(ctx context.Context, name string,path *string, pluginConfig any) error
2828
}
2929

3030
// ConfigHandler manages runtime configuration updates for Bifrost.

transports/bifrost-http/handlers/inference.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ func (h *CompletionHandler) textCompletion(ctx *fasthttp.RequestCtx) {
412412
// chatCompletion handles POST /v1/chat/completions - Process chat completion requests
413413
func (h *CompletionHandler) chatCompletion(ctx *fasthttp.RequestCtx) {
414414
var req ChatRequest
415-
if err := sonic.Unmarshal(ctx.PostBody(), &req); err != nil {
415+
if err := sonic.Unmarshal(ctx.PostBody(), &req); err != nil {
416416
SendError(ctx, fasthttp.StatusBadRequest, fmt.Sprintf("Invalid request format: %v", err), h.logger)
417417
return
418418
}

transports/bifrost-http/handlers/plugins.go

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
type PluginsLoader interface {
1919
ReloadPlugin(ctx context.Context, name string, path *string, pluginConfig any) error
2020
RemovePlugin(ctx context.Context, name string) error
21+
GetPluginStatus() []schemas.PluginStatus
2122
}
2223

2324
// PluginsHandler is the handler for the plugins API
@@ -41,11 +42,13 @@ type CreatePluginRequest struct {
4142
Name string `json:"name"`
4243
Enabled bool `json:"enabled"`
4344
Config map[string]any `json:"config"`
45+
Path *string `json:"path"`
4446
}
4547

4648
// UpdatePluginRequest is the request body for updating a plugin
4749
type UpdatePluginRequest struct {
4850
Enabled bool `json:"enabled"`
51+
Path *string `json:"path"`
4952
Config map[string]any `json:"config"`
5053
}
5154

@@ -66,10 +69,64 @@ func (h *PluginsHandler) getPlugins(ctx *fasthttp.RequestCtx) {
6669
SendError(ctx, 500, "Failed to retrieve plugins", h.logger)
6770
return
6871
}
69-
72+
// Fetching status
73+
pluginStatus := h.pluginsLoader.GetPluginStatus()
74+
// Creating ephemeral struct for the plugins
75+
finalPlugins := []struct{
76+
Name string `json:"name"`
77+
Enabled bool `json:"enabled"`
78+
Config any `json:"config"`
79+
IsCustom bool `json:"isCustom"`
80+
Path *string `json:"path"`
81+
Status schemas.PluginStatus `json:"status"`
82+
}{}
83+
// Iterating over plugin status to get the plugin info
84+
for _,pluginStatus := range pluginStatus {
85+
var pluginInfo *configstoreTables.TablePlugin
86+
for _, plugin := range plugins {
87+
if plugin.Name == pluginStatus.Name {
88+
pluginInfo = plugin
89+
break
90+
}
91+
}
92+
if pluginInfo == nil {
93+
finalPlugins = append(finalPlugins, struct{
94+
Name string `json:"name"`
95+
Enabled bool `json:"enabled"`
96+
Config any `json:"config"`
97+
IsCustom bool `json:"isCustom"`
98+
Path *string `json:"path"`
99+
Status schemas.PluginStatus `json:"status"`
100+
}{
101+
Name: pluginStatus.Name,
102+
Enabled: pluginStatus.Status != schemas.PluginStatusDisabled,
103+
Config: map[string]any{},
104+
IsCustom: false,
105+
Path: nil,
106+
Status: pluginStatus,
107+
})
108+
continue
109+
}
110+
finalPlugins = append(finalPlugins, struct{
111+
Name string `json:"name"`
112+
Enabled bool `json:"enabled"`
113+
Config any `json:"config"`
114+
IsCustom bool `json:"isCustom"`
115+
Path *string `json:"path"`
116+
Status schemas.PluginStatus `json:"status"`
117+
}{
118+
Name: pluginInfo.Name,
119+
Enabled: pluginInfo.Enabled,
120+
Config: pluginInfo.Config,
121+
IsCustom: pluginInfo.IsCustom,
122+
Path: pluginInfo.Path,
123+
Status: pluginStatus,
124+
})
125+
}
126+
// Creating ephemeral struct
70127
SendJSON(ctx, map[string]any{
71-
"plugins": plugins,
72-
"count": len(plugins),
128+
"plugins": finalPlugins,
129+
"count": len(plugins),
73130
}, h.logger)
74131
}
75132

@@ -134,6 +191,8 @@ func (h *PluginsHandler) createPlugin(ctx *fasthttp.RequestCtx) {
134191
Name: request.Name,
135192
Enabled: request.Enabled,
136193
Config: request.Config,
194+
Path: request.Path,
195+
IsCustom: true,
137196
}); err != nil {
138197
h.logger.Error("failed to create plugin: %v", err)
139198
SendError(ctx, 500, "Failed to create plugin", h.logger)
@@ -149,7 +208,7 @@ func (h *PluginsHandler) createPlugin(ctx *fasthttp.RequestCtx) {
149208

150209
// We reload the plugin if its enabled
151210
if request.Enabled {
152-
if err := h.pluginsLoader.ReloadPlugin(ctx, request.Name, nil, request.Config); err != nil {
211+
if err := h.pluginsLoader.ReloadPlugin(ctx, request.Name, request.Path, request.Config); err != nil {
153212
h.logger.Error("failed to load plugin: %v", err)
154213
SendJSON(ctx, map[string]any{
155214
"message": fmt.Sprintf("Plugin created successfully; but failed to load plugin with new config: %v", err),
@@ -188,16 +247,21 @@ func (h *PluginsHandler) updatePlugin(ctx *fasthttp.RequestCtx) {
188247
SendError(ctx, 400, "Empty 'name' parameter not allowed", h.logger)
189248
return
190249
}
191-
250+
var plugin *configstoreTables.TablePlugin
251+
var err error
192252
// Check if plugin exists
193-
if _, err := h.configStore.GetPlugin(ctx, name); err != nil {
253+
plugin, err = h.configStore.GetPlugin(ctx, name)
254+
if err != nil {
194255
// If doesn't exist, create it
195256
if errors.Is(err, configstore.ErrNotFound) {
196-
if err := h.configStore.CreatePlugin(ctx, &configstoreTables.TablePlugin{
257+
plugin = &configstoreTables.TablePlugin{
197258
Name: name,
198259
Enabled: false,
199260
Config: map[string]any{},
200-
}); err != nil {
261+
Path: nil,
262+
IsCustom: true,
263+
}
264+
if err := h.configStore.CreatePlugin(ctx, plugin); err != nil {
201265
h.logger.Error("failed to create plugin: %v", err)
202266
SendError(ctx, 500, "Failed to create plugin", h.logger)
203267
return
@@ -209,24 +273,28 @@ func (h *PluginsHandler) updatePlugin(ctx *fasthttp.RequestCtx) {
209273
}
210274
}
211275

276+
// Unmarshalling the request body
212277
var request UpdatePluginRequest
213278
if err := json.Unmarshal(ctx.PostBody(), &request); err != nil {
214279
h.logger.Error("failed to unmarshal update plugin request: %v", err)
215280
SendError(ctx, 400, "Invalid request body", h.logger)
216281
return
217282
}
218283

284+
// Updating the plugin
219285
if err := h.configStore.UpdatePlugin(ctx, &configstoreTables.TablePlugin{
220286
Name: name,
221287
Enabled: request.Enabled,
222288
Config: request.Config,
289+
Path: request.Path,
290+
IsCustom: plugin.IsCustom,
223291
}); err != nil {
224292
h.logger.Error("failed to update plugin: %v", err)
225293
SendError(ctx, 500, "Failed to update plugin", h.logger)
226294
return
227295
}
228296

229-
plugin, err := h.configStore.GetPlugin(ctx, name)
297+
plugin, err = h.configStore.GetPlugin(ctx, name)
230298
if err != nil {
231299
if errors.Is(err, gorm.ErrRecordNotFound) {
232300
SendError(ctx, fasthttp.StatusNotFound, "Plugin not found", h.logger)
@@ -238,7 +306,7 @@ func (h *PluginsHandler) updatePlugin(ctx *fasthttp.RequestCtx) {
238306
}
239307
// We reload the plugin if its enabled, otherwise we stop it
240308
if request.Enabled {
241-
if err := h.pluginsLoader.ReloadPlugin(ctx, name, nil, request.Config); err != nil {
309+
if err := h.pluginsLoader.ReloadPlugin(ctx, name, request.Path, request.Config); err != nil {
242310
h.logger.Error("failed to load plugin: %v", err)
243311
SendJSON(ctx, map[string]any{
244312
"message": fmt.Sprintf("Plugin updated successfully; but failed to load plugin with new config: %v", err),
@@ -247,6 +315,7 @@ func (h *PluginsHandler) updatePlugin(ctx *fasthttp.RequestCtx) {
247315
return
248316
}
249317
} else {
318+
ctx.SetUserValue("isDisabled", true)
250319
if err := h.pluginsLoader.RemovePlugin(ctx, name); err != nil {
251320
h.logger.Error("failed to stop plugin: %v", err)
252321
SendJSON(ctx, map[string]any{

0 commit comments

Comments
 (0)