feat(usage): surface Codex code-review weekly window + additional_rate_limits fallback (#4494)

Rebuilt onto release/v3.8.33 (squash-base-stale). Integrated into release/v3.8.33.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-21 14:06:18 -03:00
committed by GitHub
parent cd45943015
commit a37ee2fac5
5 changed files with 185 additions and 5 deletions

View File

@@ -42,6 +42,7 @@ _In development — bullets added per PR; finalized at release._
- **feat(ui): expose a `targetFormat` selector in the custom-models form** — the custom-models form now lets you pick the upstream target format explicitly, so a custom model can be pinned to the right wire format instead of relying on inference. ([#4475](https://github.com/diegosouzapw/OmniRoute/pull/4475) — thanks @adivekar-utexas)
- **feat(providers): expose `gpt-4o` on the built-in GitHub Copilot (`gh`) provider** — GitHub Copilot still serves the original `gpt-4o` chat model via its `/chat/completions` endpoint, but the OmniRoute registry only shipped the GPT-5.x family, so clients that explicitly request `gpt-4o` against `gh` got an unknown-model error. `gpt-4o` is now registered under the `github` provider next to the GPT-5.x lineup (chat/completions, 128k context — no `openai-responses` targetFormat). Ported from [9router#98](https://github.com/decolua/9router/pull/98). (thanks @I3eka)
- **feat(pricing): default pricing for Qwen `coder-model` on the `qw` provider** — the Qwen Coder Free (`qw`) registry already exposed the `coder-model` id (Qwen3.5/3.6 Coder Model) but `DEFAULT_PRICING.qw` was missing the row, so usage tracking reported `$0.00` for that model. The pricing row is now added with the same shape as the sibling `vision-model` tier, restoring non-zero cost tracking. Ported from upstream 9router PR [decolua/9router#156](https://github.com/decolua/9router/pull/156). (thanks @LinearSakana)
- **feat(usage): Codex review-quota now surfaces the weekly window and the `additional_rate_limits` fallback shape** — the dashboard's Codex usage card showed only the **session** half of `code_review_rate_limit` and dropped review descriptors that arrived inside `additional_rate_limits` (the shape some ChatGPT Codex plans report). `buildCodexUsageQuotas` now emits the secondary window as `quotas.code_review_weekly` and, when the dedicated `code_review_rate_limit` block is empty, falls back to the matching descriptor in `additional_rate_limits` (matched on `limit_name`/`metered_feature`/`limit_id` containing `code_review` / `codex_review` / `review`). The new label `code_review_weekly → "Code Review Weekly"` is registered in `ProviderLimits/utils.tsx` so the card renders both windows side-by-side. The existing `quotas.code_review` key is preserved for back-compat. Inspired by upstream decolua/9router PR #836. (thanks @hiepau1231)
### 🐛 Fixed

View File

@@ -113,6 +113,53 @@ function findCodexSparkRateLimit(data: JsonRecord): JsonRecord {
return {};
}
/**
* Upstream parity (decolua/9router PR #836): some ChatGPT Codex plans report
* the review-window rate limit inside `additional_rate_limits` rather than the
* dedicated `code_review_rate_limit` block. Detect that descriptor so the
* caller can fall back to it when the dedicated block is empty.
*/
function isCodexReviewLimitDescriptor(...values: unknown[]): boolean {
return values.some((value) => {
if (typeof value !== "string") return false;
const normalized = value.trim().toLowerCase();
if (!normalized) return false;
return (
normalized === "code_review" ||
normalized === "codex_review" ||
normalized === "review" ||
normalized.includes("code_review") ||
normalized.includes("codex_review") ||
normalized.includes("code review")
);
});
}
function findCodexReviewRateLimit(data: JsonRecord): JsonRecord {
const additionalRateLimits = getFieldValue(
data,
"additional_rate_limits",
"additionalRateLimits"
);
if (!Array.isArray(additionalRateLimits)) return {};
for (const entryValue of additionalRateLimits) {
const entry = toRecord(entryValue);
if (
isCodexReviewLimitDescriptor(
getFieldValue(entry, "limit_name", "limitName"),
getFieldValue(entry, "metered_feature", "meteredFeature"),
getFieldValue(entry, "limit_id", "limitId"),
entry["id"],
entry["name"]
)
) {
return toRecord(getFieldValue(entry, "rate_limit", "rateLimit"));
}
}
return {};
}
export function buildCodexUsageQuotas(dataValue: unknown): {
rateLimit: JsonRecord;
quotas: Record<string, CodexUsageQuota>;
@@ -128,12 +175,23 @@ export function buildCodexUsageQuotas(dataValue: unknown): {
if (Object.keys(secondaryWindow).length > 0)
quotas.weekly = buildPercentageQuota(secondaryWindow);
// Resolve the code-review rate limit block. ChatGPT Codex exposes the same
// information under two different shapes depending on the plan tier
// (decolua/9router PR #836):
// 1. Dedicated `code_review_rate_limit` block at the top level (preferred).
// 2. An entry inside `additional_rate_limits` with a `code_review` /
// `review` descriptor (fallback for plans that bucket every secondary
// limit into the same array).
const dedicatedReviewRateLimit = toRecord(
getFieldValue(data, "code_review_rate_limit", "codeReviewRateLimit")
);
const reviewRateLimit =
Object.keys(dedicatedReviewRateLimit).length > 0
? dedicatedReviewRateLimit
: findCodexReviewRateLimit(data);
const codeReviewWindow = toRecord(
getFieldValue(
toRecord(getFieldValue(data, "code_review_rate_limit", "codeReviewRateLimit")),
"primary_window",
"primaryWindow"
)
getFieldValue(reviewRateLimit, "primary_window", "primaryWindow")
);
if (
getFieldValue(codeReviewWindow, "used_percent", "usedPercent") !== null ||
@@ -142,6 +200,16 @@ export function buildCodexUsageQuotas(dataValue: unknown): {
quotas.code_review = buildPercentageQuota(codeReviewWindow);
}
const codeReviewSecondaryWindow = toRecord(
getFieldValue(reviewRateLimit, "secondary_window", "secondaryWindow")
);
if (
getFieldValue(codeReviewSecondaryWindow, "used_percent", "usedPercent") !== null ||
getFieldValue(codeReviewSecondaryWindow, "remaining_count", "remainingCount") !== null
) {
quotas.code_review_weekly = buildPercentageQuota(codeReviewSecondaryWindow);
}
const sparkRateLimit = findCodexSparkRateLimit(data);
const sparkPrimaryWindow = toRecord(
getFieldValue(sparkRateLimit, "primary_window", "primaryWindow")

View File

@@ -18,6 +18,7 @@ const QUOTA_LABEL_MAP: Record<string, string> = {
session: "Session",
weekly: "Weekly",
code_review: "Code Review",
code_review_weekly: "Code Review Weekly",
gpt_5_3_codex_spark_session: "GPT-5.3-Codex-Spark",
gpt_5_3_codex_spark_weekly: "GPT-5.3-Codex-Spark Weekly",
agentic_request: "Agentic",

View File

@@ -0,0 +1,109 @@
/**
* Regression test for Codex review-quota plumbing.
*
* Upstream parity: decolua/9router PR #836 surfaces the SECONDARY window of
* `code_review_rate_limit` and supports descriptors that arrive via the
* `additional_rate_limits` array (some ChatGPT plans report the review limit
* there rather than in the dedicated `code_review_rate_limit` block).
*
* Before this fix:
* - `buildCodexUsageQuotas` only emitted `quotas.code_review` from the
* primary window of `code_review_rate_limit`, so the WEEKLY review window
* was invisible to the dashboard.
* - Review limits surfaced under `additional_rate_limits` (with
* `limit_name`/`metered_feature` containing "review") were dropped.
*
* After this fix:
* - The secondary window of `code_review_rate_limit` is emitted as
* `quotas.code_review_weekly` (parallel to `session`/`weekly`).
* - Review entries inside `additional_rate_limits` populate the same
* `code_review`/`code_review_weekly` keys when the dedicated block is
* absent.
* - The primary `code_review` key is preserved (backward-compat for the
* existing dashboard rendering & the usage-service-hardening regression).
*/
import test from "node:test";
import assert from "node:assert/strict";
import { buildCodexUsageQuotas } from "../../open-sse/services/codexUsageQuotas.ts";
test("buildCodexUsageQuotas surfaces the secondary code_review window", () => {
const { quotas } = buildCodexUsageQuotas({
rate_limit: {
primary_window: { used_percent: 25 },
secondary_window: { used_percent: 50 },
},
code_review_rate_limit: {
primary_window: { used_percent: 40, reset_after_seconds: 45 },
secondary_window: { used_percent: 70, reset_after_seconds: 6000 },
},
});
// Pre-existing primary window stays under `code_review` (back-compat).
assert.equal(quotas.code_review?.used, 40);
assert.equal(quotas.code_review?.remaining, 60);
// New: secondary window exposed as `code_review_weekly`.
assert.equal(quotas.code_review_weekly?.used, 70);
assert.equal(quotas.code_review_weekly?.remaining, 30);
assert.equal(quotas.code_review_weekly?.total, 100);
assert.equal(quotas.code_review_weekly?.unlimited, false);
});
test("buildCodexUsageQuotas reads review windows from additional_rate_limits", () => {
const { quotas } = buildCodexUsageQuotas({
rate_limit: {
primary_window: { used_percent: 10 },
},
additional_rate_limits: [
{
limit_name: "Code Review",
metered_feature: "code_review",
rate_limit: {
primary_window: { used_percent: 33 },
secondary_window: { used_percent: 55 },
},
},
],
});
assert.equal(quotas.code_review?.used, 33);
assert.equal(quotas.code_review?.remaining, 67);
assert.equal(quotas.code_review_weekly?.used, 55);
assert.equal(quotas.code_review_weekly?.remaining, 45);
});
test("buildCodexUsageQuotas leaves review windows undefined when payload is silent", () => {
const { quotas } = buildCodexUsageQuotas({
rate_limit: {
primary_window: { used_percent: 10 },
secondary_window: { used_percent: 20 },
},
});
assert.equal(quotas.session?.used, 10);
assert.equal(quotas.weekly?.used, 20);
assert.equal(quotas.code_review, undefined);
assert.equal(quotas.code_review_weekly, undefined);
});
test("buildCodexUsageQuotas prefers dedicated block over additional_rate_limits fallback", () => {
const { quotas } = buildCodexUsageQuotas({
code_review_rate_limit: {
primary_window: { used_percent: 11 },
secondary_window: { used_percent: 22 },
},
additional_rate_limits: [
{
limit_name: "review",
rate_limit: {
primary_window: { used_percent: 99 },
secondary_window: { used_percent: 99 },
},
},
],
});
assert.equal(quotas.code_review?.used, 11);
assert.equal(quotas.code_review_weekly?.used, 22);
});

View File

@@ -146,6 +146,7 @@ test("quota labels normalize session and weekly windows while preserving readabl
assert.equal(providerLimitUtils.formatQuotaLabel("weekly (7d)"), "Weekly");
assert.equal(providerLimitUtils.formatQuotaLabel("weekly sonnet (7d)"), "Weekly Sonnet");
assert.equal(providerLimitUtils.formatQuotaLabel("code_review"), "Code Review");
assert.equal(providerLimitUtils.formatQuotaLabel("code_review_weekly"), "Code Review Weekly");
assert.equal(providerLimitUtils.formatQuotaLabel("mcp_monthly"), "Monthly");
});