Report Codex usage and account status on GET /v1/status - #122
Open
EmBista wants to merge 3 commits into
Open
Conversation
A client behind ChatMock has no way to see the plan usage the Codex backend
reports on every reply, nor which account the proxy is signed in as. Both are
now served by `GET /v1/status`, reusing the rate-limit snapshot the proxy
already records for the CLI's `info` command:
{
"account": {"name", "email", "plan_type", "account_id"},
"rate_limits": {"captured_at", "primary", "secondary"}
}
- The usage half is the snapshot from the most recent proxied request — like
the Codex CLI it is not a live probe, because the backend only reports usage
on replies it sends. Windows upstream did not report stay null; nothing
synthesises a value. `rate_limits` is null until a first request is proxied.
- The account half is derived from the id/access token claims the CLI's `info`
command already reads. Only display values are emitted, each omitted when its
claim is missing; no token or claim set is ever included. The claims only
change when the auth file does, so they are derived once and cached against
its mtime/size.
Also carries the `build_reasoning_param` fix from RayBytes#120 for RayBytes#116: an explicit
`reasoning.effort` upstream recognises is forwarded for upstream to judge,
instead of being rewritten to the server default because the model catalog
omitted it. The Responses route was clamping `gpt-5.6-luna` +
`effort: "none"` to `low` through the same function. The hunk is identical
to RayBytes#120's so whichever lands first, the other rebases clean.
Tests cover: the empty status before any request; a responses call feeding the
snapshot with account and both windows; partial upstream headers stored without
invention; usage recorded from a 429; account name fallback order; the account
derivation running once while the auth file is unchanged; the outgoing payload
keeping model / effort none / stream / store; and no token value in the status
body, headers, or verbose logs. The suite isolates CHATGPT_LOCAL_HOME so tests
never touch a real usage snapshot.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
EmBista
force-pushed
the
codex-status-headers
branch
from
August 25, 2026 22:20
31406bb to
998a4d0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What's missing
A client behind ChatMock is blind to two things the proxy already knows: how much of the plan's rate limit each reply consumed (the Codex backend reports
x-codex-*usage headers on every response, which ChatMock records for the CLI'sinfocommand and then keeps to itself), and which account the proxy is signed in as. Anyone building a dashboard or picking between accounts has to shell out tochatmock info— there is no way to get either over HTTP.Fix
GET /v1/statusserves both, reusing what the proxy already has — reading it costs no Codex requests:{ "account": { "name": "Ada Lovelace", "email": "ada@example.com", "plan_type": "plus", "account_id": "1f6f92a2-..." }, "rate_limits": { "captured_at": "2026-08-26T08:03:58+00:00", "primary": { "used_percent": 12.5, "window_minutes": 10080, "resets_in_seconds": 345600 }, "secondary": { "used_percent": 3.0, "window_minutes": 300, "resets_in_seconds": 1799 } } }rate_limitsis the snapshot from the most recent proxied request — the sameusage_limits.jsonthe CLI'sinfocommand prints, fed by the existingrecord_rate_limits_from_responsecalls on both the OpenAI and Ollama routes. Like the Codex CLI it is not a live probe: the backend only reports usage on replies it sends. A window upstream did not report staysnull; nothing synthesises a value.nulluntil a first request has been proxied.accountis derived from the id/access token claims the CLI'sinfocommand already reads. Only display values are emitted, each omitted when its claim is missing; no token or claim set is ever included. The claims only change when the auth file does, so they are derived once and cached against its mtime/size — steady state is oneos.statper candidate auth path.nullwhen signed out.Also carries the
build_reasoning_paramfix from #120 for #116: an explicitreasoning.effortupstream recognises is forwarded for upstream to judge, instead of being rewritten to the server default because the model catalog omitted it. The Responses route was clampinggpt-5.6-luna+effort: "none"tolowthrough the same function. The hunk is identical to #120's so whichever lands first, the other rebases clean.How to try locally
Verified against the live backend: the endpoint returned the signed-in account and the recorded windows, and the
effort: "none"request completed with the response echoing"reasoning": {"effort": "none"}.Checklist notes
nullsemantics. DOCKER.md not required: no new flags, env vars, or ports.chatmock infoprints. A test asserts no token value or claim blob appears in the status body, headers, or verbose logs.CHATGPT_LOCAL_HOMEinto a temp dir so tests never read or clobber a real usage snapshot, and a test pins that the account derivation runs once while the auth file is unchanged on disk.Disclosure
As with #118/#120/#121, AI was used to write this patch (Claude Code). The endpoint was exercised against the live backend, not just the route tests. An earlier revision of this branch delivered the same data as response headers on every
/v1/responsesreply; it was reworked to this endpoint as the less invasive shape. Happy to rename the route or adjust the payload if you'd prefer different conventions.