mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
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:
committed by
GitHub
parent
cd45943015
commit
a37ee2fac5
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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",
|
||||
|
||||
109
tests/unit/codex-usage-quotas-review-window.test.ts
Normal file
109
tests/unit/codex-usage-quotas-review-window.test.ts
Normal 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);
|
||||
});
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user