From 3dd7a49f8b96150ab3fc85f81a5fdb7de0907598 Mon Sep 17 00:00:00 2001 From: Vaibhav mittal Date: Thu, 21 May 2026 10:18:12 +0000 Subject: [PATCH] feat(governance): add virtual key blocked models Signed-off-by: Vaibhav mittal --- framework/configstore/migrations.go | 60 ++++++++++++++++ framework/configstore/tables/virtualkey.go | 34 +++++---- plugins/governance/main.go | 14 ++++ plugins/governance/resolver.go | 24 ++++--- plugins/governance/utils.go | 14 +++- .../bifrost-http/handlers/governance.go | 64 ++++++++++------- .../views/virtualKeyDetailsSheet.tsx | 43 +++++++++--- .../virtual-keys/views/virtualKeySheet.tsx | 70 ++++++++++++++++++- ui/lib/types/governance.ts | 3 + 9 files changed, 269 insertions(+), 57 deletions(-) diff --git a/framework/configstore/migrations.go b/framework/configstore/migrations.go index 4ddde36049c..a09bb5d7840 100644 --- a/framework/configstore/migrations.go +++ b/framework/configstore/migrations.go @@ -796,6 +796,9 @@ func triggerMigrations(ctx context.Context, db *gorm.DB) error { if err := migrationRefreshConfigHashAfterMCPExternalServerURLRemoval(ctx, db); err != nil { return err } + if err := migrationAddVirtualKeyBlacklistedModelsColumn(ctx, db); err != nil { + return err + } return nil } @@ -8539,3 +8542,60 @@ func migrationAddTempTokensTable(ctx context.Context, db *gorm.DB) error { } return nil } + +// migrationAddVirtualKeyBlacklistedModelsColumn adds the blacklisted_models JSON column +// to governance_virtual_key_provider_configs, matching the provider-key blacklist pattern. +func migrationAddVirtualKeyBlacklistedModelsColumn(ctx context.Context, db *gorm.DB) error { + m := migrator.New(db, migrator.DefaultOptions, []*migrator.Migration{{ + ID: "add_vk_provider_config_blacklisted_models_column", + Migrate: func(tx *gorm.DB) error { + tx = tx.WithContext(ctx) + mg := tx.Migrator() + if !mg.HasColumn(&tables.TableVirtualKeyProviderConfig{}, "blacklisted_models") { + if err := mg.AddColumn(&tables.TableVirtualKeyProviderConfig{}, "blacklisted_models"); err != nil { + return fmt.Errorf("failed to add blacklisted_models column: %w", err) + } + // Backfill empty arrays for existing rows (only when column is newly added) + if err := tx.Exec("UPDATE governance_virtual_key_provider_configs SET blacklisted_models = '[]' WHERE blacklisted_models IS NULL OR blacklisted_models = ''").Error; err != nil { + return fmt.Errorf("failed to backfill blacklisted_models: %w", err) + } + // Recompute config_hash for all VKs affected by the backfill so they + // do not appear stale after upgrade (same pattern as allow_all_keys migration). + var virtualKeys []tables.TableVirtualKey + if err := tx. + Preload("ProviderConfigs"). + Preload("ProviderConfigs.Keys"). + Preload("MCPConfigs"). + Find(&virtualKeys).Error; err != nil { + return fmt.Errorf("failed to fetch virtual keys for hash recomputation: %w", err) + } + for _, vk := range virtualKeys { + newHash, err := GenerateVirtualKeyHash(vk) + if err != nil { + return fmt.Errorf("failed to generate hash for VK %s: %w", vk.ID, err) + } + if err := tx.Model(&tables.TableVirtualKey{}). + Where("id = ?", vk.ID). + Update("config_hash", newHash).Error; err != nil { + return fmt.Errorf("failed to update config_hash for VK %s: %w", vk.ID, err) + } + } + } + return nil + }, + Rollback: func(tx *gorm.DB) error { + tx = tx.WithContext(ctx) + mg := tx.Migrator() + if mg.HasColumn(&tables.TableVirtualKeyProviderConfig{}, "blacklisted_models") { + if err := mg.DropColumn(&tables.TableVirtualKeyProviderConfig{}, "blacklisted_models"); err != nil { + return fmt.Errorf("failed to drop blacklisted_models column: %w", err) + } + } + return nil + }, + }}) + if err := m.Migrate(); err != nil { + return fmt.Errorf("error running add_vk_provider_config_blacklisted_models_column migration: %s", err.Error()) + } + return nil +} diff --git a/framework/configstore/tables/virtualkey.go b/framework/configstore/tables/virtualkey.go index d018dd99c5c..bf610e849ec 100644 --- a/framework/configstore/tables/virtualkey.go +++ b/framework/configstore/tables/virtualkey.go @@ -24,12 +24,13 @@ func (TableVirtualKeyProviderConfigKey) TableName() string { // TableVirtualKeyProviderConfig represents a provider configuration for a virtual key type TableVirtualKeyProviderConfig struct { - ID uint `gorm:"primaryKey;autoIncrement" json:"id"` - VirtualKeyID string `gorm:"type:varchar(255);not null" json:"virtual_key_id"` - Provider string `gorm:"type:varchar(50);not null" json:"provider"` - Weight *float64 `json:"weight"` - AllowedModels schemas.WhiteList `gorm:"type:text;serializer:json" json:"allowed_models"` // ["*"] allows all models; empty denies all (deny-by-default) - AllowAllKeys bool `gorm:"default:false" json:"allow_all_keys"` // True means all keys allowed; false with empty Keys means no keys allowed (deny-by-default) + ID uint `gorm:"primaryKey;autoIncrement" json:"id"` + VirtualKeyID string `gorm:"type:varchar(255);not null" json:"virtual_key_id"` + Provider string `gorm:"type:varchar(50);not null" json:"provider"` + Weight *float64 `json:"weight"` + AllowedModels schemas.WhiteList `gorm:"type:text;serializer:json" json:"allowed_models"` // ["*"] allows all models; empty denies all (deny-by-default) + BlacklistedModels schemas.BlackList `gorm:"type:text;serializer:json" json:"blacklisted_models"` // ["*"] blocks all models; empty blocks none + AllowAllKeys bool `gorm:"default:false" json:"allow_all_keys"` // True means all keys allowed; false with empty Keys means no keys allowed (deny-by-default) RateLimitID *string `gorm:"type:varchar(255);index" json:"rate_limit_id,omitempty"` // Relationships @@ -77,30 +78,39 @@ func (pc *TableVirtualKeyProviderConfig) UnmarshalJSON(data []byte) error { return nil } -// BeforeSave validates WhiteList fields before GORM persists the record. +// BeforeSave validates WhiteList and BlackList fields before GORM persists the record. func (pc *TableVirtualKeyProviderConfig) BeforeSave(tx *gorm.DB) error { if err := pc.AllowedModels.Validate(); err != nil { return fmt.Errorf("invalid allowed_models: %w", err) } + if err := pc.BlacklistedModels.Validate(); err != nil { + return fmt.Errorf("invalid blacklisted_models: %w", err) + } return nil } -// MarshalJSON custom marshaller to ensure AllowedModels is always an array (never null) +// MarshalJSON custom marshaller to ensure AllowedModels and BlacklistedModels are always arrays (never null) func (pc TableVirtualKeyProviderConfig) MarshalJSON() ([]byte, error) { type Alias TableVirtualKeyProviderConfig - // Ensure AllowedModels is an empty slice instead of nil + // Ensure arrays are empty slices instead of nil allowedModels := pc.AllowedModels if allowedModels == nil { allowedModels = []string{} } + blacklistedModels := pc.BlacklistedModels + if blacklistedModels == nil { + blacklistedModels = []string{} + } return json.Marshal(&struct { Alias - AllowedModels []string `json:"allowed_models"` + AllowedModels []string `json:"allowed_models"` + BlacklistedModels []string `json:"blacklisted_models"` }{ - Alias: Alias(pc), - AllowedModels: allowedModels, + Alias: Alias(pc), + AllowedModels: allowedModels, + BlacklistedModels: blacklistedModels, }) } diff --git a/plugins/governance/main.go b/plugins/governance/main.go index 879989bc522..21d5bf4e73a 100644 --- a/plugins/governance/main.go +++ b/plugins/governance/main.go @@ -767,8 +767,22 @@ func (p *GovernancePlugin) loadBalanceProvider(ctx *schemas.BifrostContext, req p.logger.Debug("[Governance] Virtual key has %d provider configs: %v", len(providerConfigs), configuredProviders) ctx.AppendRoutingEngineLog(schemas.RoutingEngineGovernance, schemas.LogLevelInfo, fmt.Sprintf("Load balancing model %s across %d configured providers: %v", modelStr, len(providerConfigs), configuredProviders)) + // Pre-pass: if any config for a provider blacklists the model, that provider is fully blocked. + blacklistedProviders := make(map[string]bool) + for _, config := range providerConfigs { + if config.BlacklistedModels.IsBlocked(modelStr) { + blacklistedProviders[config.Provider] = true + } + } + allowedProviderConfigs := make([]configstoreTables.TableVirtualKeyProviderConfig, 0) for _, config := range providerConfigs { + // Blacklist check wins over allowlist (same as provider-key enforcement) + if blacklistedProviders[config.Provider] { + ctx.AppendRoutingEngineLog(schemas.RoutingEngineGovernance, schemas.LogLevelInfo, fmt.Sprintf("Provider %s excluded: model %s is blacklisted", config.Provider, modelStr)) + continue + } + // Delegate model allowance check to model catalog // This handles all cross-provider logic (OpenRouter, Vertex, Groq, Bedrock) // and provider-prefixed allowed_models entries diff --git a/plugins/governance/resolver.go b/plugins/governance/resolver.go index f03ef62c79c..d8d901e5689 100644 --- a/plugins/governance/resolver.go +++ b/plugins/governance/resolver.go @@ -322,29 +322,37 @@ func (r *BudgetResolver) EvaluateVirtualKeyRequest(ctx *schemas.BifrostContext, } } -// isModelAllowed checks if the requested model is allowed for this VK +// isModelAllowed checks if the requested model is allowed for this VK. +// Blacklisted models win over allowed models (same semantics as provider-key enforcement). +// Two-pass: blacklist scan across all matching configs first, then allowlist scan. func (r *BudgetResolver) isModelAllowed(vk *configstoreTables.TableVirtualKey, provider schemas.ModelProvider, model string) bool { // Empty ProviderConfigs means no models are allowed (deny-by-default) if len(vk.ProviderConfigs) == 0 { return false } + // Pass 1: if any matching provider config blacklists the model, block immediately. + for _, pc := range vk.ProviderConfigs { + if pc.Provider == string(provider) && pc.BlacklistedModels.IsBlocked(model) { + return false + } + } + + // Pass 2: allowlist check — model is allowed if any matching config permits it. for _, pc := range vk.ProviderConfigs { if pc.Provider == string(provider) { - // Delegate model allowance check to model catalog - // This handles all cross-provider logic (OpenRouter, Vertex, Groq, Bedrock) - // and provider-prefixed allowed_models entries if r.modelCatalog != nil && r.governanceInMemoryStore != nil { providerConfig, ok := r.governanceInMemoryStore.GetConfiguredProviders()[provider] providerConfigPtr := &providerConfig if !ok { providerConfigPtr = nil } - return r.modelCatalog.IsModelAllowedForProvider(provider, model, providerConfigPtr, pc.AllowedModels) + if r.modelCatalog.IsModelAllowedForProvider(provider, model, providerConfigPtr, pc.AllowedModels) { + return true + } + } else if pc.AllowedModels.IsAllowed(model) { + return true } - // Fallback when model catalog is not available: simple string matching - // ["*"] = allow all models; [] = deny all models - return pc.AllowedModels.IsAllowed(model) } } diff --git a/plugins/governance/utils.go b/plugins/governance/utils.go index 260cb62e68c..486499b2184 100644 --- a/plugins/governance/utils.go +++ b/plugins/governance/utils.go @@ -111,7 +111,19 @@ func (p *GovernancePlugin) filterModelsForVirtualKey( for _, model := range models { provider, modelName := schemas.ParseModelString(model.ID, "") - // Check if this provider/model combination is allowed + // Pre-pass: if any matching config blacklists the model, block it entirely. + isBlocked := false + for _, pc := range vk.ProviderConfigs { + if pc.Provider == string(provider) && pc.BlacklistedModels.IsBlocked(modelName) { + isBlocked = true + break + } + } + if isBlocked { + continue + } + + // Allowlist check — model is allowed if any matching config permits it. isAllowed := false for _, pc := range vk.ProviderConfigs { if pc.Provider == string(provider) { diff --git a/transports/bifrost-http/handlers/governance.go b/transports/bifrost-http/handlers/governance.go index a80c2a2cd97..ee4ff77b09a 100644 --- a/transports/bifrost-http/handlers/governance.go +++ b/transports/bifrost-http/handlers/governance.go @@ -80,12 +80,13 @@ type CreateVirtualKeyRequest struct { Name string `json:"name" validate:"required"` Description string `json:"description,omitempty"` ProviderConfigs []struct { - Provider string `json:"provider" validate:"required"` - Weight *float64 `json:"weight,omitempty"` - AllowedModels schemas.WhiteList `json:"allowed_models,omitempty"` // ["*"] allows all models; empty denies all - Budgets []CreateBudgetRequest `json:"budgets,omitempty"` // Multi-budget for provider config - RateLimit *CreateRateLimitRequest `json:"rate_limit,omitempty"` // Provider-level rate limit - KeyIDs schemas.WhiteList `json:"key_ids,omitempty"` // List of DBKey UUIDs to associate with this provider config + Provider string `json:"provider" validate:"required"` + Weight *float64 `json:"weight,omitempty"` + AllowedModels schemas.WhiteList `json:"allowed_models,omitempty"` // ["*"] allows all models; empty denies all + BlacklistedModels schemas.BlackList `json:"blacklisted_models,omitempty"` // ["*"] blocks all models; empty blocks none + Budgets []CreateBudgetRequest `json:"budgets,omitempty"` // Multi-budget for provider config + RateLimit *CreateRateLimitRequest `json:"rate_limit,omitempty"` // Provider-level rate limit + KeyIDs schemas.WhiteList `json:"key_ids,omitempty"` // List of DBKey UUIDs to associate with this provider config } `json:"provider_configs,omitempty"` // Empty means no providers allowed (deny-by-default) MCPConfigs []struct { MCPClientName string `json:"mcp_client_name" validate:"required"` @@ -105,13 +106,14 @@ type UpdateVirtualKeyRequest struct { Name *string `json:"name,omitempty"` Description *string `json:"description,omitempty"` ProviderConfigs []struct { - ID *uint `json:"id,omitempty"` // null for new entries - Provider string `json:"provider" validate:"required"` - Weight *float64 `json:"weight,omitempty"` - AllowedModels schemas.WhiteList `json:"allowed_models,omitempty"` // ["*"] allows all models; empty denies all - Budgets []CreateBudgetRequest `json:"budgets,omitempty"` // Multi-budget for provider config - RateLimit *UpdateRateLimitRequest `json:"rate_limit,omitempty"` // Provider-level rate limit - KeyIDs schemas.WhiteList `json:"key_ids,omitempty"` // List of DBKey UUIDs to associate with this provider config + ID *uint `json:"id,omitempty"` // null for new entries + Provider string `json:"provider" validate:"required"` + Weight *float64 `json:"weight,omitempty"` + AllowedModels schemas.WhiteList `json:"allowed_models,omitempty"` // ["*"] allows all models; empty denies all + BlacklistedModels schemas.BlackList `json:"blacklisted_models,omitempty"` // ["*"] blocks all models; empty blocks none + Budgets []CreateBudgetRequest `json:"budgets,omitempty"` // Multi-budget for provider config + RateLimit *UpdateRateLimitRequest `json:"rate_limit,omitempty"` // Provider-level rate limit + KeyIDs schemas.WhiteList `json:"key_ids,omitempty"` // List of DBKey UUIDs to associate with this provider config } `json:"provider_configs,omitempty"` MCPConfigs []struct { ID *uint `json:"id,omitempty"` // null for new entries @@ -692,6 +694,9 @@ func (h *GovernanceHandler) createVirtualKey(ctx *fasthttp.RequestCtx) { if err := pc.AllowedModels.Validate(); err != nil { return &badRequestError{err: fmt.Errorf("invalid allowed_models for provider %s: %w", pc.Provider, err)} } + if err := pc.BlacklistedModels.Validate(); err != nil { + return &badRequestError{err: fmt.Errorf("invalid blacklisted_models for provider %s: %w", pc.Provider, err)} + } if err := pc.KeyIDs.Validate(); err != nil { return &badRequestError{err: fmt.Errorf("invalid key_ids for provider %s: %w", pc.Provider, err)} } @@ -713,12 +718,13 @@ func (h *GovernanceHandler) createVirtualKey(ctx *fasthttp.RequestCtx) { } providerConfig := &configstoreTables.TableVirtualKeyProviderConfig{ - VirtualKeyID: vk.ID, - Provider: string(providerName), - Weight: pc.Weight, - AllowedModels: pc.AllowedModels, - AllowAllKeys: allowAllKeys, - Keys: keys, + VirtualKeyID: vk.ID, + Provider: string(providerName), + Weight: pc.Weight, + AllowedModels: pc.AllowedModels, + BlacklistedModels: pc.BlacklistedModels, + AllowAllKeys: allowAllKeys, + Keys: keys, } // Create rate limit for provider config if provided @@ -1125,6 +1131,9 @@ func (h *GovernanceHandler) updateVirtualKey(ctx *fasthttp.RequestCtx) { if err := pc.AllowedModels.Validate(); err != nil { return &badRequestError{err: fmt.Errorf("invalid allowed_models for provider %s: %w", pc.Provider, err)} } + if err := pc.BlacklistedModels.Validate(); err != nil { + return &badRequestError{err: fmt.Errorf("invalid blacklisted_models for provider %s: %w", pc.Provider, err)} + } if err := pc.KeyIDs.Validate(); err != nil { return &badRequestError{err: fmt.Errorf("invalid key_ids for provider %s: %w", pc.Provider, err)} } @@ -1147,12 +1156,13 @@ func (h *GovernanceHandler) updateVirtualKey(ctx *fasthttp.RequestCtx) { // Create new provider config providerConfig := &configstoreTables.TableVirtualKeyProviderConfig{ - VirtualKeyID: vk.ID, - Provider: string(providerName), - Weight: pc.Weight, - AllowedModels: pc.AllowedModels, - AllowAllKeys: allowAllKeys, - Keys: keys, + VirtualKeyID: vk.ID, + Provider: string(providerName), + Weight: pc.Weight, + AllowedModels: pc.AllowedModels, + BlacklistedModels: pc.BlacklistedModels, + AllowAllKeys: allowAllKeys, + Keys: keys, } // Create rate limit for provider config if provided if pc.RateLimit != nil { @@ -1214,12 +1224,16 @@ func (h *GovernanceHandler) updateVirtualKey(ctx *fasthttp.RequestCtx) { if err := pc.AllowedModels.Validate(); err != nil { return &badRequestError{err: fmt.Errorf("invalid allowed_models for provider %s: %w", pc.Provider, err)} } + if err := pc.BlacklistedModels.Validate(); err != nil { + return &badRequestError{err: fmt.Errorf("invalid blacklisted_models for provider %s: %w", pc.Provider, err)} + } if err := pc.KeyIDs.Validate(); err != nil { return &badRequestError{err: fmt.Errorf("invalid key_ids for provider %s: %w", pc.Provider, err)} } existing.Provider = string(providerName) existing.Weight = pc.Weight existing.AllowedModels = pc.AllowedModels + existing.BlacklistedModels = pc.BlacklistedModels // Get keys for this provider config if specified var keys []configstoreTables.TableKey diff --git a/ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx b/ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx index a9b452b6b63..71a08eb6f39 100644 --- a/ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx +++ b/ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx @@ -190,6 +190,29 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe +
+ Blocked Models +
+ {config.blacklisted_models?.includes("*") ? ( + + All Models Blocked + + ) : config.blacklisted_models && config.blacklisted_models.length > 0 ? ( +
+ {config.blacklisted_models.map((model) => ( + + {model} + + ))} +
+ ) : ( + + No models blocked + + )} +
+
+
Allowed Keys
@@ -255,11 +278,11 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe />
- Resets {parseResetPeriod(config.rate_limit.token_reset_duration || "")} - {virtualKey.calendar_aligned && - supportsCalendarAlignment(config.rate_limit.token_reset_duration || "") && - " (calendar)"} - + Resets {parseResetPeriod(config.rate_limit.token_reset_duration || "")} + {virtualKey.calendar_aligned && + supportsCalendarAlignment(config.rate_limit.token_reset_duration || "") && + " (calendar)"} + {config.rate_limit.token_last_reset ? ( Last reset {formatDistanceToNow(new Date(config.rate_limit.token_last_reset), { addSuffix: true })} @@ -280,11 +303,11 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe />
- Resets {parseResetPeriod(config.rate_limit.request_reset_duration || "")} - {virtualKey.calendar_aligned && - supportsCalendarAlignment(config.rate_limit.request_reset_duration || "") && - " (calendar)"} - + Resets {parseResetPeriod(config.rate_limit.request_reset_duration || "")} + {virtualKey.calendar_aligned && + supportsCalendarAlignment(config.rate_limit.request_reset_duration || "") && + " (calendar)"} + {config.rate_limit.request_last_reset ? ( Last reset{" "} diff --git a/ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx b/ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx index bf5362d3e58..3bfabf131c5 100644 --- a/ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx +++ b/ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx @@ -77,6 +77,7 @@ const providerConfigSchema = z.object({ provider: z.string().min(1, "Provider is required"), weight: z.number().min(0, "Weight must be at least 0").max(1, "Weight must be at most 1").optional(), allowed_models: z.array(z.string()).optional(), + blacklisted_models: z.array(z.string()).optional(), key_ids: z.array(z.string()).optional(), // Keys associated with this provider config // Provider-level budget budgets: z @@ -234,6 +235,7 @@ export default function VirtualKeySheet({ provider: config.provider, weight: config.weight ?? undefined, allowed_models: config.allowed_models, + blacklisted_models: config.blacklisted_models, key_ids: config.allow_all_keys ? ["*"] : config.keys?.map((key) => key.key_id) || [], budgets: config.budgets?.map((b) => ({ id: b.id, @@ -371,6 +373,7 @@ export default function VirtualKeySheet({ provider: provider, weight: undefined as number | undefined, // undefined = excluded from weighted routing until user sets a weight allowed_models: ["*"], + blacklisted_models: [], key_ids: ["*"], }; @@ -1094,11 +1097,76 @@ export default function VirtualKeySheet({ ); })()}

- Select specific models or choose “Allow All Models” to allow all. Leave empty to deny all. + Select specific models or choose "Allow All Models" to allow all. Leave empty to deny all.

+
+
+
+
+ + + + + + + + + +

+ Models this VK must never serve. The denylist always wins - if a model appears in both Allowed + Models and here, it is blocked. Select "All Models" to block every model on this VK. +

+
+
+
+
+ {(() => { + const hasWildcardBlocked = (config.blacklisted_models || []).includes("*"); + return ( + { + const providerKeys = availableKeys.filter((key) => key.provider === config.provider); + const configKeyIds = config.key_ids || []; + return configKeyIds.includes("*") + ? providerKeys.map((key) => key.key_id) + : providerKeys.filter((key) => configKeyIds.includes(key.key_id)).map((key) => key.key_id); + })()} + allowAllOption={true} + value={hasWildcardBlocked ? ["*"] : config.blacklisted_models || []} + onChange={(models: string[]) => { + const hadStar = (config.blacklisted_models || []).includes("*"); + const hasStar = models.includes("*"); + if (!hadStar && hasStar) { + handleUpdateProviderConfig(index, "blacklisted_models", ["*"]); + } else if (hadStar && hasStar && models.length > 1) { + handleUpdateProviderConfig( + index, + "blacklisted_models", + models.filter((m) => m !== "*"), + ); + } else { + handleUpdateProviderConfig(index, "blacklisted_models", models); + } + }} + placeholder={ + hasWildcardBlocked + ? "All models blocked" + : (config.blacklisted_models || []).length === 0 + ? "No models blocked" + : "Search models..." + } + className="min-h-10 max-w-[500px] min-w-[200px]" + /> + ); + })()} +
+
+ {/* Allowed Keys for this provider */} {(() => { const providerKeys = availableKeys.filter((key) => key.provider === config.provider); diff --git a/ui/lib/types/governance.ts b/ui/lib/types/governance.ts index 47707ebd751..8c6a9dc7e6f 100644 --- a/ui/lib/types/governance.ts +++ b/ui/lib/types/governance.ts @@ -91,6 +91,7 @@ export interface VirtualKeyProviderConfig { provider: string; weight: number | null; allowed_models: string[]; + blacklisted_models: string[]; allow_all_keys: boolean; // True means all keys allowed; false with empty keys means no keys allowed budgets?: Budget[]; rate_limit?: RateLimit; @@ -135,6 +136,7 @@ export interface VirtualKeyProviderConfigRequest { provider: string; weight?: number | null; allowed_models?: string[]; + blacklisted_models?: string[]; budgets?: CreateBudgetRequest[]; rate_limit?: CreateRateLimitRequest; key_ids?: string[]; // List of DBKey UUIDs to associate with this provider config @@ -145,6 +147,7 @@ export interface VirtualKeyProviderConfigUpdateRequest { provider: string; weight?: number | null; allowed_models?: string[]; + blacklisted_models?: string[]; budgets?: CreateBudgetRequest[]; rate_limit?: UpdateRateLimitRequest; key_ids?: string[]; // List of DBKey UUIDs to associate with this provider config