Skip to content

feat: adding filters in budgets & limits UI for scope and providers - #3962

Merged
akshaydeo merged 1 commit into
devfrom
06-01-feat_adding_filters_in_budgets_limits_ui_for_scope_and_providers
Jun 2, 2026
Merged

feat: adding filters in budgets & limits UI for scope and providers#3962
akshaydeo merged 1 commit into
devfrom
06-01-feat_adding_filters_in_budgets_limits_ui_for_scope_and_providers

Conversation

@roroghost17

@roroghost17 roroghost17 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds scope and provider filter support to the Model Limits (Model Configs) list, allowing users to narrow results by scope (e.g. global, virtual_key) and provider (e.g. openai) independently of the existing search filter.

Changes

  • Added Scope and Provider fields to ModelConfigsQueryParams and wired them into the RDB query as exact-match WHERE clauses.
  • Extended the HTTP handler to read scope and provider query parameters and apply them on both the in-memory (non-paginated) path and the paginated database path.
  • Added scope and provider to GetModelConfigsParams and passed them through the RTK Query API call, including query string serialization.
  • Updated the optimistic cache update on model config creation to skip inserting a newly created config into cached query results when its scope or provider does not match the active filter arguments.
  • Added Scope and Provider <Select> dropdowns to the model limits toolbar. The scope options are sourced from the existing getModelLimitScopes registry; provider options are sourced from the providers API with icons and labels.
  • Added a "Clear filters" button that appears when any filter (search, scope, or provider) is active and resets all three at once.
  • Pagination offset resets to 0 when scope or provider filters change, consistent with existing search behavior.

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 ./framework/configstore/... ./transports/bifrost-http/...

# UI
cd ui
pnpm i
pnpm build
  1. Navigate to the Model Limits page.
  2. Use the Scope dropdown to select a scope (e.g. global) — only model configs with that scope should appear.
  3. Use the Provider dropdown to select a provider (e.g. openai) — only model configs for that provider should appear.
  4. Combine scope and provider filters together and verify results are correctly intersected.
  5. Verify the Clear filters button resets all three filters and restores the full list.
  6. Verify pagination resets to page 1 when either filter changes.
  7. Create a new model config while a scope/provider filter is active and confirm it only appears in the cached list if it matches the active filters.

Screenshots/Recordings

Add before/after screenshots of the model limits toolbar showing the new Scope and Provider dropdowns.

Breaking changes

  • Yes
  • No

Related issues

Link related issues and discussions.

Security considerations

The new scope and provider query parameters are passed as parameterized query arguments (WHERE scope = ?, WHERE provider = ?), preventing SQL injection.

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

Summary by CodeRabbit

  • New Features

    • Added scope and provider filtering to model configuration listings with dropdowns in the UI.
    • “Clear filters” now resets search, scope, and provider.
  • Improvements

    • Filters apply both in-memory and server-side, and pagination resets when search/scope/provider change.
    • Creation of a new model config updates visible lists immediately when relevant filters match.

@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

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c6f53438-3cab-4f0a-9478-fe43ad3bf13f

📥 Commits

Reviewing files that changed from the base of the PR and between f8fabcd and ffad4d1.

📒 Files selected for processing (7)
  • framework/configstore/rdb.go
  • framework/configstore/store.go
  • transports/bifrost-http/handlers/governance.go
  • ui/app/workspace/model-limits/views/modelLimitsTable.tsx
  • ui/app/workspace/model-limits/views/modelLimitsView.tsx
  • ui/lib/store/apis/governanceApi.ts
  • ui/lib/types/governance.ts

📝 Walkthrough

Walkthrough

This PR adds optional scope and provider filtering for model configuration listings across DB query params, HTTP handler (in-memory and DB), frontend types/API, optimistic cache updates, view state, and table filter UI.

Changes

Model Config Scope and Provider Filtering

Layer / File(s) Summary
Backend data contract and paginated filtering
framework/configstore/store.go, framework/configstore/rdb.go
ModelConfigsQueryParams gains Scope and Provider; GetModelConfigsPaginated conditionally applies scope = ? and provider = ? WHERE clauses for count and result queries.
HTTP handler extraction and in-memory filtering
transports/bifrost-http/handlers/governance.go
getModelConfigs reads scope and provider query args, filters in-memory model configs by mc.Scope/mc.Provider when provided, and forwards them into paginated DB query params when not using in-memory.
Frontend types and API wiring
ui/lib/types/governance.ts, ui/lib/store/apis/governanceApi.ts
GetModelConfigsParams adds scope and provider; getModelConfigs request includes these params. createModelConfig optimistic cache update matches cached queries by optional scope/provider and search before inserting new configs.
View state, provider fetch, and query params
ui/app/workspace/model-limits/views/modelLimitsView.tsx
Adds scope and provider state, includes them in the model-configs query (omitting when empty), resets offset when filters change, and fetches providers to supply the table.
Table props and filter controls
ui/app/workspace/model-limits/views/modelLimitsTable.tsx
ModelLimitsTable accepts providers, scope, onScopeChange, provider, and onProviderChange; toolbar now has Scope and Provider Selects and a Clear filters button; hasActiveFilters includes scope/provider.

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • Pratham-Mishra04
  • danpiths

🐰
I hopped through filters, quick and spry,
Scope and provider now clarified.
From UI selects to DB's where,
Model configs sorted with care.
A little hop — the stack's supplied!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 clearly and specifically describes the main feature addition: filtering by scope and provider in the model limits UI.
Description check ✅ Passed The description comprehensively covers all required sections: summary, detailed changes across all layers (backend, API, UI), 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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 06-01-feat_adding_filters_in_budgets_limits_ui_for_scope_and_providers

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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

@roroghost17
roroghost17 force-pushed the 06-01-feat_adding_filters_in_budgets_limits_ui_for_scope_and_providers branch from 39e9551 to 2203592 Compare June 1, 2026 17:56
@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-feat_adding_filters_in_budgets_limits_ui_for_scope_and_providers branch from 2203592 to e580631 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 danpiths June 1, 2026 18:18
@greptile-apps

greptile-apps Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

The change is additive and well-scoped: new optional filter parameters flow through parameterized queries on the backend and guarded state updates on the frontend without touching any existing write paths.

Both filter paths (DB and in-memory) are implemented symmetrically, the SQL uses parameterized arguments, the UI state resets pagination correctly on filter change, and no existing data-testid attributes are removed. No concurrency or correctness issues were identified.

No files require special attention.

Important Files Changed

Filename Overview
framework/configstore/store.go Adds Scope and Provider fields to ModelConfigsQueryParams; straightforward struct extension with clear doc comments.
framework/configstore/rdb.go Adds parameterized WHERE clauses for scope and provider to GetModelConfigsPaginated; correctly placed before the COUNT query so filtered totals are accurate.
transports/bifrost-http/handlers/governance.go Correctly plumbs scope/provider filters through both the fromMemory and paginated DB paths; condition expansion to enter the paginated path is sound and won't regress unfiltered non-paginated requests.
ui/lib/types/governance.ts Adds optional scope and provider to GetModelConfigsParams; matches the backend query params.
ui/lib/store/apis/governanceApi.ts Serializes scope/provider into the query string and extends the optimistic cache-update guard to skip inserting a newly created config when scope or provider don't match the active filter arguments.
ui/app/workspace/model-limits/views/modelLimitsView.tsx Lifts provider data fetch out of the table component, owns scope/provider state, and resets offset on filter change; clean separation of concerns.
ui/app/workspace/model-limits/views/modelLimitsTable.tsx Adds Scope and Provider Select dropdowns plus a Clear filters button; preserves existing data-testid attributes and adds new ones for the new controls.

Reviews (7): Last reviewed commit: "feat: adding filters in budgets & limits..." | Re-trigger Greptile

Comment thread ui/app/workspace/model-limits/views/modelLimitsTable.tsx
Comment thread ui/app/workspace/model-limits/views/modelLimitsTable.tsx
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 1, 2026
@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 force-pushed the 06-01-feat_adding_filters_in_budgets_limits_ui_for_scope_and_providers branch 2 times, most recently from 0f0584b to 0c39183 Compare June 2, 2026 07:22
@roroghost17
roroghost17 changed the base branch from 06-01-feat_add_v2_api_for_governance_and_update_ui_to_use_the_multi_budget_lines to graphite-base/3962 June 2, 2026 09:33
@roroghost17
roroghost17 force-pushed the graphite-base/3962 branch from 5ced207 to e63c607 Compare June 2, 2026 09:33
@roroghost17
roroghost17 force-pushed the 06-01-feat_adding_filters_in_budgets_limits_ui_for_scope_and_providers branch from 0c39183 to 2d0ef8e Compare June 2, 2026 09:33

akshaydeo commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Merge activity

@roroghost17
roroghost17 force-pushed the 06-01-feat_adding_filters_in_budgets_limits_ui_for_scope_and_providers branch from 2d0ef8e to f8fabcd Compare June 2, 2026 13:37
@roroghost17
roroghost17 force-pushed the graphite-base/3962 branch from e63c607 to 4816c78 Compare June 2, 2026 13:37

@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: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ui/lib/store/apis/governanceApi.ts (1)

562-576: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Re-key filtered model-config caches after updates.

This updates every fulfilled getModelConfigs cache by ID only. With provider/scope filters now in play, an updated row can stay in a cache it no longer matches, and it will not be inserted into the cache it now belongs to until polling corrects it. Please apply the same query-membership check here that createModelConfig now uses, and remove/reinsert accordingly.

🤖 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 `@ui/lib/store/apis/governanceApi.ts` around lines 562 - 576, The update
currently replaces a model_config by id in every fulfilled getModelConfigs cache
but doesn’t re-evaluate query membership for filters (e.g., provider/scope),
causing stale/inaccurate caches; in onQueryStarted, inside the loop over
api.queries and the governanceApi.util.updateQueryData("getModelConfigs", ...)
call, replicate the membership check logic used by createModelConfig: inspect
entry.originalArgs (filters like provider and scope) and for each cache draft,
if the updated model_config no longer matches the query filters remove it from
that draft.model_configs, and if it now matches a query it isn’t present in,
insert it (or move it) so caches are re-keyed correctly by filter. Ensure you
reference id and data.model_config when deciding remove vs insert.
🤖 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 `@framework/configstore/rdb.go`:
- Around line 4348-4357: GetModelConfig in RDBConfigStore currently treats a nil
scopeID as "scope_id IS NULL" for all scopes; change it to validate early: if
scope != "global" and scopeID == nil return a validation error (bad request)
instead of building a "scope_id IS NULL" query; allow nil scopeID only when
scope == "global". Update callers/returns accordingly so identity semantics
match transports/config.schema.json.
- Around line 3003-3025: The current cleanup only snapshots
ModelConfig.BudgetID/RateLimitID and then deletes scoped TableModelConfig rows,
which can miss budgets referenced via associated Budgets and can race with
concurrent inserts; update the logic that queries scopedModelConfigs (the
txDB.WithContext(...).Find into scopedModelConfigs) to also preload associated
Budgets (e.g., use Preload("Budgets") or GORM auto-preload) and collect IDs from
each mc.Budgets slice in addition to mc.BudgetID/mc.RateLimitID, and perform
both the snapshot and the subsequent Delete within the same txDB
transaction/WithContext so no new scoped rows are missed between Find and
Delete; ensure you then delete tables.TableBudget and rate-limit rows using the
full collected budgetIDs/rateLimitIDs lists.

In `@transports/bifrost-http/handlers/governance.go`:
- Around line 2745-2757: The in-memory pagination parsing in governance handler
currently ignores malformed or negative limit/offset (variables limitStr,
offsetStr, offset, limit) whereas the DB-backed path returns 400; update the
parsing logic in the governance.go handler to validate limitStr and offsetStr
and return an HTTP 400 when strconv.Atoi fails or when offset < 0 or limit <= 0
(or other constraints used by the DB path), instead of silently falling back to
defaults—use the same error response path/mechanism as the DB-backed branch so
both branches behave identically for invalid pagination inputs.

In `@ui/app/workspace/model-limits/views/modelLimitsTable.tsx`:
- Around line 408-409: The list rendering uses budgets.map((b, idx) => ...) with
key={b.id ?? idx}, which falls back to array index; update the code so each
budget row uses a stable unique key instead of idx: ensure budget objects have a
persistent id before rendering (normalize data in the parent or in the component
by assigning and persisting a generated id field), and then replace key={b.id ??
idx} with key={b.id} (or key={b._stableId} if you add a normalized field).
Locate the budgets.map usage in ModelLimitsTable (modelLimitsTable.tsx) and
change the data normalization or key reference so React never relies on the
array index.
- Around line 384-396: The Badge used for scope-target navigation is not
keyboard-focusable; replace the clickable Badge element (the JSX that uses Badge
with data-testid `model-limit-scope-target-${config.scope_id}` and the onClick
that calls getModelLimitScope(...).buildDeepLink(...) then navigate) with a
native interactive element (preferably a <Link> or <button> that is
keyboard-focusable) or augment it to be focusable and activatable by keyboard
(add tabIndex, onKeyDown handling for Enter/Space) and proper ARIA (aria-label)
so keyboard and screen-reader users can activate the deep link; keep the
existing visual classes and the ArrowUpRight icon and ensure you still call
getModelLimitScope(config.scope ?? "global")?.buildDeepLink?.(config.scope_id)
and navigate(target) when activated.

In `@ui/app/workspace/model-limits/views/modelLimitsView.tsx`:
- Line 26: The providers query is running even when governance is not present;
update the useGetProvidersQuery call to skip executing when hasGovernanceAccess
is false (e.g., pass a skip/skipToken option or guard so the hook only runs when
hasGovernanceAccess is true) so no request is made for unauthorized users;
locate the useGetProvidersQuery invocation in modelLimitsView.tsx and gate it by
the hasGovernanceAccess flag.

---

Outside diff comments:
In `@ui/lib/store/apis/governanceApi.ts`:
- Around line 562-576: The update currently replaces a model_config by id in
every fulfilled getModelConfigs cache but doesn’t re-evaluate query membership
for filters (e.g., provider/scope), causing stale/inaccurate caches; in
onQueryStarted, inside the loop over api.queries and the
governanceApi.util.updateQueryData("getModelConfigs", ...) call, replicate the
membership check logic used by createModelConfig: inspect entry.originalArgs
(filters like provider and scope) and for each cache draft, if the updated
model_config no longer matches the query filters remove it from that
draft.model_configs, and if it now matches a query it isn’t present in, insert
it (or move it) so caches are re-keyed correctly by filter. Ensure you reference
id and data.model_config when deciding remove vs insert.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1d7c559b-7708-4a32-9d9e-368577c1ccb8

📥 Commits

Reviewing files that changed from the base of the PR and between e580631 and f8fabcd.

📒 Files selected for processing (7)
  • framework/configstore/rdb.go
  • framework/configstore/store.go
  • transports/bifrost-http/handlers/governance.go
  • ui/app/workspace/model-limits/views/modelLimitsTable.tsx
  • ui/app/workspace/model-limits/views/modelLimitsView.tsx
  • ui/lib/store/apis/governanceApi.ts
  • ui/lib/types/governance.ts

@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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ui/lib/store/apis/governanceApi.ts (1)

562-576: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Re-key filtered model-config caches after updates.

This updates every fulfilled getModelConfigs cache by ID only. With provider/scope filters now in play, an updated row can stay in a cache it no longer matches, and it will not be inserted into the cache it now belongs to until polling corrects it. Please apply the same query-membership check here that createModelConfig now uses, and remove/reinsert accordingly.

🤖 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 `@ui/lib/store/apis/governanceApi.ts` around lines 562 - 576, The update
currently replaces a model_config by id in every fulfilled getModelConfigs cache
but doesn’t re-evaluate query membership for filters (e.g., provider/scope),
causing stale/inaccurate caches; in onQueryStarted, inside the loop over
api.queries and the governanceApi.util.updateQueryData("getModelConfigs", ...)
call, replicate the membership check logic used by createModelConfig: inspect
entry.originalArgs (filters like provider and scope) and for each cache draft,
if the updated model_config no longer matches the query filters remove it from
that draft.model_configs, and if it now matches a query it isn’t present in,
insert it (or move it) so caches are re-keyed correctly by filter. Ensure you
reference id and data.model_config when deciding remove vs insert.
🤖 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 `@framework/configstore/rdb.go`:
- Around line 4348-4357: GetModelConfig in RDBConfigStore currently treats a nil
scopeID as "scope_id IS NULL" for all scopes; change it to validate early: if
scope != "global" and scopeID == nil return a validation error (bad request)
instead of building a "scope_id IS NULL" query; allow nil scopeID only when
scope == "global". Update callers/returns accordingly so identity semantics
match transports/config.schema.json.
- Around line 3003-3025: The current cleanup only snapshots
ModelConfig.BudgetID/RateLimitID and then deletes scoped TableModelConfig rows,
which can miss budgets referenced via associated Budgets and can race with
concurrent inserts; update the logic that queries scopedModelConfigs (the
txDB.WithContext(...).Find into scopedModelConfigs) to also preload associated
Budgets (e.g., use Preload("Budgets") or GORM auto-preload) and collect IDs from
each mc.Budgets slice in addition to mc.BudgetID/mc.RateLimitID, and perform
both the snapshot and the subsequent Delete within the same txDB
transaction/WithContext so no new scoped rows are missed between Find and
Delete; ensure you then delete tables.TableBudget and rate-limit rows using the
full collected budgetIDs/rateLimitIDs lists.

In `@transports/bifrost-http/handlers/governance.go`:
- Around line 2745-2757: The in-memory pagination parsing in governance handler
currently ignores malformed or negative limit/offset (variables limitStr,
offsetStr, offset, limit) whereas the DB-backed path returns 400; update the
parsing logic in the governance.go handler to validate limitStr and offsetStr
and return an HTTP 400 when strconv.Atoi fails or when offset < 0 or limit <= 0
(or other constraints used by the DB path), instead of silently falling back to
defaults—use the same error response path/mechanism as the DB-backed branch so
both branches behave identically for invalid pagination inputs.

In `@ui/app/workspace/model-limits/views/modelLimitsTable.tsx`:
- Around line 408-409: The list rendering uses budgets.map((b, idx) => ...) with
key={b.id ?? idx}, which falls back to array index; update the code so each
budget row uses a stable unique key instead of idx: ensure budget objects have a
persistent id before rendering (normalize data in the parent or in the component
by assigning and persisting a generated id field), and then replace key={b.id ??
idx} with key={b.id} (or key={b._stableId} if you add a normalized field).
Locate the budgets.map usage in ModelLimitsTable (modelLimitsTable.tsx) and
change the data normalization or key reference so React never relies on the
array index.
- Around line 384-396: The Badge used for scope-target navigation is not
keyboard-focusable; replace the clickable Badge element (the JSX that uses Badge
with data-testid `model-limit-scope-target-${config.scope_id}` and the onClick
that calls getModelLimitScope(...).buildDeepLink(...) then navigate) with a
native interactive element (preferably a <Link> or <button> that is
keyboard-focusable) or augment it to be focusable and activatable by keyboard
(add tabIndex, onKeyDown handling for Enter/Space) and proper ARIA (aria-label)
so keyboard and screen-reader users can activate the deep link; keep the
existing visual classes and the ArrowUpRight icon and ensure you still call
getModelLimitScope(config.scope ?? "global")?.buildDeepLink?.(config.scope_id)
and navigate(target) when activated.

In `@ui/app/workspace/model-limits/views/modelLimitsView.tsx`:
- Line 26: The providers query is running even when governance is not present;
update the useGetProvidersQuery call to skip executing when hasGovernanceAccess
is false (e.g., pass a skip/skipToken option or guard so the hook only runs when
hasGovernanceAccess is true) so no request is made for unauthorized users;
locate the useGetProvidersQuery invocation in modelLimitsView.tsx and gate it by
the hasGovernanceAccess flag.

---

Outside diff comments:
In `@ui/lib/store/apis/governanceApi.ts`:
- Around line 562-576: The update currently replaces a model_config by id in
every fulfilled getModelConfigs cache but doesn’t re-evaluate query membership
for filters (e.g., provider/scope), causing stale/inaccurate caches; in
onQueryStarted, inside the loop over api.queries and the
governanceApi.util.updateQueryData("getModelConfigs", ...) call, replicate the
membership check logic used by createModelConfig: inspect entry.originalArgs
(filters like provider and scope) and for each cache draft, if the updated
model_config no longer matches the query filters remove it from that
draft.model_configs, and if it now matches a query it isn’t present in, insert
it (or move it) so caches are re-keyed correctly by filter. Ensure you reference
id and data.model_config when deciding remove vs insert.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1d7c559b-7708-4a32-9d9e-368577c1ccb8

📥 Commits

Reviewing files that changed from the base of the PR and between e580631 and f8fabcd.

📒 Files selected for processing (7)
  • framework/configstore/rdb.go
  • framework/configstore/store.go
  • transports/bifrost-http/handlers/governance.go
  • ui/app/workspace/model-limits/views/modelLimitsTable.tsx
  • ui/app/workspace/model-limits/views/modelLimitsView.tsx
  • ui/lib/store/apis/governanceApi.ts
  • ui/lib/types/governance.ts
🛑 Comments failed to post (7)
framework/configstore/rdb.go (2)

3003-3025: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Snapshot model-config IDs and preload Budgets before deleting scoped rows.

This cleanup only records the legacy BudgetID, but scoped model configs can also own rows through Budgets. It also deletes by scope/scope_id after the snapshot, so a config inserted between Find and Delete gets removed without its child IDs being collected. Both cases can orphan owned budgets/rate limits.

Suggested fix
 	var scopedModelConfigs []tables.TableModelConfig
 	if err := txDB.WithContext(ctx).
+		Preload("Budgets").
 		Where("scope = ? AND scope_id = ?", tables.ModelConfigScopeVirtualKey, id).
 		Find(&scopedModelConfigs).Error; err != nil {
 		return err
 	}
-	budgetIDs := make([]string, 0, len(scopedModelConfigs))
+	mcIDs := make([]string, 0, len(scopedModelConfigs))
+	budgetIDs := make([]string, 0, len(scopedModelConfigs))
 	rateLimitIDs := make([]string, 0, len(scopedModelConfigs))
-	for _, mc := range scopedModelConfigs {
+	for i := range scopedModelConfigs {
+		mcIDs = append(mcIDs, scopedModelConfigs[i].ID)
+		for j := range scopedModelConfigs[i].Budgets {
+			budgetIDs = append(budgetIDs, scopedModelConfigs[i].Budgets[j].ID)
+		}
-		if mc.BudgetID != nil {
-			budgetIDs = append(budgetIDs, *mc.BudgetID)
+		if scopedModelConfigs[i].BudgetID != nil {
+			budgetIDs = append(budgetIDs, *scopedModelConfigs[i].BudgetID)
 		}
-		if mc.RateLimitID != nil {
-			rateLimitIDs = append(rateLimitIDs, *mc.RateLimitID)
+		if scopedModelConfigs[i].RateLimitID != nil {
+			rateLimitIDs = append(rateLimitIDs, *scopedModelConfigs[i].RateLimitID)
 		}
 	}
-	if err := txDB.WithContext(ctx).
-		Where("scope = ? AND scope_id = ?", tables.ModelConfigScopeVirtualKey, id).
-		Delete(&tables.TableModelConfig{}).Error; err != nil {
+	if len(mcIDs) > 0 {
+		if err := txDB.WithContext(ctx).
+			Where("id IN ?", mcIDs).
+			Delete(&tables.TableModelConfig{}).Error; err != nil {
+			return err
+		}
+	}
-		return err
-	}
🤖 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 `@framework/configstore/rdb.go` around lines 3003 - 3025, The current cleanup
only snapshots ModelConfig.BudgetID/RateLimitID and then deletes scoped
TableModelConfig rows, which can miss budgets referenced via associated Budgets
and can race with concurrent inserts; update the logic that queries
scopedModelConfigs (the txDB.WithContext(...).Find into scopedModelConfigs) to
also preload associated Budgets (e.g., use Preload("Budgets") or GORM
auto-preload) and collect IDs from each mc.Budgets slice in addition to
mc.BudgetID/mc.RateLimitID, and perform both the snapshot and the subsequent
Delete within the same txDB transaction/WithContext so no new scoped rows are
missed between Find and Delete; ensure you then delete tables.TableBudget and
rate-limit rows using the full collected budgetIDs/rateLimitIDs lists.

4348-4357: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reject nil scopeID for non-global lookups.

scope_id is only optional for global model configs. Translating scope != "global" plus scopeID == nil into scope_id IS NULL makes an invalid identity look like a valid query shape instead of failing fast.

As per coding guidelines, transports/config.schema.json is the source of truth here: scope defaults to "global" and scope_id is required when scope != "global".

🤖 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 `@framework/configstore/rdb.go` around lines 4348 - 4357, GetModelConfig in
RDBConfigStore currently treats a nil scopeID as "scope_id IS NULL" for all
scopes; change it to validate early: if scope != "global" and scopeID == nil
return a validation error (bad request) instead of building a "scope_id IS NULL"
query; allow nil scopeID only when scope == "global". Update callers/returns
accordingly so identity semantics match transports/config.schema.json.
transports/bifrost-http/handlers/governance.go (2)

2745-2757: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Return 400 for invalid from_memory pagination params.

This branch silently ignores malformed or negative limit/offset, while the DB-backed path rejects the same inputs with 400. The endpoint will behave differently depending on from_memory, which makes client bugs harder to detect.

🤖 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.go` around lines 2745 - 2757, The
in-memory pagination parsing in governance handler currently ignores malformed
or negative limit/offset (variables limitStr, offsetStr, offset, limit) whereas
the DB-backed path returns 400; update the parsing logic in the governance.go
handler to validate limitStr and offsetStr and return an HTTP 400 when
strconv.Atoi fails or when offset < 0 or limit <= 0 (or other constraints used
by the DB path), instead of silently falling back to defaults—use the same error
response path/mechanism as the DB-backed branch so both branches behave
identically for invalid pagination inputs.

3090-3092: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Map invalid model-config updates to 400 instead of 500.

reconcileModelConfigBudgets() returns *badRequestError for duplicate or invalid budgets, but updateModelConfig converts every transaction failure into a 500. Bad input on this route will now be reported as a server error.

💡 Suggested fix
 	}); err != nil {
+		var badReqErr *badRequestError
+		if errors.As(err, &badReqErr) {
+			SendError(ctx, 400, err.Error())
+			return
+		}
 		logger.Error("failed to update model config: %v", err)
 		SendError(ctx, 500, fmt.Sprintf("Failed to update model config: %v", err))
 		return
 	}

Also applies to: 3158-3161

ui/app/workspace/model-limits/views/modelLimitsTable.tsx (2)

384-396: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use a real button/link for scope-target navigation.

This new deep-link affordance is mouse-only right now. A clickable Badge is not keyboard focusable or activatable, so keyboard users cannot open the scope target from the table.

♿ Proposed fix
-																<Badge
-																	variant="secondary"
-																	className="flex max-w-[160px] cursor-pointer items-center gap-1 hover:opacity-80"
-																	data-testid={`model-limit-scope-target-${config.scope_id}`}
-																	onClick={() => {
-																		if (!config.scope_id) return;
-																		const target = getModelLimitScope(config.scope ?? "global")?.buildDeepLink?.(config.scope_id);
-																		if (target) navigate(target as never);
-																	}}
-																>
+																<Button
+																	type="button"
+																	variant="secondary"
+																	className="h-auto max-w-[160px] justify-start gap-1 px-2 py-0.5 hover:opacity-80"
+																	data-testid={`model-limit-scope-target-${config.scope_id}`}
+																	onClick={() => {
+																		if (!config.scope_id) return;
+																		const target = getModelLimitScope(config.scope ?? "global")?.buildDeepLink?.(config.scope_id);
+																		if (target) navigate(target as never);
+																	}}
+																>
 																	<span className="truncate">{config.scope_name}</span>
 																	<ArrowUpRight className="h-3 w-3 shrink-0" />
-																</Badge>
+																</Button>
🤖 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 `@ui/app/workspace/model-limits/views/modelLimitsTable.tsx` around lines 384 -
396, The Badge used for scope-target navigation is not keyboard-focusable;
replace the clickable Badge element (the JSX that uses Badge with data-testid
`model-limit-scope-target-${config.scope_id}` and the onClick that calls
getModelLimitScope(...).buildDeepLink(...) then navigate) with a native
interactive element (preferably a <Link> or <button> that is keyboard-focusable)
or augment it to be focusable and activatable by keyboard (add tabIndex,
onKeyDown handling for Enter/Space) and proper ARIA (aria-label) so keyboard and
screen-reader users can activate the deep link; keep the existing visual classes
and the ArrowUpRight icon and ensure you still call
getModelLimitScope(config.scope ?? "global")?.buildDeepLink?.(config.scope_id)
and navigate(target) when activated.

408-409: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid the index fallback for budget row keys.

Falling back to idx here can make React reuse the wrong subtree when budgets are inserted, removed, or reordered. Please use a stable persisted key only, or normalize the data before render so every budget row has one.

As per coding guidelines, "Always use stable, unique keys in lists; never use array index as key unless unavoidable".

🤖 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 `@ui/app/workspace/model-limits/views/modelLimitsTable.tsx` around lines 408 -
409, The list rendering uses budgets.map((b, idx) => ...) with key={b.id ??
idx}, which falls back to array index; update the code so each budget row uses a
stable unique key instead of idx: ensure budget objects have a persistent id
before rendering (normalize data in the parent or in the component by assigning
and persisting a generated id field), and then replace key={b.id ?? idx} with
key={b.id} (or key={b._stableId} if you add a normalized field). Locate the
budgets.map usage in ModelLimitsTable (modelLimitsTable.tsx) and change the data
normalization or key reference so React never relies on the array index.
ui/app/workspace/model-limits/views/modelLimitsView.tsx (1)

26-26: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Skip the providers query when governance access is missing.

useGetProvidersQuery() still runs even when hasGovernanceAccess causes the main model-config query to skip. That sends an unnecessary request for unauthorized users and can surface avoidable 401/403 noise.

🤖 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 `@ui/app/workspace/model-limits/views/modelLimitsView.tsx` at line 26, The
providers query is running even when governance is not present; update the
useGetProvidersQuery call to skip executing when hasGovernanceAccess is false
(e.g., pass a skip/skipToken option or guard so the hook only runs when
hasGovernanceAccess is true) so no request is made for unauthorized users;
locate the useGetProvidersQuery invocation in modelLimitsView.tsx and gate it by
the hasGovernanceAccess flag.

@akshaydeo
akshaydeo changed the base branch from graphite-base/3962 to dev June 2, 2026 14:05
@akshaydeo
akshaydeo dismissed coderabbitai[bot]’s stale review June 2, 2026 14:05

The base branch was changed.

@akshaydeo
akshaydeo force-pushed the 06-01-feat_adding_filters_in_budgets_limits_ui_for_scope_and_providers branch from f8fabcd to ffad4d1 Compare June 2, 2026 14:06
@akshaydeo
akshaydeo merged commit a0d518e into dev Jun 2, 2026
12 of 14 checks passed
@akshaydeo
akshaydeo deleted the 06-01-feat_adding_filters_in_budgets_limits_ui_for_scope_and_providers branch June 2, 2026 14:08
akshaydeo pushed a commit that referenced this pull request Jun 2, 2026
…3962)

## Summary

Adds `scope` and `provider` filter support to the Model Limits (Model Configs) list, allowing users to narrow results by scope (e.g. `global`, `virtual_key`) and provider (e.g. `openai`) independently of the existing search filter.

## Changes

- Added `Scope` and `Provider` fields to `ModelConfigsQueryParams` and wired them into the RDB query as exact-match `WHERE` clauses.
- Extended the HTTP handler to read `scope` and `provider` query parameters and apply them on both the in-memory (non-paginated) path and the paginated database path.
- Added `scope` and `provider` to `GetModelConfigsParams` and passed them through the RTK Query API call, including query string serialization.
- Updated the optimistic cache update on model config creation to skip inserting a newly created config into cached query results when its `scope` or `provider` does not match the active filter arguments.
- Added Scope and Provider `<Select>` dropdowns to the model limits toolbar. The scope options are sourced from the existing `getModelLimitScopes` registry; provider options are sourced from the providers API with icons and labels.
- Added a "Clear filters" button that appears when any filter (search, scope, or provider) is active and resets all three at once.
- Pagination offset resets to 0 when scope or provider filters change, consistent with existing search behavior.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

```sh
# Core/Transports
go test ./framework/configstore/... ./transports/bifrost-http/...

# UI
cd ui
pnpm i
pnpm build
```

1. Navigate to the Model Limits page.
2. Use the **Scope** dropdown to select a scope (e.g. `global`) — only model configs with that scope should appear.
3. Use the **Provider** dropdown to select a provider (e.g. `openai`) — only model configs for that provider should appear.
4. Combine scope and provider filters together and verify results are correctly intersected.
5. Verify the **Clear filters** button resets all three filters and restores the full list.
6. Verify pagination resets to page 1 when either filter changes.
7. Create a new model config while a scope/provider filter is active and confirm it only appears in the cached list if it matches the active filters.

## Screenshots/Recordings

_Add before/after screenshots of the model limits toolbar showing the new Scope and Provider dropdowns._

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

Link related issues and discussions.

## Security considerations

The new `scope` and `provider` query parameters are passed as parameterized query arguments (`WHERE scope = ?`, `WHERE provider = ?`), preventing SQL injection.

## 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

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
  * Added scope and provider filtering capabilities to model configuration listings
  * UI now includes dropdown controls for filtering by scope and provider
  * Filters work alongside existing search functionality for comprehensive model discovery

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
akshaydeo pushed a commit that referenced this pull request Jun 4, 2026
…3962)

## Summary

Adds `scope` and `provider` filter support to the Model Limits (Model Configs) list, allowing users to narrow results by scope (e.g. `global`, `virtual_key`) and provider (e.g. `openai`) independently of the existing search filter.

## Changes

- Added `Scope` and `Provider` fields to `ModelConfigsQueryParams` and wired them into the RDB query as exact-match `WHERE` clauses.
- Extended the HTTP handler to read `scope` and `provider` query parameters and apply them on both the in-memory (non-paginated) path and the paginated database path.
- Added `scope` and `provider` to `GetModelConfigsParams` and passed them through the RTK Query API call, including query string serialization.
- Updated the optimistic cache update on model config creation to skip inserting a newly created config into cached query results when its `scope` or `provider` does not match the active filter arguments.
- Added Scope and Provider `<Select>` dropdowns to the model limits toolbar. The scope options are sourced from the existing `getModelLimitScopes` registry; provider options are sourced from the providers API with icons and labels.
- Added a "Clear filters" button that appears when any filter (search, scope, or provider) is active and resets all three at once.
- Pagination offset resets to 0 when scope or provider filters change, consistent with existing search behavior.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

```sh
# Core/Transports
go test ./framework/configstore/... ./transports/bifrost-http/...

# UI
cd ui
pnpm i
pnpm build
```

1. Navigate to the Model Limits page.
2. Use the **Scope** dropdown to select a scope (e.g. `global`) — only model configs with that scope should appear.
3. Use the **Provider** dropdown to select a provider (e.g. `openai`) — only model configs for that provider should appear.
4. Combine scope and provider filters together and verify results are correctly intersected.
5. Verify the **Clear filters** button resets all three filters and restores the full list.
6. Verify pagination resets to page 1 when either filter changes.
7. Create a new model config while a scope/provider filter is active and confirm it only appears in the cached list if it matches the active filters.

## Screenshots/Recordings

_Add before/after screenshots of the model limits toolbar showing the new Scope and Provider dropdowns._

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

Link related issues and discussions.

## Security considerations

The new `scope` and `provider` query parameters are passed as parameterized query arguments (`WHERE scope = ?`, `WHERE provider = ?`), preventing SQL injection.

## 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

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
  * Added scope and provider filtering capabilities to model configuration listings
  * UI now includes dropdown controls for filtering by scope and provider
  * Filters work alongside existing search functionality for comprehensive model discovery

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@akshaydeo akshaydeo mentioned this pull request Jun 7, 2026
akshaydeo pushed a commit that referenced this pull request Jun 7, 2026
…3962)

## Summary

Adds `scope` and `provider` filter support to the Model Limits (Model Configs) list, allowing users to narrow results by scope (e.g. `global`, `virtual_key`) and provider (e.g. `openai`) independently of the existing search filter.

## Changes

- Added `Scope` and `Provider` fields to `ModelConfigsQueryParams` and wired them into the RDB query as exact-match `WHERE` clauses.
- Extended the HTTP handler to read `scope` and `provider` query parameters and apply them on both the in-memory (non-paginated) path and the paginated database path.
- Added `scope` and `provider` to `GetModelConfigsParams` and passed them through the RTK Query API call, including query string serialization.
- Updated the optimistic cache update on model config creation to skip inserting a newly created config into cached query results when its `scope` or `provider` does not match the active filter arguments.
- Added Scope and Provider `<Select>` dropdowns to the model limits toolbar. The scope options are sourced from the existing `getModelLimitScopes` registry; provider options are sourced from the providers API with icons and labels.
- Added a "Clear filters" button that appears when any filter (search, scope, or provider) is active and resets all three at once.
- Pagination offset resets to 0 when scope or provider filters change, consistent with existing search behavior.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

```sh
# Core/Transports
go test ./framework/configstore/... ./transports/bifrost-http/...

# UI
cd ui
pnpm i
pnpm build
```

1. Navigate to the Model Limits page.
2. Use the **Scope** dropdown to select a scope (e.g. `global`) — only model configs with that scope should appear.
3. Use the **Provider** dropdown to select a provider (e.g. `openai`) — only model configs for that provider should appear.
4. Combine scope and provider filters together and verify results are correctly intersected.
5. Verify the **Clear filters** button resets all three filters and restores the full list.
6. Verify pagination resets to page 1 when either filter changes.
7. Create a new model config while a scope/provider filter is active and confirm it only appears in the cached list if it matches the active filters.

## Screenshots/Recordings

_Add before/after screenshots of the model limits toolbar showing the new Scope and Provider dropdowns._

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

Link related issues and discussions.

## Security considerations

The new `scope` and `provider` query parameters are passed as parameterized query arguments (`WHERE scope = ?`, `WHERE provider = ?`), preventing SQL injection.

## 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

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
  * Added scope and provider filtering capabilities to model configuration listings
  * UI now includes dropdown controls for filtering by scope and provider
  * Filters work alongside existing search functionality for comprehensive model discovery

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
akshaydeo added a commit that referenced this pull request Jun 7, 2026
## ✨ 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)
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.

3 participants