【免费下载链接】opencodex
Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code
本文基于 opencodex 仓库 devlog 中WP070(PR #139 quota rows and usage UI)的实现规划,结合gui/src/components/QuotaBars.tsx、gui/src/codex-quota-utils.ts、gui/src/styles/provider-quota.css与tests/gui/quota-bars-rows.test.ts等源码,完整讲解配额条组件从"单条模糊进度条"重构为"带来源与重置元数据的有界行模型"的过程:包括基于 RAW 窗口标识的排序、计划归一化、未来导向的本地化重置文案,以及对应的测试与验证方法。读者读完可获得该功能模块完整的实现原理与可复现的验证命令。
背景:WP020 配额合约与旧 QuotaBars 的缺陷
WP070 是 PR139 栈式改造中的一个子任务,其上级工作项(WP020)为配额数据定义了归一化后的AccountQuota合约(见 codex-quota-utils.ts),其中新增了fiveHourPercent/fiveHourResetAt字段(Codex 账户 API 与本地快照对同一五小时突发窗口的别名short*被合并)。当时这两个字段"没有消费者"——WP070 的buildQuotaRows正是这个迟到的消费者,与 WP020 审计时的预测一致。
旧版 QuotaBars 仅 76 行,渲染单条横条与含义模糊的重置信息;WP070 将其重写为约 300 行的组件,遵循既定决策:渲染 WP020 归一化合约提供的内容,UI 层不做业务判断。改造的"前后对比"是:
- Before:单条 bar / 歧义的重置时间;
- After:有界的行模型(bounded row model),每行携带窗口来源(windowKey / customLabel)与重置元数据(resetAt)。
文档中还记录了一个工程约束:本次提交(P-amendment,2026-07-17)直接落在dev@daa4669b上,且本次改动的关注范围(Ledger scope)严格限定为QuotaBars.tsx+ 配额重置/信用测试;Providers.tsx中配额抓取相关代码归属 WP040/WP090,本子任务不触碰。
buildQuotaRows:先按 RAW 标识排序,再做本地化
重写后的核心是buildQuotaRows(QuotaBars.tsx)。它把归一化后的配额拆解为一组有排名的QuotaBarRow,再按排名输出,QuotaBarRow定义如下:
export type QuotaWindowKey = "fiveHour" | "weekly" | "monthly"; export type QuotaBarRow = { windowKey?: QuotaWindowKey; customLabel?: string; label: string; // 本地化后的展示标签 limitLabel: string; // 本地化后的限额标签 percent: number; resetAt?: number; };A-audit 修正:Rank BEFORE localization
初版源码存在一个易碎的缺陷:quotaWindowRank对翻译后的标签做字符串匹配(例如猜测 "5-stunden"、"자사"、"官方" 这类译文),一旦某个 locale 改文案就会失效。审计(claude-fable-5)要求修正为两步流程:
- 用 RAW 窗口身份计算排名——五小时/周/月三个标准槽位拥有内在排名;自定义窗口按其 RAW 线上标签排名(
"5h"→0、"First-party models"→2、"API usage"→3、"Total subscription credits"→4.5,其余→5); - 再对展示标签做本地化——彻底丢弃基于已翻译字符串的启发式判断。
源码中的rawCustomWindowRank(QuotaBars.tsx)与localizeCustomQuotaLabel(L55-L66)正是这一决策的直接体现。排序逻辑上,"窗口越短越靠前",因此典型顺序为:5 小时 → 每周 → cursor 一方模型 → cursor API → 每月 → 订阅信用额度 → 其余自定义窗口。
五大行来源与去重
buildQuotaRows依次处理五类来源:
- fiveHour 行(rank 0):当
fiveHourPercent为 number 时输出,标签来自codexAuth.fiveHour,限额来自quota.fiveHourLimit; - weekly 行(rank 1):
weeklyPercent存在时输出; - monthly 行(rank 4):
monthlyPercent存在时输出; - customWindows 行:遍历归一化合约中的
customWindows,canonicalCustomWindowLabel把" TOTAL SUBSCRIPTION CREDITS "这类带空白/大小写差异的原始标签规整为规范身份"Total subscription credits"; - creditsUsd 行:当存在
creditsUsd.percent且自定义窗口没有包含规范化的订阅信用额度标签时,补充渲染订阅信用额度行(rank 4.5,位于 monthly 之后、其余自定义窗口之前),resetAt取creditsUsd.expiresAt。
订阅信用的"规范化身份"(SUBSCRIPTION_CREDITS_LABEL)还用于两处覆盖判定:isCustomQuotaWindowIncomplete(L43-L53)让 overview 视图的"部分覆盖"徽标(quota-window-partial)能跨原始标签匹配;maxQuotaUtilisation则保证直接信用与自定义窗口不重复计入。
计划归一化:normalizeQuotaForPlan
buildQuotaRows第一步调用normalizeQuotaForPlan(codex-quota-utils.ts):
- 普通计划:把
shortPercent/shortResetAt别名折叠为fiveHourPercent/fiveHourResetAt后原样返回,五小时行正常渲染; - 30 天计划(
plan为"go"或"free",见isThirtyDayOnlyPlan):有意剥离五小时与周窗口,仅保留monthlyPercent/monthlyResetAt、creditsUsd、resetCredits与updatedAt——因此三十天计划不显示五小时行是正确的预期行为(测试 quota-bars-rows.test.ts 验证了"go"/"free"下仅输出 monthly 与订阅信用两行)。
maxQuotaUtilisation:跨窗口最大利用率
maxQuotaUtilisation(QuotaBars.tsx)供 WP090 的提供商排序消费:取 fiveHour/weekly/monthly 与全部自定义窗口(以及未重复的creditsUsd.percent)的最大值,用于按"紧迫程度"给提供商排序。混合/缺省值语义:
- 配额为
null或空对象 → 返回-1(表示不可用); - 订阅信用窗口已以自定义窗口形式出现时,直接
creditsUsd不计入重复值(测试见 L200-L208)。
展示层细节:最小可见宽度、告警与耗尽阈值
三个展示级函数(均只影响显示、不涉及路由,属审计中明确记录的有依据判断 TAKEs):
| 函数 | 行为 | 阈值/边界 | 设计意图 |
|---|---|---|---|
barWidth | 钳制在 0–100,<=0返回 0,否则最低 4 | 1% → 4%(最小可见宽度) | 0–2% 的微小用量不至于隐形,但 0 保持 0 |
isQuotaExhausted | percent >= 99.5判定耗尽 | 99.5(展示层边界) | 四舍五入后 100% 不代表真耗尽,取 99.5 折衷 |
isQuotaWarn | threshold > 0 && percent >= threshold | 阈值传 0 即完全禁用告警 | 由调用方决定告警阈值 |
quotaBarTone组合二者:warn 或 exhausted 都映射为bar-warn(琥珀色),否则bar-green。测试 quota-bars-rows.test.ts 用边界用例锁定了这些规则:barWidth(1)===4、barWidth(0)===0、isQuotaExhausted(99.4)===false、isQuotaExhausted(99.5)===true、isQuotaWarn(100,0)===false、quotaBarTone(99.5,0)==="bar-warn"。
此外QuotaBars支持pending占位模式(配额仍为 null 时先保留行槽位,避免数据填充后页面下移),并区分compact(经典单行)与stacked(overview 卡片)两种布局——stacked 布局的骨架屏与完整行分别由quota-stacked-row--skeleton与quota-stacked-row驱动。
本地化:bcp47 映射与未来导向的重置文案
语言环境与键面
bcp47(QuotaBars.tsx)把内部Locale(en/de/fr/ko/zh/zh-TW/ru/ja/tr/vi)映射为Intl可用的区域标签(如zh-TW、ja-JP)。重写消费的新quota.*键(见 en.ts)包括:
- 限额标签:
quota.fiveHourLimit("5-hour limit")、quota.weeklyLimit("Weekly limit")、quota.monthlyLimit("30-day limit")、quota.cursorFirstParty("First-party models")、quota.cursorApiUsage("API usage")、quota.totalSubscriptionCredits; - 用量与状态:
quota.usedPercent("{pct}% used")、quota.limitReached("Limit reached"); - 重置文案:
quota.resetsToday、quota.resetsTomorrow、quota.resetsAt、quota.resetsRelativeMinutes、quota.resetsRelativeHours。
这些键同时维护在 en/de/fr/ja/ko/ru/tr/vi/zh/zh-TW 等多语言文件中(如 de.ts 的 "5-Stunden-Limit"、"Wochenlimit"、"Zurücksetzung heute um {time}"),文档也要求codexAuth.fiveHour等基础键同步到位。
formatResetFuture:面向未来的重置文案
formatResetFuture(QuotaBars.tsx)是重置文案的核心,采用"未来导向"措辞,分支如下:
- 明天(
dayDiff === 1)→quota.resetsTomorrow("Resets tomorrow at {time}"); - 分钟级(
minutes < 60)→quota.resetsRelativeMinutes("Resets in {n} min"); - 小时级(
hours < 12且同一天)→quota.resetsRelativeHours("Resets in {n} h"); - 今天(同一天但已过 12 小时相对窗口)→
quota.resetsToday; - 绝对日期(更远)→
quota.resetsAt,跨年时自动附上年份(date.getFullYear() !== nowDate.getFullYear()时在Intl.DateTimeFormat中加入year: "numeric"); - 已过去的时间戳回退到绝对形式;无效输入(
undefined、NaN、超出 Date 范围)返回空串。
配套的formatObservedAge(L222-L229)按分钟/小时/天分桶输出"数据已观测多久"(低于一分钟或未观测时不渲染),用于被动观测的配额(数据是真实请求的副产物、无刷新机制时),避免把几天前的读数表现得像实时数据。两个函数都先用resetDate(L442-L448)把秒级或毫秒级的 epoch 归一化,并拒绝超出 JS Date 范围的值。
测试 quota-bars-rows.test.ts 以固定时间 2026-07-17 12:00 为基准锁定了全部分支:30 分钟→minutes、3 小时→hours、当天 23:59→today、次日→tomorrow、下周→绝对日期、次年→带 2027 年份,以及秒级 epoch 自动归一化。
样式:provider-quota.css
新增样式文件 provider-quota.css 只添加配额/用量相关的选择器,不重复已有的 compact 类:
bar-warn:扁平琥珀色(非渐变——数据条用渐变色被视为"AI 味"并违反平面语法 FE-GRADIENT-02,warn 与琥珀色标签/数值色调一致);quota-row--warn/quota-val--warn/quota-stacked-used--warn:告警态文字与数值;quota-stacked*系列:overview 卡片布局、骨架屏、quota-window-partial部分覆盖徽标、quota-stacked-limit-reached耗尽提示;@container quota-compact (max-width: 440px):窄卡片内长订阅标签换行为独立行,避免溢出。
测试与验证:覆盖所有条件激活分支
文档要求新增独立的tests/gui/quota-bars-rows.test.ts(而不是塞进provider-quota.test.ts),用例清单与实现一一对应:
- five-hour-only / weekly-only:各渲染单行(L24-L29);
- 双窗口排序:5h → weekly → cursor 池 → monthly 的顺序断言(L31-L48);
- Anthropic 原始 "5h" 自定义窗口:即使与标准 weekly 并存也排第一(L50-L56);
- 不可用配额:
null/ 空对象 →[](L169-L181); - 自定义窗口本地化映射:
"First-party models"→quota.cursorFirstParty、"API usage"→quota.cursorApiUsage,未知标签保留原文并排最后; - maxQuotaUtilisation:混合/缺省值、订阅信用去重、null → -1;
- formatResetFuture 分支:today/tomorrow/minutes/hours/date+year/past/invalid 全分支;
- 边界补充(按审计要求):
percent 0 → barWidth 0、percent 1 → min 4、99.5边界、threshold 0禁用 warn、混合自定义+标准窗口排序、过去日期分支。
同时扩展tests/gui/rate-limit-reset-credits.test.ts与tests/gui/provider-quota.test.ts,覆盖缺省与双窗口用例;若 bar JSX 断言漂移,同步校验源码读取。
验证命令、GUI QA 与回滚
WP070 的完整验证流程(在仓库根目录执行):
bun test tests/gui/rate-limit-reset-credits.test.ts tests/gui/provider-quota.test.ts tests/gui/quota-bars-rows.test.ts bun run --cwd gui lint bun run --cwd gui build git diff --checkGUI 运行时 QA 按006_gui_qa_protocol.md执行,设置WP_ID=070 ROUTE='#providers',覆盖 five-hour / weekly / dual-window / unavailable 四个状态,并在evidence/WP070/<state>.md保存结果与截图 JSON(运行时 GUI QA 在 WP100 统一收口,属既定裁决)。
回滚策略:配额 UI 可整体回退,而 WP020 的线上合约保持向后兼容——即数据层不受影响,UI 层独立可逆,这是栈式改造中"显示层与合约解耦"的关键保障。
小结
WP070 展示了 opencodex 在 GUI 侧处理多供应商配额的一个完整范式:以 WP020 归一化合约(AccountQuota)为唯一数据源,UI 只负责"渲染合约提供的内容";排序基于 RAW 线上标识而非翻译文本,从而在新增 locale 或文案变更时保持稳定;重置文案采用未来导向且 bcp47 感知的措辞;告警/耗尽阈值作为显式、有记录依据的展示层判断与路由完全解耦。相关实现均可继续在 QuotaBars.tsx、codex-quota-utils.ts、provider-quota.css 与 quota-bars-rows.test.ts 中追踪。
【免费下载链接】opencodex
Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code
相关推荐
Appium Session Settings 完全指南:会话内动态调整测试行为的扩展 API
Appium Session Settings 完全指南:会话内动态调整测试行为的扩展 API 导读 Appium 提供了一套名为 Settings(设置) 的
在 Xinference 中使用 minicpm-reranker 模型进行文档重排序
在 Xinference 中使用 minicpm reranker 模型进行文档重排序 本篇技术指南聚焦开源推理框架 Xinference 中内置的 minic
模型推理服务人工智能大模型本地部署多模态语音Genkit Python 环境搭建与项目初始化实战:基于 uv 的完整配置指南
Genkit Python 环境搭建与项目初始化实战:基于 uv 的完整配置指南 本篇指南以 Genkit Python Setup https://link.
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考