diff --git a/.github/workflows/configs/default/config.json b/.github/workflows/configs/default/config.json index c16511cbcc..e3ac85b6a7 100644 --- a/.github/workflows/configs/default/config.json +++ b/.github/workflows/configs/default/config.json @@ -31,6 +31,7 @@ "name": "e2e-openai-key", "value": "env.OPENAI_API_KEY", "weight": 1, + "models": ["*"], "use_for_batch_api": true } ], @@ -44,6 +45,7 @@ "name": "e2e-anthropic-key", "value": "env.ANTHROPIC_API_KEY", "weight": 1, + "models": ["*"], "use_for_batch_api": true } ], diff --git a/.github/workflows/configs/withpostgresmcpclientsinconfig/config.json b/.github/workflows/configs/withpostgresmcpclientsinconfig/config.json index 78dfa0620e..bba1d30021 100644 --- a/.github/workflows/configs/withpostgresmcpclientsinconfig/config.json +++ b/.github/workflows/configs/withpostgresmcpclientsinconfig/config.json @@ -88,6 +88,8 @@ "provider_configs": [ { "provider": "openai", + "allowed_models": ["*"], + "key_ids": ["*"], "weight": 1.0 } ] @@ -109,6 +111,8 @@ "provider_configs": [ { "provider": "openai", + "allowed_models": ["*"], + "key_ids": ["*"], "weight": 1.0 } ] @@ -130,7 +134,8 @@ { "name": "openai-primary", "value": "env.OPENAI_API_KEY", - "weight": 1 + "weight": 1, + "models": ["*"] } ] } diff --git a/core/schemas/images.go b/core/schemas/images.go index 7f5ded7d7c..6f07d09935 100644 --- a/core/schemas/images.go +++ b/core/schemas/images.go @@ -71,6 +71,11 @@ type BifrostImageGenerationResponse struct { func (r *BifrostImageGenerationResponse) BackfillParams(req *BifrostRequest) { numInputImages, size, quality := getNumInputImagesSizeAndQualityFromRequest(req) + // Backfill Model if not returned by the provider + if r.Model == "" { + r.Model = getModelFromRequest(req) + } + // Backfill NumInputImages if numInputImages > 0 { if r.Usage == nil { @@ -96,6 +101,22 @@ func (r *BifrostImageGenerationResponse) BackfillParams(req *BifrostRequest) { } } +// getModelFromRequest extracts the model from any image-related request. +func getModelFromRequest(req *BifrostRequest) string { + if req == nil { + return "" + } + switch { + case req.ImageGenerationRequest != nil: + return req.ImageGenerationRequest.Model + case req.ImageEditRequest != nil: + return req.ImageEditRequest.Model + case req.ImageVariationRequest != nil: + return req.ImageVariationRequest.Model + } + return "" +} + // getNumInputImagesSizeAndQualityFromRequest extracts request params for cost calculation. // Quality is only returned when it is one of low, medium, high, auto. func getNumInputImagesSizeAndQualityFromRequest(req *BifrostRequest) (numInputImages int, size string, quality string) { diff --git a/docs/quickstart/go-sdk/setting-up.mdx b/docs/quickstart/go-sdk/setting-up.mdx index e909ab578e..35087db0cc 100644 --- a/docs/quickstart/go-sdk/setting-up.mdx +++ b/docs/quickstart/go-sdk/setting-up.mdx @@ -52,7 +52,7 @@ func (a *MyAccount) GetKeysForProvider(ctx *context.Context, provider schemas.Mo if provider == schemas.OpenAI { return []schemas.Key{{ Value: os.Getenv("OPENAI_API_KEY"), - Models: []string{}, // Keep Models empty to use any model + Models: schemas.WhiteList{"*"}, // Keep Models ["*"] to use any model Weight: 1.0, }}, nil } diff --git a/framework/vectorstore/redis.go b/framework/vectorstore/redis.go index a9e4c5f94e..847cad59b1 100644 --- a/framework/vectorstore/redis.go +++ b/framework/vectorstore/redis.go @@ -1610,5 +1610,9 @@ func newRedisStore(_ context.Context, config RedisConfig, logger schemas.Logger) logger: logger, namespaceFieldTypes: make(map[string]map[string]VectorStorePropertyType), } + // Eagerly verify connectivity, consistent with other store constructors (e.g. Qdrant) + if err := store.Ping(context.Background()); err != nil { + return nil, fmt.Errorf("failed to connect to redis: %w", err) + } return store, nil } diff --git a/helm-charts/bifrost/Chart.yaml b/helm-charts/bifrost/Chart.yaml index bef0444c77..be4d7742aa 100644 --- a/helm-charts/bifrost/Chart.yaml +++ b/helm-charts/bifrost/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: bifrost description: A Helm chart for deploying Bifrost - AI Gateway with unified interface for multiple providers type: application -version: 2.0.14 +version: 2.0.15 appVersion: "1.4.11" keywords: - ai diff --git a/helm-charts/bifrost/README.md b/helm-charts/bifrost/README.md index 3e4b9675e6..69d5ef8094 100644 --- a/helm-charts/bifrost/README.md +++ b/helm-charts/bifrost/README.md @@ -4,10 +4,24 @@ Official Helm charts for deploying [Bifrost](https://github.com/maximhq/bifrost) - a high-performance AI gateway with unified interface for multiple providers. -**Latest Version:** 2.0.14 +**Latest Version:** 2.0.15 ## Changelog +### v2.0.15 + +- Synced helm schema with transport `config.schema.json` — added missing properties: + - `client.mcpDisableAutoToolInject` — disable automatic MCP tool injection + - `governance.budgets[].calendar_aligned` — snap budget resets to calendar boundaries + - `governance.pricingOverrides` — scoped pricing overrides for the model catalog + - `mcp.clientConfigs[].allowedExtraHeaders` — header allowlist per MCP client + - `mcp.clientConfigs[].allowOnAllVirtualKeys` — make MCP server accessible to all virtual keys + - `mcp.toolManagerConfig.disableAutoToolInject` — disable auto tool injection at manager level + - `networkConfig.beta_header_overrides` — override Anthropic beta header support per provider + - `websocket` — full WebSocket gateway tuning (connections, pool, transcript buffer) +- Fixed SSE `connectionString` not being rendered in `_helpers.tpl` for MCP clients +- Added template rendering for all new properties in `_helpers.tpl` + ### v2.0.14 - Added `placement` and `order` fields to custom plugin schema and template rendering diff --git a/helm-charts/bifrost/templates/_helpers.tpl b/helm-charts/bifrost/templates/_helpers.tpl index c8af17eb14..a3df13bb04 100644 --- a/helm-charts/bifrost/templates/_helpers.tpl +++ b/helm-charts/bifrost/templates/_helpers.tpl @@ -281,6 +281,9 @@ false {{- if hasKey .Values.bifrost.client "hideDeletedVirtualKeysInFilters" }} {{- $_ := set $client "hide_deleted_virtual_keys_in_filters" .Values.bifrost.client.hideDeletedVirtualKeysInFilters }} {{- end }} +{{- if hasKey .Values.bifrost.client "mcpDisableAutoToolInject" }} +{{- $_ := set $client "mcp_disable_auto_tool_inject" .Values.bifrost.client.mcpDisableAutoToolInject }} +{{- end }} {{- $_ := set $config "client" $client }} {{- end }} {{- /* Framework */ -}} @@ -354,6 +357,9 @@ false {{- if .Values.bifrost.governance.providers }} {{- $_ := set $governance "providers" .Values.bifrost.governance.providers }} {{- end }} +{{- if .Values.bifrost.governance.pricingOverrides }} +{{- $_ := set $governance "pricing_overrides" .Values.bifrost.governance.pricingOverrides }} +{{- end }} {{- if .Values.bifrost.governance.authConfig }} {{- $authConfig := dict }} {{- if and .Values.bifrost.governance.authConfig.existingSecret .Values.bifrost.governance.authConfig.usernameKey }} @@ -376,7 +382,7 @@ false {{- $_ := set $governance "auth_config" $authConfig }} {{- end }} {{- end }} -{{- if or $governance.budgets $governance.rate_limits $governance.customers $governance.teams $governance.virtual_keys $governance.routing_rules $governance.model_configs $governance.providers $governance.auth_config }} +{{- if or $governance.budgets $governance.rate_limits $governance.customers $governance.teams $governance.virtual_keys $governance.routing_rules $governance.model_configs $governance.providers $governance.pricing_overrides $governance.auth_config }} {{- $_ := set $config "governance" $governance }} {{- end }} {{- end }} @@ -667,6 +673,10 @@ false {{- if and (eq $client.connectionType "websocket") $client.websocketConfig }} {{- $_ := set $cc "connection_string" $client.websocketConfig.url }} {{- end }} +{{- /* Map connectionString for SSE connections */ -}} +{{- if and (eq $client.connectionType "sse") $client.connectionString }} +{{- $_ := set $cc "connection_string" $client.connectionString }} +{{- end }} {{- /* Map stdioConfig -> stdio_config */ -}} {{- if $client.stdioConfig }} {{- $stdio := dict "command" $client.stdioConfig.command }} @@ -709,6 +719,12 @@ false {{- if $client.toolPricing }} {{- $_ := set $cc "tool_pricing" $client.toolPricing }} {{- end }} +{{- if $client.allowedExtraHeaders }} +{{- $_ := set $cc "allowed_extra_headers" $client.allowedExtraHeaders }} +{{- end }} +{{- if hasKey $client "allowOnAllVirtualKeys" }} +{{- $_ := set $cc "allow_on_all_virtual_keys" $client.allowOnAllVirtualKeys }} +{{- end }} {{- $clientConfigs = append $clientConfigs $cc }} {{- end }} {{- $mcpConfig := dict "client_configs" $clientConfigs }} @@ -723,6 +739,9 @@ false {{- if .Values.bifrost.mcp.toolManagerConfig.codeModeBindingLevel }} {{- $_ := set $tmConfig "code_mode_binding_level" .Values.bifrost.mcp.toolManagerConfig.codeModeBindingLevel }} {{- end }} +{{- if hasKey .Values.bifrost.mcp.toolManagerConfig "disableAutoToolInject" }} +{{- $_ := set $tmConfig "disable_auto_tool_inject" .Values.bifrost.mcp.toolManagerConfig.disableAutoToolInject }} +{{- end }} {{- if $tmConfig }} {{- $_ := set $mcpConfig "tool_manager_config" $tmConfig }} {{- end }} @@ -900,6 +919,37 @@ false {{- $_ := set $config "audit_logs" $auditLogs }} {{- end }} {{- end }} +{{- /* WebSocket Config */ -}} +{{- if .Values.bifrost.websocket }} +{{- $ws := dict }} +{{- if .Values.bifrost.websocket.maxConnectionsPerUser }} +{{- $_ := set $ws "max_connections_per_user" .Values.bifrost.websocket.maxConnectionsPerUser }} +{{- end }} +{{- if .Values.bifrost.websocket.transcriptBufferSize }} +{{- $_ := set $ws "transcript_buffer_size" .Values.bifrost.websocket.transcriptBufferSize }} +{{- end }} +{{- if .Values.bifrost.websocket.pool }} +{{- $pool := dict }} +{{- if .Values.bifrost.websocket.pool.maxIdlePerKey }} +{{- $_ := set $pool "max_idle_per_key" .Values.bifrost.websocket.pool.maxIdlePerKey }} +{{- end }} +{{- if .Values.bifrost.websocket.pool.maxTotalConnections }} +{{- $_ := set $pool "max_total_connections" .Values.bifrost.websocket.pool.maxTotalConnections }} +{{- end }} +{{- if .Values.bifrost.websocket.pool.idleTimeoutSeconds }} +{{- $_ := set $pool "idle_timeout_seconds" .Values.bifrost.websocket.pool.idleTimeoutSeconds }} +{{- end }} +{{- if .Values.bifrost.websocket.pool.maxConnectionLifetimeSeconds }} +{{- $_ := set $pool "max_connection_lifetime_seconds" .Values.bifrost.websocket.pool.maxConnectionLifetimeSeconds }} +{{- end }} +{{- if $pool }} +{{- $_ := set $ws "pool" $pool }} +{{- end }} +{{- end }} +{{- if $ws }} +{{- $_ := set $config "websocket" $ws }} +{{- end }} +{{- end }} {{- $config | toJson }} {{- end }} diff --git a/helm-charts/bifrost/values.schema.json b/helm-charts/bifrost/values.schema.json index 4d15e9a712..946d04d30d 100644 --- a/helm-charts/bifrost/values.schema.json +++ b/helm-charts/bifrost/values.schema.json @@ -376,6 +376,10 @@ "hideDeletedVirtualKeysInFilters": { "type": "boolean", "description": "When true, deleted virtual keys are omitted from logs and MCP logs filter data" + }, + "mcpDisableAutoToolInject": { + "type": "boolean", + "description": "When true, MCP tools are not automatically injected into requests. Tools are only included when explicitly specified via request context filters or headers." } }, "additionalProperties": false @@ -449,6 +453,11 @@ "type": "string", "enum": ["server", "tool"], "description": "How tools are exposed in VFS for code execution" + }, + "disableAutoToolInject": { + "type": "boolean", + "description": "When true, MCP tools are not automatically injected into requests. Tools are only included when explicitly specified.", + "default": false } } }, @@ -910,6 +919,11 @@ "last_reset": { "type": "string", "format": "date-time" + }, + "calendar_aligned": { + "type": "boolean", + "description": "Snap resets to calendar boundaries (day/week/month/year start)", + "default": false } }, "required": [ @@ -1213,6 +1227,40 @@ }, "required": ["name"] } + }, + "pricingOverrides": { + "type": "array", + "description": "Scoped pricing overrides applied at runtime by the model catalog", + "items": { + "type": "object", + "properties": { + "id": { "type": "string", "description": "Unique pricing override ID" }, + "name": { "type": "string", "description": "Human-readable name for this override" }, + "scope_kind": { + "type": "string", + "enum": ["global", "provider", "provider_key", "virtual_key", "virtual_key_provider", "virtual_key_provider_key"], + "description": "Scope level for this override" + }, + "virtual_key_id": { "type": "string", "description": "Virtual key ID (required for virtual_key* scopes)" }, + "provider_id": { "type": "string", "description": "Provider ID (required for provider* scopes)" }, + "provider_key_id": { "type": "string", "description": "Provider key ID (required for provider_key and virtual_key_provider_key scopes)" }, + "match_type": { + "type": "string", + "enum": ["exact", "wildcard"], + "description": "How the pattern is matched against model names" + }, + "pattern": { "type": "string", "description": "Model name pattern to match" }, + "request_types": { + "type": "array", + "minItems": 1, + "items": { "type": "string" }, + "description": "Request types this override applies to" + }, + "pricing_patch": { "type": "string", "description": "JSON-encoded pricing fields to override" }, + "config_hash": { "type": "string", "description": "Internal hash for change detection (auto-managed)" } + }, + "required": ["id", "name", "scope_kind", "match_type", "pattern", "request_types"] + } } }, "additionalProperties": false @@ -1599,6 +1647,54 @@ "type": "string" } } + }, + "websocket": { + "type": "object", + "description": "Optional tuning for the WebSocket gateway (Responses API WebSocket Mode, Realtime API)", + "properties": { + "maxConnectionsPerUser": { + "type": "integer", + "minimum": 1, + "description": "Maximum concurrent WebSocket connections per user", + "default": 100 + }, + "transcriptBufferSize": { + "type": "integer", + "minimum": 1, + "description": "Number of transcript entries to buffer for Realtime API mid-session fallback", + "default": 100 + }, + "pool": { + "type": "object", + "description": "Upstream WebSocket connection pool configuration", + "properties": { + "maxIdlePerKey": { + "type": "integer", + "minimum": 1, + "description": "Maximum idle connections per provider/key combination", + "default": 50 + }, + "maxTotalConnections": { + "type": "integer", + "minimum": 1, + "description": "Maximum total idle connections across all providers", + "default": 1000 + }, + "idleTimeoutSeconds": { + "type": "integer", + "minimum": 1, + "description": "Seconds before an idle connection is evicted", + "default": 600 + }, + "maxConnectionLifetimeSeconds": { + "type": "integer", + "minimum": 1, + "description": "Maximum lifetime of a connection in seconds", + "default": 7200 + } + } + } + } } } }, @@ -2513,6 +2609,11 @@ "minimum": 1, "maximum": 10000, "description": "Maximum number of TCP connections per provider host. For HTTP/2 (e.g. Bedrock), each connection supports ~100 concurrent streams. Default: 5000." + }, + "beta_header_overrides": { + "type": "object", + "additionalProperties": { "type": "boolean" }, + "description": "Override default Anthropic beta header support per provider. Keys are header prefixes, values are true (supported) or false (unsupported)." } } }, @@ -2676,6 +2777,16 @@ "type": "number", "minimum": 0 } + }, + "allowedExtraHeaders": { + "type": "array", + "items": { "type": "string" }, + "description": "Allowlist of request-level headers that callers may forward to this MCP server. Use ['*'] to allow all headers." + }, + "allowOnAllVirtualKeys": { + "type": "boolean", + "description": "When true, this MCP server is accessible to all virtual keys without requiring explicit per-key assignment.", + "default": false } }, "required": [ @@ -2831,7 +2942,6 @@ "description": "Azure API version" } }, - "required": ["endpoint"], "additionalProperties": false }, "vertex_key_config": { @@ -2861,7 +2971,6 @@ "description": "Model to deployment mappings" } }, - "required": ["project_id", "region"], "additionalProperties": false }, "bedrock_key_config": { @@ -2909,10 +3018,6 @@ "description": "Exact model name served on this VLLM instance" } }, - "required": [ - "url", - "model_name" - ], "additionalProperties": false } }, @@ -2967,8 +3072,7 @@ ] } } - ], - "required": ["key_id", "name", "value"] + ] } } }, diff --git a/helm-charts/index.yaml b/helm-charts/index.yaml index ab0417821f..2ef7fa343d 100644 --- a/helm-charts/index.yaml +++ b/helm-charts/index.yaml @@ -1,6 +1,27 @@ apiVersion: v1 entries: bifrost: + - apiVersion: v2 + appVersion: 1.4.11 + created: "2026-04-01T12:00:00.000000+00:00" + description: A Helm chart for deploying Bifrost - AI Gateway with unified interface for multiple providers + digest: "" + home: https://www.getmaxim.ai/bifrost + icon: https://www.getmaxim.ai/bifrost/bifrost-logo-only.png + keywords: + - ai + - gateway + - llm + maintainers: + - email: akshay@getmaxim.ai + name: Bifrost Team + name: bifrost + sources: + - https://github.com/maximhq/bifrost + type: application + urls: + - https://maximhq.github.io/bifrost/helm-charts/bifrost-2.0.15.tgz + version: 2.0.15 - apiVersion: v2 appVersion: 1.4.11 created: "2026-03-20T12:00:00.000000+00:00" @@ -544,4 +565,4 @@ entries: urls: - https://maximhq.github.io/bifrost/helm-charts/bifrost-1.3.36.tgz version: 1.3.36 -generated: "2026-03-11T12:00:00.000000+00:00" +generated: "2026-04-01T12:00:00.000000+00:00" diff --git a/plugins/governance/http_transport_prehook_test.go b/plugins/governance/http_transport_prehook_test.go index f50511d740..eb29a3bd60 100644 --- a/plugins/governance/http_transport_prehook_test.go +++ b/plugins/governance/http_transport_prehook_test.go @@ -30,7 +30,7 @@ func TestHTTPTransportPreHook_VirtualKeyReplicateRefinesNestedModel(t *testing.T "sk-bf-test", "replicate-only", []configstoreTables.TableVirtualKeyProviderConfig{ - buildProviderConfig("replicate", nil), + buildProviderConfig("replicate", []string{"*"}), }, ) store, err := NewLocalGovernanceStore(context.Background(), logger, nil, &configstore.GovernanceConfig{ diff --git a/plugins/governance/model_provider_governance_test.go b/plugins/governance/model_provider_governance_test.go index a17855552a..98565bbb4b 100644 --- a/plugins/governance/model_provider_governance_test.go +++ b/plugins/governance/model_provider_governance_test.go @@ -1479,7 +1479,9 @@ func TestPreLLMHook_ModelProviderPass_VirtualKeyChecksPass(t *testing.T) { logger := NewMockLogger() // Model/provider checks pass (no limits) // Virtual key checks also pass - vk := buildVirtualKey("vk1", "sk-bf-test", "Test VK", true) + vk := buildVirtualKeyWithProviders("vk1", "sk-bf-test", "Test VK", []configstoreTables.TableVirtualKeyProviderConfig{ + buildProviderConfig("openai", []string{"*"}), + }) store, err := NewLocalGovernanceStore(context.Background(), logger, nil, &configstore.GovernanceConfig{ VirtualKeys: []configstoreTables.TableVirtualKey{*vk}, }, nil) diff --git a/plugins/governance/resolver_test.go b/plugins/governance/resolver_test.go index ed51b51f0c..4d06c29a82 100644 --- a/plugins/governance/resolver_test.go +++ b/plugins/governance/resolver_test.go @@ -16,7 +16,9 @@ import ( // TestBudgetResolver_EvaluateRequest_AllowedRequest tests happy path func TestBudgetResolver_EvaluateRequest_AllowedRequest(t *testing.T) { logger := NewMockLogger() - vk := buildVirtualKey("vk1", "sk-bf-test", "Test VK", true) + vk := buildVirtualKeyWithProviders("vk1", "sk-bf-test", "Test VK", []configstoreTables.TableVirtualKeyProviderConfig{ + buildProviderConfig("openai", []string{"*"}), + }) store, err := NewLocalGovernanceStore(context.Background(), logger, nil, &configstore.GovernanceConfig{ VirtualKeys: []configstoreTables.TableVirtualKey{*vk}, @@ -467,7 +469,9 @@ func TestBudgetResolver_IsModelAllowed(t *testing.T) { // TestBudgetResolver_ContextPopulation tests context values are set correctly func TestBudgetResolver_ContextPopulation(t *testing.T) { logger := NewMockLogger() - vk := buildVirtualKey("vk1", "sk-bf-test", "Test VK", true) + vk := buildVirtualKeyWithProviders("vk1", "sk-bf-test", "Test VK", []configstoreTables.TableVirtualKeyProviderConfig{ + buildProviderConfig("openai", []string{"*"}), + }) customer := buildCustomer("cust1", "Customer 1", nil) team := buildTeam("team1", "Team 1", nil) team.CustomerID = &customer.ID diff --git a/plugins/governance/test_utils.go b/plugins/governance/test_utils.go index a6da347cbf..ccd1e5968d 100644 --- a/plugins/governance/test_utils.go +++ b/plugins/governance/test_utils.go @@ -92,6 +92,9 @@ func buildVirtualKeyWithBudget(id, value, name string, budget *configstoreTables vk.Budget = budget budgetID := budget.ID vk.BudgetID = &budgetID + vk.ProviderConfigs = []configstoreTables.TableVirtualKeyProviderConfig{ + buildProviderConfig("openai", []string{"*"}), + } return vk } @@ -100,6 +103,9 @@ func buildVirtualKeyWithRateLimit(id, value, name string, rateLimit *configstore vk.RateLimit = rateLimit rateLimitID := rateLimit.ID vk.RateLimitID = &rateLimitID + vk.ProviderConfigs = []configstoreTables.TableVirtualKeyProviderConfig{ + buildProviderConfig("openai", []string{"*"}), + } return vk } diff --git a/plugins/semanticcache/plugin_core_test.go b/plugins/semanticcache/plugin_core_test.go index 6e29d002c1..822fc1f645 100644 --- a/plugins/semanticcache/plugin_core_test.go +++ b/plugins/semanticcache/plugin_core_test.go @@ -390,7 +390,7 @@ func TestCacheConfiguration(t *testing.T) { Dimension: 1536, Threshold: 0.95, // Very high threshold Keys: []schemas.Key{ - {Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"), Models: []string{}, Weight: 1.0}, + {Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"), Models: schemas.WhiteList{"*"}, Weight: 1.0}, }, }, expectedBehavior: "strict_matching", @@ -403,7 +403,7 @@ func TestCacheConfiguration(t *testing.T) { Dimension: 1536, Threshold: 0.1, // Very low threshold Keys: []schemas.Key{ - {Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"), Models: []string{}, Weight: 1.0}, + {Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"), Models: schemas.WhiteList{"*"}, Weight: 1.0}, }, }, expectedBehavior: "loose_matching", @@ -417,7 +417,7 @@ func TestCacheConfiguration(t *testing.T) { Threshold: 0.8, TTL: 1 * time.Hour, // Custom TTL Keys: []schemas.Key{ - {Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"), Models: []string{}, Weight: 1.0}, + {Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"), Models: schemas.WhiteList{"*"}, Weight: 1.0}, }, }, expectedBehavior: "custom_ttl", @@ -550,7 +550,7 @@ func TestInvalidProviderRejection(t *testing.T) { Keys: []schemas.Key{ { Value: *schemas.NewEnvVar("env.TEST_API_KEY"), - Models: []string{}, + Models: schemas.WhiteList{"*"}, Weight: 1.0, }, }, @@ -587,7 +587,7 @@ func TestValidProviderAccepted(t *testing.T) { Keys: []schemas.Key{ { Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"), - Models: []string{}, + Models: schemas.WhiteList{"*"}, Weight: 1.0, }, }, diff --git a/plugins/semanticcache/plugin_image_generation_test.go b/plugins/semanticcache/plugin_image_generation_test.go index ce70dd1698..f50f3c5c9b 100644 --- a/plugins/semanticcache/plugin_image_generation_test.go +++ b/plugins/semanticcache/plugin_image_generation_test.go @@ -129,7 +129,7 @@ func TestImageGenerationSemanticSearch(t *testing.T) { Dimension: 1536, Threshold: 0.5, Keys: []schemas.Key{ - {Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"), Models: []string{}, Weight: 1.0}, + {Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"), Models: []string{"*"}, Weight: 1.0}, }, } setup := NewTestSetupWithConfig(t, config) diff --git a/plugins/semanticcache/plugin_vectorstore_test.go b/plugins/semanticcache/plugin_vectorstore_test.go index 5ac3029523..d0b9ef78d0 100644 --- a/plugins/semanticcache/plugin_vectorstore_test.go +++ b/plugins/semanticcache/plugin_vectorstore_test.go @@ -58,7 +58,7 @@ func getDefaultTestConfig() *Config { Keys: []schemas.Key{ { Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"), - Models: []string{}, + Models: schemas.WhiteList{"*"}, Weight: 1.0, }, }, diff --git a/plugins/semanticcache/test_utils.go b/plugins/semanticcache/test_utils.go index 5bda10e858..1aad7d7ada 100644 --- a/plugins/semanticcache/test_utils.go +++ b/plugins/semanticcache/test_utils.go @@ -122,7 +122,7 @@ func (baseAccount *BaseAccount) GetKeysForProvider(ctx context.Context, provider return []schemas.Key{ { Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"), - Models: []string{}, // Empty models array means it supports ALL models + Models: schemas.WhiteList{"*"}, // "*" means allow all models Weight: 1.0, }, }, nil @@ -374,7 +374,7 @@ func NewTestSetup(t *testing.T) *TestSetup { Keys: []schemas.Key{ { Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"), - Models: []string{}, + Models: schemas.WhiteList{"*"}, Weight: 1.0, }, }, @@ -651,7 +651,7 @@ func CreateTestSetupWithConversationThreshold(t *testing.T, threshold int) *Test Keys: []schemas.Key{ { Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"), - Models: []string{}, + Models: []string{"*"}, Weight: 1.0, }, }, @@ -672,7 +672,7 @@ func CreateTestSetupWithExcludeSystemPrompt(t *testing.T, excludeSystem bool) *T Keys: []schemas.Key{ { Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"), - Models: []string{}, + Models: []string{"*"}, Weight: 1.0, }, }, @@ -694,7 +694,7 @@ func CreateTestSetupWithThresholdAndExcludeSystem(t *testing.T, threshold int, e Keys: []schemas.Key{ { Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"), - Models: []string{}, + Models: []string{"*"}, Weight: 1.0, }, }, diff --git a/tests/e2e/api/collections/bifrost-v1-vk-auth.postman_collection.json b/tests/e2e/api/collections/bifrost-v1-vk-auth.postman_collection.json index c4f494c37e..0404a316d4 100644 --- a/tests/e2e/api/collections/bifrost-v1-vk-auth.postman_collection.json +++ b/tests/e2e/api/collections/bifrost-v1-vk-auth.postman_collection.json @@ -60,7 +60,7 @@ "exec": [ "var timestamp = Date.now();", "var uniqueName = 'VK Auth Test ' + timestamp;", - "pm.request.body.raw = JSON.stringify({name: uniqueName});" + "pm.request.body.raw = JSON.stringify({name: uniqueName, provider_configs: [{provider: 'openai', weight: 1.0, allowed_models: ['*'], key_ids: ['*']}]});" ] } }, diff --git a/tests/e2e/api/collections/bifrost-v1-vk-routing.postman_collection.json b/tests/e2e/api/collections/bifrost-v1-vk-routing.postman_collection.json index 669349c46a..f003e19d6a 100644 --- a/tests/e2e/api/collections/bifrost-v1-vk-routing.postman_collection.json +++ b/tests/e2e/api/collections/bifrost-v1-vk-routing.postman_collection.json @@ -26,7 +26,8 @@ " provider_configs: [{", " provider: pm.collectionVariables.get('provider') || 'openai',", " weight: 1.0,", - " allowed_models: [pm.collectionVariables.get('chat_model') || 'gpt-4o']", + " allowed_models: ['*'],", + " key_ids: ['*']", " }]", "};", "pm.request.body.raw = JSON.stringify(body);" diff --git a/tests/e2e/core/fixtures/test-data.fixture.ts b/tests/e2e/core/fixtures/test-data.fixture.ts index 3b3d6018bf..43fe37aef1 100644 --- a/tests/e2e/core/fixtures/test-data.fixture.ts +++ b/tests/e2e/core/fixtures/test-data.fixture.ts @@ -80,7 +80,7 @@ export class TestDataFactory { return { name: this.uniqueId('key'), value: `sk-test-${this.uniqueId()}`, - models: [], + models: ['*'], weight: 1.0, ...overrides, } @@ -155,8 +155,8 @@ export class TestDataFactory { return { provider: 'openai', weight: 1.0, - allowedModels: [], - keyIds: [], + allowedModels: ['*'], + keyIds: ['*'], ...overrides, } } diff --git a/tests/e2e/features/providers/providers.data.ts b/tests/e2e/features/providers/providers.data.ts index 763a29e094..3e80f31f45 100644 --- a/tests/e2e/features/providers/providers.data.ts +++ b/tests/e2e/features/providers/providers.data.ts @@ -8,7 +8,7 @@ export function createProviderKeyData(overrides: Partial = {} return { name: `Test Key ${timestamp}`, value: `sk-test-${timestamp}-${Math.random().toString(36).substring(7)}`, - models: [], + models: ['*'], weight: 1.0, ...overrides, } diff --git a/tests/e2e/features/virtual-keys/pages/virtual-keys.page.ts b/tests/e2e/features/virtual-keys/pages/virtual-keys.page.ts index 69726a7b39..58a4d877b2 100644 --- a/tests/e2e/features/virtual-keys/pages/virtual-keys.page.ts +++ b/tests/e2e/features/virtual-keys/pages/virtual-keys.page.ts @@ -60,6 +60,7 @@ export interface ProviderConfig { provider: string weight?: number allowedModels?: string[] + keyIds?: string[] budget?: BudgetConfig rateLimit?: RateLimitConfig } diff --git a/tests/e2e/features/virtual-keys/virtual-keys.data.ts b/tests/e2e/features/virtual-keys/virtual-keys.data.ts index 23d2e49c39..5d3a8fb007 100644 --- a/tests/e2e/features/virtual-keys/virtual-keys.data.ts +++ b/tests/e2e/features/virtual-keys/virtual-keys.data.ts @@ -30,6 +30,8 @@ export function createVirtualKeyWithProvider( { provider, weight: 1.0, + allowedModels: ['*'], + keyIds: ['*'], }, ], ...vkOverrides, @@ -87,6 +89,8 @@ export function createVirtualKeyWithMultipleProviders( providerConfigs: providers.map((provider) => ({ provider, weight, + allowedModels: ['*'], + keyIds: ['*'], })), ...vkOverrides, } @@ -99,6 +103,8 @@ export function createProviderConfig(overrides: Partial = {}): P return { provider: 'openai', weight: 1.0, + allowedModels: ['*'], + keyIds: ['*'], ...overrides, } } diff --git a/tests/governance/advancedscenarios_test.go b/tests/governance/advancedscenarios_test.go index f613cbfe0d..aa928c7dc9 100644 --- a/tests/governance/advancedscenarios_test.go +++ b/tests/governance/advancedscenarios_test.go @@ -976,8 +976,10 @@ func TestProviderConfigBudgetUpdateAfterExhaustion(t *testing.T) { Name: vkName, ProviderConfigs: []ProviderConfigRequest{ { - Provider: "openai", - Weight: 1.0, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, Budget: &BudgetRequest{ MaxLimit: initialBudget, ResetDuration: "1h", @@ -1063,9 +1065,11 @@ func TestProviderConfigBudgetUpdateAfterExhaustion(t *testing.T) { Body: UpdateVirtualKeyRequest{ ProviderConfigs: []ProviderConfigRequest{ { - ID: &providerConfigID, - Provider: "openai", - Weight: 1.0, + ID: &providerConfigID, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, Budget: &BudgetRequest{ MaxLimit: newBudget, ResetDuration: "1h", @@ -1132,8 +1136,10 @@ func TestVKDeletionCascadeComplete(t *testing.T) { }, ProviderConfigs: []ProviderConfigRequest{ { - Provider: "openai", - Weight: 1.0, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, Budget: &BudgetRequest{ MaxLimit: 5.0, ResetDuration: "1h", diff --git a/tests/governance/config.json b/tests/governance/config.json index 2c01230fd9..bb92606007 100644 --- a/tests/governance/config.json +++ b/tests/governance/config.json @@ -7,6 +7,7 @@ "name": "OpenAI Test Key", "value": "env.OPENAI_API_KEY", "weight": 1, + "models": ["*"], "use_for_batch_api": true } ], @@ -20,6 +21,7 @@ "name": "Anthropic Test Key", "value": "env.ANTHROPIC_API_KEY", "weight": 1, + "models": ["*"], "use_for_batch_api": true } ], @@ -32,7 +34,8 @@ { "name": "OpenRouter Test Key", "value": "env.OPENROUTER_API_KEY", - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { diff --git a/tests/governance/configupdatesync_test.go b/tests/governance/configupdatesync_test.go index 6f2abd87c4..994d0448f6 100644 --- a/tests/governance/configupdatesync_test.go +++ b/tests/governance/configupdatesync_test.go @@ -421,8 +421,10 @@ func TestProviderRateLimitUpdateSyncToMemory(t *testing.T) { Name: vkName, ProviderConfigs: []ProviderConfigRequest{ { - Provider: "openai", - Weight: 1.0, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, RateLimit: &CreateRateLimitRequest{ TokenMaxLimit: &initialTokenLimit, TokenResetDuration: &tokenResetDuration, @@ -512,9 +514,11 @@ func TestProviderRateLimitUpdateSyncToMemory(t *testing.T) { Body: UpdateVirtualKeyRequest{ ProviderConfigs: []ProviderConfigRequest{ { - ID: &providerConfigID, - Provider: "openai", - Weight: 1.0, + ID: &providerConfigID, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, RateLimit: &CreateRateLimitRequest{ TokenMaxLimit: &newLowerLimit, TokenResetDuration: &tokenResetDuration, @@ -955,8 +959,10 @@ func TestProviderBudgetUpdateSyncToMemory(t *testing.T) { Name: vkName, ProviderConfigs: []ProviderConfigRequest{ { - Provider: "openai", - Weight: 1.0, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, Budget: &BudgetRequest{ MaxLimit: initialBudget, ResetDuration: resetDuration, @@ -1054,9 +1060,11 @@ func TestProviderBudgetUpdateSyncToMemory(t *testing.T) { Body: UpdateVirtualKeyRequest{ ProviderConfigs: []ProviderConfigRequest{ { - ID: &providerConfigID, - Provider: "openai", - Weight: 1.0, + ID: &providerConfigID, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, Budget: &BudgetRequest{ MaxLimit: newLowerBudget, ResetDuration: resetDuration, diff --git a/tests/governance/e2e_test.go b/tests/governance/e2e_test.go index ae56ffe0dd..381a83ade1 100644 --- a/tests/governance/e2e_test.go +++ b/tests/governance/e2e_test.go @@ -252,8 +252,10 @@ func TestFullBudgetHierarchyEnforcement(t *testing.T) { }, ProviderConfigs: []ProviderConfigRequest{ { - Provider: "openai", - Weight: 1.0, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, Budget: &BudgetRequest{ MaxLimit: providerBudget, ResetDuration: "1h", @@ -1348,13 +1350,15 @@ func TestWeightedProviderLoadBalancing(t *testing.T) { ProviderConfigs: []ProviderConfigRequest{ { Provider: "openai", - Weight: openaiWeight, + Weight: &openaiWeight, AllowedModels: []string{"gpt-4o"}, + KeyIDs: []string{"*"}, }, { Provider: "azure", - Weight: azureWeight, + Weight: &azureWeight, AllowedModels: []string{"gpt-4o"}, + KeyIDs: []string{"*"}, }, }, }, @@ -1482,13 +1486,15 @@ func TestProviderFallbackMechanism(t *testing.T) { ProviderConfigs: []ProviderConfigRequest{ { Provider: "anthropic", - Weight: anthropicWeight, + Weight: &anthropicWeight, AllowedModels: []string{"claude-3-sonnet"}, // Does NOT include gpt-4o + KeyIDs: []string{"*"}, }, { Provider: "openai", - Weight: openaiWeight, + Weight: &openaiWeight, AllowedModels: []string{"gpt-4o"}, // DOES include gpt-4o + KeyIDs: []string{"*"}, }, }, }, diff --git a/tests/governance/edgecases_test.go b/tests/governance/edgecases_test.go index 13fbed4a6e..32dad1bbe6 100644 --- a/tests/governance/edgecases_test.go +++ b/tests/governance/edgecases_test.go @@ -71,8 +71,10 @@ func TestCrissCrossComplexBudgetHierarchy(t *testing.T) { }, ProviderConfigs: []ProviderConfigRequest{ { - Provider: "openai", - Weight: 1.0, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, Budget: &BudgetRequest{ MaxLimit: 0.08, // Even tighter provider budget ResetDuration: "1h", diff --git a/tests/governance/providerbudget_test.go b/tests/governance/providerbudget_test.go index c39dcbb7fd..7b34437eb7 100644 --- a/tests/governance/providerbudget_test.go +++ b/tests/governance/providerbudget_test.go @@ -23,16 +23,20 @@ func TestProviderBudgetExceeded(t *testing.T) { }, ProviderConfigs: []ProviderConfigRequest{ { - Provider: "openai", - Weight: 1.0, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, Budget: &BudgetRequest{ MaxLimit: 0.01, // Specific OpenAI budget ResetDuration: "1h", }, }, { - Provider: "anthropic", - Weight: 1.0, + Provider: "anthropic", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, Budget: &BudgetRequest{ MaxLimit: 0.01, // Specific Anthropic budget ResetDuration: "1h", diff --git a/tests/governance/ratelimit_test.go b/tests/governance/ratelimit_test.go index aff8eac941..a7a45ffa92 100644 --- a/tests/governance/ratelimit_test.go +++ b/tests/governance/ratelimit_test.go @@ -169,8 +169,10 @@ func TestProviderConfigTokenRateLimit(t *testing.T) { Name: vkName, ProviderConfigs: []ProviderConfigRequest{ { - Provider: "openai", - Weight: 1.0, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, RateLimit: &CreateRateLimitRequest{ TokenMaxLimit: &providerTokenLimit, TokenResetDuration: &tokenResetDuration, @@ -248,8 +250,10 @@ func TestProviderConfigRequestRateLimit(t *testing.T) { Name: vkName, ProviderConfigs: []ProviderConfigRequest{ { - Provider: "openai", - Weight: 1.0, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, RateLimit: &CreateRateLimitRequest{ RequestMaxLimit: &providerRequestLimit, RequestResetDuration: &requestResetDuration, @@ -328,16 +332,20 @@ func TestMultipleProvidersSeparateRateLimits(t *testing.T) { Name: vkName, ProviderConfigs: []ProviderConfigRequest{ { - Provider: "openai", - Weight: 1.0, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, RateLimit: &CreateRateLimitRequest{ TokenMaxLimit: &openaiLimit, TokenResetDuration: &tokenResetDuration, }, }, { - Provider: "anthropic", - Weight: 1.0, + Provider: "anthropic", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, RateLimit: &CreateRateLimitRequest{ TokenMaxLimit: &anthropicLimit, TokenResetDuration: &tokenResetDuration, @@ -400,8 +408,10 @@ func TestProviderAndVKRateLimitTogether(t *testing.T) { }, ProviderConfigs: []ProviderConfigRequest{ { - Provider: "openai", - Weight: 1.0, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, RateLimit: &CreateRateLimitRequest{ TokenMaxLimit: &providerTokenLimit, TokenResetDuration: &providerTokenResetDuration, @@ -840,16 +850,20 @@ func TestProviderLevelRateLimitUsageTracking(t *testing.T) { Name: vkName, ProviderConfigs: []ProviderConfigRequest{ { - Provider: "openai", - Weight: 1.0, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, RateLimit: &CreateRateLimitRequest{ TokenMaxLimit: &openaiTokenLimit, TokenResetDuration: &tokenResetDuration, }, }, { - Provider: "anthropic", - Weight: 1.0, + Provider: "anthropic", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, RateLimit: &CreateRateLimitRequest{ TokenMaxLimit: &anthropicTokenLimit, TokenResetDuration: &tokenResetDuration, diff --git a/tests/governance/ratelimitenforcement_test.go b/tests/governance/ratelimitenforcement_test.go index ab8ab6ca92..006a836f1b 100644 --- a/tests/governance/ratelimitenforcement_test.go +++ b/tests/governance/ratelimitenforcement_test.go @@ -230,8 +230,10 @@ func TestProviderConfigTokenRateLimitEnforcement(t *testing.T) { Name: vkName, ProviderConfigs: []ProviderConfigRequest{ { - Provider: "openai", - Weight: 1.0, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, RateLimit: &CreateRateLimitRequest{ TokenMaxLimit: &providerTokenLimit, TokenResetDuration: &tokenResetDuration, @@ -357,8 +359,10 @@ func TestProviderConfigRequestRateLimitEnforcement(t *testing.T) { Name: vkName, ProviderConfigs: []ProviderConfigRequest{ { - Provider: "openai", - Weight: 1.0, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, RateLimit: &CreateRateLimitRequest{ RequestMaxLimit: &providerRequestLimit, RequestResetDuration: &requestResetDuration, @@ -452,8 +456,10 @@ func TestProviderAndVKRateLimitBothEnforced(t *testing.T) { }, ProviderConfigs: []ProviderConfigRequest{ { - Provider: "openai", - Weight: 1.0, + Provider: "openai", + Weight: float64Ptr(1.0), + AllowedModels: []string{"*"}, + KeyIDs: []string{"*"}, RateLimit: &CreateRateLimitRequest{ RequestMaxLimit: &providerRequestLimit, RequestResetDuration: &requestResetDuration, diff --git a/tests/governance/test_utils.go b/tests/governance/test_utils.go index 2e415250ce..487598ef46 100644 --- a/tests/governance/test_utils.go +++ b/tests/governance/test_utils.go @@ -221,12 +221,18 @@ type CreateVirtualKeyRequest struct { type ProviderConfigRequest struct { ID *uint `json:"id,omitempty"` Provider string `json:"provider"` - Weight float64 `json:"weight,omitempty"` + Weight *float64 `json:"weight,omitempty"` AllowedModels []string `json:"allowed_models,omitempty"` + KeyIDs []string `json:"key_ids,omitempty"` Budget *BudgetRequest `json:"budget,omitempty"` RateLimit *CreateRateLimitRequest `json:"rate_limit,omitempty"` } +// float64Ptr returns a pointer to a float64 value +func float64Ptr(v float64) *float64 { + return &v +} + // BudgetRequest represents a budget request type BudgetRequest struct { MaxLimit float64 `json:"max_limit"` diff --git a/tests/integrations/python/config.json b/tests/integrations/python/config.json index fbe264ca92..befa8fc4b5 100644 --- a/tests/integrations/python/config.json +++ b/tests/integrations/python/config.json @@ -23,6 +23,7 @@ "name": "OpenAI API Key", "value": "env.OPENAI_API_KEY", "weight": 1, + "models": ["*"], "use_for_batch_api": true } ], @@ -36,6 +37,7 @@ "name": "ElevenLabs API Key", "value": "env.ELEVENLABS_API_KEY", "weight": 1, + "models": ["*"], "use_for_batch_api": false } ], @@ -49,6 +51,7 @@ "name": "Xai API Key", "value": "env.XAI_API_KEY", "weight": 1, + "models": ["*"], "use_for_batch_api": false } ], @@ -62,6 +65,7 @@ "name": "Hugging Face API Key", "value": "env.HUGGING_FACE_API_KEY", "weight": 1, + "models": ["*"], "use_for_batch_api": false } ], @@ -75,6 +79,7 @@ "name": "Anthropic API Key", "value": "env.ANTHROPIC_API_KEY", "weight": 1, + "models": ["*"], "use_for_batch_api": true } ], @@ -88,6 +93,7 @@ "name": "Gemini API Key", "value": "env.GEMINI_API_KEY", "weight": 1, + "models": ["*"], "use_for_batch_api": true } ], @@ -104,7 +110,8 @@ "region": "env.GOOGLE_LOCATION", "auth_credentials": "env.VERTEX_CREDENTIALS" }, - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -116,7 +123,8 @@ { "name": "Mistral API Key", "value": "env.MISTRAL_API_KEY", - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -128,7 +136,8 @@ { "name": "Cohere API Key", "value": "env.COHERE_API_KEY", - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -140,7 +149,8 @@ { "name": "Parasail API Key", "value": "env.PARASAIL_API_KEY", - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -152,7 +162,8 @@ { "name": "Groq API Key", "value": "env.GROQ_API_KEY", - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -164,7 +175,8 @@ { "name": "Perplexity API Key", "value": "env.PERPLEXITY_API_KEY", - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -176,7 +188,8 @@ { "name": "Cerebras API Key", "value": "env.CEREBRAS_API_KEY", - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -188,7 +201,8 @@ { "name": "OpenRouter API Key", "value": "env.OPENROUTER_API_KEY", - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -214,7 +228,8 @@ "gpt-image-1": "gpt-image-1" } }, - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -232,6 +247,7 @@ "arn": "env.AWS_ARN" }, "weight": 1, + "models": ["*"], "use_for_batch_api": true } ], @@ -259,7 +275,25 @@ { "id": "vk-test", "value": "sk-bf-test-key", - "is_active": true + "is_active": true, + "provider_configs": [ + { "provider": "openai", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "elevenlabs", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "xai", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "huggingface", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "anthropic", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "gemini", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "vertex", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "mistral", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "cohere", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "parasail", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "groq", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "perplexity", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "cerebras", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "openrouter", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "azure", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "bedrock", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 } + ] } ] }, @@ -275,4 +309,4 @@ "max_request_body_size_mb": 100, "enable_litellm_fallbacks": false } -} \ No newline at end of file +} diff --git a/tests/integrations/typescript/config.json b/tests/integrations/typescript/config.json index 02c8e02f22..179ef73a3c 100644 --- a/tests/integrations/typescript/config.json +++ b/tests/integrations/typescript/config.json @@ -7,6 +7,7 @@ "name": "OpenAI API Key", "value": "env.OPENAI_API_KEY", "weight": 1, + "models": ["*"], "use_for_batch_api": true } ], @@ -20,6 +21,7 @@ "name": "Anthropic API Key", "value": "env.ANTHROPIC_API_KEY", "weight": 1, + "models": ["*"], "use_for_batch_api": true } ], @@ -33,6 +35,7 @@ "name": "Gemini API Key", "value": "env.GEMINI_API_KEY", "weight": 1, + "models": ["*"], "use_for_batch_api": true } ], @@ -44,11 +47,12 @@ "keys": [ { "name": "Vertex API Key", - "vertex_key_config": { + "vertex_key_config": { "project_id": "env.GOOGLE_PROJECT_ID", "region": "env.GOOGLE_LOCATION" }, - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -60,7 +64,8 @@ { "name": "Mistral API Key", "value": "env.MISTRAL_API_KEY", - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -72,7 +77,8 @@ { "name": "Cohere API Key", "value": "env.COHERE_API_KEY", - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -84,7 +90,8 @@ { "name": "Groq API Key", "value": "env.GROQ_API_KEY", - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -96,7 +103,8 @@ { "name": "Perplexity API Key", "value": "env.PERPLEXITY_API_KEY", - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -108,7 +116,8 @@ { "name": "Cerebras API Key", "value": "env.CEREBRAS_API_KEY", - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -120,7 +129,8 @@ { "name": "OpenRouter API Key", "value": "env.OPENROUTER_API_KEY", - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -136,7 +146,8 @@ "endpoint": "env.AZURE_OPENAI_ENDPOINT", "api_version": "env.AZURE_OPENAI_API_VERSION" }, - "weight": 1 + "weight": 1, + "models": ["*"] } ], "network_config": { @@ -154,6 +165,7 @@ "arn": "env.AWS_ARN" }, "weight": 1, + "models": ["*"], "use_for_batch_api": true } ], @@ -181,7 +193,21 @@ { "id": "vk-test", "value": "sk-bf-test-key", - "is_active": true + "is_active": true, + "provider_configs": [ + { "provider": "openai", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "anthropic", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "gemini", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "vertex", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "mistral", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "cohere", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "groq", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "perplexity", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "cerebras", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "openrouter", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "azure", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 }, + { "provider": "bedrock", "allowed_models": ["*"], "key_ids": ["*"], "weight": 1.0 } + ] } ] }, @@ -197,4 +223,4 @@ "max_request_body_size_mb": 100, "enable_litellm_fallbacks": false } -} \ No newline at end of file +} diff --git a/transports/bifrost-http/handlers/wsresponses.go b/transports/bifrost-http/handlers/wsresponses.go index b36ba31123..a1608e9722 100644 --- a/transports/bifrost-http/handlers/wsresponses.go +++ b/transports/bifrost-http/handlers/wsresponses.go @@ -513,7 +513,7 @@ func (h *WSResponsesHandler) createBifrostContext(auth *authHeaders) (*schemas.B key := schemas.Key{ ID: "header-provided", Value: *schemas.NewEnvVar(token), - Models: []string{}, + Models: schemas.WhiteList{"*"}, Weight: 1.0, } ctx.SetValue(schemas.BifrostContextKeyDirectKey, key) @@ -527,7 +527,7 @@ func (h *WSResponsesHandler) createBifrostContext(auth *authHeaders) (*schemas.B key := schemas.Key{ ID: "header-provided", Value: *schemas.NewEnvVar(auth.apiKey), - Models: []string{}, + Models: schemas.WhiteList{"*"}, Weight: 1.0, } ctx.SetValue(schemas.BifrostContextKeyDirectKey, key) @@ -540,7 +540,7 @@ func (h *WSResponsesHandler) createBifrostContext(auth *authHeaders) (*schemas.B key := schemas.Key{ ID: "header-provided", Value: *schemas.NewEnvVar(auth.googAPIKey), - Models: []string{}, + Models: schemas.WhiteList{"*"}, Weight: 1.0, } ctx.SetValue(schemas.BifrostContextKeyDirectKey, key) diff --git a/transports/bifrost-http/lib/config.go b/transports/bifrost-http/lib/config.go index 1ebc985a2f..7e52c799ae 100644 --- a/transports/bifrost-http/lib/config.go +++ b/transports/bifrost-http/lib/config.go @@ -3622,7 +3622,7 @@ func (c *Config) autoDetectProviders(ctx context.Context) { ID: keyID, Name: fmt.Sprintf("%s_auto_detected", envVar), Value: *schemas.NewEnvVar(apiKey), - Models: []string{}, // Empty means all supported models + Models: schemas.WhiteList{"*"}, Weight: 1.0, }, }, diff --git a/ui/app/workspace/providers/fragments/betaHeadersFormFragment.tsx b/ui/app/workspace/providers/fragments/betaHeadersFormFragment.tsx index 1147fc88f6..4f252264ba 100644 --- a/ui/app/workspace/providers/fragments/betaHeadersFormFragment.tsx +++ b/ui/app/workspace/providers/fragments/betaHeadersFormFragment.tsx @@ -8,6 +8,7 @@ import { Badge } from "@/components/ui/badge"; import { getErrorMessage, setProviderFormDirtyState, useAppDispatch } from "@/lib/store"; import { useUpdateProviderMutation } from "@/lib/store/apis/providersApi"; import { ModelProvider, NetworkConfig } from "@/lib/types/config"; +import { buildProviderUpdatePayload } from "@/app/workspace/providers/views/utils"; import { betaHeadersFormSchema, type BetaHeadersFormSchema } from "@/lib/types/schemas"; import { RbacOperation, RbacResource, useRbac } from "@enterprise/lib"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -112,14 +113,12 @@ export function BetaHeadersFormFragment({ provider }: BetaHeadersFormFragmentPro } } - const updatedProvider: ModelProvider = { - ...provider, + updateProvider(buildProviderUpdatePayload(provider, { network_config: { ...(provider.network_config ?? {} as NetworkConfig), beta_header_overrides: Object.keys(cleanedOverrides).length > 0 ? cleanedOverrides : undefined, }, - }; - updateProvider(updatedProvider) + })) .unwrap() .then(() => { toast.success("Beta header configuration updated successfully"); diff --git a/ui/app/workspace/providers/fragments/openaiConfigFormFragment.tsx b/ui/app/workspace/providers/fragments/openaiConfigFormFragment.tsx index 39a40c2a10..0cc5ecaa7b 100644 --- a/ui/app/workspace/providers/fragments/openaiConfigFormFragment.tsx +++ b/ui/app/workspace/providers/fragments/openaiConfigFormFragment.tsx @@ -6,6 +6,7 @@ import { Switch } from "@/components/ui/switch"; import { getErrorMessage, setProviderFormDirtyState, useAppDispatch } from "@/lib/store"; import { useUpdateProviderMutation } from "@/lib/store/apis/providersApi"; import { ModelProvider } from "@/lib/types/config"; +import { buildProviderUpdatePayload } from "@/app/workspace/providers/views/utils"; import { openaiConfigFormSchema, type OpenAIConfigFormSchema } from "@/lib/types/schemas"; import { RbacOperation, RbacResource, useRbac } from "@enterprise/lib"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -41,13 +42,11 @@ export function OpenAIConfigFormFragment({ provider }: OpenAIConfigFormFragmentP }, [form, provider.name, provider.openai_config?.disable_store]); const onSubmit = (data: OpenAIConfigFormSchema) => { - const updatedProvider: ModelProvider = { - ...provider, + updateProvider(buildProviderUpdatePayload(provider, { openai_config: { disable_store: data.disable_store, }, - }; - updateProvider(updatedProvider) + })) .unwrap() .then(() => { toast.success("OpenAI configuration updated successfully");