feat(providers): fill rate-limit gaps in the providers overview - #1157
Conversation
The providers overview ("Rate limits" section) only showed live
utilization bars for the ~5 providers with quota probes (openai, xai,
anthropic, cursor, kimi, google-antigravity, a6api). Every other
provider — all plain API-key providers, local runtimes, and free tiers —
showed "No rate-limit data yet".
Two-layer fill:
1. Live probes for providers with a real, authenticated usage/balance
endpoint:
- openrouter: GET /api/v1/key — renders a credit window against the
per-key spending cap (skipped when uncapped)
- deepseek: GET /user/balance — renders a balance window against the
granted allowance (skipped for top-up-only accounts)
Both follow the existing probe contract: canonical-host guard,
redirect: "error", 8s timeout, 4xx(except 408/429) terminal.
2. Documented reference limits for every provider with public rate-limit
docs: a new `rateLimits` field on the registry entry (rpm/tpm/rpd/
freeTier/source/updatedAt), threaded through derived presets and the
/api/providers response to the GUI. Providers populated: groq, google
(gemini), deepseek, cerebras, deepinfra, sambanova, nebius, together,
fireworks, openrouter, zai, minimax/minimax-cn, mistral, ollama/vllm/
lm-studio (local), opencode-free, opencode-zen.
GUI: new ProviderDocumentedLimits renderer shows the reference alongside
the live bars; the dashboard gains a "Documented" section for providers
without a live bar; per-provider overview shows both. New i18n strings
added to all six locales.
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughChangesProvider metadata and projection
Quota integrations
Workspace display
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant ProviderRegistry
participant ProviderRoutes
participant ProviderOverviewDashboard
participant ProviderDocumentedLimits
ProviderRegistry->>ProviderRoutes: provide rateLimits metadata
ProviderRoutes-->>ProviderOverviewDashboard: return provider rateLimits
ProviderOverviewDashboard->>ProviderDocumentedLimits: render documented limits
ProviderDocumentedLimits-->>ProviderOverviewDashboard: display localized limit summary
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Follow-up to the rate-limits overview fill: three more providers get REFRESHING live utilization bars, not just documented reference: - clinepass: GET /api/v1/users/me/plan/usage-limits — maps the rolling five-hour / weekly / monthly ClinePass utilization directly onto the existing ProviderQuota windows. A 404 (no active plan) is a no-report, not terminal. - zai: GET /api/monitor/usage/quota/limit — maps the GLM Coding Plan 5h/weekly/monthly quota windows. Sends the token RAW (no Bearer prefix), matching Z.AI's API contract. - minimax / minimax-cn: GET /v1/token_plan/remains — renders the Token Plan remaining-time countdown as a custom window. All follow the existing probe contract: canonical-host guard before sending credentials, redirect: "error", 8s timeout, 4xx (except 408/429) terminal, 5xx/network transient. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@src/providers/registry.ts`:
- Around line 36-37: Remove English prose from the provider response contracts:
in src/providers/registry.ts lines 36-37, replace freeTier with a localizable
key and parameters or structured limit data; in src/providers/quota.ts line 361,
return an apiCredits kind containing numeric remaining and limit values; in
src/providers/quota.ts line 398, return an apiBalance kind containing numeric
balance and grant values.
🪄 Autofix
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 Plus
Run ID: 55b822ed-6324-470f-a76b-50f8312dca6c
📒 Files selected for processing (19)
gui/src/components/provider-catalog/provider-presets.tsgui/src/components/provider-workspace/ProviderDocumentedLimits.tsxgui/src/components/provider-workspace/ProviderOverview.tsxgui/src/components/provider-workspace/ProviderOverviewDashboard.tsxgui/src/i18n/de.tsgui/src/i18n/en.tsgui/src/i18n/ja.tsgui/src/i18n/ko.tsgui/src/i18n/ru.tsgui/src/i18n/zh.tsgui/src/provider-workspace/catalog.tsgui/tests/provider-capacity-shell.test.tsxgui/tests/provider-capacity.test.tssrc/providers/derive.tssrc/providers/quota.tssrc/providers/registry.tssrc/server/management/provider-routes.tstests/provider-quota.test.tstests/provider-registry-parity.test.ts
| /** Free-tier cap, prose (e.g. "~200 req / 5 hours"). */ | ||
| freeTier?: string; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Keep user-visible text out of server response contracts.
The registry and quota APIs serialize English prose to the GUI. Locale files cannot translate these values after serialization.
src/providers/registry.ts#L36-L37: replacefreeTierprose with a localizable key plus parameters, or structured limit data.src/providers/quota.ts#L361-L361: return anapiCreditskind with numeric remaining and limit values instead of an English label.src/providers/quota.ts#L398-L398: return anapiBalancekind with numeric balance and grant values instead of an English label.
📍 Affects 2 files
src/providers/registry.ts#L36-L37(this comment)src/providers/quota.ts#L361-L361src/providers/quota.ts#L398-L398
🤖 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 `@src/providers/registry.ts` around lines 36 - 37, Remove English prose from
the provider response contracts: in src/providers/registry.ts lines 36-37,
replace freeTier with a localizable key and parameters or structured limit data;
in src/providers/quota.ts line 361, return an apiCredits kind containing numeric
remaining and limit values; in src/providers/quota.ts line 398, return an
apiBalance kind containing numeric balance and grant values.
Source: Path instructions
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 783e4f2b53
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const totalBalance = toFiniteNumber(body?.total_balance); | ||
| const grantedBalance = toFiniteNumber(body?.granted_balance); |
There was a problem hiding this comment.
Read DeepSeek balances from balance_infos
DeepSeek's /user/balance payload places total_balance and granted_balance inside entries of balance_infos, but this reads nonexistent top-level fields, so normal successful responses produce undefined and the new probe returns no report. The regression fixture masks this by duplicating the same values at both levels; select the appropriate currency row from balance_infos instead.
Useful? React with 👍 / 👎.
| // top-up-only account (granted = 0) has no limit to render utilization. | ||
| if (grantedBalance === undefined || grantedBalance <= 0) return null; | ||
| if (totalBalance === undefined || totalBalance < 0) return null; | ||
| const percent = normalizePercent((totalBalance / grantedBalance) * 100); |
There was a problem hiding this comment.
Do not report remaining balance as utilization
For a DeepSeek account with grant funds remaining, this ratio increases with the remaining balance, while QuotaBars treats the value as percent used and warns at high percentages. Thus the fixture's $6 of $8 balance is shown as 75% used rather than 25%, and a full balance appears exhausted; moreover, granted_balance is a current balance component rather than the original grant ceiling, so no reliable consumed percentage should be emitted unless an actual ceiling is available.
Useful? React with 👍 / 👎.
| discovery: p.liveModels === false ? undefined : getProviderDiscoveryStatus(name), | ||
| }))); | ||
| return jsonResponse(Object.entries(config.providers).map(([name, p]) => { | ||
| const registry = getProviderRegistryEntry(name); |
There was a problem hiding this comment.
Resolve documented limits by provider destination
When a GUI preset is saved under a custom name, such as my-groq with Groq's canonical adapter and base URL, this name-only lookup returns no registry entry and /api/providers omits the documented limits. The dashboard consequently claims that the renamed provider has no rate-limit data; use the existing destination-based registry resolver, as other user-facing provider metadata paths do.
Useful? React with 👍 / 👎.
| if (rateLimits.rpm !== undefined) parts.push(t("pws.rateLimits.rpm", { value: formatNumber(rateLimits.rpm) })); | ||
| if (rateLimits.tpm !== undefined) parts.push(t("pws.rateLimits.tpm", { value: formatNumber(rateLimits.tpm) })); | ||
| if (rateLimits.rpd !== undefined) parts.push(t("pws.rateLimits.rpd", { value: formatNumber(rateLimits.rpd) })); | ||
| if (rateLimits.freeTier) parts.push(rateLimits.freeTier); |
There was a problem hiding this comment.
Translate documented free-tier descriptions
In every non-English locale, this appends the English freeTier prose from the registry verbatim, so rows such as Google and DeepSeek remain partly English despite the surrounding RPM/TPM labels being translated. Model these descriptions as translation keys or structured values and provide them in every locale rather than rendering backend-authored English directly.
AGENTS.md reference: gui/AGENTS.md:L14-L18
Useful? React with 👍 / 👎.
| )} | ||
| </section> | ||
|
|
||
| {documentedLimitProviders.length > 0 && ( |
There was a problem hiding this comment.
Suppress the empty message when documented limits exist
When there are documented providers but no live quota reports, the live section immediately above still renders pws.dashboard.noRateLimits, and this newly added section then renders the documented data below it. The dashboard therefore simultaneously says “No rate-limit data yet” and displays rate-limit data, contradicting both the feature's fallback behavior and the new regression test; make the empty branch conditional on documentedLimitProviders.length === 0 or change its copy to distinguish live data.
Useful? React with 👍 / 👎.
| <div className="pws-documented-limits"> | ||
| <span className="pws-documented-limits-label">{t("pws.rateLimits.documented")}</span> | ||
| <span className="pws-documented-limits-value">{summary}</span> | ||
| {(rateLimits.source || rateLimits.updatedAt) && ( | ||
| <span className="muted pws-documented-limits-meta"> |
There was a problem hiding this comment.
Separate and wrap the documented-limit fields
None of the newly introduced pws-documented-limits* classes has a stylesheet rule, and adjacent JSX elements do not introduce text whitespace. Consequently the component renders text such as Documented30 req/min2026-08-06 · https://… with the label, value, and provenance concatenated; the unbroken source URL can also overflow narrow provider rows. Add an explicit layout with gaps and wrapping, or render textual separators.
Useful? React with 👍 / 👎.
| reasoningSplitModels: MINIMAX_MODELS, | ||
| thinkingToggleModels: ["MiniMax-M3"], | ||
| jawcodeBundle: "minimax", metadataModelIdNormalize: "case-insensitive", note: "Subscription Key or API Key", | ||
| rateLimits: { rpm: 100, tpm: 200_000, freeTier: "Coding plan subscription; per-plan quotas", source: "https://platform.minimax.io/docs/guides/rate-limits", updatedAt: "2026-08-06" }, |
There was a problem hiding this comment.
Add rate-limit metadata for the MiniMax CN preset
The limits are attached only to minimax, while minimax-cn is a separate registry entry and /api/providers performs an exact registry lookup. A configured MiniMax CN provider therefore remains in the “no data” path even though this change explicitly treats the MiniMax coding-plan variants as covered; add the applicable CN metadata to its canonical registry entry rather than relying on the sibling preset.
AGENTS.md reference: src/AGENTS.md:L18-L18
Useful? React with 👍 / 👎.
| return parts.join(" · "); | ||
| } | ||
|
|
||
| export function ProviderDocumentedLimits({ rateLimits, t }: { rateLimits: DocumentedRateLimits; t: TFn }) { |
There was a problem hiding this comment.
Document the new dashboard rate-limit behavior
This introduces a new user-visible distinction between live utilization and static, tier-dependent documented limits, including provenance and verification dates, but the commit makes no corresponding docs-site/ update. Users need documentation explaining that these figures are references rather than account-specific limits and can drift from their actual tier, as required for dashboard behavior changes.
AGENTS.md reference: gui/AGENTS.md:L36-L36
Useful? React with 👍 / 👎.
| const used = usage !== undefined && usage > 0 | ||
| ? usage | ||
| : limitRemaining !== undefined ? Math.max(0, limit - limitRemaining) : undefined; |
There was a problem hiding this comment.
Prefer OpenRouter's authoritative remaining-cap value
For keys with a periodic reset or a spending limit changed after prior usage, usage can represent accumulated spend while limit_remaining represents the current cap window. Preferring usage whenever it is positive therefore makes a reset key appear partly or fully exhausted even when the API reports most of the current limit remaining; derive utilization from limit_remaining when it is present and use usage only as a fallback.
Useful? React with 👍 / 👎.
| function isCanonicalDeepSeekBaseUrl(baseUrl: string): boolean { | ||
| const normalized = normalizedBaseUrl(baseUrl); | ||
| return normalized === DEEPSEEK_BASE_URL; |
There was a problem hiding this comment.
Accept the supported DeepSeek /v1 base URL
Existing and legacy provider configurations in this repository use https://api.deepseek.com/v1, but this canonical-host check accepts only the equivalent root form. Those valid DeepSeek rows silently skip the new balance probe even though credentials could safely be sent to the fixed root balance endpoint; normalize and accept both canonical path variants, as is already done for A6API.
AGENTS.md reference: src/AGENTS.md:L10-L10
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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 `@src/providers/quota.ts`:
- Around line 551-558: Update the Token Plan handling near the remainsMs
calculation to avoid assuming a 30-day window: obtain the provider-supplied
total duration before deriving percent, and only calculate/report percentage
when that total is known. When no total duration exists, extend the quota result
to report remainsMs as a duration without a percentage, and add a regression
assertion covering the reported percentage or duration.
- Line 41: Update fetchMinimaxQuota and its caller to select the quota endpoint
by provider region: retain MINIMAX_REMAINS_URL for minimax and use
https://api.minimaxi.com/v1/token_plan/remains for minimax-cn, or disable the CN
probe until that endpoint is explicitly supported. Add coverage verifying the
minimax-cn canonical base URL and ensure both provider paths avoid targeting the
wrong region.
In `@tests/provider-quota.test.ts`:
- Line 691: Remove the duplicate const seen declarations so each test callback
has exactly one declaration: tests/provider-quota.test.ts:691-691 in the
ClinePass test, tests/provider-quota.test.ts:747-747 in the Z.AI test, and
tests/provider-quota.test.ts:800-800 in the MiniMax test. Preserve separate seen
declarations across distinct callback scopes.
🪄 Autofix
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 Plus
Run ID: a7981029-105f-4444-9f87-d98f00cdbb66
📒 Files selected for processing (2)
src/providers/quota.tstests/provider-quota.test.ts
Following the CodexBar provider reference, five more providers with real authenticated usage endpoints get REFRESHING live bars (not just documented reference): - moonshot: GET /v1/users/me/balance (intl + CN hosts) — account balance window from available/voucher/cash. - venice: GET /api/v1/billing/balance — DIEM balance, or a DIEM epoch allocation progress window when present. - synthetic: GET /v2/quotas — rolling 5-hour / weekly token / search-hourly lanes mapped onto the quota windows. - deepinfra: GET /payment/checklist?compute_owed=true — billing-cycle spend against the spending limit, or prepaid balance when no limit is set. - neuralwatt: GET /v1/quota — subscription kWh usage + prepaid USD credits. All follow the existing probe contract: canonical-host guard before sending credentials, redirect: "error", 8s timeout, 4xx (except 408/429) terminal, 5xx/network transient. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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 `@src/providers/quota.ts`:
- Around line 616-624: Use consumed capacity for quota percentages: in
src/providers/quota.ts lines 616-624, remove the fabricated Moonshot utilization
calculation and emit the established balance-only or zero-utilization fallback;
in src/providers/quota.ts lines 799-803, calculate Neuralwatt utilization as
consumed credits (totalCredits minus remainingCredits) divided by totalCredits.
Update tests/provider-quota.test.ts line 850 to assert the selected Moonshot
balance-only behavior and line 975 to expect 30 percent.
In `@tests/provider-quota.test.ts`:
- Line 846: Add focused regression cases in tests/provider-quota.test.ts at
lines 846-846 and 937-937: extend the Moonshot tests around
fetchProviderQuotaReports and keyQuotaConfig to cover the CN base URL, asserting
a request to https://api.moonshot.cn/v1/users/me/balance; add a DeepInfra
root-base case, asserting a request to
https://api.deepinfra.com/payment/checklist?compute_owed=true.
- Line 836: Remove the duplicate const seen declarations so each test callback
retains exactly one declaration: tests/provider-quota.test.ts lines 836-836 in
the Moonshot test, 874-874 in the Venice test, and 927-927 in the DeepInfra
test. Keep the declarations separate across their distinct test() callback
scopes.
🪄 Autofix
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 Plus
Run ID: d43c5616-b32c-4437-b69f-d03f1a2362a8
📒 Files selected for processing (2)
src/providers/quota.tstests/provider-quota.test.ts
…nd UI Codex connector findings: - DeepSeek: read total/granted from `balance_infos` rows (currency-selected) instead of nonexistent top-level fields; report a balance-only window (granted_balance is a current component, not a grant ceiling — no fabricated utilization). Accept the canonical `/v1` base URL. - OpenRouter: derive utilization from `limit_remaining` (authoritative for reset/re-capped keys) and treat a successful no-cap response as TERMINAL so an old capped bar is dropped instead of preserved as last-good. - Moonshot: report balance-only (percent 0) — no fabricated utilization. - Neuralwatt: report CONSUMED credits (total - remaining)/total, not the remaining share. - MiniMax: select the CN `token_plan/remains` host for minimax-cn; never assume a 30-day window — report a duration-only window unless the API supplies a plan total. - provider-routes: resolve documented limits by provider DESTINATION (registryEntryForProviderDestination) so a preset saved under a custom name still shows its limits. - registry: add rateLimits to the minimax-cn entry (exact lookup). - GUI: nest documented rows inside the rate-limits column (preserves the 2-col dashboard grid); suppress "No rate-limit data yet" when documented limits exist; add documented-limits CSS (gaps, wrapping, overflow). - i18n: localize free-tier prose via translation keys in all six locales instead of rendering backend-authored English verbatim. - docs-site: document the live-vs-documented rate-limit distinction. CodeRabbit findings: - MiniMax: no presumed 30-day window; regression coverage for the duration-only and consumed-share paths, and the CN host. - Tests: update assertions for the behavior changes (deepseek balance-only, moonshot balance-only, neuralwatt consumed, minimax duration-only) and add regression cases (moonshot CN, deepinfra root base, deepseek /v1, openrouter reset-key + cap-removal). Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 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 `@gui/src/components/provider-workspace/ProviderDocumentedLimits.tsx`:
- Around line 28-46: Update FREE_TIER_KEYS to type its values as
Parameters<TFn>[0] so mapped translation keys remain validated against TFn’s
accepted key type, then change localizeFreeTier to call t(key) directly without
the never cast.
In `@src/providers/quota.ts`:
- Around line 597-610: Update the MiniMax quota handling around the totalMs
fallback so an unknown plan duration never reports percent: 0; extend the quota
result contract to carry the remaining duration with an optional percentage, or
suppress the row until totalMs is available. Update the existing provider quota
regression test near the MiniMax cases to assert the percentage is absent when
the API omits the total duration.
- Around line 402-405: Update the quota calculation around used so usage is
accepted when it is finite and equal to zero, while still treating absent or
invalid usage as unavailable when limitRemaining is undefined. Preserve the
existing limitRemaining precedence and null behavior otherwise, and add a
regression case for an OpenRouter response with limit 20 and usage 0 that
reports zero consumption and $20 remaining.
In `@tests/provider-quota.test.ts`:
- Around line 641-643: In the “DeepSeek quota renders a balance-only window from
balance_infos” test callback, remove the duplicate seen declarations and retain
a single const seen variable for the fetch mock and assertions so the
provider-quota test file parses successfully.
🪄 Autofix
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 Plus
Run ID: 556c8d7b-0c8d-43cc-abd5-d586255035e1
📒 Files selected for processing (14)
docs-site/src/content/docs/guides/providers.mdgui/src/components/provider-workspace/ProviderDocumentedLimits.tsxgui/src/components/provider-workspace/ProviderOverviewDashboard.tsxgui/src/i18n/de.tsgui/src/i18n/en.tsgui/src/i18n/ja.tsgui/src/i18n/ko.tsgui/src/i18n/ru.tsgui/src/i18n/zh.tsgui/src/styles/provider-overview-dashboard.csssrc/providers/quota.tssrc/providers/registry.tssrc/server/management/provider-routes.tstests/provider-quota.test.ts
Three valid findings from the latest review round: - GUI: type FREE_TIER_KEYS values as `Parameters<TFn>[0]` so mapped translation keys stay validated against TFn's TKey constraint, and call `t(key)` directly without the `never` cast. - OpenRouter: accept `usage: 0` (a capped key with zero consumption must render 0% used / full cap remaining); `usage >= 0` replaces `usage > 0`. - MiniMax: when the API omits the plan total duration, suppress the row instead of reporting `percent: 0` — a 0% bar would falsely claim zero consumption. Only render a consumed share when a provider-supplied total is known. The remaining reported items (duplicate `const seen` in tests, and several "Addressed in commit 118ba32" markers) were verified against the current tree: each `seen` declaration lives in its own test callback scope and the suite parses and passes (84 quota tests green), so those findings are stale. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@gui/src/components/provider-workspace/ProviderDocumentedLimits.tsx`:
- Around line 44-46: The localizeFreeTier function must not return unmapped
freeTier text directly. Add locale entries and FREE_TIER_KEYS mappings for every
provider-owned description, and use an existing localized fallback for unknown
values; then add a regression test covering an unmapped value without hardcoding
user-visible text in gui code.
In `@src/providers/quota.ts`:
- Around line 601-602: The MiniMax quota path incorrectly treats a successful
response without total duration as transient, preserving stale utilization. In
src/providers/quota.ts lines 601-602, update the handling around totalMs in
fetchProviderQuotaReports to return the established cache-clearing sentinel,
matching the OpenRouter no-cap path. Add the focused regression test in
tests/provider-quota.test.ts lines 856-869: cache a valid total-duration row,
force-refresh with no total duration, and assert reports is empty.
🪄 Autofix
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 Plus
Run ID: 4aec6368-ea32-4dad-baba-ec39a30a29bf
📒 Files selected for processing (3)
gui/src/components/provider-workspace/ProviderDocumentedLimits.tsxsrc/providers/quota.tstests/provider-quota.test.ts
✅ READY
|
|
not gui |
|
✅ Deterministic PR hygiene checks passed. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dd7c7ce374
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const registry = getProviderRegistryEntry(name) | ||
| ?? registryEntryForProviderDestination(p); |
There was a problem hiding this comment.
Match documented limits to the actual provider destination
For a configured key provider whose name collides with a registry id but whose transport was edited, e.g. groq pointing at https://custom.example/v1, this exact-id lookup wins before the destination resolver and /api/providers still returns Groq's documented limits for an unrelated upstream. Fresh evidence: the current resolver still chooses getProviderRegistryEntry(name) before checking the provider's adapter/baseUrl, so the dashboard can display authoritative-looking limits for the wrong vendor; gate the exact lookup with a transport match or prefer the destination resolver for key-auth rows.
Useful? React with 👍 / 👎.
| * weekly token, search-hourly) mapped onto the quota windows. | ||
| */ | ||
| async function fetchSyntheticQuota(provider: string, config: OcxProviderConfig): Promise<ProviderQuotaProbeResult> { | ||
| if (!isCanonicalSyntheticBaseUrl(config.baseUrl)) return null; |
There was a problem hiding this comment.
Accept the Synthetic preset URL for quota probing
The built-in Synthetic preset still routes traffic through https://api.synthetic.new/openai/v1, but this new quota probe only accepts https://api.synthetic.new/v2 before fetching /quotas. A normal Synthetic provider added from the catalog therefore never reaches the live quota endpoint and silently falls back to no live row, despite the docs listing Synthetic as probed; derive/allow the quota host alongside the registry transport instead of hardcoding a conflicting canonical base URL.
Useful? React with 👍 / 👎.
| disabled: p.disabled === true, | ||
| codexAccountMode: providerCodexAccountMode(name, p), | ||
| discovery: p.liveModels === false ? undefined : getProviderDiscoveryStatus(name), | ||
| ...(registry?.rateLimits ? { rateLimits: { ...registry.rateLimits } } : {}), |
There was a problem hiding this comment.
Put documented limits on the config DTO
The Providers workspace never consumes this /api/providers list for its provider map: useProvidersFetch.fetchConfig() loads /api/config and passes config.providers into ProviderWorkspaceShell. Because safeConfigDTO() still omits rateLimits, the new documented-limit UI receives no data in normal use (the regression test only injects the field manually), so the documented rows do not render; add the resolved metadata to the /api/config DTO or switch the workspace to the enriched provider list.
Useful? React with 👍 / 👎.
| const apiKey = resolveEnvValue(config.apiKey)?.trim(); | ||
| if (!apiKey) return null; | ||
| const response = await fetch(`${ZAI_BASE_URL}/api/monitor/usage/quota/limit`, { | ||
| headers: { Accept: "application/json", Authorization: apiKey }, |
There was a problem hiding this comment.
Send the Z.AI quota key as a bearer token
For standard Z.AI Coding Plan keys, this sends the raw key as the entire Authorization value, while Z.AI's own API reference requires HTTP Bearer authentication (Authorization: Bearer ZAI_API_KEY) and the existing OpenAI-chat adapter sends Z.AI model calls that way. The quota probe will therefore get 401/auth-missing for the same key that successfully serves completions; prefix the key with Bearer here as well.
Useful? React with 👍 / 👎.
| // total there is no percentage to render, so suppress the row rather than | ||
| // report a false 0% (zero would mean "no consumption"). | ||
| const totalMs = toFiniteNumber(data.total_time ?? data.plan_duration_ms ?? data.total_duration_ms); | ||
| if (totalMs === undefined || totalMs <= 0) return null; |
There was a problem hiding this comment.
Drop stale MiniMax rows when no total is available
When MiniMax returns a valid remains payload without total_time after a previous refresh did include it, this null is treated as a transient probe failure by the aggregator and preserves the old utilization row for up to 30 minutes. The comment says this case should suppress the row rather than fabricate utilization, so return the terminal/no-row result instead of the transient one.
Useful? React with 👍 / 👎.
| const fiveHour = percentAt("rollingFiveHourLimit"); | ||
| const weekly = percentAt("weeklyTokenLimit"); |
There was a problem hiding this comment.
Parse Synthetic's documented subscription quota shape
Even after the Synthetic probe reaches /v2/quotas, the current documented response shape is subscription: { limit, requests, renewsAt }, not rollingFiveHourLimit or weeklyTokenLimit. A normal successful response from the official endpoint therefore produces no windows and no live row; map subscription.requests / subscription.limit (and renewsAt) before falling back to the older field names.
Useful? React with 👍 / 👎.
| if (!response.ok) { | ||
| // 404 = no active plan; a plain "no plan" is a no-report, everything else | ||
| // 4xx (except 408/429) is a credential/contract problem. | ||
| if (response.status === 404) return null; |
There was a problem hiding this comment.
Treat Cline's no-plan response as clearing the row
If a ClinePass account previously returned usage limits and a later refresh returns the documented 404/no-active-plan response, this null makes the aggregator preserve the old bars as a transient failure for up to 30 minutes. Since this branch explicitly classifies 404 as a real no-report state, it should suppress any previous row rather than leave stale plan usage visible.
Useful? React with 👍 / 👎.
Five findings from the latest Codex review: - provider-routes + config DTO: documented limits now resolve purely by DESTINATION for key providers (a renamed preset like "my-groq" keeps Groq's limits; a key provider whose transport was edited to a custom host does NOT inherit the registry id's limits). Forward/oauth/local presets resolve by id, matching the note-resolution pattern. - config DTO (/api/config): safeConfigDTO now carries rateLimits so the Providers workspace (which loads /api/config, not /api/providers) gets the documented limits; the GUI ProvidersConfig type gained the field. - Z.AI: send the key as a Bearer token (Authorization: Bearer <key>) per the Z.AI API reference, not the raw key. - MiniMax: a valid response that omits the plan total after a prior refresh had it is a DELIBERATE contract change — return TERMINAL so the stale row is dropped instead of preserved as a transient last-good. - Synthetic: accept the preset base URL (api.synthetic.new/openai/v1) in addition to /v2 for the quota probe. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Two dev/runtime fixes for the Providers rate-limits UI: - Upgrade @vitejs/plugin-react 6.0.3 -> 6.0.5. The 6.0.3 release failed to inject the fast-refresh preamble in dev on Vite 8, so every component module threw `Uncaught ReferenceError: $RefreshReg$ is not defined` and the dashboard would not boot under `bun run dev`. - Split ProviderDocumentedLimits.tsx into a component-only module plus a pure provider-workspace/documented-limits.ts (types, FREE_TIER_KEYS, formatDocumentedLimits). The component file previously exported non-component helpers, which violates react-refresh/only-export-components and breaks fast refresh; the lint gate flagged it. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Command Code (commandcode) gets a live utilization probe using the API key already stored by `ocx login command-code`: - GET https://api.commandcode.ai/internal/billing/credits — rolling 5-hour and weekly utilization windows plus monthly credit balances - GET https://api.commandcode.ai/internal/billing/subscriptions — the active plan, whose grant total produces the monthly consumed-share bar Maps onto the ProviderQuota windows (fiveHourPercent/weeklyPercent/ monthlyPercent) and follows the existing probe contract: canonical-host guard before sending the credential, redirect: "error", 8s timeout, 4xx (except 408/429) terminal, 5xx/network transient. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Drop the "Documented" reference-limits feature entirely — it was bloat. The providers overview now shows only LIVE utilization bars from probed usage endpoints. Removed: - ProviderRateLimits type + rateLimits data on all registry entries - rateLimits plumbing through derived presets, safeConfigDTO, and /api/providers - the ProviderDocumentedLimits component + documented-limits.ts pure module, the dashboard "Documented" section, catalog/preset/config DTO rateLimits fields, and all pws.rateLimits.* i18n keys (en + 5 locales) - the documented-limits CSS and the related tests (registry parity, config DTO, GUI capacity + shell) Docs updated to describe only the live utilization probes. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
… API) The Command Code billing endpoints (/internal/billing/credits, /internal/billing/subscriptions) require the browser better-auth session cookie — the API key opencodex stores via `ocx login command-code` is rejected with 401 "You're logged out" (only /alpha/whoami accepts the key). There is no API-key-accessible quota endpoint, so the probe could never produce a report. Remove the probe, its dispatcher branch, constants, and tests rather than ship dead code that claims a live meter. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Summary
The providers overview ("Rate limits" section) only showed live utilization bars for the ~5 providers that have quota probes (openai, xai, anthropic, cursor, kimi, google-antigravity, a6api). Every other provider — plain API-key providers, local runtimes, free tiers — showed "No rate-limit data yet".
This PR fills the gaps with a two-layer approach: live probes where a real usage/balance endpoint exists, and documented reference limits (from official docs) for the rest.
1. Live quota probes (
src/providers/quota.ts)New probes wired into
maybeFetchProviderQuota, following the existing probe contract (canonical-host guard before sending credentials,redirect: "error", 8s timeout, 4xx except 408/429 → terminal, 5xx/network → transient):GET /api/v1/key: renders a credit window against the per-key spending cap (limit/usage/limit_remaining). No cap configured → no bar (falls back to documented reference).GET /user/balance: renders a balance window against the granted allowance (total_balance/granted_balance). Top-up-only accounts (granted = 0) → no bar.2. Documented reference limits (
src/providers/registry.ts)New
rateLimitsfield onProviderRegistryEntry(rpm/tpm/rpd/freeTier/source/updatedAt), populated for providers with public rate-limit docs:Threaded through
derive.tspresets and/api/providers→ GUI.3. GUI display (bars + reference)
ProviderDocumentedLimitsrenderer — shows the documented numbers as reference text (never as live utilization).Tests
tests/provider-quota.test.ts: 9 new tests (openrouter + deepseek happy paths, canonical-host guards, no-cap/no-grant drops, terminal vs transient).tests/provider-registry-parity.test.ts: rate-limit entries must carrysource+updatedAt; presets round-trip.gui/tests/provider-capacity.test.ts:formatDocumentedLimitspure formatting.Summary by CodeRabbit