Files
cmshoppe/docs/tasks/T-677.md
T
2026-07-20 15:04:33 +08:00

120 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: T-677
title: ⑤生成网关来源切换(默认网关/自定义网关)
phase: 7
deps: [T-517, T-529]
status: TODO
created: 2026-07-20
---
# T-677 ⑤生成网关来源切换(默认网关/自定义网关)
## 问题 / 背景
T-529 把普通用户的 AI 后端收口为 cmhub:⑤ 隐藏「AI 后端」下拉,保存固定写 `ai.backend=cmhub`。当前生文、生图、图片理解全部只走 cmhub 托管并扣点。
现在需要给用户一条自定义(OpenAI 兼容)网关的逃生口,用于 cmhub 托管上游不稳或用户自备算力的场景。相关代码事实:
- `direct` 路径并未删除,且本身就是 OpenAI 规范:`app/ai.py` 生文走 `chat/completions`,生图支持 `api_type` = `chat` / `images_edits` / `auto`(`app/appconfig.py:41`),`appconfig` 会自动补 endpoint;配置 schema 与校验在 `app/appconfig.py`(`list_ai_models` / 模型规范化)。
- ⑤ 的自定义模型 UI(模型列表、模型详情、direct 角色选择)也都还在,只是被 `_on_backend_changed()` 无条件 `setVisible(False)` 硬藏(`app/gui/tabs/settings.py:580-584`),`backend_combo` 同样常隐。
- 因此本任务的主要工作是**恢复并规范双面板切换**,不是新建自定义配置体系。
覆盖面不对称是本任务最大的风险点:cmhub 有生文/生图/图片理解三个角色,外加 AI工场;`direct` 只有生文、生图两条路径。
- `app/ai.py` 的 `analyze_product_images()` 在非 cmhub 时直接抛错(⑥商品套图AI帮写);
- `app/image_studio_generation.py` 全文只有 cmhub 分支,其 `_runtime()` 直接取 `ai._cmhub_runtime()`,**完全不看 `ai.backend`**。
若只改 UI 不补联动,用户切到「自定义网关」后 AI工场仍会用 cmhub Key 继续跑、继续扣点——用户以为切走了而费用照扣,属于资金口径错误,必须在本任务同版本内处理。
## 方案
### 1. ⑤ 网关分区改为来源选择
- `app/gui/tabs/settings.py` 第一段 `_section_title("cmhub 网关", "settingsAiModelSectionTitle")` 文案改为「生成网关」,**保留该 section title**(⑤ 是长页面,分区标题与「角色与生成参数」「蝦皮更新执行」「基础设施」并列承担导航作用,不可直接删掉换控件)。
- 在该分区标题所在位置增加**互斥选中态**的来源选择器:「默认网关」/「自定义网关」。
- 用两个 `setCheckable(True)` 的按钮加入同一 `QButtonGroup`(`setExclusive(True)`)实现分段选择器外观,或等价的两个 `QRadioButton`;
- **不得使用普通 `QPushButton`**:这是状态选择不是动作,普通按钮无法表达"当前选中项",也无法表达"点击即生效还是保存生效";
- 两个控件各自设置 `objectName`(如 `gatewayDefaultButton` / `gatewayCustomButton`)供 GUI 测试选择。
- 选择器只改 pending 状态并切换面板显隐,**落盘发生在「保存设置」时**;选择器需接入 `_connect_dirty_signals()`,切换触发未保存提示,与⑤其他控件行为一致。载入配置时在 `_dirty_tracking_suspended()` 内设置选中态。
### 2. 恢复双面板显隐
- `_on_backend_changed()`(`app/gui/tabs/settings.py:579-587`)去掉硬编码隐藏,改为按当前来源双向显隐:
- 默认网关 → 显示 `cmhub_panel`,隐藏 `model_picker_panel`、`model_detail_section_title`、`model_detail_panel`、`direct_role_panel`;
- 自定义网关 → 相反。
- 自定义侧复用现有面板与控件,不新建:模型列表、模型详情(名称/接口地址/模型ID/API Key/api_type/超时/启用)、`direct_role_panel` 的默认文本模型与默认图像模型选择、以及现有模型测试按钮(`AIModelTestWorker`)。
- 两套配置的**测试按钮与结果标签必须分开**,自定义侧不得复用 `cmhub_result_label`。
- 别名下拉(`cmhub_title_alias_combo` / `cmhub_image_alias_combo` / `cmhub_vision_alias_combo`)属于 `cmhub_panel`,随面板整体显隐即可。
### 3. 配置读写与迁移
- 沿用现有 `ai.backend` 字段与取值 `cmhub` / `direct`(`appconfig.AI_BACKENDS`、`app/ai.py` 的 `_ai_backend()` 校验保持不变),**不新增第三种取值、不新增并行的来源字段**。
- `_collect_settings_values()` 中 `backend = "cmhub"` 的硬编码(`app/gui/tabs/settings.py:751`)改为读选择器当前值。
- **两套配置并存、切换不清空**:`data/config/cmhub.json` 与 `data/config/ai_models.json` 互不影响,来回切换后原配置仍在。
- 存量迁移天然成立:老配置 `backend=direct` 载入后选中「自定义网关」,`backend=cmhub` 或缺失按现状选中「默认网关」。
- 沿用 v3.1 约定:**允许先保存不完整配置**,②生成时再提示"去⑤补齐",切换来源时不弹阻断式校验。
### 4. 必须同版本处理的四个联动
1. **AI工场入口拦截**:`ai.backend != "cmhub"` 时,AI工场(⑥)生成入口置灰或拦截并提示「AI工场仅支持默认网关」,**禁止在自定义网关下继续用 cmhub Key 提交任务扣点**。
2. **⑥商品套图AI帮写**:`analyze_product_images()` 的现有抛错行为保持(不会静默扣费),但⑤自定义面板需增加说明行,明确「图片理解与AI工场仅默认网关支持」,避免用户切换后靠报错才发现。
3. **「批量生成前检查余额」**(`cmhub_check_balance_checkbox`):自定义网关下隐藏——点数余额是默认网关概念。
4. **点数展示文案**:②运行日志/状态栏在自定义网关下不得显示 `0 点`或空白,应显示「自定义网关(不计点数,费用由服务商收取)」;默认网关下 `points_cost` / `points_balance` 展示保持不变。
### 5. ⑤ 面板内文案收敛
⑤ 面板内用户可见文案中的 `cmhub` 字样收敛为「默认网关」口径,包括分区标题、测试按钮与结果提示、明文保存警告(`PLAINTEXT_CMHUB_API_KEY_WARNING`,`app/gui/tabs/settings.py:17`)。`app/ai.py` 中的中文错误消息(如"连接 cmhub 超时")数量多、属于另一层,本任务不改。
### 6. 效果图
按 `AGENTS.md` 约定出 `docs/ui/tab5-settings-T677.svg`(⑤ 是改版,已有 `tab5-settings.svg`),在 `docs/ui/README.md` 登记,随本任务文档一起提交。
## 验收要点
- [ ] ⑤ 生成网关分区显示互斥选中态的「默认网关」/「自定义网关」,当前选中项视觉可辨;两者均有 `objectName`。
- [ ] 切换来源立即切面板显隐并标记未保存;未点「保存设置」时不落盘,取消/重新载入后恢复原选择。
- [ ] 选默认网关时只显示 cmhub 配置(Base URL / API Key / 三个别名 / 连接超时 / 检查余额 / 测试连接·查余额);选自定义网关时只显示自定义模型列表、模型详情与默认文本/图像模型选择。
- [ ] 保存后 `config.json` 的 `ai.backend` 正确写入 `cmhub` / `direct`;`cmhub.json` 与 `ai_models.json` 在来回切换保存后内容均不丢失。
- [ ] 载入 `backend=direct` 的存量配置时选中「自定义网关」并正确回填自定义模型;缺失或 `cmhub` 时选中「默认网关」。
- [ ] 自定义网关下②生文、②生图可用;⑥AI工场入口被拦截并提示仅默认网关支持,**不会用 cmhub Key 提交任务或扣点**。
- [ ] 自定义网关下「批量生成前检查余额」不可见;②日志/状态栏显示"不计点数"口径而非 `0 点`;默认网关下点数与余额展示不回归。
- [ ] 自定义配置不完整时可保存,②生成时给出"去⑤补齐"的可读提示,不崩溃。
- [ ] ⑤ 面板内可见文案不再出现 `cmhub` 字样(`app/ai.py` 错误消息不在本次范围)。
- [ ] `tests/test_gui.py` 覆盖:选择器互斥与默认选中、面板显隐、dirty 与保存生效、存量 `backend=direct` 载入、AI工场拦截、余额控件显隐、点数文案分支;现有 backend 相关断言同步更新不回归。
- [ ] `docs/ui/tab5-settings-T677.svg` 已产出并在 `docs/ui/README.md` 登记。
## 测试与文档
- 更新 `docs/cmhub-integration-design.md`:补一条修订说明,记录 T-529 的"普通 UI 不提供后端切换"在本任务被有限放开,及自定义网关的定位与覆盖边界。
- 更新 `docs/routes.md` / `docs/api.md` 中⑤设置与生成来源的描述。
- 运行:
```bash
py -3.10 -m unittest discover -s tests -p "test_gui.py"
py -3.10 -m unittest discover -s tests
py -3.10 -m ruff check app tests main.py
py -3.10 -m compileall app main.py
git diff --check
```
## 边界(不改什么)
- 不给 AI工场(`app/image_studio_generation.py`)和图片理解(`analyze_product_images()`)新增自定义网关路径;本任务只做入口拦截与提示。
- 不实现方案中经 cmhub 代理透传的正式 BYOK(T-596 / T-599~T-602 仍按其门禁推进);本任务的自定义网关是**客户端直连的过渡形态**,正式商业版需迁移到网关代理并重新收口 direct 入口。
- 不修改 `ai_models.json` 的 schema 与 `API_TYPES` 取值,不新增 provider 适配。
- 不改 `app/ai.py` 的 cmhub 请求协议、重试与超时策略、异步 submit/poll 状态机、幂等键。
- 不改 `app/ai.py` 中文错误消息里的 `cmhub` 字样。
- 不改 SQLite schema、Chrome/CDP、Excel 回写、蝦皮更新流程。
- 不涉及卡密、设备绑定、订阅授权(见 Obsidian《卡密设备绑定与套餐授权方案》,另行拆任务)。
## 关联
- T-529:默认 cmhub 网关并隐藏 AI 后端选择(本任务有限放开其 UI 收口)。
- T-517:⑤设置分区(分区标题与布局约定)。
- T-596:AI工场自定义模型(BYOK)总设计与启动门禁(`status: BLOCKED`,正式形态,本任务不解除其门禁)。
- Obsidian《卡密设备绑定与套餐授权方案》第五节 §9、第六节 §5:正式发行版需移除普通用户可通过配置开启的 direct 直连路径。
## 执行记录
(做完在这里写:改了什么文件、跑了什么验证命令及结果、遇到的阻塞、关键决策。)