Skip to content

Fix OpenAI usage percent normalization for low values#178

Closed
ktmyname wants to merge 1 commit into
1jehuang:masterfrom
ktmyname:fix/openai-usage-normalize-ratio
Closed

Fix OpenAI usage percent normalization for low values#178
ktmyname wants to merge 1 commit into
1jehuang:masterfrom
ktmyname:fix/openai-usage-normalize-ratio

Conversation

@ktmyname

@ktmyname ktmyname commented May 8, 2026

Copy link
Copy Markdown

Summary

jcode usage (and the inline /usage overlay) shows ChatGPT subscriptions
as fully rate-limited (100% used, exhausted bar) even when the live
wham/usage endpoint reports only a few percent consumed. The Codex
desktop app, which reads the same endpoint with the same OAuth token,
correctly shows the actual remaining quota.

Root cause

Both src/usage/openai_helpers.rs and src/usage_openai.rs define a
normalize_ratio helper:

pub(super) fn normalize_ratio(raw: f32) -> f32 {
    if !raw.is_finite() { return 0.0; }
    if raw > 1.0 {
        (raw / 100.0).clamp(0.0, 1.0)
    } else {
        raw.clamp(0.0, 1.0)
    }
}

It tries to auto-detect whether the input is a ratio (0..=1) or a
percent (0..=100) using raw > 1.0. The OpenAI wham/usage endpoint
unambiguously returns used_percent in [0, 100]. A real production
payload with a barely-touched weekly bucket looks like:

{
  "rate_limit": {
    "primary_window":  { "used_percent": 5, ... },
    "secondary_window":{ "used_percent": 1, ... }
  }
}

used_percent: 1 (1% used) hits the else branch, clamps to 1.0,
and is rendered as 100% used / fully exhausted — even though the
user has 99% of their weekly Codex quota remaining.

The existing tests only exercise values like 25.0, 50.0, 75.0,
100.0, so the bug never surfaced in CI.

Fix

The wham/usage and OpenAI account-usage endpoints always report
percentages in [0, 100]. Drop the heuristic and just divide:

pub(super) fn normalize_ratio(raw: f32) -> f32 {
    if !raw.is_finite() { return 0.0; }
    (raw / 100.0).clamp(0.0, 1.0)
}

Applied to both src/usage/openai_helpers.rs and the legacy
src/usage_openai.rs.

Tests

Added two tests in src/usage/tests.rs:

  1. test_normalize_ratio_treats_low_integer_values_as_percent — direct
    unit coverage of the helper across 0, 1, 5, 50, 100, plus
    >100, negative, and NaN inputs.
  2. test_parse_openai_usage_payload_reports_low_percentages_correctly
    regression test built from a real wham/usage payload with
    used_percent: 1 in the secondary window. Asserts the parser
    returns 1.0, not 100.0.

All 33 usage::tests::* pass on cargo test --release --lib.

Verification (live API)

Before patch:

OpenAI (ChatGPT) (k***9@gmail.com)
----------------------------------
5-hour window: █░░░░░░░░░░░░░░ 5% (resets in 3h 0m)
7-day window:  ███████████████ 100% (resets in 6d 22h)
GPT-5.3-Codex-Spark (5h): █░░░░░░░░░░░░░░ 5%
GPT-5.3-Codex-Spark (7d): ███████████████ 100%

After patch (same account, same wham/usage response, ~10 minutes later):

OpenAI (ChatGPT) (k***9@gmail.com)
----------------------------------
5-hour window: █░░░░░░░░░░░░░░ 5% (resets in 2h 26m)
7-day window:  ░░░░░░░░░░░░░░░ 1% (resets in 6d 21h)
GPT-5.3-Codex-Spark (5h): █░░░░░░░░░░░░░░ 5%
GPT-5.3-Codex-Spark (7d): ░░░░░░░░░░░░░░░ 1%

This now matches what the Codex desktop app shows for the same account.

Impact

  • jcode usage, the /usage overlay, and the info-widget compact usage
    bar all consume normalize_ratio via parse_wham_window, so all three
    now report correctly.
  • Cross-provider failover heuristics that key off usage_ratio /
    seven_day_ratio previously assumed users were exhausted at 1% real
    usage; they now see the true value and stop spuriously failing over.

View in Codesmith
Need help on this PR? Tag @codesmith with what you need.

  • Let Codesmith autofix CI failures and bot reviews

The wham/usage endpoint and OpenAI account-usage helpers report
'used_percent' as a value in [0, 100]. The previous heuristic in
'normalize_ratio' tried to auto-detect ratio-vs-percent by checking
'raw > 1.0', which incorrectly mapped legitimate API responses like
'used_percent: 1' (1% used) to a ratio of 1.0, displayed as a fully
exhausted '100%' bar.

This made 'jcode usage' (and the inline /usage overlay) show
ChatGPT subscriptions as completely rate-limited even when only ~1%
of the weekly bucket had been consumed. The Codex desktop app, which
reads the same endpoint, correctly showed the actual remaining quota.

Treat the API value as a percent unconditionally and clamp to [0, 1].
Also add direct unit tests and a regression test that mirrors a real
production payload from the wham/usage endpoint with single-digit
'used_percent' values.
zombi3butt pushed a commit to zombi3butt/jcode that referenced this pull request May 22, 2026
The wham/usage and OpenAI account-usage endpoints always return
`used_percent` in [0, 100]. The previous heuristic (`raw > 1.0`)
misclassified low integer values like `used_percent: 1` (1% used) as
the already-normalized ratio `1.0` (100% used), which made the CLI
and `/usage` overlay report ChatGPT subscriptions as fully exhausted
when the live endpoint reported only a few percent consumed.

Drop the heuristic in both src/usage/openai_helpers.rs and the legacy
src/usage_openai.rs and divide by 100 unconditionally. Add two
regression tests in src/usage/tests.rs:
  - direct unit coverage of the helper across 0/1/5/50/100/>100/-5/NaN
  - parse_openai_usage_payload regression built from a real wham/usage
    response with used_percent: 1 in the secondary window

Ports upstream PR 1jehuang#178 by
@ktmyname (Cargo.lock version-bump hunk skipped, our fork tracks its
own version).

Closes quangdang46#137
@1jehuang 1jehuang added the triage: superseded PR triage: already fixed in master label Jun 12, 2026
@1jehuang

Copy link
Copy Markdown
Owner

Thanks for the contribution! This has since been fixed in master (see ded7cf9), so I'm closing this PR.

@1jehuang 1jehuang closed this Jun 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

triage: superseded PR triage: already fixed in master

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants