feat: adding filters in budgets & limits UI for scope and providers - #3962
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughThis 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. ChangesModel Config Scope and Provider Filtering
🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 |
39e9551 to
2203592
Compare
37612ab to
10da233
Compare
2203592 to
e580631
Compare
10da233 to
d7f6f08
Compare
Confidence Score: 5/5The 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
Reviews (7): Last reviewed commit: "feat: adding filters in budgets & limits..." | Re-trigger Greptile |
d7f6f08 to
5ced207
Compare
0f0584b to
0c39183
Compare
5ced207 to
e63c607
Compare
0c39183 to
2d0ef8e
Compare
Merge activity
|
2d0ef8e to
f8fabcd
Compare
e63c607 to
4816c78
Compare
There was a problem hiding this comment.
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 winRe-key filtered model-config caches after updates.
This updates every fulfilled
getModelConfigscache by ID only. Withprovider/scopefilters 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 thatcreateModelConfignow 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
📒 Files selected for processing (7)
framework/configstore/rdb.goframework/configstore/store.gotransports/bifrost-http/handlers/governance.goui/app/workspace/model-limits/views/modelLimitsTable.tsxui/app/workspace/model-limits/views/modelLimitsView.tsxui/lib/store/apis/governanceApi.tsui/lib/types/governance.ts
There was a problem hiding this comment.
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 winRe-key filtered model-config caches after updates.
This updates every fulfilled
getModelConfigscache by ID only. Withprovider/scopefilters 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 thatcreateModelConfignow 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
📒 Files selected for processing (7)
framework/configstore/rdb.goframework/configstore/store.gotransports/bifrost-http/handlers/governance.goui/app/workspace/model-limits/views/modelLimitsTable.tsxui/app/workspace/model-limits/views/modelLimitsView.tsxui/lib/store/apis/governanceApi.tsui/lib/types/governance.ts
🛑 Comments failed to post (7)
framework/configstore/rdb.go (2)
3003-3025:
⚠️ Potential issue | 🟠 Major | ⚡ Quick winSnapshot model-config IDs and preload
Budgetsbefore deleting scoped rows.This cleanup only records the legacy
BudgetID, but scoped model configs can also own rows throughBudgets. It also deletes byscope/scope_idafter the snapshot, so a config inserted betweenFindandDeletegets 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 winReject nil
scopeIDfor non-global lookups.
scope_idis only optional for global model configs. Translatingscope != "global"plusscopeID == nilintoscope_id IS NULLmakes an invalid identity look like a valid query shape instead of failing fast.As per coding guidelines,
transports/config.schema.jsonis the source of truth here:scopedefaults to"global"andscope_idis required whenscope != "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 winReturn 400 for invalid
from_memorypagination 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 onfrom_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 winMap invalid model-config updates to 400 instead of 500.
reconcileModelConfigBudgets()returns*badRequestErrorfor duplicate or invalid budgets, butupdateModelConfigconverts 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 winUse a real button/link for scope-target navigation.
This new deep-link affordance is mouse-only right now. A clickable
Badgeis 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 winAvoid the index fallback for budget row keys.
Falling back to
idxhere 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 winSkip the providers query when governance access is missing.
useGetProvidersQuery()still runs even whenhasGovernanceAccesscauses 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.
The base branch was changed.
f8fabcd to
ffad4d1
Compare
…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 -->
…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 -->
…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 -->
## ✨ Features - **OpenAI Compaction** — Added OpenAI conversation compaction support across core, framework, logging, and the API surface (#4053) - **Multi-Customer & Org Hierarchy** — Logs and usage tracking now support multiple customers, teams, and business units, including business unit CRUD, team assignment, and governance endpoints in the OpenAPI spec (#4066, #4041, #4082) - **Provider-Level Governance** — Budgets & limits are now scope-aware and can be applied at the virtual-key top level and per provider, wired from the model configs table, with UI filters for scope and providers (#3938, #3937, #3939, #3981, #3962) - **Customer Budgets** — Customers support multiple budgets and `calendar_aligned` budget windows (#3998, #3997) - **Virtual Key Attribution & Controls** — Added a `created_by` user attribution column and a `blacklisted_models` column for virtual key provider configs (#3672, #3653) - **Request Header Capture** — OTel and Maxim observability plugins capture `request_headers` by pattern, with wildcard support (e.g. `x-custom-*`); logging gained the same wildcard header capture (#4012, #3958) - **OTel Content Controls & Collectors** — New `disable_content_logging` option drops message/tool content from exported spans, plus support for multiple OTel collectors (#4064, #3894) - **xAI x_search** — Added xAI `x_search` tool support (#3976) - **URL Validation** — Added fetch URL validation with private-network configuration and link-local blocking (#3947, #3991) - **File Scheme Pricing URLs** — Pricing source URLs now accept the `file://` scheme for air-gapped and self-hosted deployments (#4045) - **Paginated Virtual Keys** — Virtual key fetching is paginated to handle deployments with very large numbers of keys (#3957) - **Client IP Resolution** — Resolve client IP from `X-Forwarded-For`/`X-Real-IP` headers - **SCIM Provisioning** — Added `attributeType`/`attributeValue` SCIM provisioning fields - **Helm/Config Schema** — Added `roles` RBAC governance config and `per_user_oauth` MCP auth to the Helm chart and config schema (#4004, #4009) - **Log Navigation UI** — Added a "View logs" menu item to customer, team, and virtual key tables, clickable links in log detail views, a customer detail sheet, and a reusable `BudgetDisplay` component (#4073, #4054, #4026, #4055) - **Faster First Paint** — Added an inline loading shell to `#root` before React mounts (#4063) - **Materialized View Alias** — Added an `alias` column to the materialized view with filter support (#4078) ## 🐞 Fixed - **Fetch URL IP Checks** — Hardened fetch URL IP checks against SSRF (#4092) - **Mantle Model Matching** — Broadened Mantle model matching to all `gpt` variants (#4091) - **Empty Thinking Blocks** — Strip thinking blocks when the signature is empty (#4079) - **OpenAI Stream Usage** — Removed usage from the `responses.created` event in the OpenAI stream (#4080) - **Prompt Cache Key** — Set the prompt cache key from the Anthropic integration (#4086) - **Upstream Failure Status** — Map upstream connection failures to 502 instead of 400 (#3929) (thanks [@chris-colinsky](https://github.com/chris-colinsky)!) - **Gemini Schema Constraints** — Accept numeric schema integer constraints for Gemini (#3994) (thanks [@yanhao98](https://github.com/yanhao98)!) - **Files Provider Param** — Accept the `?provider=` query param on `GET /v1/files` (#3971) (thanks [@alexef](https://github.com/alexef)!) - **Optional Batch Model** — Made the `model` field optional on `POST /v1/batches` (#3973) (thanks [@alexef](https://github.com/alexef)!) - **Helm Azure Config** — Added missing `azure_key_config` fields to the Helm schema (#3996) (thanks [@axelray-dev](https://github.com/axelray-dev)!) - **Text Completion Chunk Model** — Added the missing `Model` field to `TextCompletionChunkResponse` (#3970) (thanks [@kuishou68](https://github.com/kuishou68)!) - **MCP Inline stdio Env** — MCP stdio server configs accept inline environment variable assignments (#3861) (thanks [@Shushmitaaaa](https://github.com/Shushmitaaaa)!) - **Orphaned Tool Results** — Orphaned tool results in the OpenAI to Anthropic conversion flow are no longer rejected by the Anthropic API (#3919) - **Node Usage Reconciliation** — Added a monotonic `inc_number` log cursor so node usage reconciliation does not skip late async log writes (#3664) - **Bedrock Output Assessments** — Corrected the type of `outputAssessments` in Bedrock responses (#4028) - **Model Pool Pricing Reloads** — Preserve non-pricing model pool entries across pricing reloads (#3999) - **Ghost Node Reconciliation** — Replicate the VK hierarchy flow for ghost node reconciliation (#4088) - **VK Double Usage Counting** — Fixed double usage counting when creating a virtual key (#4070) - **Model Config Lifecycle** — Cascade deletes for model configs and removal of stale in-memory model configs (#4051, #4043) - **FTS Index Cap** — Reduced the FTS index `left()` cap from 800k to 250k chars to stay within the tsvector limit (#4057) - **Sync Worker Drift** — Reduced the sync worker ticker period to 5m to prevent threshold drift (#4023) - **Passthrough** — Fixed passthrough budgets, gated passthrough models per VK, model extraction for Azure passthrough, and restricted fallbacks/provider selection to the VK boundary (#3941, #3988, #3983, #3924) - **Provider Response Headers** — Strip provider response headers and add a content-type filter (#3955, #4024) - **Stream Handling** — Drain non-SSE stream readers and retry stale connections (#3956, #3967) - **Azure Claude** — Strip Azure diagnostic property for Claude models (#3925) - **Compat max_tokens** — Preserve chat `max_tokens` during param filtering (#3992) - **Raw Request Flag** — Removed the raw request flag from providers that don't support it (#4058) - **UI Fixes** — Standardized page container layout, virtual key model configs UI, and dashboard chart tooltips (#4046, #4052, #4044) ## 🔧 Maintenance - **Dependency Upgrades** — Bumped transitive `golang.org/x` dependencies (crypto, net, sys, text) for Docker Scout CVE remediation and `recharts` to 3.8.1; cascaded version bumps across all modules (#3900, #4003)

Summary
Adds
scopeandproviderfilter 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
ScopeandProviderfields toModelConfigsQueryParamsand wired them into the RDB query as exact-matchWHEREclauses.scopeandproviderquery parameters and apply them on both the in-memory (non-paginated) path and the paginated database path.scopeandprovidertoGetModelConfigsParamsand passed them through the RTK Query API call, including query string serialization.scopeorproviderdoes not match the active filter arguments.<Select>dropdowns to the model limits toolbar. The scope options are sourced from the existinggetModelLimitScopesregistry; provider options are sourced from the providers API with icons and labels.Type of change
Affected areas
How to test
global) — only model configs with that scope should appear.openai) — only model configs for that provider should appear.Screenshots/Recordings
Add before/after screenshots of the model limits toolbar showing the new Scope and Provider dropdowns.
Breaking changes
Related issues
Link related issues and discussions.
Security considerations
The new
scopeandproviderquery parameters are passed as parameterized query arguments (WHERE scope = ?,WHERE provider = ?), preventing SQL injection.Checklist
docs/contributing/README.mdand followed the guidelinesSummary by CodeRabbit
New Features
Improvements