mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-06 20:02:49 +08:00
解决用户反馈的两个问题:晴辰助手设置里"测试"按钮在某些 provider
(如 gpt.qt.cool)上显示 Response Status: 0、Body 空,以及只能配
一个模型、挂了就没法用。
## 1. 测试按钮 status 0 根因 & 修复
**根因**:Tauri 桌面端以前走 webview 的 `fetch()` 直打外部 API,
受 Chromium 网络栈限制 —— 某些 provider 的 HTTP/2 分块编码、TLS
握手、CORS 预检、或特殊响应头会被静默拒绝并抛 TypeError: Failed
to fetch,前端 catch 后把 `respStatus` 写死 0、`respBody` 空。这
不是 provider 的问题,也不是 key 的问题,是 Chromium net stack 的
兼容性问题。
**修复**:新增 Rust 命令 `test_model_verbose`(基于已有的 reqwest
HTTP 客户端),返回结构化 JSON
`{success, status, reqUrl, reqBody, respBody, reply, error,
elapsedMs, usedApi}`。
前端测试按钮无论 Tauri/Web 模式,一律调 `api.testModelVerbose()`:
- Tauri → `invoke('test_model_verbose')` → 走原生 reqwest
- Web → `fetch('/__api/test_model_verbose')` → 走 dev-api.js 服务端
fetch
这样绕过了 webview net stack 所有兼容性陷阱,拿到的永远是真实
HTTP status(含 401/429/5xx)和原始 body,debug 面板展示完整信息。
相比旧的 `test_model` 命令,`test_model_verbose` 不会因 400/422/429
就吞错误返回 "连接正常",而是如实回传,便于用户排查。
## 2. 备用模型组 failover(参考 OpenClaw)
**新增配置**:`_config.fallbackModels: Array<{label, baseUrl, apiKey,
model, apiType, enabled}>`,存在 localStorage 里。
**callAI 改造**:
- 旧的 `callAI` 改名为 `_callAIOnce`,保持不变
- 新增 `callAIWithSlot(slot, messages, onChunk)`:临时把 slot 注入
到 `_config`,调 `_callAIOnce`,finally 恢复(单线程安全,因为
`_isStreaming` 防并发)
- 新的 `callAI`:`buildActiveSlots()` 收集主模型 + 启用且配置完整
的 fallback,按序尝试
- 成功 → return
- `AbortError`(用户中止)→ 直抛,不 failover
- 鉴权错误 401/403/`unauthorized`/`invalid api key` → 直抛,不
failover(切也白切)
- 其他可重试错误(网络/超时/5xx/429/400 请求错/模型不存在)→ 在
聊天里插入 `⚠ 模型「X」失败,切换到备用「Y」` 引用块,继续下
一个 slot
- 全部 slot 都失败 → 抛最后一个错误,触发既有 retry bar + circuit
breaker 流程
**UI**:设置面板 API 标签页,在晴辰云 promo 卡片下方新增一个默认
折叠的 `<details>` 区块"备用模型组":
- 顶部 summary 显示启用数量 + 折叠箭头
- 每张卡片:label / baseUrl / apiType / apiKey / model(紧凑 2 列
栅格)+ enabled 开关 + 删除按钮
- 顶部 "添加备用模型" 按钮:默认继承主模型的 apiType,减少配置项
- 编辑态用 fallbackDrafts(深拷贝),保存按钮才过滤空卡片写回
`_config.fallbackModels`
- 单个 input 变化时只同步 drafts + 更新计数,不重渲染列表(保持
输入框焦点)
**文件改动**:
- `src-tauri/src/commands/config.rs`:+175 行 `test_model_verbose`
- `src-tauri/src/lib.rs`:注册新命令
- `src/lib/tauri-api.js`:+1 行 `testModelVerbose` 封装
- `scripts/dev-api.js`:+75 行 Web 模式 test_model_verbose handler
- `src/pages/assistant.js`:
- `loadConfig`: 新增 `fallbackModels = []` 默认值
- `callAI` 重构为 failover loop(+80 行)
- 测试按钮:移除 90 行的 webview fetch 双分支,统一调 verbose
API(净减 ~60 行)
- `showSettings`: 新增备用模型 UI + 事件绑定(+85 行)
- 保存按钮:收集 fallbackDrafts 写回 _config
- `src/locales/modules/assistant.js`:11 语言翻译(slotPrimary /
failoverNotice / fallbackModelsTitle / fallbackModelsDesc /
fallbackEnabledSuffix / fallbackEmpty / fallbackAdd /
fallbackRemove / fallbackEnabled / placeholders)
## 验证
- `npm run build` 通过(assistant chunk 149.85 → 153.98 kB)
- `cargo fmt --check` 通过
- `cargo clippy --all-targets -- -D warnings` 通过
- 向后兼容:旧用户的 `localStorage` 无 `fallbackModels` 字段,
loadConfig 会初始化空数组,既有行为不变
Refs: 模型兼容性优化 + 多模型 failover 需求