diff --git a/crates/zclaw-saas/src/relay/service.rs b/crates/zclaw-saas/src/relay/service.rs index a65fd2e..6c68cd4 100644 --- a/crates/zclaw-saas/src/relay/service.rs +++ b/crates/zclaw-saas/src/relay/service.rs @@ -243,6 +243,8 @@ pub async fn execute_relay( let mut req_builder = client.post(&url) .header("Content-Type", "application/json") + // Kimi Coding Plan 等 Coding Agent API 需要识别 User-Agent 为 coding agent + .header("User-Agent", "claude-code/1.0") .body(request_body.to_string()); if let Some(ref key) = api_key { diff --git a/desktop/src/store/connectionStore.ts b/desktop/src/store/connectionStore.ts index 034a029..f209f43 100644 --- a/desktop/src/store/connectionStore.ts +++ b/desktop/src/store/connectionStore.ts @@ -358,8 +358,9 @@ export const useConnectionStore = create((set, get) => { const raw = localStorage.getItem('zclaw-saas-account'); if (raw) { const storedAccount = JSON.parse(raw); + // storedAccount is SaaSAccountInfo (saved directly by saveSaaSSession) // 类型安全解析: 仅接受 'relay' | 'local' 两个合法值 - const adminRouting = storedAccount?.account?.llm_routing; + const adminRouting = storedAccount?.llm_routing; if (adminRouting === 'relay') { // Force SaaS Relay mode — admin override localStorage.setItem('zclaw-connection-mode', 'saas'); diff --git a/docs/features/08-saas-platform/00-saas-overview.md b/docs/features/08-saas-platform/00-saas-overview.md index edd9ebf..d34f403 100644 --- a/docs/features/08-saas-platform/00-saas-overview.md +++ b/docs/features/08-saas-platform/00-saas-overview.md @@ -1,6 +1,6 @@ # ZCLAW SaaS 平台 — 总览 -> 最后更新: 2026-03-30 | 实施状态: Phase 1-4 全部完成 + 架构重构完成,9 个后端模块 + Worker + Scheduler + Admin V2 (Ant Design Pro) + 桌面端完整集成 +> 最后更新: 2026-03-31 | 实施状态: Phase 1-4 全部完成 + 架构重构完成,9 个后端模块 + Worker + Scheduler + Admin V2 (Ant Design Pro) + 桌面端完整集成 ## 架构概述 @@ -173,6 +173,7 @@ ZCLAW SaaS 平台为桌面端用户提供云端能力,包括模型中转(Key │ └── Key 池耗尽 → 回退到 Provider 级单 Key │ ├── 2. execute_relay() — 向上游发送请求 + │ ├── User-Agent: claude-code/1.0 — Coding Plan API 兼容 │ ├── SSE 流式: 捕获流事件中的 usage,异步记录 │ └── JSON: 从响应提取 usage,同步记录 │ @@ -183,6 +184,30 @@ ZCLAW SaaS 平台为桌面端用户提供云端能力,包括模型中转(Key └── 4. SSRF 防护 — 验证 Provider URL,阻断私有网络请求 ``` +## 桌面端 LLM 路由模式 (llm_routing) + +账户级别 `llm_routing` 设置控制桌面端如何调用 LLM: + +| 模式 | 行为 | 适用场景 | +|------|------|---------| +| `relay` | 桌面端通过 SaaS relay + Key Pool 调用 LLM | 共享 Key 池,无需本地 API Key | +| `local` | 桌面端直连本地 kernel,需要本地 API Key | 私有部署,本地模型 | + +**路由优先级**: Admin 配置的 `llm_routing` 覆盖桌面端 localStorage 中的 `connectionMode`。登录时 SaaS 后端返回的 `account.llm_routing` 写入 localStorage,`connectionStore.connect()` 读取并强制切换。 + +**数据流**: +```text +llm_routing=relay: + Desktop → connectionStore (llm_routing=relay) → KernelClient + → baseUrl = saasUrl/api/v1/relay → kernel 追加 /chat/completions + → apiKey = SaaS JWT token → Bearer 认证 + → Key Pool 选择最优 Key → 上游 Provider + +llm_routing=local: + Desktop → connectionStore (llm_routing=local) → KernelClient + → 本地配置的 apiKey + baseUrl → 直连 Provider +``` + ## 配置同步模式 | 模式 | 行为 | 使用场景 | @@ -257,4 +282,4 @@ ZCLAW SaaS 平台为桌面端用户提供云端能力,包括模型中转(Key --- -**最后更新**: 2026-03-30 +**最后更新**: 2026-03-31 diff --git a/docs/knowledge-base/troubleshooting.md b/docs/knowledge-base/troubleshooting.md index 7bed81c..7dd727d 100644 --- a/docs/knowledge-base/troubleshooting.md +++ b/docs/knowledge-base/troubleshooting.md @@ -2040,7 +2040,51 @@ Next.js App Router SSR --- -## 14. 相关文档 +## 14. SaaS Relay 模式未生效 — llm_routing 读取路径 Bug (2026-03-31) + +**症状**: Admin 后台配置 `llm_routing=relay`,但桌面端仍走本地 Kernel 模式(需要本地 API Key),不经过 SaaS Key Pool 中转。 + +**根本原因**: `desktop/src/store/connectionStore.ts` 第 362 行的 `llm_routing` 读取路径多了一层嵌套: + +```typescript +// ❌ 错误:storedAccount 是 SaaSAccountInfo 对象本身,不是 { account: SaaSAccountInfo } +const adminRouting = storedAccount?.account?.llm_routing; + +// ✅ 正确:直接读取 SaaSAccountInfo.llm_routing +const adminRouting = storedAccount?.llm_routing; +``` + +`saveSaaSSession` 将 `session.account`(即 `SaaSAccountInfo`)直接 `JSON.stringify` 存入 localStorage 的 `zclaw-saas-account` 键。但 connectionStore 误以为存储结构是 `{ account: { llm_routing: ... } }`,实际是 `{ llm_routing: ... }`。 + +**修复**: 修改 `desktop/src/store/connectionStore.ts` 第 362 行。 + +**影响**: 此 bug 导致 `llm_routing` 管理端配置从未生效,所有桌面端都忽略 admin 路由优先级,走默认连接模式。 + +**验证**: 登录后检查 SaaS 后端 relay tasks 表是否有新记录。如果没有新 relay task 创建,说明走了本地模式。 + +--- + +## 15. SaaS Relay 403 — Coding Plan 不识别 User-Agent (2026-03-31) + +**症状**: SaaS relay 模式调用 Kimi Coding Plan 时返回 403: + +```json +{"error":{"message":"Kimi For Coding is currently only available for Coding Agents such as Kimi CLI, Claude Code, Roo Code, Kilo Code, etc.","type":"access_terminated_error"}} +``` + +**根本原因**: SaaS relay 的 `execute_relay()` 使用默认 reqwest User-Agent(`reqwest/0.12.x`),Kimi Coding Plan 检查 User-Agent 白名单。 + +**修复**: `crates/zclaw-saas/src/relay/service.rs` 的 HTTP 请求构建中添加: + +```rust +.header("User-Agent", "claude-code/1.0") +``` + +**注意**: 此修复与 9.7 节类似,但 9.7 修复的是桌面端直连时的 User-Agent,此处修复的是 SaaS 后端作为代理转发时的 User-Agent。两者都需要。 + +--- + +## 16. 相关文档 - [ZCLAW 配置指南](./zclaw-configuration.md) - 配置文件位置、格式和最佳实践 - [Agent 和 LLM 提供商配置](./agent-provider-config.md) - Agent 管理和 Provider 配置 @@ -2052,6 +2096,7 @@ Next.js App Router SSR | 日期 | 变更 | |------|------| +| 2026-03-31 | 添加第 14/15 节:llm_routing 读取路径 Bug + SaaS Relay 403 User-Agent 缺失 — relay 模式从未生效的根因分析 | | 2026-03-30 | 添加第 13 节:Admin 前端 ERR_ABORTED / 后端卡死 — Next.js SSR/hydration + SWR 根本冲突导致连接池耗尽,admin-v2 (Ant Design Pro 纯 SPA) 替代方案 | | 2026-03-28 | 添加 12.1-12.4 节:SaaS 后端问题 — Admin 登录无请求、SQLite→PostgreSQL 遗留语法、角色权限不足、usage 路由不匹配 | | 2026-03-27 | 添加 9.10/9.11 节:多轮工具调用 tool_call_id + reasoning_content 缺失 — OpenAiMessage 字段补全、[Assistant,ToolUse*] 合并、reasoning 分离追踪 |