Repository navigation
feat: enhance cost formatting and add Codex GPT-5.5 pricing support - #1944
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces dynamic currency formatting in the cost dashboard to support higher precision for small values and refactors the analytics API's model pricing resolution to handle various model name formats and suffixes more robustly. Additionally, the SQL logic for calculating fallback statistics was refined to exclude combo-routed requests and ensure case-insensitive comparisons. Feedback was provided regarding the performance of the new currency formatting function, suggesting that Intl.NumberFormat instances should be cached to avoid unnecessary overhead during component re-renders.
| function formatCurrencyCost(locale: string, value: number): string { | ||
| const numericValue = Number(value || 0); | ||
| if (!Number.isFinite(numericValue) || numericValue === 0) { | ||
| return new Intl.NumberFormat(locale, { | ||
| style: "currency", | ||
| currency: "USD", | ||
| minimumFractionDigits: 2, | ||
| maximumFractionDigits: 2, | ||
| }).format(0); | ||
| } | ||
|
|
||
| const absValue = Math.abs(numericValue); | ||
| const fractionDigits = absValue < 0.01 ? 6 : absValue < 1 ? 4 : 2; | ||
| return new Intl.NumberFormat(locale, { | ||
| style: "currency", | ||
| currency: "USD", | ||
| minimumFractionDigits: fractionDigits, | ||
| maximumFractionDigits: fractionDigits, | ||
| }).format(numericValue); | ||
| } |
There was a problem hiding this comment.
Creating a new Intl.NumberFormat instance on every call to formatCurrencyCost is inefficient, as this function is executed multiple times during component rendering and Intl.NumberFormat instantiation is computationally expensive. Consider caching the formatters for the different precision levels (2, 4, and 6 digits) to improve performance.
const currencyFormatters = new Map<string, Intl.NumberFormat>();
function getCurrencyFormatter(locale: string, digits: number) {
const key = locale + "-" + digits;
let formatter = currencyFormatters.get(key);
if (!formatter) {
formatter = new Intl.NumberFormat(locale, {
style: "currency",
currency: "USD",
minimumFractionDigits: digits,
maximumFractionDigits: digits,
});
currencyFormatters.set(key, formatter);
}
return formatter;
}
function formatCurrencyCost(locale: string, value: number): string {
const numericValue = Number(value || 0);
if (!Number.isFinite(numericValue) || numericValue === 0) {
return getCurrencyFormatter(locale, 2).format(0);
}
const absValue = Math.abs(numericValue);
const fractionDigits = absValue < 0.01 ? 6 : absValue < 1 ? 4 : 2;
return getCurrencyFormatter(locale, fractionDigits).format(numericValue);
}
There was a problem hiding this comment.
Pull request overview
This PR fixes cost estimation and fallback-rate reporting for Codex GPT-5.5 (including provider alias resolution and codex-auto-review), and improves the Costs dashboard display so sub-cent spend isn’t rounded down to $0.00.
Changes:
- Extend pricing resolution in usage analytics to handle provider aliases, Codex GPT-5.5 effort variants, and
codex-auto-reviewmapping. - Adjust fallback statistics to exclude normal combo routing and make model comparisons case-insensitive.
- Update Costs dashboard KPI formatting to show more precision for small currency values; add unit tests covering the new analytics behaviors.
Validation / Coverage (from PR description):
- Commands run by author:
npm run lint(pass); targetedusage-analytics-route.test.ts(pass). npm run test:coveragecurrently exits non-zero due to an unrelated failing unit test (plan3-p0.test.ts), though reported coverage remains ≥ 60% (Statements/Lines 83.43%, Functions 86.53%, Branches 75.2%).- Commands run in this review environment: not run.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| tests/unit/usage-analytics-route.test.ts | Adds unit coverage for Codex GPT-5.5 alias pricing, codex-auto-review pricing mapping, and combo routing exclusion from fallback stats. |
| src/shared/constants/pricing.ts | Adds default pricing entry for codex-auto-review under Codex (cx) defaults. |
| src/app/api/usage/analytics/route.ts | Improves pricing lookup candidate resolution and refines fallback eligibility/rate calculations to exclude combo routing. |
| src/app/(dashboard)/dashboard/costs/CostOverviewTab.tsx | Improves displayed currency precision for sub-cent KPI values on the Costs overview. |
| return new Intl.NumberFormat(locale, { | ||
| style: "currency", | ||
| currency: "USD", | ||
| minimumFractionDigits: 2, | ||
| maximumFractionDigits: 2, | ||
| }).format(0); | ||
| } |
| return new Intl.NumberFormat(locale, { | ||
| style: "currency", | ||
| currency: "USD", | ||
| minimumFractionDigits: fractionDigits, | ||
| maximumFractionDigits: fractionDigits, | ||
| }).format(numericValue); |
|
@copilot apply changes based on the comments in this thread |
9577a8d
into
diegosouzapw:release/v3.7.9
|
Thanks @JxnLexn for this great contribution! 🎉 I resolved some branch conflicts by rebasing it onto our latest release/v3.7.9 branch. It's now officially merged! We appreciate your effort! |
… envelope (port from 9router#1926) (#4485) The unified thinking adapter can set Claude/OpenAI-native thinking fields (thinking, reasoning_effort, reasoning, enable_thinking, thinking_budget) at the request body root. The Antigravity envelope spreads ...passthroughFields, so these leaked into the Google Cloud Code envelope and Google rejected the request with `400 Bad input: oneOf at '/' not met` (or `Unknown name "thinking"`), breaking every reasoning/thinking model served via Antigravity (e.g. claude-opus-4-x-thinking). Extends the existing #1944 envelope strip (output_config/output_format) to also drop the whole thinking family. Gemini's own generationConfig.thinkingConfig travels inside the inner request and is unaffected, so thinking still works. Reported-by: theseven99 (decolua/9router#1926) Co-authored-by: Arcfoz <62255009+Arcfoz@users.noreply.github.com>
… envelope (port from 9router#1926) (diegosouzapw#4485) The unified thinking adapter can set Claude/OpenAI-native thinking fields (thinking, reasoning_effort, reasoning, enable_thinking, thinking_budget) at the request body root. The Antigravity envelope spreads ...passthroughFields, so these leaked into the Google Cloud Code envelope and Google rejected the request with `400 Bad input: oneOf at '/' not met` (or `Unknown name "thinking"`), breaking every reasoning/thinking model served via Antigravity (e.g. claude-opus-4-x-thinking). Extends the existing diegosouzapw#1944 envelope strip (output_config/output_format) to also drop the whole thinking family. Gemini's own generationConfig.thinkingConfig travels inside the inner request and is unaffected, so thinking still works. Reported-by: theseven99 (decolua/9router#1926) Co-authored-by: Arcfoz <62255009+Arcfoz@users.noreply.github.com> (cherry picked from commit 5a7c1e1)
…iegosouzapw#1944) Integrated into release/v3.7.9
…iegosouzapw#1944) Integrated into release/v3.7.9
… envelope (port from 9router#1926) (diegosouzapw#4485) The unified thinking adapter can set Claude/OpenAI-native thinking fields (thinking, reasoning_effort, reasoning, enable_thinking, thinking_budget) at the request body root. The Antigravity envelope spreads ...passthroughFields, so these leaked into the Google Cloud Code envelope and Google rejected the request with `400 Bad input: oneOf at '/' not met` (or `Unknown name "thinking"`), breaking every reasoning/thinking model served via Antigravity (e.g. claude-opus-4-x-thinking). Extends the existing diegosouzapw#1944 envelope strip (output_config/output_format) to also drop the whole thinking family. Gemini's own generationConfig.thinkingConfig travels inside the inner request and is unaffected, so thinking still works. Reported-by: theseven99 (decolua/9router#1926) Co-authored-by: Arcfoz <62255009+Arcfoz@users.noreply.github.com> (cherry picked from commit 10412d0)
… envelope (port from 9router#1926) (diegosouzapw#4485) The unified thinking adapter can set Claude/OpenAI-native thinking fields (thinking, reasoning_effort, reasoning, enable_thinking, thinking_budget) at the request body root. The Antigravity envelope spreads ...passthroughFields, so these leaked into the Google Cloud Code envelope and Google rejected the request with `400 Bad input: oneOf at '/' not met` (or `Unknown name "thinking"`), breaking every reasoning/thinking model served via Antigravity (e.g. claude-opus-4-x-thinking). Extends the existing diegosouzapw#1944 envelope strip (output_config/output_format) to also drop the whole thinking family. Gemini's own generationConfig.thinkingConfig travels inside the inner request and is unaffected, so thinking still works. Reported-by: theseven99 (decolua/9router#1926) Co-authored-by: Arcfoz <62255009+Arcfoz@users.noreply.github.com>
…iegosouzapw#1944) Integrated into release/v3.7.9
Summary
$0.00when token usage exists and pricing is configured.codex-auto-review.$0.00.Related Issues
Validation
npm run lint0 errors,1994 warnings.npm run test:unitgetModelInfoCore resolves unique non-openai unprefixed modelnull !== 'claude'14/14).npm run test:coverage>= 60%for statements, lines, functions, and branches83.43%83.43%86.53%75.2%sonar-scannerwas not installed.Tests Added Or Updated
codex-auto-review→ GPT-5.5 pricing coverage.Coverage Notes
codex/gpt-5.5,codex-auto-review,14/14).60%threshold for all metrics, but the coverage command exited non-zero due to the unrelatedplan3-p0test failure.Reviewer Notes
combo_namefrom fallback counting, because combo model selection is normal routing, not a fallback.