Skip to content

feat: Add v2 API for governance and update UI to use the multi budget lines - #3960

Closed
roroghost17 wants to merge 1 commit into
06-01-refactor_makes_scope-level_check_methods_extensiblefrom
06-01-feat_add_v2_api_for_governance_and_update_ui_to_use_the_multi_budget_lines
Closed

feat: Add v2 API for governance and update UI to use the multi budget lines#3960
roroghost17 wants to merge 1 commit into
06-01-refactor_makes_scope-level_check_methods_extensiblefrom
06-01-feat_add_v2_api_for_governance_and_update_ui_to_use_the_multi_budget_lines

Conversation

@roroghost17

@roroghost17 roroghost17 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR introduces a v2 API for provider governance that supports multiple budgets per provider, alongside a calendar_aligned field. Previously, the provider governance API exposed only a single budget; the v2 endpoints expose the full list. The UI is updated to consume the v2 endpoints and render all budgets.

Changes

  • Introduced versionedRoute struct and a routes() method on GovernanceHandler to declare the full route table in one place. Each route registers under /api (compat), /api/v1, and optionally /api/v2 when a v2 handler is explicitly provided — no implicit v1 fallback under /api/v2.
  • Added getProviderGovernanceV2 and updateProviderGovernanceV2 handlers. The v2 GET returns ProviderGovernanceResponseV2 (all budgets + calendar_aligned); the v2 PUT accepts UpdateProviderGovernanceRequestV2 where Budgets is *[] so a nil field means "no change" and a non-nil empty slice means "remove all".
  • Added modelConfigToProviderGovernanceV2 which returns all budgets (copied, not aliased) and calendar_aligned, replacing the v1 single-budget surface.
  • The v1 comment was updated to clarify it intentionally surfaces only the first budget.
  • UpdateProviderGovernanceRequestV2.Budgets uses a pointer-to-slice to distinguish "absent" from "empty array" after JSON unmarshal.
  • The UI governanceApi GET and PUT provider governance queries now target /v2/governance/providers.
  • ProviderGovernance type updated to budgets?: Budget[] (dropping the single budget field) and UpdateProviderGovernanceRequest updated to budgets?: CreateBudgetRequest[].
  • Provider governance table and form updated to iterate over all budgets, sorted newest-first by created_at. Budget cards are labeled with their reset duration when multiple budgets exist.
  • GovernanceFormFragment replaced the single-budget FormField with the MultiBudgetLines component and simplified the two useEffect reset blocks via a shared governanceToFormValues helper.
  • Model limits table budgets are also sorted newest-first.
  • Tests added for modelConfigToProviderGovernanceV2 (filter logic, field mapping, copy semantics), getProviderGovernanceV2 (from-memory, nil data, DB path, DB error), UpdateProviderGovernanceRequestV2 pointer semantics, and the routes table contract (every route has a v1 handler, provider GET/PUT have v2 handlers, DELETE does not, no duplicates).

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

# Core/Transports
go test ./transports/bifrost-http/handlers/...

# UI
cd ui
pnpm i || npm i
pnpm test || npm test
pnpm build || npm run build

To validate the v2 endpoints manually:

# GET all provider governance (multi-budget response)
curl -s http://localhost:PORT/api/v2/governance/providers | jq .

# PUT provider governance with multiple budgets
curl -s -X PUT http://localhost:PORT/api/v2/governance/providers/openai \
  -H 'Content-Type: application/json' \
  -d '{"budgets":[{"max_limit":100,"reset_duration":"1d"},{"max_limit":500,"reset_duration":"1M"}]}'

# PUT with empty budgets array to remove all budgets
curl -s -X PUT http://localhost:PORT/api/v2/governance/providers/openai \
  -H 'Content-Type: application/json' \
  -d '{"budgets":[]}'

# Confirm v1 compat path still works
curl -s http://localhost:PORT/api/governance/providers | jq .
curl -s http://localhost:PORT/api/v1/governance/providers | jq .

Screenshots/Recordings

Provider governance table and form now display one card/row per budget, labeled with the reset duration when more than one budget is present.

Breaking changes

  • Yes
  • No

The ProviderGovernance TypeScript type drops the single budget field in favour of budgets: Budget[]. Any UI code referencing providerGovernance.budget directly will need to be updated to use providerGovernance.budgets?.[0] or iterate the array. The backend v1 endpoint is unchanged and continues to return a single budget field; only the UI now calls v2.

Related issues

Security considerations

No new auth surfaces. The v2 endpoints are registered with the same admin middleware chain as v1. The self-service /api/governance/virtual-keys/quota endpoint continues to bypass admin middlewares as before.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@CLAassistant

CLAassistant commented Jun 1, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5dcdcd8b-be22-43cb-be5c-a2a662c33070

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR introduces versioned v2 API endpoints for provider governance with multi-budget support and calendar alignment. The backend refactors route registration to support versioned handlers, adds v2 response/request types, implements GET/PUT handlers with multi-budget semantics, and includes comprehensive tests. The frontend updates type definitions, API layer endpoints, form component logic, and display components to work with multi-budget governance.

Changes

Multi-Budget Provider Governance v2 Support

Layer / File(s) Summary
Backend v2 Types and Route Registration
transports/bifrost-http/handlers/governance.go
Adds ProviderGovernanceResponseV2 and UpdateProviderGovernanceRequestV2 types. Introduces versionedRoute abstraction and routes() method to register all v1 handlers under /api and /api/v1, with v2 handlers additionally registered under /api/v2. Provider governance GET/PUT use v2 handlers while DELETE remains v1.
Backend v2 Mapping and Handlers
transports/bifrost-http/handlers/governance.go
Refines v1 provider governance mapping to return only the first budget. Adds modelConfigToProviderGovernanceV2 to return all owned budgets and calendar_aligned. Implements getProviderGovernanceV2 handler supporting both in-memory and DB-backed paths. Implements updateProviderGovernanceV2 handler with multi-budget reconciliation, calendar_aligned field updates, and rate-limit lifecycle handling.
Backend v2 Tests
transports/bifrost-http/handlers/governance_test.go
Adds test mocks for governance manager and config store. Tests modelConfigToProviderGovernanceV2 mapping (validation, field correctness, budget copying). Tests getProviderGovernanceV2 for in-memory path, nil error, DB-backed path, and DB errors. Tests UpdateProviderGovernanceRequestV2 JSON pointer semantics for budgets field. Validates route contract: all v1 handlers present, v2 handlers required for GET/PUT, no v2 for DELETE, no duplicate routes.
Frontend Type Updates and API Layer
ui/lib/types/governance.ts, ui/lib/store/apis/governanceApi.ts
Budget adds optional created_at field. ProviderGovernance replaces single-budget fields (budget_id, budget, rate_limit_id) with budgets?: Budget[], rate_limit?: RateLimit, and calendar_aligned?: boolean. UpdateProviderGovernanceRequest replaces budget with budgets?: CreateBudgetRequest[] for add/replace/remove semantics. API endpoints updated to /v2/governance/providers paths.
Frontend Form Component for Multi-Budget Governance
ui/app/workspace/providers/fragments/governanceFormFragment.tsx
Replaces single-budget form fields with Zod schema using budgetLineSchema array. Introduces MultiBudgetLines component for budget management and governanceToFormValues mapper for initialization. Submit logic constructs budgets payload with explicit removal via empty array. "Current Usage" section maps and sorts provider budgets using resetDurationLabels.
Frontend Multi-Budget Display Components
ui/app/workspace/providers/views/providerGovernanceTable.tsx, ui/app/workspace/model-limits/views/modelLimitsTable.tsx
ProviderGovernanceTable derives sorted budgets array, introduces multipleBudgets flag, and renders one budget card per entry with conditional reset duration label. ModelLimitsTable sorts budgets by created_at descending. Both components now handle multi-budget governance instead of single-budget.

Sequence Diagram(s)

sequenceDiagram
    participant Frontend as Frontend Client
    participant APILayer as API Layer
    participant BackendV2 as Backend v2 Handler
    participant Storage as ConfigStore
    participant Mapper as Model Mapper
    
    Frontend->>APILayer: getProviderGovernance()
    APILayer->>BackendV2: GET /api/v2/governance/providers
    BackendV2->>Storage: GetProviderGovernanceModelConfigs()
    Storage-->>BackendV2: TableModelConfig[]
    BackendV2->>Mapper: modelConfigToProviderGovernanceV2()
    Mapper-->>BackendV2: ProviderGovernanceResponseV2[]
    BackendV2-->>APILayer: JSON response
    APILayer-->>Frontend: ProviderGovernance {budgets[], rate_limit, calendar_aligned}
    
    Frontend->>APILayer: updateProviderGovernance(provider, updates)
    APILayer->>BackendV2: PUT /api/v2/governance/providers/{provider}
    BackendV2->>Storage: UpdateProviderGovernanceModelConfig()
    alt Budgets Changed
        BackendV2->>Storage: reconcileModelConfigBudgets()
    end
    alt CalendarAligned Changed
        BackendV2->>Storage: Update calendar_aligned field
    end
    Storage-->>BackendV2: Updated config
    BackendV2->>Mapper: modelConfigToProviderGovernanceV2()
    Mapper-->>BackendV2: ProviderGovernanceResponseV2
    BackendV2-->>APILayer: JSON response
    APILayer-->>Frontend: Updated ProviderGovernance
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • akshaydeo

🐰 A v2 endpoint hops in with budgets galore,
Multi-bucket governance now at the core!
Form fields give way to budget arrays bright,
Calendar alignment and rate-limits aligned just right,
The frontend rejoices with sorted display—
Governance evolved in a hop-along way! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: introducing a v2 governance API and updating the UI to support multiple budget lines.
Description check ✅ Passed The description comprehensively covers all required sections including summary, detailed changes, type of change, affected areas, testing instructions, breaking changes, and security considerations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 06-01-feat_add_v2_api_for_governance_and_update_ui_to_use_the_multi_budget_lines

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

roroghost17 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@roroghost17
roroghost17 force-pushed the 06-01-feat_add_v2_api_for_governance_and_update_ui_to_use_the_multi_budget_lines branch from 37612ab to 10da233 Compare June 1, 2026 17:56
@roroghost17
roroghost17 force-pushed the 06-01-refactor_makes_scope-level_check_methods_extensible branch 2 times, most recently from 096e68f to e63c607 Compare June 1, 2026 18:06
@roroghost17
roroghost17 force-pushed the 06-01-feat_add_v2_api_for_governance_and_update_ui_to_use_the_multi_budget_lines branch from 10da233 to d7f6f08 Compare June 1, 2026 18:06
@roroghost17
roroghost17 marked this pull request as ready for review June 1, 2026 18:13
@coderabbitai
coderabbitai Bot requested a review from akshaydeo June 1, 2026 18:19
@greptile-apps

greptile-apps Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 4/5

Safe to merge after fixing the rate limit dropdown regression; the Go backend and test additions are solid.

The Go backend changes are well-structured and the orphaned-budget deletion is correctly handled. The one concrete defect is in the UI form: the rate limit duration dropdowns were silently narrowed from 9 options to 4, meaning any operator whose saved rate limit uses a sub-hour or 6-hour interval will see that value absent from the select options — re-saving the form would silently overwrite the stored duration with whichever option the control falls back to.

ui/app/workspace/providers/fragments/governanceFormFragment.tsx — rate limit NumberAndSelect options need to be restored to the full set (resetDurationOptions or equivalent).

Important Files Changed

Filename Overview
transports/bifrost-http/handlers/governance.go Adds versionedRoute routing table, getProviderGovernanceV2 and updateProviderGovernanceV2 handlers, and modelConfigToProviderGovernanceV2 helper; orphaned-budget deletion is correctly handled in the !hasGovernance && !isNew branch; reconcileModelConfigBudgets updates mc.Budgets in-place so the post-transaction reload condition is accurate.
transports/bifrost-http/handlers/governance_test.go Good coverage added for filter logic, field mapping, copy semantics, pointer-to-slice JSON semantics, and route-table contract; all new test scenarios look correct.
ui/app/workspace/providers/fragments/governanceFormFragment.tsx Switches budget field to MultiBudgetLines and consolidates useEffect via governanceToFormValues helper; rate limit dropdowns now inline only 4 options instead of the 9 in resetDurationOptions, dropping sub-hour and 6h granularities and risking silent overwrite of existing fine-grained limits on save.
ui/app/workspace/providers/views/providerGovernanceTable.tsx Correctly iterates all budgets and sorts newest-first; null created_at fallback uses Number.MAX_SAFE_INTEGER (sorts first) instead of 0 (sorts last) used by sibling components, causing minor ordering inconsistency.
ui/lib/store/apis/governanceApi.ts GET and PUT provider governance endpoints switched to /v2/governance/providers; change is minimal and correct.
ui/lib/types/governance.ts ProviderGovernance updated from single budget? to budgets?: Budget[], UpdateProviderGovernanceRequest switched from budget to budgets, calendar_aligned added; breaking change is documented in the PR.
ui/app/workspace/model-limits/views/modelLimitsTable.tsx Adds newest-first sort to model-limits budget list using epoch as null fallback; consistent with governanceFormFragment.

Comments Outside Diff (1)

  1. ui/app/workspace/providers/fragments/governanceFormFragment.tsx, line 1010-1048 (link)

    P1 Rate limit dropdown options regressed from 9 to 4

    The refactor replaces resetDurationOptions (9 values: 1m, 5m, 15m, 30m, 1h, 6h, 1d, 1w, 1M) with two inline 4-option arrays (1h, 1d, 1w, 1M) for both token and request rate limit selects. Any operator who previously configured a provider rate limit with 5m, 15m, 30m, or 6h will see their saved value set as selectValue but absent from the rendered <select> options — most select implementations will either display a blank or silently snap to the first option, meaning re-saving the form would overwrite the fine-grained limit with a coarser duration. Using resetDurationOptions (already imported in MultiBudgetLines) or the shared constant restores the full option set.

Reviews (2): Last reviewed commit: "feat: Add v2 API for governance and upda..." | Re-trigger Greptile

Comment thread transports/bifrost-http/handlers/governance.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
transports/bifrost-http/handlers/governance_test.go (1)

1860-1860: ⚡ Quick win

Use existing schemas.Ptr instead of introducing a new strPtr helper.

This file already uses schemas.Ptr extensively (lines 135, 136, 137, 143, 182, etc.). Introducing a new strPtr helper creates inconsistency. Replace strPtr("openai") calls with schemas.Ptr("openai") throughout the new tests and remove this helper.

♻️ Proposed fix
-func strPtr(s string) *string { return &s }

Then update line 1556 and similar usages:

-			mc:     &configstoreTables.TableModelConfig{Scope: "virtual_key", ModelName: "*", Provider: strPtr("openai")},
+			mc:     &configstoreTables.TableModelConfig{Scope: "virtual_key", ModelName: "*", Provider: schemas.Ptr("openai")},
🤖 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 `@transports/bifrost-http/handlers/governance_test.go` at line 1860, Replace
the local helper function strPtr and all its usages with the existing
schemas.Ptr helper to keep consistency: remove the strPtr declaration (func
strPtr(s string) *string { return &s }) and change each call like
strPtr("openai") in the new tests to schemas.Ptr("openai"); ensure
imports/reference to schemas remain intact and run tests to confirm no
regressions.
🤖 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.

Inline comments:
In `@transports/bifrost-http/handlers/governance.go`:
- Around line 3671-3687: When deleting a model config in the branch handling
"!hasGovernance && !isNew" you must delete its owned budgets before removing the
config to avoid orphaned rows; before the
tx.Delete(&configstoreTables.TableModelConfig{}, "id = ?", mc.ID) call, run the
same budget-delete logic used in deleteVKModelConfig (delete budgets where
model_config_id = mc.ID within the same tx) or call the existing configStore
method that removes model config budgets, then proceed to delete the model
config; this ensures reconcileModelConfigBudgets (which is skipped when deleted
== true) cannot leave budget rows dangling.

In `@ui/app/workspace/providers/fragments/governanceFormFragment.tsx`:
- Around line 26-30: The budgetLineSchema lacks user-facing Zod error messages;
update the fields in budgetLineSchema (the z.object with keys id, max_limit,
reset_duration) to include meaningful .error messages and validators: e.g., for
max_limit (currently z.number().nonnegative().optional()) add a .nonnegative({
message: "Maximum limit must be 0 or greater" }) and .optional() handling error
text, for reset_duration (currently z.string()) require nonempty with
z.string().nonempty({ message: "Reset duration is required" }) or a
z.enum/refinement with a clear message, and for id keep z.string().optional()
but supply a validation/message if you expect a UUID (z.string().uuid({ message:
"Invalid id format" }).optional()) or at least .optional().refine if needed;
ensure all validators on budgetLineSchema provide explicit message strings to
satisfy the UI Zod schema guideline.

In `@ui/app/workspace/providers/views/providerGovernanceTable.tsx`:
- Around line 188-190: The sort currently uses new Date(b.created_at ??
0).getTime() which can misplace items with missing created_at; explicitly map
created_at to a numeric timestamp (e.g., const ts = created_at ?
Date.parse(created_at) : Number.NEGATIVE_INFINITY) and use those timestamps in
the comparator so missing created_at are treated as oldest and appear last in
the newest-first sort; update the budgets creation that references
providerGovernance?.budgets to use this explicit timestamp parsing in the (a,b)
=> comparator.

---

Nitpick comments:
In `@transports/bifrost-http/handlers/governance_test.go`:
- Line 1860: Replace the local helper function strPtr and all its usages with
the existing schemas.Ptr helper to keep consistency: remove the strPtr
declaration (func strPtr(s string) *string { return &s }) and change each call
like strPtr("openai") in the new tests to schemas.Ptr("openai"); ensure
imports/reference to schemas remain intact and run tests to confirm no
regressions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0942185f-4fac-4d21-86c3-a5c8fea0c790

📥 Commits

Reviewing files that changed from the base of the PR and between e63c607 and d7f6f08.

📒 Files selected for processing (7)
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/governance_test.go
  • ui/app/workspace/model-limits/views/modelLimitsTable.tsx
  • ui/app/workspace/providers/fragments/governanceFormFragment.tsx
  • ui/app/workspace/providers/views/providerGovernanceTable.tsx
  • ui/lib/store/apis/governanceApi.ts
  • ui/lib/types/governance.ts

Comment thread transports/bifrost-http/handlers/governance.go
Comment thread ui/app/workspace/providers/fragments/governanceFormFragment.tsx
Comment thread ui/app/workspace/providers/views/providerGovernanceTable.tsx Outdated
@roroghost17
roroghost17 force-pushed the 06-01-feat_add_v2_api_for_governance_and_update_ui_to_use_the_multi_budget_lines branch from d7f6f08 to 5ced207 Compare June 1, 2026 18:50
@roroghost17 roroghost17 closed this Jun 2, 2026
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.

2 participants