restrict fallbacks and provider selection to vk boundry - #3924
Conversation
|
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR threads provider availability constraints through the request handling pipeline. The governance plugin now sets allowed providers from virtual key constraints into the Bifrost context, the router respects this constraint when selecting catalog providers, and fallback extraction filters fallbacks accordingly. ChangesProvider Availability Constraint Through Request Pipeline
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Confidence Score: 3/5The core constraint propagation mechanism works correctly in the happy path, but two previously-flagged structural gaps mean the VK provider boundary is not reliably enforced for fallbacks: the router narrows the allowed-provider list to the route's native provider before the fallback filter reads it, and governance's early-return for provider-prefixed models skips populating the context key entirely. Both flagged gaps produce silent mis-enforcement: a VK explicitly permitting multiple providers will still drop valid cross-provider fallbacks after the router's single-provider narrowing step, and any request using a slash-prefixed primary model bypasses constraint propagation to the fallback stage completely. These are real, reproducible behavioral defects on the changed code paths rather than theoretical risks. transports/bifrost-http/integrations/router.go (lines 815–828, where the context key is overwritten to a single-element list before fallback extraction) and plugins/governance/main.go (lines 740–751, the early return for provider-prefixed models that skips setting the context key). Important Files Changed
Reviews (7): Last reviewed commit: "restrict fallbacks and provider selectio..." | Re-trigger Greptile |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
plugins/governance/main.go (1)
739-761:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftPreserve VK provider constraints on prefixed-model requests before early return.
Line 740-Line 751 can return before Line 758/Line 827 are reached. That skips
BifrostContextKeyAvailableProviderspropagation for provider-prefixed models, so downstream routing/fallback filtering may miss VK boundaries on this path.🔧 Suggested direction
- // Check if model already has provider prefix (contains "/") - if strings.Contains(modelStr, "/") { - provider, _ := schemas.ParseModelString(modelStr, "") - // Checking valid provider when store is available; if store is nil, - // assume the prefixed model should be left unchanged. - if p.inMemoryStore != nil { - if _, ok := p.inMemoryStore.GetConfiguredProviders()[provider]; ok { - return body, nil - } - } else { - return body, nil - } - } + // Keep track of provider-pinned input, but still compute/set available providers first + alreadyPinnedProvider := false + if strings.Contains(modelStr, "/") { + provider, _ := schemas.ParseModelString(modelStr, "") + if p.inMemoryStore == nil { + alreadyPinnedProvider = true + } else if _, ok := p.inMemoryStore.GetConfiguredProviders()[provider]; ok { + alreadyPinnedProvider = true + } + } @@ ctx.SetValue(schemas.BifrostContextKeyAvailableProviders, allowedModelProviders) @@ + // Model already pinned by caller: keep model unchanged, but constraints are now propagated. + if alreadyPinnedProvider { + return body, nil + }As per coding guidelines,
virtual_keys[].provider_configs: empty array = no providers allowed (deny-by-default)and the effective result should behave like no allowed providers.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/governance/main.go` around lines 739 - 761, The early return for prefixed models in the code that checks modelStr (the block using strings.Contains(modelStr), schemas.ParseModelString, and p.inMemoryStore) skips setting the per-virtual-key available providers context (ctx.SetValue with schemas.BifrostContextKeyAvailableProviders) and its warning log, which breaks VK provider constraints for prefixed requests; fix by, before any return in that prefixed-model branch, computing and setting the available providers from virtualKey.ProviderConfigs (if len(virtualKey.ProviderConfigs)==0 set an empty []schemas.ModelProvider and append the warn log via ctx.AppendRoutingEngineLog), then return body,nil as before so downstream routing/fallback sees the VK constraints.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@plugins/governance/main.go`:
- Around line 739-761: The early return for prefixed models in the code that
checks modelStr (the block using strings.Contains(modelStr),
schemas.ParseModelString, and p.inMemoryStore) skips setting the per-virtual-key
available providers context (ctx.SetValue with
schemas.BifrostContextKeyAvailableProviders) and its warning log, which breaks
VK provider constraints for prefixed requests; fix by, before any return in that
prefixed-model branch, computing and setting the available providers from
virtualKey.ProviderConfigs (if len(virtualKey.ProviderConfigs)==0 set an empty
[]schemas.ModelProvider and append the warn log via ctx.AppendRoutingEngineLog),
then return body,nil as before so downstream routing/fallback sees the VK
constraints.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: aa0fa3b8-75d8-478b-b9bd-c3748f0c8dce
📒 Files selected for processing (6)
plugins/governance/httptransportprehook_test.goplugins/governance/main.gotransports/bifrost-http/integrations/router.gotransports/bifrost-http/integrations/router_test.gotransports/bifrost-http/integrations/utils.gotransports/bifrost-http/integrations/utils_test.go
ee05752 to
98a3454
Compare
10db96d to
4d3097e
Compare
98a3454 to
3f3b757
Compare
3f3b757 to
fe395bf
Compare
4d3097e to
6f2eeb0
Compare
6f2eeb0 to
3fec8de
Compare
fe395bf to
1e39cba
Compare
Merge activity
|
## Summary When a virtual key has provider constraints (but no weights), the governance plugin now propagates the list of allowed providers into the Bifrost context. The router then intersects that list with the model catalog's provider list, ensuring that only providers permitted by the virtual key are considered for routing and fallbacks. ## Changes - The governance plugin sets `BifrostContextKeyAvailableProviders` on the context after filtering provider configs, including setting an empty slice when no provider configs exist or when no providers pass the model filter. - The router's `createHandler` intersects the catalog-derived provider list with any pre-existing `BifrostContextKeyAvailableProviders` value set by upstream plugins (e.g., governance). If the intersection is empty, an empty provider list is stored rather than falling back to the full catalog set. - `extractAndParseFallbacks` now accepts a `BifrostContext` and filters parsed fallbacks to only those whose provider appears in `BifrostContextKeyAvailableProviders`. If all fallbacks are filtered out, the fallback list on the request is explicitly cleared to `nil`. ## Type of change - [ ] Bug fix - [x] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [x] Transports (HTTP) - [ ] Providers/Integrations - [x] Plugins ## How to test ```sh go test ./plugins/governance/... go test ./transports/bifrost-http/integrations/... ``` - A virtual key with `openai/gpt-4o` and `anthropic/claude-3-5-sonnet` provider configs (no weights) and a request for `gpt-4o` should result in `BifrostContextKeyAvailableProviders` containing only `openai`. - A virtual key with only `openai/gpt-4o` and a request for `claude-3-5-sonnet` should result in an empty `BifrostContextKeyAvailableProviders`. - A request with fallbacks that include providers not in the allowed list should have those fallbacks stripped before the request is dispatched. ## Breaking changes - [ ] Yes - [x] No ## Related issues Closes #2516 ## Security considerations Provider constraints enforced by virtual keys are now respected end-to-end through routing and fallback resolution, preventing requests from being routed to providers that the virtual key does not permit. ## Checklist - [ ] I read `docs/contributing/README.md` and followed the guidelines - [x] I added/updated tests where appropriate - [ ] I updated documentation where needed - [x] I verified builds succeed (Go and UI) - [ ] I verified the CI pipeline passes locally if applicable <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Tests** * Added comprehensive test coverage for governance HTTP transport pre-hook with provider-constrained virtual keys. * Added router tests verifying proper provider constraint enforcement during request handling. * **Bug Fixes** * Router now correctly respects provider availability constraints when selecting providers for requests. * Fallback extraction now filters to only providers permitted by governance constraints. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary When a virtual key has provider constraints (but no weights), the governance plugin now propagates the list of allowed providers into the Bifrost context. The router then intersects that list with the model catalog's provider list, ensuring that only providers permitted by the virtual key are considered for routing and fallbacks. ## Changes - The governance plugin sets `BifrostContextKeyAvailableProviders` on the context after filtering provider configs, including setting an empty slice when no provider configs exist or when no providers pass the model filter. - The router's `createHandler` intersects the catalog-derived provider list with any pre-existing `BifrostContextKeyAvailableProviders` value set by upstream plugins (e.g., governance). If the intersection is empty, an empty provider list is stored rather than falling back to the full catalog set. - `extractAndParseFallbacks` now accepts a `BifrostContext` and filters parsed fallbacks to only those whose provider appears in `BifrostContextKeyAvailableProviders`. If all fallbacks are filtered out, the fallback list on the request is explicitly cleared to `nil`. ## Type of change - [ ] Bug fix - [x] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [x] Transports (HTTP) - [ ] Providers/Integrations - [x] Plugins ## How to test ```sh go test ./plugins/governance/... go test ./transports/bifrost-http/integrations/... ``` - A virtual key with `openai/gpt-4o` and `anthropic/claude-3-5-sonnet` provider configs (no weights) and a request for `gpt-4o` should result in `BifrostContextKeyAvailableProviders` containing only `openai`. - A virtual key with only `openai/gpt-4o` and a request for `claude-3-5-sonnet` should result in an empty `BifrostContextKeyAvailableProviders`. - A request with fallbacks that include providers not in the allowed list should have those fallbacks stripped before the request is dispatched. ## Breaking changes - [ ] Yes - [x] No ## Related issues Closes #2516 ## Security considerations Provider constraints enforced by virtual keys are now respected end-to-end through routing and fallback resolution, preventing requests from being routed to providers that the virtual key does not permit. ## Checklist - [ ] I read `docs/contributing/README.md` and followed the guidelines - [x] I added/updated tests where appropriate - [ ] I updated documentation where needed - [x] I verified builds succeed (Go and UI) - [ ] I verified the CI pipeline passes locally if applicable <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Tests** * Added comprehensive test coverage for governance HTTP transport pre-hook with provider-constrained virtual keys. * Added router tests verifying proper provider constraint enforcement during request handling. * **Bug Fixes** * Router now correctly respects provider availability constraints when selecting providers for requests. * Fallback extraction now filters to only providers permitted by governance constraints. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary When a virtual key has provider constraints (but no weights), the governance plugin now propagates the list of allowed providers into the Bifrost context. The router then intersects that list with the model catalog's provider list, ensuring that only providers permitted by the virtual key are considered for routing and fallbacks. ## Changes - The governance plugin sets `BifrostContextKeyAvailableProviders` on the context after filtering provider configs, including setting an empty slice when no provider configs exist or when no providers pass the model filter. - The router's `createHandler` intersects the catalog-derived provider list with any pre-existing `BifrostContextKeyAvailableProviders` value set by upstream plugins (e.g., governance). If the intersection is empty, an empty provider list is stored rather than falling back to the full catalog set. - `extractAndParseFallbacks` now accepts a `BifrostContext` and filters parsed fallbacks to only those whose provider appears in `BifrostContextKeyAvailableProviders`. If all fallbacks are filtered out, the fallback list on the request is explicitly cleared to `nil`. ## Type of change - [ ] Bug fix - [x] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [x] Transports (HTTP) - [ ] Providers/Integrations - [x] Plugins ## How to test ```sh go test ./plugins/governance/... go test ./transports/bifrost-http/integrations/... ``` - A virtual key with `openai/gpt-4o` and `anthropic/claude-3-5-sonnet` provider configs (no weights) and a request for `gpt-4o` should result in `BifrostContextKeyAvailableProviders` containing only `openai`. - A virtual key with only `openai/gpt-4o` and a request for `claude-3-5-sonnet` should result in an empty `BifrostContextKeyAvailableProviders`. - A request with fallbacks that include providers not in the allowed list should have those fallbacks stripped before the request is dispatched. ## Breaking changes - [ ] Yes - [x] No ## Related issues Closes #2516 ## Security considerations Provider constraints enforced by virtual keys are now respected end-to-end through routing and fallback resolution, preventing requests from being routed to providers that the virtual key does not permit. ## Checklist - [ ] I read `docs/contributing/README.md` and followed the guidelines - [x] I added/updated tests where appropriate - [ ] I updated documentation where needed - [x] I verified builds succeed (Go and UI) - [ ] I verified the CI pipeline passes locally if applicable <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Tests** * Added comprehensive test coverage for governance HTTP transport pre-hook with provider-constrained virtual keys. * Added router tests verifying proper provider constraint enforcement during request handling. * **Bug Fixes** * Router now correctly respects provider availability constraints when selecting providers for requests. * Fallback extraction now filters to only providers permitted by governance constraints. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## ✨ Features - **OpenAI Compaction** — Added OpenAI conversation compaction support across core, framework, logging, and the API surface (#4053) - **Multi-Customer & Org Hierarchy** — Logs and usage tracking now support multiple customers, teams, and business units, including business unit CRUD, team assignment, and governance endpoints in the OpenAPI spec (#4066, #4041, #4082) - **Provider-Level Governance** — Budgets & limits are now scope-aware and can be applied at the virtual-key top level and per provider, wired from the model configs table, with UI filters for scope and providers (#3938, #3937, #3939, #3981, #3962) - **Customer Budgets** — Customers support multiple budgets and `calendar_aligned` budget windows (#3998, #3997) - **Virtual Key Attribution & Controls** — Added a `created_by` user attribution column and a `blacklisted_models` column for virtual key provider configs (#3672, #3653) - **Request Header Capture** — OTel and Maxim observability plugins capture `request_headers` by pattern, with wildcard support (e.g. `x-custom-*`); logging gained the same wildcard header capture (#4012, #3958) - **OTel Content Controls & Collectors** — New `disable_content_logging` option drops message/tool content from exported spans, plus support for multiple OTel collectors (#4064, #3894) - **xAI x_search** — Added xAI `x_search` tool support (#3976) - **URL Validation** — Added fetch URL validation with private-network configuration and link-local blocking (#3947, #3991) - **File Scheme Pricing URLs** — Pricing source URLs now accept the `file://` scheme for air-gapped and self-hosted deployments (#4045) - **Paginated Virtual Keys** — Virtual key fetching is paginated to handle deployments with very large numbers of keys (#3957) - **Client IP Resolution** — Resolve client IP from `X-Forwarded-For`/`X-Real-IP` headers - **SCIM Provisioning** — Added `attributeType`/`attributeValue` SCIM provisioning fields - **Helm/Config Schema** — Added `roles` RBAC governance config and `per_user_oauth` MCP auth to the Helm chart and config schema (#4004, #4009) - **Log Navigation UI** — Added a "View logs" menu item to customer, team, and virtual key tables, clickable links in log detail views, a customer detail sheet, and a reusable `BudgetDisplay` component (#4073, #4054, #4026, #4055) - **Faster First Paint** — Added an inline loading shell to `#root` before React mounts (#4063) - **Materialized View Alias** — Added an `alias` column to the materialized view with filter support (#4078) ## 🐞 Fixed - **Fetch URL IP Checks** — Hardened fetch URL IP checks against SSRF (#4092) - **Mantle Model Matching** — Broadened Mantle model matching to all `gpt` variants (#4091) - **Empty Thinking Blocks** — Strip thinking blocks when the signature is empty (#4079) - **OpenAI Stream Usage** — Removed usage from the `responses.created` event in the OpenAI stream (#4080) - **Prompt Cache Key** — Set the prompt cache key from the Anthropic integration (#4086) - **Upstream Failure Status** — Map upstream connection failures to 502 instead of 400 (#3929) (thanks [@chris-colinsky](https://github.com/chris-colinsky)!) - **Gemini Schema Constraints** — Accept numeric schema integer constraints for Gemini (#3994) (thanks [@yanhao98](https://github.com/yanhao98)!) - **Files Provider Param** — Accept the `?provider=` query param on `GET /v1/files` (#3971) (thanks [@alexef](https://github.com/alexef)!) - **Optional Batch Model** — Made the `model` field optional on `POST /v1/batches` (#3973) (thanks [@alexef](https://github.com/alexef)!) - **Helm Azure Config** — Added missing `azure_key_config` fields to the Helm schema (#3996) (thanks [@axelray-dev](https://github.com/axelray-dev)!) - **Text Completion Chunk Model** — Added the missing `Model` field to `TextCompletionChunkResponse` (#3970) (thanks [@kuishou68](https://github.com/kuishou68)!) - **MCP Inline stdio Env** — MCP stdio server configs accept inline environment variable assignments (#3861) (thanks [@Shushmitaaaa](https://github.com/Shushmitaaaa)!) - **Orphaned Tool Results** — Orphaned tool results in the OpenAI to Anthropic conversion flow are no longer rejected by the Anthropic API (#3919) - **Node Usage Reconciliation** — Added a monotonic `inc_number` log cursor so node usage reconciliation does not skip late async log writes (#3664) - **Bedrock Output Assessments** — Corrected the type of `outputAssessments` in Bedrock responses (#4028) - **Model Pool Pricing Reloads** — Preserve non-pricing model pool entries across pricing reloads (#3999) - **Ghost Node Reconciliation** — Replicate the VK hierarchy flow for ghost node reconciliation (#4088) - **VK Double Usage Counting** — Fixed double usage counting when creating a virtual key (#4070) - **Model Config Lifecycle** — Cascade deletes for model configs and removal of stale in-memory model configs (#4051, #4043) - **FTS Index Cap** — Reduced the FTS index `left()` cap from 800k to 250k chars to stay within the tsvector limit (#4057) - **Sync Worker Drift** — Reduced the sync worker ticker period to 5m to prevent threshold drift (#4023) - **Passthrough** — Fixed passthrough budgets, gated passthrough models per VK, model extraction for Azure passthrough, and restricted fallbacks/provider selection to the VK boundary (#3941, #3988, #3983, #3924) - **Provider Response Headers** — Strip provider response headers and add a content-type filter (#3955, #4024) - **Stream Handling** — Drain non-SSE stream readers and retry stale connections (#3956, #3967) - **Azure Claude** — Strip Azure diagnostic property for Claude models (#3925) - **Compat max_tokens** — Preserve chat `max_tokens` during param filtering (#3992) - **Raw Request Flag** — Removed the raw request flag from providers that don't support it (#4058) - **UI Fixes** — Standardized page container layout, virtual key model configs UI, and dashboard chart tooltips (#4046, #4052, #4044) ## 🔧 Maintenance - **Dependency Upgrades** — Bumped transitive `golang.org/x` dependencies (crypto, net, sys, text) for Docker Scout CVE remediation and `recharts` to 3.8.1; cascaded version bumps across all modules (#3900, #4003)

Summary
When a virtual key has provider constraints (but no weights), the governance plugin now propagates the list of allowed providers into the Bifrost context. The router then intersects that list with the model catalog's provider list, ensuring that only providers permitted by the virtual key are considered for routing and fallbacks.
Changes
BifrostContextKeyAvailableProviderson the context after filtering provider configs, including setting an empty slice when no provider configs exist or when no providers pass the model filter.createHandlerintersects the catalog-derived provider list with any pre-existingBifrostContextKeyAvailableProvidersvalue set by upstream plugins (e.g., governance). If the intersection is empty, an empty provider list is stored rather than falling back to the full catalog set.extractAndParseFallbacksnow accepts aBifrostContextand filters parsed fallbacks to only those whose provider appears inBifrostContextKeyAvailableProviders. If all fallbacks are filtered out, the fallback list on the request is explicitly cleared tonil.Type of change
Affected areas
How to test
openai/gpt-4oandanthropic/claude-3-5-sonnetprovider configs (no weights) and a request forgpt-4oshould result inBifrostContextKeyAvailableProviderscontaining onlyopenai.openai/gpt-4oand a request forclaude-3-5-sonnetshould result in an emptyBifrostContextKeyAvailableProviders.Breaking changes
Related issues
Closes #2516
Security considerations
Provider constraints enforced by virtual keys are now respected end-to-end through routing and fallback resolution, preventing requests from being routed to providers that the virtual key does not permit.
Checklist
docs/contributing/README.mdand followed the guidelinesSummary by CodeRabbit
Release Notes
Tests
Bug Fixes