codex: mark dirty quota

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
zyycn
2026-06-15 10:05:44 +08:00
co-authored by Codex
parent da870157a1
commit 0ecd630923
3 changed files with 6 additions and 3 deletions
+4 -3
View File
@@ -479,6 +479,7 @@ Rust 证据:
- `src/codex/serving/dispatch/usage.rs:7-30` 成功 usage 会同步运行时窗口 token usage,并写入数据库累计 usage。
- `src/codex/accounts/pool.rs:403-441` acquire 时更新运行时 `request_count`、`window_request_count`、`last_used_at`,并在已知窗口长度但无窗口开始时间时推导窗口 reset。
- `src/codex/accounts/pool.rs:502-571` 运行时会在窗口到期时重置窗口计数、清除过期 quota cooldown,并推进下一窗口 reset。
- 本轮修正后,`src/codex/accounts/pool.rs` 在本地解除 `quota_limit_reached` 时会设置 `quota_verify_required=true`,避免本地窗口推断后直接把账号当成干净账号使用;`acquire_should_refresh_expired_cooldowns_before_selecting_account` 覆盖该标记。
- 本轮修正后,`src/codex/gateway/transport/usage_events.rs` 的 SSE usage 提取按原版 Responses passthrough 语义优先使用 `response.completed.response.usage`,不会把 `response.created`/中间事件中的重复 usage 累加;若没有 completed usage,才回退到最后一份可见 usage。
- `tests/codex_accounts/repository.rs:230-326` 覆盖 window usage 持久化与恢复。
- `tests/codex_serving/responses_http_sse.rs:279-350` 覆盖 passive rate-limit headers 缓存 quota、窗口字段和 cooldown。
@@ -498,18 +499,18 @@ Rust 证据:
- account retry decision 的失败请求持久 request_count 已对齐原版 release 语义:进入 429/402/403/401/model unsupported/Cloudflare 等账号 fallback/hold 处理时会写入一次无 token 的 request attempt;`v1_responses_should_mark_quota_exhausted_after_402_and_retry_next_account` 覆盖 402 fallback 失败账号计数。
- 非 account retry decision 的最终失败请求持久 request_count 已对齐到原版 release 语义:未进入账号 fallback/hold 的最终错误会写入一次无 token 的 request attempt;`v1_responses_should_record_request_count_when_5xx_retries_are_exhausted` 覆盖 5xx 内部重试 3 次但持久 request_count 只计 1 次。
- transport error 的最终失败请求持久 request_count 已有覆盖;`v1_responses_should_record_request_count_when_http_transport_fails` 证明 HTTP transport 失败也会按原版 release 语义写入一次无 token 的 request attempt。
- dirty quota verification 的状态持久化已补齐第一层:`quota_verify_required` 进入 schema、runtime `Account` 和启动恢复路径;`sqlite_schema_should_persist_quota_verify_required_flag` 与 `account_repository_should_restore_quota_verify_required_into_runtime_pool_accounts` 覆盖该行为。
- dirty quota verification 的状态层已补齐:`quota_verify_required` 进入 schema、runtime `Account` 和启动恢复路径;本地解除 quota 限制时会把账号标成 dirty,等待请求前 `/usage` 校验。
未完全对齐:
- image_generation usage 未对齐。原版记录 image input/output tokens、image request success/failure 和对应窗口计数;Rust `TokenUsage` 只有 input/output/cached/total,没有解析或持久化 `tool_usage.image_gen`。
- dirty quota verification 请求前校验尚未对齐。状态字段已经持久化,但 Rust 还未在本地 quota reset 后设置 dirty 标记,也未在下一次请求前最多 5 次调用 `/usage` 验证 dirty 账号。
- dirty quota verification 请求前校验尚未对齐。状态字段和本地 dirty 标记已经存在,但 Rust 还未在下一次请求前最多 5 次调用 `/usage` 验证 dirty 账号。
- Cookie domain/path 语义不同。原版 CookieJar 是 per-account 简单 map,不按 domain/path 过滤;Rust 按 domain/path 存储和匹配。对 `chatgpt.com` 主链路更严格,但不是原版逐字行为。
- quota window reset 细节不完全一致。原版 reset 后设置 `window_counters_reset_at` 并可能标记 quotaVerifyRequired;Rust 设置 `window_started_at`,没有 `window_counters_reset_at` 字段,也不触发 quotaVerifyRequired。
- passive header 文档提到 `x-codex-active-limit`,原版和 Rust 当前都没有实际解析该 header;如果 Codex Desktop 当前依赖 active-limit,这两边都需要重新确认。
缺口/后续动作:
- 补 `TokenUsage` 的 image_generation 字段与数据库/运行时窗口字段,或明确 Rust 不支持 image_generation usage 统计。
- 继续补 dirty quota verification 运行时机制:窗口离线 reset 后标记需要校验,请求前调用 usage endpoint,失败时保留 dirty 标记并限制放大次数。
- 继续补 dirty quota verification 请求前校验:调用 usage endpoint,失败时保留 dirty 标记并限制放大次数。
- 明确 cookie domain/path 精细化是 Rust 新架构选择还是需要回到原版 per-account map;如果保留,应补 domain/path 行为测试。
- 若 OpenAI/Codex 当前返回 `x-codex-active-limit`,补解析和 quota_json 存储;否则将其记录为双方未使用字段。
+1
View File
@@ -556,6 +556,7 @@ fn refresh_quota_window(account: &mut Account, now: DateTime<Utc>) {
}
if account.quota_limit_reached && (cooldown_expired || window_expired) {
account.quota_verify_required = true;
account.quota_limit_reached = false;
account.quota_cooldown_until = None;
}
+1
View File
@@ -159,6 +159,7 @@ fn acquire_should_refresh_expired_cooldowns_before_selecting_account() {
.unwrap();
assert!(!acquired.account.quota_limit_reached);
assert!(acquired.account.quota_verify_required);
assert!(acquired.account.quota_cooldown_until.is_none());
assert!(acquired.account.cloudflare_cooldown_until.is_none());
assert_eq!(acquired.account.window_request_count, 1);