4.9 KiB
Plan: rewrite token selection around a simple disk-first state model
Goal
Throw away the current layered selection/cooldown/state model and replace it with a small implementation that:
- reads the main JSON file on every
/tokenrequest - keeps only the minimum necessary account fields on disk
- decides from file state first
- refreshes usage only when missing or stale
- validates the selected token before returning it
- moves invalid accounts to
failed.json - does not touch the helper scripts in this pass
Required file model
Main state file
accounts.json
{
"active_account": "user@example.com",
"accounts": [
{
"email": "user@example.com",
"access_token": "...",
"refresh_token": "...",
"token_refresh_at": 1710000000,
"usage": {
"primary": {
"used_percent": 72,
"reset_at": 1710018000
},
"secondary": {
"used_percent": 18,
"reset_at": 1710600000
}
},
"usage_checked_at": 1710000000,
"disabled": false
}
]
}
Only these fields should exist for account state.
Failed state file
failed.json
{
"accounts": [
{
"email": "bad@example.com",
"access_token": "...",
"refresh_token": "...",
"token_refresh_at": 1710000000,
"usage": {
"primary": {
"used_percent": 100,
"reset_at": 1710018000
},
"secondary": {
"used_percent": 100,
"reset_at": 1710600000
}
},
"usage_checked_at": 1710000000,
"disabled": false
}
]
}
Top-level must contain only accounts.
Selection rules
Active account first
For each /token request:
- Read
accounts.jsonfresh from disk. - Resolve
active_accountby email. - Evaluate active first.
When an account is usable
An account is usable when:
disabled == falsesecondary.used_percent < 100primary.used_percent < GIBBY_EXHAUSTED_USAGE_THRESHOLD
Default threshold remains 95.
Usage freshness
Usage must be refreshed only when missing or stale.
Add env:
GIBBY_USAGE_STALE_SECONDS, default3600
Usage is stale when:
usageis missingusage_checked_atis missingnow - usage_checked_at > GIBBY_USAGE_STALE_SECONDS
If active account usage is stale or missing, refresh usage for that account before deciding if it is usable.
Fallback selection
If active account cannot be used, choose the next account by:
- filtering to usable accounts
- sorting by highest primary
used_percent - using file order as the tie-breaker
If a new account is chosen, write its email into active_account in accounts.json.
Token flow
For the chosen account:
- Ensure token is fresh enough.
- If
token_refresh_atsays refresh is needed, refresh token and persist new values. - After selection decisions are finished and the token is ready, validate it by calling:
https://chatgpt.com/backend-api/codex/models
- Only return the token if validation returns
200.
Invalid account handling
If refresh, usage auth, or final validation shows the token/account is invalid:
- Read current main state.
- Remove that full account object from
accounts.json. - Append the same full account object to
failed.json.accounts. - If it was the active account, clear
active_accountbefore reselection. - Persist both files atomically.
No failed.txt in the rewritten core flow.
Files to rewrite
/home/wzray/AI/gibby/src/gibby/settings.py- keep only env needed for the new flow
/home/wzray/AI/gibby/src/gibby/store.py- rewrite as simple JSON read/write helpers for
accounts.jsonandfailed.json
- rewrite as simple JSON read/write helpers for
/home/wzray/AI/gibby/src/gibby/client.py- keep only token refresh, usage fetch, and token validation calls
/home/wzray/AI/gibby/src/gibby/manager.py- rewrite into one small service for
/token
- rewrite into one small service for
/home/wzray/AI/gibby/src/gibby/app.py- keep thin FastAPI wiring for
/healthand/token
- keep thin FastAPI wiring for
Files to remove or stop using
/home/wzray/AI/gibby/src/gibby/models.py/home/wzray/AI/gibby/src/gibby/account_ops.py
Their logic should be folded into the new minimal data model and service flow instead of preserved.
Out of scope for this pass
- do not touch
scripts/oauth_helper.py - do not touch
scripts/refresh_limits.py - do not preserve old cooldown, failed.txt, dual-state, or derived snapshot machinery unless absolutely required to keep app booting during rewrite
Verification
uv run pytest -q- API tests for:
/healthreturnsok/tokenreturns503when file has no usable accounts/tokenprefers active account when usable/tokenrereads the file between requests- stale usage triggers a refresh before decision
- fresh usage skips refresh
- invalid token moves full account object to
failed.json - fallback chooses highest primary usage among usable non-disabled accounts
- direct file tests for exact
accounts.jsonandfailed.jsonschema