feat(dashboard): 供应商状态卡片新增「状态复位」按钮,并修复 reset 误清配额用量; - #279
Merged
ThreeFish-AI merged 5 commits intoSep 10, 2026
Conversation
Overview 页「供应商状态」卡片标题栏右侧新增 ⟲ 状态复位 按钮,把处于熔断 / 限流等异常态的供应商一键复位为可正常访问,无需切到终端执行 CLI。 根因修复:QuotaGuard.reset() 此前把「状态机复位」与「用量计数清零」两件正交 的事耦合在一个方法里(_entries.clear() + _total = 0)。而窗口基线 load_baseline() 的唯一调用点在进程启动的 lifespan 钩子中、运行期不再回填,导致 Dashboard 的 「1d配额 45%」徽章一经 reset 便永久停在 0%。现只保留 _transition_to(WITHIN_QUOTA) (该方法本身已清 _cap_error_active 并还原探测间隔),CLI / API / Dashboard 三条 路径经单一事实源同时修复,无需 --keep-quota 之类开关。 语义取舍为「如实」:用量确已超过 token_budget × threshold_percent 时,复位后下 一次判定立即回落 QUOTA_EXCEEDED,不伪造用量数字;真正被解开的是熔断、Rate Limit 与上游 cap 错误卡死标志。 实现要点: - 按钮调用无 body 的 POST /api/reset,服务端据此跳过重排序,故不改动供应商优先级; - 新增 .btn-card-action 卡片标题栏控件通用类,复用 .card-title 既有的 justify-content: space-between 实现右对齐,零布局 CSS 改动,并纳入 :focus-visible 焦点环列表; - 交互沿用 persistTierOrder 的 in-flight 防并发范式与 copyFromParent 的瞬时反馈 范式(复位中… → ✓ 已复位 / ✗ 失败,1.5s 还原),成功后定向重渲染供应商列表, 不必等 10 分钟轮询。 顺带修复:「限速中」徽章读 rlInfo.limited,而后端 VendorTier.get_rate_limit_info() 产出的键是 is_rate_limited,键名不匹配使该徽章从未渲染过、Rate Limit 异常态在 UI 上完全不可观测。 测试与文档:新增 7 条用例(配额用量保留、cap 卡死解开、真实超限不放行、/api/reset 无 body 与 -v 两种形态均保留用量、按钮与键名前端守卫);旧用例 test_reset_clears_all_state 曾断言 window_usage_tokens == 0,等于把缺陷固化为契约, 已改写为 test_reset_preserves_window_usage。cli-reference / api-reference 补明「仅 复位状态、不清用量」,dashboard.md 新增「供应商状态复位」小节,issue.md 归档复盘。 隔离实例(端口 3399,复刻 anthropic 熔断 + zhipu 45.6% 配额 + 限速中)实机验证: 复位后熔断转正常、限速徽章消失、配额 456,193,510 tokens 原样保留、链路顺序不变; CLI `reset -p 3399 -v anthropic,zhipu` 同样保留用量。全量 1656 条测试通过。 🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist) Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
处理 PR #279 的两条评审意见。 回归修复(评审 #1):初版修复让 reset() 无条件 _transition_to(WITHIN_QUOTA), 用量仍超阈值时下一次判定再回落 QUOTA_EXCEEDED —— 而该回环会经 _transition_to(EXCEEDED) 把 _last_probe 刷成当前时刻,令探测恢复凭空推迟一个 probe_interval(默认 300s);反复点击「状态复位」可无限饿死探测(旧实现清零 用量,不存在此回环)。现 reset() 在用量仍超阈值且已处 EXCEEDED 时不再触发任何 状态转移,仅清 _cap_error_active 并把被 Retry-After 拉长的 _effective_probe_interval 还原为默认值——即「宁可不放行,也不推迟恢复」。 补 2 条回归用例(mock 时钟锁定探测点不被推后 / cap 拉长的探测间隔被还原), 并改写 test_reset_does_not_unblock_genuinely_exhausted_quota:超阈值时复位后 状态保持 quota_exceeded(旧断言 within_quota 恰是回环存在的证据)。 误报修复(评审 #2):/api/reset 已返回 200 后,紧随的 /api/status 定向刷新若 失败(网络抖动、进程重载)会被同一个 catch 误标为「✗ 失败」,诱导用户重复 点击——复位其实已生效。现刷新失败单独吞掉,仅影响展示。 文档(cli-reference / api-reference / dashboard.md)与 CHANGELOG、issue.md 复盘同步对齐终态语义:超阈值 → 保持 EXCEEDED、清 cap 卡死标志、还原探测间隔。 全量 1658 条测试通过。 🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist) Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
删除 AGENTS.md / CLAUDE.md(Agent 工程行为准则与协作协议)及其卫星规范 docs/.agents/browser-validation.md(浏览器验证协议)与 docs/.agents/reference-specifications.md(IEEE 引用规范模板)——四者均为 面向本机 AI Agent 环境的私有治理内容,不属于开源仓库交付物。 🤖 Generated with [Claude Code](https://claude.com/claude-code), [CodeX](https://openai.com), [Gemini Code Assist](https://github.com/apps/gemini-code-assist) Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
pyproject.toml 已 bump 至 0.5.2a8,但 uv.lock 仍停留在 0.5.2a7, 补上发布时遗漏的锁文件版本同步。 🤖 Generated with [Claude Code](https://claude.com/claude-code), [CodeX](https://openai.com), [Gemini Code Assist](https://github.com/apps/gemini-code-assist) Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
随 8fc09d9 移除 AGENTS.md / browser-validation.md / reference-specifications.md 后同步收尾:knowledge-map 移除死链行与 AGENTS.md 锚定表述,合并「Agent 协作」 与「问题档案」两节(issue.md 实际位于 docs/.agents/,原 ../issue.md 链接为 死链,一并修正),路径基准勘误为 docs/.agents/;中英 README 的文档地图同步 移除 AGENTS.md 条目。全部相对链接经脚本自检可解析。 🤖 Generated with [Claude Code](https://claude.com/claude-code), [CodeX](https://openai.com), [Gemini Code Assist](https://github.com/apps/gemini-code-assist) Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
ThreeFish-AI
added a commit
that referenced
this pull request
Sep 10, 2026
版本号升级至 0.5.2,CHANGELOG 定版。本版为 v0.5.2a1–a8 预发布周期的正式收口版, 增量收敛 PR #279 三项改动:QuotaGuard.reset() 拆解「状态机复位」与「用量清零」的 耦合(复位后 1d 配额徽章不再永久停 0%);Overview 供应商状态卡片新增「状态复位」 按钮(POST /api/reset,不重排序、不清用量);修复 rlInfo.limited 与 is_rate_limited 键名不匹配致「限速中」徽章从未渲染。 🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
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.
背景
Dashboard
?tab=overview的「供应商状态」卡片能显示熔断 ×1,却没有任何复位入口,只能切到终端跑coding-proxy reset。而该指令本身存在一个非预期行为:会把 vendor 的额度用量一并清零。根因
QuotaGuard.reset()把两件正交的事耦合在了一个方法里 —— 状态机复位与用量计数清零:之所以「不自愈」:窗口基线
load_baseline()的唯一调用点在进程启动的 lifespan 钩子中,运行期不再回填,因此「1d配额 45%」徽章一经 reset 便永久停在 0% 直到重启进程。而文档口径(cli-reference.md/api-reference.md)自始至终只承诺「配额守卫状态 → WITHIN_QUOTA」,从未声明清空用量 —— 实现与契约不一致。CLI
reset只是POST /api/reset的瘦 HTTP 客户端,故 CLI 与 API 两条路径同时中招。改动
1. 根因修复(单一事实源) — 删除
_entries.clear()/_total = 0,只保留_transition_to(WITHIN_QUOTA)(该方法本身已清_cap_error_active并还原探测间隔)。CLI / API / Dashboard 三条路径同时受益,无需--keep-quota之类开关。2. 复位按钮 — 「供应商状态」标题栏右侧新增
⟲ 状态复位,调用无 body 的POST /api/reset,服务端据此跳过重排序,因此不改动供应商优先级。刻意不新建端点:修复后/api/reset无 body 形态与本需求完全同构,另起端点会制造 SSOT 断裂。3. 顺带修复 — 「限速中」徽章读
rlInfo.limited,而后端VendorTier.get_rate_limit_info()产出的键是is_rate_limited,键名不匹配使该徽章从未渲染过,Rate Limit 异常态在 UI 上完全不可观测。实现细节
.btn-card-action卡片标题栏控件通用类:.card-title本就是justify-content: space-between,追加子元素即自动右对齐,零布局 CSS 改动;并纳入:focus-visible焦点环列表。persistTierOrder的 in-flight 防并发守卫 +copyFromParent的瞬时反馈(复位中… → ✓ 已复位 / ✗ 失败,1.5s 还原);成功后定向重渲染供应商列表,不必等 10 分钟轮询。验证
隔离实例(:3399,复刻现场 anthropic 熔断 + zhipu 45.6% 配额 + 限速中)实机验证:
open(熔断 ×3)closed(正常)true(限速中)false(徽章消失)reset -p 3399 -v anthropic,zhipu(用户点名的指令形态)同样保留用量;✗ 失败+console.error)均实测通过,in-flight 期间二次点击被拦截;test_reset_clears_all_state曾断言window_usage_tokens == 0(等于把缺陷固化为契约),已改写为test_reset_preserves_window_usage;文档同步:
cli-reference/api-reference补明「仅复位状态、不清用量」,dashboard.md新增「供应商状态复位」小节,issue.md归档复盘(含防范条目:写操作测试须补「不误伤其他运行时状态」守卫断言)。