Skip to content

feat: add virtual key provider configs to budget/rate limit status and sort results#1535

Merged
Pratham-Mishra04 merged 1 commit intomainfrom
02-03-fix_minor_routing_rules_fixes
Feb 4, 2026
Merged

feat: add virtual key provider configs to budget/rate limit status and sort results#1535
Pratham-Mishra04 merged 1 commit intomainfrom
02-03-fix_minor_routing_rules_fixes

Conversation

@Pratham-Mishra04
Copy link
Collaborator

Summary

Added consistent ordering to database queries and enhanced virtual key provider-specific rate limits and budgets in the routing system.

Changes

  • Added Order("created_at ASC") to database queries for virtual keys, teams, customers, rate limits, and budgets to ensure consistent ordering in API responses
  • Enhanced the GetBudgetAndRateLimitStatus function to consider virtual key level provider-specific rate limits and budgets
  • Fixed routing rule updates to properly handle scope changes by removing rules from all scopes before adding to the new scope
  • Added sorting of governance data by creation time for consistent API responses
  • Updated the UI to properly handle scope_id when scope is "global"
  • Fixed routing rule scope handling in the HTTP transport

Type of change

  • Bug fix
  • Feature
  • Refactor

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Plugins
  • UI (Next.js)

How to test

Test the ordering of items in the UI to ensure they appear in a consistent order:

# Core/Transports
go test ./plugins/governance/...
go test ./transports/bifrost-http/...

# UI
cd ui
pnpm i
pnpm build

Test virtual key provider-specific rate limits by creating a virtual key with provider configs and verifying the rate limits are applied correctly.

Breaking changes

  • Yes
  • No

Related issues

Addresses inconsistent ordering in API responses and improves virtual key provider-specific rate limiting.

Security considerations

No security implications.

Checklist

  • I added/updated tests where appropriate
  • I verified builds succeed (Go and UI)

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 3, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Virtual-key aware budgets and rate limits considered during routing decisions
    • Routing rule scope and scope_id management added with validation for global scope
    • Routing rule creation accepts optional provider and expression fields
  • Improvements

    • Deterministic ordering of governance and virtual-key lists (sorted by creation time)
    • Routing flow reordered so routing decisions influence subsequent virtual-key processing
  • Bug Fixes

    • In-memory routing rule moves now reliably update caches and trigger recompilation

Walkthrough

This PR makes governance budget/rate-limit checks virtual-key aware, moves VK-specific routing actions to occur after routing decisions, enforces deterministic ordering by created_at in multiple config queries, introduces scope/scope_id fields and validation for routing-rule updates, and makes cel_expression and provider optional in the routing-rule creation type.

Changes

Cohort / File(s) Summary
ConfigStore Query Ordering
framework/configstore/rdb.go
Added ORDER BY created_at ASC to multiple Get* retrievals (GetVirtualKeys, GetTeams, GetCustomers, GetRateLimits, GetBudgets) to enforce deterministic ordering.
Governance Plugin Flow
plugins/governance/main.go
Reordered VK handling so routing rules are evaluated before VK-specific actions; routing decision now occurs prior to VK load-balancing and MCP header injection; call to store updated to pass virtualKey.
Governance Store & Logic
plugins/governance/store.go
Extended GetBudgetAndRateLimitStatus signature to accept vk *configstoreTables.TableVirtualKey; integrated VK-level provider configs, budgets, baselines, and rate limits into aggregation; sorted model/provider lists by CreatedAt; improved in-memory routing-rule relocation and cache invalidation; GetAllRoutingRules now returns rules sorted by priority and creation time.
Governance Tests
plugins/governance/store_test.go
Updated test call sites to match new GetBudgetAndRateLimitStatus arity (added extra trailing nil/vk argument).
HTTP Handler: Routing Rules
transports/bifrost-http/handlers/governance.go
Added Scope and ScopeID fields to UpdateRoutingRuleRequest; enforce clearing scope_id for global scope and require scope_id for non-global scopes; added deterministic in-memory sorting for virtual keys; imported sort.
Frontend: Routing Rule UI & Types
ui/app/workspace/routing-rules/views/routingRuleSheet.tsx, ui/lib/types/routingRules.ts
Make cel_expression and provider optional in CreateRoutingRuleRequest; ensure payload omits scope_id when scope is global.

Sequence Diagram

sequenceDiagram
    participant Client
    participant GovPlugin as Governance Plugin
    participant GovStore as Governance Store
    participant ConfigStore as ConfigStore/DB

    Client->>GovPlugin: Request (model, provider, optional virtualKey)
    GovPlugin->>GovPlugin: Evaluate routing rules (routing program)
    GovPlugin->>GovStore: GetBudgetAndRateLimitStatus(model, provider, virtualKey)
    GovStore->>ConfigStore: Fetch model/provider configs (ordered by created_at)
    ConfigStore-->>GovStore: Return configs
    alt virtualKey present
        GovStore->>ConfigStore: Fetch virtual-key specific configs
        ConfigStore-->>GovStore: Return VK configs
    end
    GovStore->>GovStore: Aggregate model/provider/VK baselines and limits
    GovStore-->>GovPlugin: Return BudgetAndRateLimitStatus
    alt virtualKey present
        GovPlugin->>GovPlugin: Perform VK load-balancing and inject MCP headers
    end
    GovPlugin-->>Client: Return routed response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐇 I hopped through configs, sorted by time,

VKs and budgets now fall in line.
Rules find their scope, global or tied,
Routing decides, then I balance with pride.
A tiny carrot for CI's green light! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding virtual key provider configs to budget/rate limit status and sorting results. It is concise and specific.
Description check ✅ Passed The PR description is comprehensive and follows the template structure with all required sections completed: Summary, Changes, Type of change, Affected areas, How to test, Breaking changes, and Checklist items addressed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 02-03-fix_minor_routing_rules_fixes

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@plugins/governance/store.go`:
- Around line 2915-2964: The code currently reads rate-limit data directly from
pc.RateLimit which can be stale; instead, when iterating vk.ProviderConfigs in
the block handling provider == string(provider), look up the canonical rate
limit from gs.rateLimits using pc.RateLimit.ID (or pc.RateLimitID/RateLimitID
field) and use that object for TokenCurrentUsage/TokenMaxLimit and
RequestCurrentUsage/RequestMaxLimit calculations, falling back to pc.RateLimit
only if the lookup misses or is nil; update references in the
tokenPercent/requestPercent calculations to use this fetched rateLimit and keep
the existing baseline and result logic unchanged (refer to
UpdateVirtualKeyRateLimitUsageInMemory, gs.rateLimits, pc.RateLimit,
ProviderConfigs, and vk).

In `@transports/bifrost-http/handlers/governance.go`:
- Around line 2522-2532: When applying the update, compute the resulting scope
and scope_id (apply incoming req.Scope and req.ScopeID on top of existing
rule.Scope and rule.ScopeID), then validate: if the resulting scope != "global"
ensure the resulting scope_id is non-nil and non-empty and return a 400 error on
violation; if the resulting scope == "global" set rule.ScopeID = nil, otherwise
set rule.ScopeID to the validated value (use req.Scope, req.ScopeID, rule.Scope,
rule.ScopeID to locate the fields). Ensure you perform this check before
persisting or calling the DB so invalid inputs return 400 rather than causing a
DB error.
🧹 Nitpick comments (2)
framework/configstore/rdb.go (1)

1287-1303: Add a stable tiebreaker to CreatedAt ordering.

Ordering only by created_at can still be nondeterministic when timestamps collide (e.g., batch inserts). Consider adding a secondary key like id for stable ordering; same idea applies to the other new Order clauses in this file. Also, is this PR part of a stack? If yes, please share the stack context so I can review across it.

🔧 Suggested tweak
-		Order("created_at ASC").
+		Order("created_at ASC, id ASC").

As per coding guidelines: always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews).

transports/bifrost-http/handlers/governance.go (1)

294-296: Consider a deterministic tie-breaker when CreatedAt matches.

Map iteration order is random; if multiple virtual keys share the same CreatedAt, ordering can still vary. Adding ID as a secondary key makes the in-memory ordering fully stable.

🔧 Suggested tweak
-		sort.Slice(virtualKeys, func(i, j int) bool {
-			return virtualKeys[i].CreatedAt.Before(virtualKeys[j].CreatedAt)
-		})
+		sort.Slice(virtualKeys, func(i, j int) bool {
+			if virtualKeys[i].CreatedAt.Equal(virtualKeys[j].CreatedAt) {
+				return virtualKeys[i].ID < virtualKeys[j].ID
+			}
+			return virtualKeys[i].CreatedAt.Before(virtualKeys[j].CreatedAt)
+		})

@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 02-03-fix_minor_routing_rules_fixes branch from e05b657 to 6ec3119 Compare February 3, 2026 09:16
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@plugins/governance/main.go`:
- Around line 349-365: The code assumes req.Headers is non-nil and will panic
when assigning headers; change how p.addMCPIncludeTools is used so it safely
merges headers: call p.addMCPIncludeTools with the existing req.Headers (or nil)
and virtualKey, check the returned headers map, initialize req.Headers if it's
nil, then copy/merge each returned header into req.Headers (instead of directly
assigning into a possibly nil map); keep the existing error handling and
preserve needsMarshal = true. Use the symbols virtualKey, p.addMCPIncludeTools,
req.Headers, headers, p.loadBalanceProvider, and needsMarshal to locate and
update the logic.

@danpiths danpiths changed the base branch from feature/02-03-feat_real-time_governance_updates_via_websocket_and_base_model_selection_in_ui to graphite-base/1535 February 3, 2026 10:34
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 02-03-fix_minor_routing_rules_fixes branch from 6ec3119 to 1aa4a9c Compare February 3, 2026 17:16
@Pratham-Mishra04 Pratham-Mishra04 changed the base branch from graphite-base/1535 to feature/02-03-feat_real-time_governance_updates_via_websocket_and_base_model_selection_in_ui February 3, 2026 17:16
@Pratham-Mishra04 Pratham-Mishra04 mentioned this pull request Feb 3, 2026
18 tasks
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

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)

340-371: ⚠️ Potential issue | 🟠 Major

Routing decision can be overridden for /genai after reordering.

For /genai requests, loadBalanceProvider reads the model from the URL path (unchanged by applyRoutingRules) and can overwrite a routing decision that was just applied via ctx.SetValue("model", ...). This makes routing rules ineffective for genai requests. Consider skipping load balancing when a routing decision has already been made (or using the routed model when available).

🔧 Suggested guard
- if virtualKey != nil {
- 	//2. Load balance provider
- 	payload, err = p.loadBalanceProvider(ctx, req, payload, virtualKey)
- 	if err != nil {
- 		return nil, err
- 	}
+ if virtualKey != nil {
+ 	//2. Load balance provider (skip when routing rules already selected provider/model)
+ 	if routingDecision == nil {
+ 		payload, err = p.loadBalanceProvider(ctx, req, payload, virtualKey)
+ 		if err != nil {
+ 			return nil, err
+ 		}
+ 	}
 	//3. Add MCP tools
 	headers, err := p.addMCPIncludeTools(nil, virtualKey)
 	if err != nil {
 		p.logger.Error("failed to add MCP include tools: %v", err)
 		return nil, nil
 	}
 	for header, value := range headers {
 		req.Headers[header] = value
 	}
 	needsMarshal = true
 }
🤖 Fix all issues with AI agents
In `@transports/bifrost-http/handlers/governance.go`:
- Around line 10-11: The current use of sort.Slice to order virtual keys by
CreatedAt is unstable when CreatedAt ties; replace the unstable sort with a
stable comparison that deterministically breaks ties (e.g., use sort.SliceStable
or add a secondary comparison on the unique ID field) wherever ordering is
performed (the sort.Slice calls that compare CreatedAt, including the one around
lines 10-11 and the similar block at 281-283); update the comparator to first
compare CreatedAt and if equal compare the key ID (or another unique, stable
field) so responses are deterministic even when CreatedAt is identical or zero.

@danpiths danpiths changed the base branch from feature/02-03-feat_real-time_governance_updates_via_websocket_and_base_model_selection_in_ui to graphite-base/1535 February 3, 2026 18:12
Copy link
Collaborator Author

Pratham-Mishra04 commented Feb 4, 2026

Merge activity

  • Feb 4, 7:46 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Feb 4, 7:58 AM UTC: Graphite couldn't merge this PR because it had merge conflicts.
  • Feb 4, 8:01 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Feb 4, 8:01 AM UTC: @Pratham-Mishra04 merged this pull request with Graphite.

@Pratham-Mishra04 Pratham-Mishra04 changed the base branch from graphite-base/1535 to main February 4, 2026 07:58
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 02-03-fix_minor_routing_rules_fixes branch from 1aa4a9c to f5e9f3f Compare February 4, 2026 08:00
@Pratham-Mishra04 Pratham-Mishra04 merged commit ec6688d into main Feb 4, 2026
7 of 8 checks passed
@Pratham-Mishra04 Pratham-Mishra04 deleted the 02-03-fix_minor_routing_rules_fixes branch February 4, 2026 08:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant