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
@@ -0,0 +1,87 @@
"""脚本助手文本模型从 tokenssr 切到 YunQi(tokenssr 欠费致 GPT-5.5/Gemini 全挂)。
YunQi 同一地址(https://www.yunqiai.chat/v1)对 gpt / gemini 分开计费、各一把 key,故按
「一 provider 一 key」拆成两个 provider:
- yunqi_gpt → gpt-5.5(默认脚本模型) · key=.env YUNQI_GPT_API_KEY
- yunqi_gemini → gemini-3.1-pro-preview · key=.env YUNQI_GEMINI_API_KEY
凭证留空 → 运行时由 settings.PROVIDER_KEYS 从 .env 解析(密钥不进库)。
模型名经实测确认:gpt-5.5 / gemini-3.1-pro-preview 在 YunQi 返回 200 且能流式出正文;
其余名(gpt-5 / gemini-3-pro-preview …)YunQi 报 model_not_found。
同时停用旧的 tokenssr 文本模型(gpt-5.5 / gemini-3-pro-preview),让 get_default_model(text)
选到新的 YunQi gpt-5.5。幂等:全部 update_or_create / 显式置位,可重复 apply。
"""
from django.db import migrations
YUNQI_BASE_URL = "https://www.yunqiai.chat/v1"
# (provider_name, display, model_name, model_display, is_default)
YUNQI_TEXT = [
("yunqi_gpt", "YunQi · GPT", "gpt-5.5", "GPT-5.5", True),
("yunqi_gemini", "YunQi · Gemini", "gemini-3.1-pro-preview", "Gemini 3.1 Pro", False),
]
def seed(apps, schema_editor):
ModelProvider = apps.get_model("ai", "ModelProvider")
ModelConfig = apps.get_model("ai", "ModelConfig")
for prov_name, prov_display, model_name, model_display, is_default in YUNQI_TEXT:
provider, _ = ModelProvider.objects.get_or_create(
name=prov_name,
defaults={
"display_name": prov_display,
"status": "active",
"base_url": YUNQI_BASE_URL,
"api_key": "", # 运行时从 .env(PROVIDER_KEYS)解析,密钥不进库
"metadata": {"kind": "relay", "gateway": "yunqi"},
},
)
# down→up 循环后 get_or_create 命中旧行(可能 disabled / 缺 base_url),显式补齐
changed = []
if provider.status != "active":
provider.status = "active"; changed.append("status")
if not provider.base_url:
provider.base_url = YUNQI_BASE_URL; changed.append("base_url")
if changed:
provider.save(update_fields=changed)
ModelConfig.objects.update_or_create(
provider=provider,
name=model_name,
capability="text",
defaults={
"display_name": model_display,
"endpoint": "chat/completions",
"unit_price": "1.0000",
"status": "active",
"is_default": is_default,
"metadata": {"gateway": "yunqi", "verified": True},
},
)
# 停用旧的 tokenssr 文本模型(欠费),并清掉它们可能带的 is_default,避免与新默认打架
ModelConfig.objects.filter(
provider__name="tokenssr", capability="text",
).update(status="disabled", is_default=False)
def unseed(apps, schema_editor):
ModelConfig = apps.get_model("ai", "ModelConfig")
# 回滚:停用 YunQi 文本模型,恢复 tokenssr 文本模型为 active(gpt-5.5 复位默认)
ModelConfig.objects.filter(provider__name__in=["yunqi_gpt", "yunqi_gemini"], capability="text").update(
status="disabled", is_default=False,
)
ModelConfig.objects.filter(provider__name="tokenssr", capability="text").update(status="active")
ModelConfig.objects.filter(provider__name="tokenssr", capability="text", name="gpt-5.5").update(is_default=True)
class Migration(migrations.Migration):
dependencies = [
("ai", "0016_person_triview_16_9"),
]
operations = [
migrations.RunPython(seed, unseed),
]
@@ -0,0 +1,38 @@
"""脚本默认模型从 gpt-5.5 改为 gemini-3.1-pro-preview,并停用 YunQi 的 gpt-5.5。
实测(完整电商脚本 skill ~28KB 重型结构化任务,各连发 5 次):
- YunQi gpt-5.5 → 仅 1/5 成功(3 次吐空、1 次出文无合法 JSON,成功那次还要 69-95s)→ 频道扛不住,不可用
- YunQi gemini-3.1-pro → 5/5 成功(每次 ~2300 字合法 JSON,30-48s 稳定)
故默认切 gemini;gpt-5.5 停用(YunQi 频道修好后改回 active 即可,一行)。
幂等:显式置位,可重复 apply。
"""
from django.db import migrations
def apply(apps, schema_editor):
ModelConfig = apps.get_model("ai", "ModelConfig")
# gemini 设默认 + 确保 active
ModelConfig.objects.filter(
provider__name="yunqi_gemini", capability="text", name="gemini-3.1-pro-preview",
).update(is_default=True, status="active")
# gpt-5.5 取消默认 + 停用(频道不可用)
ModelConfig.objects.filter(
provider__name="yunqi_gpt", capability="text", name="gpt-5.5",
).update(is_default=False, status="disabled")
def revert(apps, schema_editor):
ModelConfig = apps.get_model("ai", "ModelConfig")
ModelConfig.objects.filter(provider__name="yunqi_gemini", capability="text", name="gemini-3.1-pro-preview").update(is_default=False)
ModelConfig.objects.filter(provider__name="yunqi_gpt", capability="text", name="gpt-5.5").update(is_default=True, status="active")
class Migration(migrations.Migration):
dependencies = [
("ai", "0017_switch_script_to_yunqi"),
]
operations = [
migrations.RunPython(apply, revert),
]