Skip to content

feat(providers): cc-switch 式供应商自动故障转移(同厂商同模型) - #385

Open
coder-hhx wants to merge 4 commits into
mainfrom
feat/provider-auto-failover
Open

feat(providers): cc-switch 式供应商自动故障转移(同厂商同模型)#385
coder-hhx wants to merge 4 commits into
mainfrom
feat/provider-auto-failover

Conversation

@coder-hhx

@coder-hhx coder-hhx commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Linked issue

Closes #384

Summary

修复自动故障转移设置无法持久化的 bug,并将故障转移重构为 cc-switch 语义的供应商级设计:

  • 持久化修复modelFailover 此前未接入任何持久化通道(SQLite 与 localStorage 均无),退出设置页重进即丢失。现已接入 localStorage 通道(与 selectedModel 同级),读/写/变更检测三处补齐。
  • 供应商级队列(cc-switch 语义):failover 只换供应商、不换模型——失败请求用当前对话的模型 ID 原样发给队列里下一个同厂商供应商;未激活该模型的供应商在本轮自动跳过。队列旧格式(供应商+模型对)自动迁移为供应商 id 并去重。
  • 运行时:按 provider::model 维度的熔断器(连续失败阈值 → 熔断 → 冷却 → 半开探测);错误分类器仅对供应商故障类错误(5xx/网络/鉴权/配额)转移,客户端请求类错误(如上下文超限)不转移;流包装器在首个可见内容前缓冲事件,丢弃的尝试对用户不可见;成功应答的供应商粘性保持并写回对话选择。
  • UI:每个厂商标签页下的故障转移卡片管理供应商队列(名称 + baseUrl 展示)与熔断参数;zh/en 文案更新;修复 {vendor} 占位符多次出现时只替换首个的问题。

Change scope

  • Modules: agent-gui / agent-gateway(web 前端)
  • Key paths:
    • crates/agent-gui/src/lib/providers/runtime/providerFailover.ts(新增:熔断器 + 错误分类 + 流包装器)
    • crates/agent-gui/src/lib/settings/{index,storage}.ts(队列模型 + 迁移 + 持久化)
    • crates/agent-gui/src/pages/chat/runtime/{providerRuntimeConfig,useSendChatTurn}.ts(每轮 failover 计划)
    • crates/agent-gui/src/lib/chat/runner/agentRunner.ts(候选装配 + withProviderFailover 接入)
    • crates/agent-gui/src/pages/settings/ProvidersSection.tsx + web 端孪生 + 双端 i18n

Screenshots / preview

设置页(Anthropic 标签)——供应商级故障转移队列,退出设置重进后配置保留:

image

实测转移过程:将 P1 供应商(RightCode sale)的 API key 置为无效后发起对话,状态栏显示「第 1 轮:RightCode(sale)· claude-fable-5 不可用,正在切换到 RightCode · claude-fable-5…」,随后由 P2 供应商以同一模型完成应答,对话选择自动切到应答的供应商。

28e03389b75e80a37b17d8fce6edf06c

Verification

  • cd crates/agent-gui && ./node_modules/.bin/tsc --noEmit ✅(web 端同样通过)
  • biome check 两端 0 error ✅
  • node --test test/settings/model-failover-normalize.test.mjs test/providers/provider-failover-plan.test.mjs test/providers/provider-failover.test.mjs — 29 项全部通过(熔断器/流包装器 16、计划构建 5、归一化+迁移 8)
  • node --test test/providers/provider-runtime-config.test.mjs test/chat/agent-runner.test.mjs — 上游相关套件 39 项通过
  • node scripts/check-mirror.mjs ✅(115 文件)
  • 真机验证:pnpm tauri dev 启动桌面端,复现原 bug 路径(退出设置→重进),配置保留;旧数据自动迁移;实际断供触发转移成功(见截图说明)

Pre-submit checklist

  • A requirement issue is linked (or this is a trivial fix that needs no issue, as explained in the summary).
  • Synced with the target branch; no merge conflicts.
  • The change is focused, with no unrelated modifications.
  • No secrets, tokens, or personal data included.
  • Docs are updated for changes affecting user behavior, deployment, or configuration.

🤖 Generated with Claude Code

…viders

Adds automatic provider failover for chat requests, configured per vendor
tab in provider settings and modeled after cc-switch's proxy-side design:

- Failover switches providers, never models: the failed request is re-sent
  to the next provider in the queue with the same model id the conversation
  was using; queued providers without that model active are skipped.
- Per-target circuit breaker (consecutive-failure threshold, cooldown with
  half-open probe) keyed by provider::model, plus an error classifier that
  only fails over on provider-fault-class errors (5xx/network/auth/quota),
  never on client-request-class errors (context overflow etc.).
- Stream wrapper buffers events until first committed content so consumers
  never see output from a discarded attempt; sticky winner keeps follow-up
  rounds on the provider that answered, and onSwitched pins the conversation
  selection to it.
- Settings: modelFailover is now persisted (it previously round-tripped
  through neither the SQLite nor the localStorage channel, so the toggle
  and queue silently reset on every settings reopen); queue entries are
  provider ids with legacy {customProviderId, model} entries migrated by
  collapsing to their provider id.
- UI: failover card under each vendor tab manages an ordered provider
  queue with breaker knobs; i18n zh/en updated, {vendor} placeholder now
  replaced at every occurrence.

Covered by unit tests for the breaker/stream wrapper, plan builder, and
settings normalization/migration; verified end-to-end in the running app.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@StackCairn
StackCairn marked this pull request as draft August 5, 2026 13:11
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

PR governance checks passed. Awaiting human review.

coder-hhx and others added 2 commits August 5, 2026 21:19
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coder-hhx
coder-hhx marked this pull request as ready for review August 5, 2026 13:27
@FlowerRealm

Copy link
Copy Markdown
Contributor

没啥希望通过, 仓库所有者表示希望更简洁的provider, 如有此需求推荐安装一个cc-switch

@FlowerRealm

Copy link
Copy Markdown
Contributor

pr可以留着, 之后有插件体系了可以放插件里

…s drawer

The failover card no longer renders under each vendor's provider list; it
now lives in the drawer behind the gear button in the providers header,
scoped to the active vendor tab. The drawer is renamed from "Custom
Settings" to "Advanced Settings" (自定义设置 → 高级设置) in zh/en on both
the GUI and WebUI ends.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

供应商自动故障转移:设置无法持久化,且队列应为供应商级(cc-switch 语义)

2 participants