fix: 修复资产/视频/三视图三处 bug + 脚本模型切 YunQi gemini

1. 视频「全部生成」串行短路:任一段提交失败就 break 致后面段不提交
   (4 个框只跑 2 个)→ 改 Promise.allSettled 并发,单段失败不拖累其余,
   部分失败也刷新并如实报告。(App.tsx)
2. 基础资产/三视图 AI 出图偶发失败「生成不出来」:中转站瞬时抖动
   (网络/5xx/429/空返回)一次失败即退费 → 加退避重试 3 次,永久错误
   (4xx 内容违规)仍立即退费。(services.py run_base_asset_task/run_triview_task)
3. 商品三视图无法用已有素材:商品卡加「使用已有素材」,选商品已有图 /
   上传本地图直接 attach 成三视图(不走平台 AI 生成、不计费)。(pipeline.tsx)
4. 脚本助手:tokenssr 欠费致 GPT-5.5/Gemini 全挂 → 文本模型切 YunQi。
   实测 YunQi gpt-5.5 频道扛不住重型结构化任务(skill 全提示词仅 1/5 成功、
   常吐空无 JSON),gemini-3.1-pro 5/5 稳定 → 默认改 gemini,停用 gpt-5.5。
   按「一 provider 一 key」拆 yunqi_gpt / yunqi_gemini 两 provider。
   (.env / settings / 迁移 0017+0018)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
zyc
2026-06-25 12:00:03 +08:00
co-authored by Claude Opus 4.8
parent 8222a46202
commit dea9dbb3ed
7 changed files with 274 additions and 20 deletions
+19 -6
View File
@@ -916,7 +916,7 @@ export function App() {
onAdoptBaseAsset={(groupId, assetId) => action(() => api.adoptBaseAsset(pipelineProject.id, { group_id: groupId, asset_id: assetId }), "已采用该候选")}
onSetAdoptState={(groupId, state) => action(() => api.setBaseAssetAdopt(pipelineProject.id, { group_id: groupId, state }), state === "adopted" ? "已采用" : "已标为未采用")}
onDeleteBaseAsset={(groupId) => action(() => api.deleteBaseAsset(pipelineProject.id, groupId), "已删除")}
onAttachBaseAsset={(target, assetId) => action(() => api.attachBaseAsset(pipelineProject.id, { ...target, asset_id: assetId }), "已替换为所选演员")}
onAttachBaseAsset={(target, assetId) => action(() => api.attachBaseAsset(pipelineProject.id, { ...target, asset_id: assetId }), "已采用所选素材")}
onGenerateTriview={async (portraitAssetId) => {
// 异步:image_edit 慢出图在 worker 跑,轮询期间整站不卡;出图后刷新即见三视图
const assetId = await submitAndPollAsset(() => api.generateTriview(pipelineProject.id, { portrait_asset_id: portraitAssetId }), "三视图已据立绘生成");
@@ -940,11 +940,24 @@ export function App() {
action(async () => {
// 「全部重跑」要把已完成的也重跑(否则全成功的项目点了等于没点);只跳过在途(running/queued)避免重复提交
const targets = pipelineProject.video_segments.filter((segment) => !["running", "queued"].includes(segment.status));
for (const segment of targets) {
await api.submitVideo(pipelineProject.id, {
video_segment_id: segment.id,
prompt: `${prompt}${segment.sort_order + 1} 段,时长 ${segment.target_duration_seconds}`
});
// ★ 并发提交,且单段失败绝不拖累其余段。旧实现用 for+await 串行,任一段抛错(如人脸需走素材库
// 的 502、中转限流)整个循环就 break → 后面的段永不提交,前端出现「4 个框只有 2 个在跑」。
// 改 allSettled:每段各自独立提交,失败只标该段,成功的照常进生成。
const results = await Promise.allSettled(
targets.map((segment) =>
api.submitVideo(pipelineProject.id, {
video_segment_id: segment.id,
prompt: `${prompt}${segment.sort_order + 1} 段,时长 ${segment.target_duration_seconds}`,
})
)
);
const failed = results.filter((r): r is PromiseRejectedResult => r.status === "rejected");
if (failed.length) {
// 部分失败也要刷新,让已成功提交的段立刻进「生成中」(否则 action 走 catch 分支会跳过刷新)
await refreshProjectDetail();
const firstErr = failed[0].reason;
const detail = firstErr instanceof Error ? firstErr.message : "提交失败";
throw new Error(`${targets.length - failed.length}/${targets.length} 段已提交,${failed.length} 段失败:${detail}(可对失败的段单独点重跑)`);
}
return targets.length;
}, "多段视频已提交,生成中…")