test(product-suite): complete gateway integration
This commit is contained in:
@@ -455,11 +455,11 @@ data/images/<batch_id>/<slug>/<task_id>_<item_id>_new.<ext> # AI 生成的新
|
|||||||
- 进度:标题和图片两条进度分开显示;只生成标题时图片进度显示本轮未生成/0 张,并在运行日志写明本轮生成内容。
|
- 进度:标题和图片两条进度分开显示;只生成标题时图片进度显示本轮未生成/0 张,并在运行日志写明本轮生成内容。
|
||||||
- 任一组件生成后 `stage=generated`;**不设逐条人工审核阶段**。若只有标题,③可选择只更新标题;若只有封面,③可选择只更新封面,②标题状态仍为待生成,后续补标题会保留已有封面且不重复生图。双击任务弹窗查看旧封面、新封面和历史候选图;T-577 后弹窗内「重置图片」只清当前任务 `new_cover_path` 并归档旧图,不启动单条 `GenerateWorker`,用户退出后用状态筛选「待生成」批量补生成封面。②「重置生成结果」提供标题/封面/全部的多选或当前筛选范围重置,默认不删除本地新封面文件;已生成且未提交线上的新标题可在②表格本地微调。
|
- 任一组件生成后 `stage=generated`;**不设逐条人工审核阶段**。若只有标题,③可选择只更新标题;若只有封面,③可选择只更新封面,②标题状态仍为待生成,后续补标题会保留已有封面且不重复生图。双击任务弹窗查看旧封面、新封面和历史候选图;T-577 后弹窗内「重置图片」只清当前任务 `new_cover_path` 并归档旧图,不启动单条 `GenerateWorker`,用户退出后用状态筛选「待生成」批量补生成封面。②「重置生成结果」提供标题/封面/全部的多选或当前筛选范围重置,默认不删除本地新封面文件;已生成且未提交线上的新标题可在②表格本地微调。
|
||||||
- 并发数、重试、分辨率、jpg 质量、模型/Key 均来自设置(`data/config.json` 的 `ai` 段;Key 存 `data/config/cmhub.json` 或 direct 兼容清单)。T-547 后标题并发和图片并发都限制为 1..5,失败重试次数限制为 0..10;旧 `config.json` 或手工配置的超限值会在加载/保存时夹紧。设置仍只展示一个「图片并发」设置;cmhub 模式下②运行日志显示“图片并发 X,cmhub实际生图并发 Y,下载并发 Y”。
|
- 并发数、重试、分辨率、jpg 质量、模型/Key 均来自设置(`data/config.json` 的 `ai` 段;Key 存 `data/config/cmhub.json` 或 direct 兼容清单)。T-547 后标题并发和图片并发都限制为 1..5,失败重试次数限制为 0..10;旧 `config.json` 或手工配置的超限值会在加载/保存时夹紧。设置仍只展示一个「图片并发」设置;cmhub 模式下②运行日志显示“图片并发 X,cmhub实际生图并发 Y,下载并发 Y”。
|
||||||
- T-679a 后商品套图按 worker 启动时冻结的配置建立并持久化来源:默认网关 job 精确为 `cmhub/cmhub`,保留异步提交、轮询、下载、幂等键和点数账本;自定义网关 job 精确为 `direct/openai_images_edits`,走 T-678 的同步多图编辑、固定 `n=1`、无 `task_id`、无轮询和无自动重试。执行分派只读 job 持久化来源,不能受运行中设置变化影响;不匹配的来源/提供方或 direct job 带 task ID 均安全失败,避免跨来源串路。T-679b 已接通⑥新建入口与确认;T-679c 已补齐手动重试与混合来源历史展示,完整发布验收仍由 T-679 完成。
|
- T-679a 后商品套图按 worker 启动时冻结的配置建立并持久化来源:默认网关 job 精确为 `cmhub/cmhub`,保留异步提交、轮询、下载、幂等键和点数账本;自定义网关 job 精确为 `direct/openai_images_edits`,走 T-678 的同步多图编辑、固定 `n=1`、无 `task_id`、无轮询和无自动重试。执行分派只读 job 持久化来源,不能受运行中设置变化影响;不匹配的来源/提供方或 direct job 带 task ID 均安全失败,避免跨来源串路。T-679b 已接通⑥新建入口与确认,T-679c 已补齐手动重试与混合来源历史展示,T-679 已完成跨来源集成验收。
|
||||||
- T-679b 后,⑥在自定义网关下仅本地校验当前已启用图片模型的接口类型、网址、模型 ID、密钥与连接超时;合格后可新建同步 direct job,但不读取 cmhub 模型目录、余额或价格。确认框按冻结 specs 展示数量、主参考图/参考图、逐图模式、请求尺寸和近似比例,并明确“自定义网关不计点数,实际费用以服务商为准”及主体一致性限制。AI帮写始终使用 cmhub `vision_alias`,其可用性只取决于 cmhub 图片理解配置,不受当前生图 backend 影响。生成期间当前任务输入锁定,任务切换、历史、日志和已完成图片查看不阻塞。
|
- T-679b 后,⑥在自定义网关下仅本地校验当前已启用图片模型的接口类型、网址、模型 ID、密钥与连接超时;合格后可新建同步 direct job,但不读取 cmhub 模型目录、余额或价格。确认框按冻结 specs 展示数量、主参考图/参考图、逐图模式、请求尺寸和近似比例,并明确“自定义网关不计点数,实际费用以服务商为准”及主体一致性限制。AI帮写始终使用 cmhub `vision_alias`,其可用性只取决于 cmhub 图片理解配置,不受当前生图 backend 影响。生成期间当前任务输入锁定,任务切换、历史、日志和已完成图片查看不阻塞。
|
||||||
- direct job 提交前写入 `running + run_session_id`;停止只取消尚未提交的 job。同步请求已返回图片字节时,即使停止信号随后到达,仍保存资产并标成功。连接/读取超时或进程中断不自动补发、不进入 cmhub 恢复轮询,用户只能手动重新生成。应用仅在启动阶段持有单进程恢复租约时处理遗留 direct `running` job;活动会话跳过,确认陈旧的 job 标为“程序中断,无法确认生成结果,请手动重新生成”。进入项目、切换任务或刷新结果不做该清理。T-679c 后「继续查询已提交图片」严格只处理 `cmhub/cmhub + task_id`,切换⑤设置不会改变该筛选;direct 永不进入此入口。结果卡、项目历史与全局历史均以 job 的持久化来源显示“默认网关 / 自定义网关”,不读取当前设置。direct 失败只允许用户手动“重新生成”:当前设置仍为自定义网关且配置合格时,必须重新展示完整生成确认,并额外警告服务商可能已经对上次未确认请求计费、本次可能再次收费;确认后始终新建 job,绝不复用旧 job 或自动补发。
|
- direct job 提交前写入 `running + run_session_id`;停止只取消尚未提交的 job。同步请求已返回图片字节时,即使停止信号随后到达,仍保存资产并标成功。连接/读取超时或进程中断不自动补发、不进入 cmhub 恢复轮询,用户只能手动重新生成。应用仅在启动阶段持有单进程恢复租约时处理遗留 direct `running` job;活动会话跳过,确认陈旧的 job 标为“程序中断,无法确认生成结果,请手动重新生成”。进入项目、切换任务或刷新结果不做该清理。T-679c 后「继续查询已提交图片」严格只处理 `cmhub/cmhub + task_id`,切换⑤设置不会改变该筛选;direct 永不进入此入口。结果卡、项目历史与全局历史均以 job 的持久化来源显示“默认网关 / 自定义网关”,不读取当前设置。direct 失败只允许用户手动“重新生成”:当前设置仍为自定义网关且配置合格时,必须重新展示完整生成确认,并额外警告服务商可能已经对上次未确认请求计费、本次可能再次收费;确认后始终新建 job,绝不复用旧 job 或自动补发。
|
||||||
- 套图比例输出固定映射为 `1:1 -> 1024x1024`、`3:4/9:16 -> 1024x1536`、`4:3/16:9 -> 1536x1024`。`image_studio_assets.aspect_ratio` 保持用户原选项,新增 `requested_output_size`、`rendered_width`、`rendered_height` 记录请求尺寸与真实像素;旧记录保持空值,SQLite 迁移仅附加且幂等。服务返回近似比例结构化结果,T-679b 在确认界面呈现。
|
- 套图比例输出固定映射为 `1:1 -> 1024x1024`、`3:4/9:16 -> 1024x1536`、`4:3/16:9 -> 1536x1024`。`image_studio_assets.aspect_ratio` 保持用户原选项,新增 `requested_output_size`、`rendered_width`、`rendered_height` 记录请求尺寸与真实像素;旧记录保持空值,SQLite 迁移仅附加且幂等。服务返回近似比例结构化结果,T-679b 在确认界面呈现。
|
||||||
- 商品套图的生图来源在 T-679 组全部验收前不得进入发布版本;图片理解「AI帮写」固定使用 cmhub `vision_alias`,即使套图生图选择自定义网关也不改走 direct。
|
- T-679 组发布门禁已通过:同一项目可保留默认网关与自定义网关历史,worker 启动后保存⑤设置不改变本轮冻结来源,切换来源后仍可继续查询已有 cmhub task ID 且不会重新提交。图片理解「AI帮写」固定使用 cmhub `vision_alias`,即使套图生图选择自定义网关也不改走 direct。
|
||||||
- T-658a 后商品套图异步生图提交统一使用 `images` 数组,单图也使用单元素 `{"image_base64": ...}`,不再提交顶层 `image_base64`。提交层最多传 8 张本地图片,单图原文件上限10MiB、编码后总输入上限32MiB;超限在请求前以中文错误阻断,后续多图参考的资产快照和提示词语义由 T-658b/T-658c 负责。
|
- T-658a 后商品套图异步生图提交统一使用 `images` 数组,单图也使用单元素 `{"image_base64": ...}`,不再提交顶层 `image_base64`。提交层最多传 8 张本地图片,单图原文件上限10MiB、编码后总输入上限32MiB;超限在请求前以中文错误阻断,后续多图参考的资产快照和提示词语义由 T-658b/T-658c 负责。
|
||||||
- T-658b 后 `image_studio_jobs.reference_asset_ids` 以有序 JSON 图片 ID 列表冻结每个商品套图 job 的参考图;新一轮未勾选逐图主图时,第1张为主图、后续最多7张写入快照,勾选时写空数组。历史 `NULL` 行继续按单图任务处理。商品原图 UI 的第1张明确显示「主图」。内部排序由列表自行创建 `QDrag`,不得调用 `QAbstractItemView.startDrag(MoveAction)` 的源端清理;`dropEvent()` 按坐标以 `takeItem()` + `insertItem()` 唯一移动同一个项,不调用 Qt 默认 model drop,也不走“投放到图片上”的复制路径。`QDrag` 返回并清理拖拽上下文后才发送一次排序信号;结束时数量或 ID 集合异常则不写库,并从 SQLite 恢复界面。最终 ID 顺序唯一且确有变化时才由事务更新 `source_order`,排序只影响之后新建的一轮 job,不回写既有 job 快照。外部文件 URL 继续走导入。恢复或重试只读 job 快照,不回读当前原图列表;参考图资产或本地文件缺失时提交前失败,不静默减少提交数量。
|
- T-658b 后 `image_studio_jobs.reference_asset_ids` 以有序 JSON 图片 ID 列表冻结每个商品套图 job 的参考图;新一轮未勾选逐图主图时,第1张为主图、后续最多7张写入快照,勾选时写空数组。历史 `NULL` 行继续按单图任务处理。商品原图 UI 的第1张明确显示「主图」。内部排序由列表自行创建 `QDrag`,不得调用 `QAbstractItemView.startDrag(MoveAction)` 的源端清理;`dropEvent()` 按坐标以 `takeItem()` + `insertItem()` 唯一移动同一个项,不调用 Qt 默认 model drop,也不走“投放到图片上”的复制路径。`QDrag` 返回并清理拖拽上下文后才发送一次排序信号;结束时数量或 ID 集合异常则不写库,并从 SQLite 恢复界面。最终 ID 顺序唯一且确有变化时才由事务更新 `source_order`,排序只影响之后新建的一轮 job,不回写既有 job 快照。外部文件 URL 继续走导入。恢复或重试只读 job 快照,不回读当前原图列表;参考图资产或本地文件缺失时提交前失败,不静默减少提交数量。
|
||||||
- T-658d 后逐图主图 checkbox 右侧显示可换行中文 helper,说明多SKU应逐图生成、同商品多角度应作为参考图一同提交。常规生成确认框从最终 specs 计算主图/参考图数量;未开启逐图主图且可用原图超过8张时,明确第1张主图加前7张参考图的上限和忽略数量。确认总点数仍只按 job 请求数计算,不因单请求图片数量重复计算。
|
- T-658d 后逐图主图 checkbox 右侧显示可换行中文 helper,说明多SKU应逐图生成、同商品多角度应作为参考图一同提交。常规生成确认框从最终 specs 计算主图/参考图数量;未开启逐图主图且可用原图超过8张时,明确第1张主图加前7张参考图的上限和忽略数量。确认总点数仍只按 job 请求数计算,不因单请求图片数量重复计算。
|
||||||
|
|||||||
@@ -449,6 +449,7 @@ export_generation_round(project_id, generation_round_key, parent_dir, path=None,
|
|||||||
- 原图下载走 `image_studio_images` 的公网 URL、大小、Content-Type、重定向和 PIL 解码校验;只在用户单击时落盘。
|
- 原图下载走 `image_studio_images` 的公网 URL、大小、Content-Type、重定向和 PIL 解码校验;只在用户单击时落盘。
|
||||||
- `remove_original_assets_if_unused()` 会先校验整批原图的项目归属、资产类型、终选直接引用,以及 job 的 `source_asset_id/reference_asset_ids` 快照;运行中或仍可 `resume/regenerate` 的任务阻断删除,`succeeded + recovery_action=none` 等不可再执行的历史 job 不阻断。参考图快照无效时保守阻断。删除在单个事务中完成并连续重排 `source_order`;历史 job/输出 asset 的外键关联按 SQLite 置空,状态、提示词、轮次和生成结果保留。任一图片不可删除时整批不变,本地源文件和蝦皮线上图片始终保留。
|
- `remove_original_assets_if_unused()` 会先校验整批原图的项目归属、资产类型、终选直接引用,以及 job 的 `source_asset_id/reference_asset_ids` 快照;运行中或仍可 `resume/regenerate` 的任务阻断删除,`succeeded + recovery_action=none` 等不可再执行的历史 job 不阻断。参考图快照无效时保守阻断。删除在单个事务中完成并连续重排 `source_order`;历史 job/输出 asset 的外键关联按 SQLite 置空,状态、提示词、轮次和生成结果保留。任一图片不可删除时整批不变,本地源文件和蝦皮线上图片始终保留。
|
||||||
- T-679a 后,商品套图 job 固定持久化来源,不以运行时设置反推:默认网关为 `generation_source="cmhub"`、`provider="cmhub"`,沿用托管异步 `task_key/task_id/status/call_id/points_cost/points_balance` 及 submit/poll/download;自定义网关为 `generation_source="direct"`、`provider="openai_images_edits"`,`task_id` 必须为空,复用 T-678 的同步多图编辑请求,固定 `n=1`,收到图片字节即保存本地资产,不轮询、不写远程 URL、不自动重试。任何来源/提供方组合不精确匹配的 job 都拒绝执行,不能借 task ID 串到 cmhub。
|
- T-679a 后,商品套图 job 固定持久化来源,不以运行时设置反推:默认网关为 `generation_source="cmhub"`、`provider="cmhub"`,沿用托管异步 `task_key/task_id/status/call_id/points_cost/points_balance` 及 submit/poll/download;自定义网关为 `generation_source="direct"`、`provider="openai_images_edits"`,`task_id` 必须为空,复用 T-678 的同步多图编辑请求,固定 `n=1`,收到图片字节即保存本地资产,不轮询、不写远程 URL、不自动重试。任何来源/提供方组合不精确匹配的 job 都拒绝执行,不能借 task ID 串到 cmhub。
|
||||||
|
- T-679 集成验收确认 `ProductSuiteGenerateWorker` 在构造时冻结来源、模型和密钥快照;之后保存⑤设置只影响新 worker。同一项目的 direct 新 job 与已有 cmhub task ID 可并存,`resume_image_jobs()` 仍只读取 `cmhub/cmhub + task_id`,不会把 direct job 送入轮询或把旧任务重新提交。
|
||||||
- direct job 进入 `running` 时保存仅用于本次进程的 `run_session_id`。只有应用启动阶段持有本地单进程恢复租约时,才将不属于活动会话的遗留 direct `running` job 标为失败,提示“程序中断,无法确认生成结果,请手动重新生成”;切换项目、刷新和恢复 cmhub 任务绝不触发该清理。同步请求返回图片后即使用户刚点击停止,也必须保存为成功;停止只取消未开始 job。连接/读取超时或进程中断的 direct job 不自动补发、不进入继续查询。
|
- direct job 进入 `running` 时保存仅用于本次进程的 `run_session_id`。只有应用启动阶段持有本地单进程恢复租约时,才将不属于活动会话的遗留 direct `running` job 标为失败,提示“程序中断,无法确认生成结果,请手动重新生成”;切换项目、刷新和恢复 cmhub 任务绝不触发该清理。同步请求返回图片后即使用户刚点击停止,也必须保存为成功;停止只取消未开始 job。连接/读取超时或进程中断的 direct job 不自动补发、不进入继续查询。
|
||||||
- `image_studio_assets.aspect_ratio` 始终保存用户选择值;新生成资产另保存可空的 `requested_output_size`、`rendered_width`、`rendered_height`。比例映射为 `1:1 -> 1024x1024`、`3:4/9:16 -> 1024x1536`、`4:3/16:9 -> 1536x1024`,后四种由运行结果返回近似比例标记。旧资产字段保持 `NULL`;初始化迁移只附加字段且可重复执行。
|
- `image_studio_assets.aspect_ratio` 始终保存用户选择值;新生成资产另保存可空的 `requested_output_size`、`rendered_width`、`rendered_height`。比例映射为 `1:1 -> 1024x1024`、`3:4/9:16 -> 1024x1536`、`4:3/16:9 -> 1536x1024`,后四种由运行结果返回近似比例标记。旧资产字段保持 `NULL`;初始化迁移只附加字段且可重复执行。
|
||||||
- T-658a 后商品套图异步提交的图片字段统一为 `images` 数组(每项仅含本地编码的 `image_base64`),单图不再保留顶层 `image_base64` 兼容字段。提交层限制最多8张、单图原文件10MiB、编码后总输入32MiB;参考图快照字段由 T-658b 扩展。
|
- T-658a 后商品套图异步提交的图片字段统一为 `images` 数组(每项仅含本地编码的 `image_base64`),单图不再保留顶层 `image_base64` 兼容字段。提交层限制最多8张、单图原文件10MiB、编码后总输入32MiB;参考图快照字段由 T-658b 扩展。
|
||||||
|
|||||||
@@ -188,6 +188,7 @@
|
|||||||
- 图片模型按钮显示「检查图片配置」,只在本地检查接口类型、URL、模型 ID、密钥和超时,绝不对 `/images/edits` 发送无图片的测试请求;实际可用性必须以②或后续⑥的正常生成确认判断。允许保存不完整配置。②点击生成后、自定义 worker 创建前按本轮标题/封面选择校验所需模型的启用状态、类别、地址、模型 ID、API Key 和接口类型,失败提示去⑤补齐且不发请求、不改任务状态;自定义网关不显示默认网关余额,运行日志和状态栏说明“不计点数,费用由服务商收取”。
|
- 图片模型按钮显示「检查图片配置」,只在本地检查接口类型、URL、模型 ID、密钥和超时,绝不对 `/images/edits` 发送无图片的测试请求;实际可用性必须以②或后续⑥的正常生成确认判断。允许保存不完整配置。②点击生成后、自定义 worker 创建前按本轮标题/封面选择校验所需模型的启用状态、类别、地址、模型 ID、API Key 和接口类型,失败提示去⑤补齐且不发请求、不改任务状态;自定义网关不显示默认网关余额,运行日志和状态栏说明“不计点数,费用由服务商收取”。
|
||||||
- T-679b 后,⑥商品套图在自定义网关下先本地校验已启用的图片模型、接口类型、网址、模型 ID、密钥与连接超时;不合格时阻止创建 job 并引导到⑤补齐。合格后可创建同步直连 job,确认框显示数量、主图/参考图、逐图模式、比例映射与“自定义网关不计点数,实际费用以服务商为准”,并说明首图为主要商品参考、主体一致性可能弱于默认网关;不请求 cmhub 生图模型目录、余额或价格。AI帮写与生图来源解耦,只要 cmhub 地址、API Key 和图片理解别名完整,在自定义生图网关下仍可使用。已有 `generation_source=cmhub`、`provider=cmhub` 且 `task_id` 非空的可恢复任务仍显示「继续查询已提交图片」,仅轮询、下载和保存,不新建 job 或重复扣点。所有②/⑥ worker 开始时冻结来源、实际模型和密钥的仅内存快照,保存设置不会改变正在执行的一轮。
|
- T-679b 后,⑥商品套图在自定义网关下先本地校验已启用的图片模型、接口类型、网址、模型 ID、密钥与连接超时;不合格时阻止创建 job 并引导到⑤补齐。合格后可创建同步直连 job,确认框显示数量、主图/参考图、逐图模式、比例映射与“自定义网关不计点数,实际费用以服务商为准”,并说明首图为主要商品参考、主体一致性可能弱于默认网关;不请求 cmhub 生图模型目录、余额或价格。AI帮写与生图来源解耦,只要 cmhub 地址、API Key 和图片理解别名完整,在自定义生图网关下仍可使用。已有 `generation_source=cmhub`、`provider=cmhub` 且 `task_id` 非空的可恢复任务仍显示「继续查询已提交图片」,仅轮询、下载和保存,不新建 job 或重复扣点。所有②/⑥ worker 开始时冻结来源、实际模型和密钥的仅内存快照,保存设置不会改变正在执行的一轮。
|
||||||
- T-679c 后,「继续查询已提交图片」只显示并处理 `generation_source=cmhub`、`provider=cmhub` 且 `task_id` 非空的任务,切换到自定义网关后仍可继续查询旧默认网关任务;direct job 永不进入该入口。结果卡、项目历史和全局历史固定显示 job 已保存的“默认网关 / 自定义网关”,不因⑤设置切换改写;同轮混合来源显示“默认网关、自定义网关”。direct 失败只能手动“重新生成”,并再次显示完整生成确认及“服务商可能已对上次未确认请求计费,本次可能再次收费”的默认取消警告。确认后新建 job,不自动重试或复用旧 job;默认网关历史、导出、删除撤销和继续查询保持原有行为。
|
- T-679c 后,「继续查询已提交图片」只显示并处理 `generation_source=cmhub`、`provider=cmhub` 且 `task_id` 非空的任务,切换到自定义网关后仍可继续查询旧默认网关任务;direct job 永不进入该入口。结果卡、项目历史和全局历史固定显示 job 已保存的“默认网关 / 自定义网关”,不因⑤设置切换改写;同轮混合来源显示“默认网关、自定义网关”。direct 失败只能手动“重新生成”,并再次显示完整生成确认及“服务商可能已对上次未确认请求计费,本次可能再次收费”的默认取消警告。确认后新建 job,不自动重试或复用旧 job;默认网关历史、导出、删除撤销和继续查询保持原有行为。
|
||||||
|
- T-679 集成验收后,两来源混合项目、运行中保存⑤设置和切换来源后继续查询旧默认网关任务均有跨层回归保护;来源切换只影响后续新建 worker,不改写当前运行快照、已保存 job 或历史展示。
|
||||||
- T-531 已完成:设置页任意可编辑控件变更都进入未保存状态,保存按钮旁显示“● 未保存更改”;切换到其它 Tab 或关闭窗口时弹出保存/放弃/取消。放弃会重新从本地配置文件回填控件,避免未保存的 URL/API Key 留在界面上;程序化回填、保存后重载和刷新别名填充下拉不会误触发未保存状态。
|
- T-531 已完成:设置页任意可编辑控件变更都进入未保存状态,保存按钮旁显示“● 未保存更改”;切换到其它 Tab 或关闭窗口时弹出保存/放弃/取消。放弃会重新从本地配置文件回填控件,避免未保存的 URL/API Key 留在界面上;程序化回填、保存后重载和刷新别名填充下拉不会误触发未保存状态。
|
||||||
- 自定义模型清单继续保存于 `data/config/ai_models.json`;在自定义网关面板中可通过「新增 / 重命名 / 删除」维护并立即保存,切换回默认网关不会清除这些配置。重命名会保留模型的连接参数和本地 API Key,并同步迁移 `config.json` 中标题/图片默认模型对旧名称的引用;取消、空名称或重名不会写入。
|
- 自定义模型清单继续保存于 `data/config/ai_models.json`;在自定义网关面板中可通过「新增 / 重命名 / 删除」维护并立即保存,切换回默认网关不会清除这些配置。重命名会保留模型的连接参数和本地 API Key,并同步迁移 `config.json` 中标题/图片默认模型对旧名称的引用;取消、空名称或重名不会写入。
|
||||||
- AI 生成参数:标题并发、图片并发、失败重试、分辨率、返回超时等短字段按三列排列;标题/图片并发可选 1..5,失败重试可选 0..10,旧配置超限值会自动夹紧;图片保存质量保留内部默认 90,不在普通 UI 展示。自定义模型角色选择只在自定义网关面板显示。
|
- AI 生成参数:标题并发、图片并发、失败重试、分辨率、返回超时等短字段按三列排列;标题/图片并发可选 1..5,失败重试可选 0..10,旧配置超限值会自动夹紧;图片保存质量保留内部默认 90,不在普通 UI 展示。自定义模型角色选择只在自定义网关面板显示。
|
||||||
|
|||||||
+6
-2
@@ -3,7 +3,7 @@ id: T-679
|
|||||||
title: 商品套图接入自定义网关(集成验收)
|
title: 商品套图接入自定义网关(集成验收)
|
||||||
phase: 7
|
phase: 7
|
||||||
deps: [T-679a, T-679b, T-679c]
|
deps: [T-679a, T-679b, T-679c]
|
||||||
status: TODO
|
status: DONE
|
||||||
created: 2026-07-20
|
created: 2026-07-20
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -59,4 +59,8 @@ git diff --check
|
|||||||
|
|
||||||
## 执行记录
|
## 执行记录
|
||||||
|
|
||||||
(做完在这里写:集成测试覆盖、验证命令和结果、发现的跨层问题及处理决定。)
|
- 2026-07-23:按 T-679a/T-679b/T-679c 的来源状态机、生成入口、恢复重试和历史展示逐层核对现有实现;未发现需要修改生产代码的跨层缺陷。
|
||||||
|
- 新增 worker 集成回归:同一项目保留已提交的 `cmhub/cmhub + task_id` 后启动 direct worker,再模拟用户保存⑤设置切回默认网关;本轮新 job 仍按冻结快照写为 `direct/openai_images_edits` 且不带 task ID,旧默认网关任务仍留在继续查询集合中,两种来源可在同一项目并存。
|
||||||
|
- 既有回归继续覆盖切到自定义网关后实际轮询、下载旧 cmhub 任务且不重新提交,direct 不调用 cmhub 提交/轮询/价格/余额,混合来源结果卡、项目历史和全局历史按 job 持久化来源显示,以及 direct 失败重复计费确认。
|
||||||
|
- 更新架构、路由、API 和⑥效果图索引说明,将 T-679 组发布门禁标记为已通过;本任务未改变界面布局,因此复用 T-679b/T-679c 效果图。
|
||||||
|
- 验证通过:T-679a/b/c 相关定向测试 415 项、`py -3.10 -m unittest discover -s tests`(685 项)、`py -3.10 -m ruff check app tests main.py`、`py -3.10 -m compileall app main.py`、`git diff --check`。
|
||||||
|
|||||||
+2
-2
@@ -17,8 +17,8 @@
|
|||||||
| [tab6-ai-studio-v3.svg](tab6-ai-studio-v3.svg) | AI工场(需求定稿·v3):合并提示词、完整模板 CRUD、异步任务照片池、部分导出与终选排序 |
|
| [tab6-ai-studio-v3.svg](tab6-ai-studio-v3.svg) | AI工场(需求定稿·v3):合并提示词、完整模板 CRUD、异步任务照片池、部分导出与终选排序 |
|
||||||
| [tab6-suite-package-v1.svg](tab6-suite-package-v1.svg) | 商品套图(T-622 已实现):从「虾皮圈电商图生成器」商品套餐交互迁移并替换主界面 AI工场入口;单一套图类型,多任务标签,商品原图,平台/国家/语言/比例,AI 帮写,白底/场景/卖点及自定义分类,结果历史、重试、删除撤销与生成进度 |
|
| [tab6-suite-package-v1.svg](tab6-suite-package-v1.svg) | 商品套图(T-622 已实现):从「虾皮圈电商图生成器」商品套餐交互迁移并替换主界面 AI工场入口;单一套图类型,多任务标签,商品原图,平台/国家/语言/比例,AI 帮写,白底/场景/卖点及自定义分类,结果历史、重试、删除撤销与生成进度 |
|
||||||
| [tab6-suite-prompt-dialog-T637.svg](tab6-suite-prompt-dialog-T637.svg) | 商品套图(T-637 提案):标题行「AI帮写/取消」紧随 label、最右新增「提示词设置」;提示词设置弹窗——左编辑基础模板(行序对齐调研笔记组装顺序,套图名称+补充描述、插入变量菜单、占位符校验、只读规则占位符内联)、右只读实时最终预览(预览分类下拉)、保存/恢复默认共用原子写盘;第一版不做同类多张序号/差异化 |
|
| [tab6-suite-prompt-dialog-T637.svg](tab6-suite-prompt-dialog-T637.svg) | 商品套图(T-637 提案):标题行「AI帮写/取消」紧随 label、最右新增「提示词设置」;提示词设置弹窗——左编辑基础模板(行序对齐调研笔记组装顺序,套图名称+补充描述、插入变量菜单、占位符校验、只读规则占位符内联)、右只读实时最终预览(预览分类下拉)、保存/恢复默认共用原子写盘;第一版不做同类多张序号/差异化 |
|
||||||
| [tab6-suite-gateway-T679b.svg](tab6-suite-gateway-T679b.svg) | 商品套图(T-679b 已实现):自定义网关生成确认展示主图/参考图、近似比例尺寸、服务商费用与主体一致性边界;生成中锁定当前任务输入,停止等待已发出同步请求返回,同时仍可切换其它任务与查看历史/结果 |
|
| [tab6-suite-gateway-T679b.svg](tab6-suite-gateway-T679b.svg) | 商品套图(T-679b 已实现、T-679 已验收):自定义网关生成确认展示主图/参考图、近似比例尺寸、服务商费用与主体一致性边界;生成中锁定当前任务输入,停止等待已发出同步请求返回,同时仍可切换其它任务与查看历史/结果 |
|
||||||
| [tab6-suite-recovery-T679c.svg](tab6-suite-recovery-T679c.svg) | 商品套图(T-679c 已实现):结果卡和历史按已保存 job 显示默认网关/自定义网关;默认网关任务可继续查询,直连失败图只能手动重新生成并再次确认可能重复收费 |
|
| [tab6-suite-recovery-T679c.svg](tab6-suite-recovery-T679c.svg) | 商品套图(T-679c 已实现、T-679 已验收):结果卡和历史按已保存 job 显示默认网关/自定义网关;默认网关任务可继续查询,直连失败图只能手动重新生成并再次确认可能重复收费 |
|
||||||
| [settings-gateway-radio-prototype-v1.html](settings-gateway-radio-prototype-v1.html) | 设置(T-680 已实现):在原 AI 模型配置标题下保留同行位置,仅以原生单选框替换默认网关/自定义网关的分段按钮;可在浏览器中模拟切换、待保存状态和保存,不改其它设置组件 |
|
| [settings-gateway-radio-prototype-v1.html](settings-gateway-radio-prototype-v1.html) | 设置(T-680 已实现):在原 AI 模型配置标题下保留同行位置,仅以原生单选框替换默认网关/自定义网关的分段按钮;可在浏览器中模拟切换、待保存状态和保存,不改其它设置组件 |
|
||||||
| [workbench-redesign-v1.html](workbench-redesign-v1.html) | 工作台视觉提案(HTML 可交互原型):以当前 6 Tab 信息架构为基础,统一「数据准备区→表格命令区→表格→状态反馈区→完成任务区」页面骨架;命令按作用域分区(导入/筛选/选择集/工作流完成),表格补齐当前行、选择集、三态全选与排序契约;范围确认、部分成功、取消中间态和破坏性确认按干扰层级分配到对话框、信息条与 Toast;覆盖浅色/深色/高对比度近似、键盘(Tab/方向键/F6/Ctrl+F/F5)与 Windows 断点。商品状态相关交互已对齐 T-662b/c/d、T-663~T-665、T-667 定稿(无状态列与状态下拉筛选,③无强制更新入口)。颜色 Token 取自 `app/gui/widgets.py`。使用模拟数据,不接生产接口;页脚「原型说明与验收边界」列出假设与不可仅靠 HTML 验收的项。 |
|
| [workbench-redesign-v1.html](workbench-redesign-v1.html) | 工作台视觉提案(HTML 可交互原型):以当前 6 Tab 信息架构为基础,统一「数据准备区→表格命令区→表格→状态反馈区→完成任务区」页面骨架;命令按作用域分区(导入/筛选/选择集/工作流完成),表格补齐当前行、选择集、三态全选与排序契约;范围确认、部分成功、取消中间态和破坏性确认按干扰层级分配到对话框、信息条与 Toast;覆盖浅色/深色/高对比度近似、键盘(Tab/方向键/F6/Ctrl+F/F5)与 Windows 断点。商品状态相关交互已对齐 T-662b/c/d、T-663~T-665、T-667 定稿(无状态列与状态下拉筛选,③无强制更新入口)。颜色 Token 取自 `app/gui/widgets.py`。使用模拟数据,不接生产接口;页脚「原型说明与验收边界」列出假设与不可仅靠 HTML 验收的项。 |
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ sys.path.insert(0, os.path.dirname(__file__))
|
|||||||
|
|
||||||
from _helpers import TempDirMixin
|
from _helpers import TempDirMixin
|
||||||
|
|
||||||
from app import db, image_studio
|
from app import appconfig, db, image_studio
|
||||||
from app import gui
|
from app import gui
|
||||||
|
|
||||||
if gui.QT_IMPORT_ERROR is not None:
|
if gui.QT_IMPORT_ERROR is not None:
|
||||||
@@ -18,6 +18,51 @@ from app.gui.workers import ProductSuiteGenerateWorker
|
|||||||
|
|
||||||
|
|
||||||
class ProductSuiteWorkerTests(TempDirMixin, unittest.TestCase):
|
class ProductSuiteWorkerTests(TempDirMixin, unittest.TestCase):
|
||||||
|
@staticmethod
|
||||||
|
def _direct_config(temp_dir, db_path):
|
||||||
|
models_path = os.path.join(temp_dir, "ai_models.json")
|
||||||
|
config = {
|
||||||
|
"db_path": db_path,
|
||||||
|
"ai_models_path": models_path,
|
||||||
|
"ai": {
|
||||||
|
"backend": "direct",
|
||||||
|
"default_text_model": "测试文本模型",
|
||||||
|
"default_image_model": "测试图片模型",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
appconfig.save_ai_models_config(
|
||||||
|
{
|
||||||
|
"models": [
|
||||||
|
{
|
||||||
|
"name": "测试文本模型",
|
||||||
|
"category": "text",
|
||||||
|
"enabled": True,
|
||||||
|
"url": "https://text.example.com/v1",
|
||||||
|
"model": "text-model",
|
||||||
|
"api_key": "sk-text-test",
|
||||||
|
"api_type": "chat",
|
||||||
|
"connect_timeout_seconds": 3,
|
||||||
|
"timeout_seconds": 10,
|
||||||
|
"extra_body": {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "测试图片模型",
|
||||||
|
"category": "image",
|
||||||
|
"enabled": True,
|
||||||
|
"url": "https://image.example.com/v1",
|
||||||
|
"model": "image-model",
|
||||||
|
"api_key": "sk-image-test",
|
||||||
|
"api_type": "images_edits",
|
||||||
|
"connect_timeout_seconds": 7,
|
||||||
|
"timeout_seconds": 12,
|
||||||
|
"extra_body": {},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
path=models_path,
|
||||||
|
)
|
||||||
|
return config
|
||||||
|
|
||||||
def test_generate_worker_creates_category_jobs_and_forwards_ratio(self):
|
def test_generate_worker_creates_category_jobs_and_forwards_ratio(self):
|
||||||
with self.make_temp_dir() as temp_dir:
|
with self.make_temp_dir() as temp_dir:
|
||||||
db_path = os.path.join(temp_dir, "cmshopee.db")
|
db_path = os.path.join(temp_dir, "cmshopee.db")
|
||||||
@@ -83,6 +128,98 @@ class ProductSuiteWorkerTests(TempDirMixin, unittest.TestCase):
|
|||||||
|
|
||||||
self.assert_removed(temp_dir)
|
self.assert_removed(temp_dir)
|
||||||
|
|
||||||
|
def test_worker_keeps_frozen_direct_source_in_mixed_project_after_settings_switch(self):
|
||||||
|
with self.make_temp_dir() as temp_dir:
|
||||||
|
db_path = os.path.join(temp_dir, "cmshopee.db")
|
||||||
|
db.init_db(db_path)
|
||||||
|
project = image_studio.create_or_get_project(
|
||||||
|
account_alias="alias",
|
||||||
|
account_slug="alias_slug",
|
||||||
|
item_id="51100639510",
|
||||||
|
path=db_path,
|
||||||
|
)
|
||||||
|
source = image_studio.add_asset(
|
||||||
|
project.id,
|
||||||
|
image_studio.ASSET_KIND_ORIGINAL,
|
||||||
|
local_path=os.path.join(temp_dir, "source.png"),
|
||||||
|
path=db_path,
|
||||||
|
)
|
||||||
|
submitted = image_studio.create_job(
|
||||||
|
project.id,
|
||||||
|
source_asset_id=source.id,
|
||||||
|
prompt="默认网关已提交任务",
|
||||||
|
generation_source=image_studio.GENERATION_SOURCE_CMHUB,
|
||||||
|
provider=image_studio.PROVIDER_CMHUB,
|
||||||
|
path=db_path,
|
||||||
|
)
|
||||||
|
submitted = image_studio.set_job_submitted(
|
||||||
|
submitted.id,
|
||||||
|
"cmhub-task-existing",
|
||||||
|
path=db_path,
|
||||||
|
)
|
||||||
|
config = self._direct_config(temp_dir, db_path)
|
||||||
|
worker = ProductSuiteGenerateWorker(
|
||||||
|
project.id,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"source_asset_id": source.id,
|
||||||
|
"job_type": "场景图",
|
||||||
|
"prompt": "自定义网关新任务",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
run_token="direct-frozen-run",
|
||||||
|
db_path=db_path,
|
||||||
|
config=config,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 模拟 worker 已创建后,用户在⑤设置切回默认网关并保存。
|
||||||
|
config["ai"]["backend"] = "cmhub"
|
||||||
|
with mock.patch(
|
||||||
|
"app.gui.workers.image_studio_generation.run_jobs",
|
||||||
|
return_value={
|
||||||
|
"total": 1,
|
||||||
|
"success": 0,
|
||||||
|
"failed": 0,
|
||||||
|
"cancelled": 0,
|
||||||
|
"jobs": [],
|
||||||
|
},
|
||||||
|
) as run_jobs:
|
||||||
|
worker.execute()
|
||||||
|
|
||||||
|
new_job = image_studio.get_job(worker.job_ids[0], path=db_path)
|
||||||
|
self.assertEqual(image_studio.GENERATION_SOURCE_DIRECT, new_job.generation_source)
|
||||||
|
self.assertEqual(image_studio.PROVIDER_OPENAI_IMAGES_EDITS, new_job.provider)
|
||||||
|
self.assertIsNone(new_job.task_id)
|
||||||
|
frozen_config = run_jobs.call_args.kwargs["config"]
|
||||||
|
self.assertEqual("direct", appconfig.ai_backend(frozen_config))
|
||||||
|
self.assertEqual(
|
||||||
|
"sk-image-test",
|
||||||
|
frozen_config["_cmshopee_ai_runtime"]["direct_models"][1]["api_key"],
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
[submitted.id],
|
||||||
|
[
|
||||||
|
job.id
|
||||||
|
for job in image_studio.list_resumable_jobs(
|
||||||
|
project_id=project.id,
|
||||||
|
include_failed_downloads=True,
|
||||||
|
path=db_path,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
{
|
||||||
|
image_studio.GENERATION_SOURCE_CMHUB,
|
||||||
|
image_studio.GENERATION_SOURCE_DIRECT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
job.generation_source
|
||||||
|
for job in image_studio.list_jobs(project.id, path=db_path)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assert_removed(temp_dir)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user