feat(ai): 图像主力从 tokenssr 切到 yunqi(支持 images/edits 图生图)

TOKENSSR 余额将尽,切换图像默认 provider 到 yunqi。yunqi 的图生图
端点需 Azure 风格 ?api-version=2025-04-01-preview,不带会 404。

- provider 加 api_version 字段,OpenAICompatible 生图/改图端点自动附加
  ?api-version=(配了才加,tokenssr 等零影响)
- build_provider 解析 api_version:DB(ModelProvider.metadata)优先 → .env 回退
- 迁移 0007:启用 yunqi:gpt-image-2 为唯一 active 图像模型,禁用其余
- 摘除死代码 YunqiProvider 导出(旧类写死"不支持图生图"会误导)

端到端验证:图生图(三视图,36s)+文生图(14s)均正常出图。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
zyc
2026-06-18 18:15:34 +08:00
co-authored by Claude Opus 4.8
parent a71be19500
commit 4399da2090
6 changed files with 89 additions and 5 deletions
@@ -0,0 +1,66 @@
"""图像主力从 tokenssr:gpt-image-2 切到 yunqi:gpt-image-2(TOKENSSR 余额将尽)。
yunqi 的图生图(images/edits)需 Azure 风格 ?api-version=2025-04-01-preview —— 存进
ModelProvider.metadata.api_version,运行时由 services.build_provider 注入,provider 据此拼 URL。
凭证仍走 .env(YUNQI_API_KEY,即能走 edits 的老 key),不进库。
幂等:get_or_create / update_or_create,可重复 apply。
"""
from django.db import migrations
YUNQI_BASE_URL = "https://www.yunqiai.chat/v1"
API_VERSION = "2025-04-01-preview"
def switch(apps, schema_editor):
ModelProvider = apps.get_model("ai", "ModelProvider")
ModelConfig = apps.get_model("ai", "ModelConfig")
yunqi, _ = ModelProvider.objects.get_or_create(
name="yunqi",
defaults={
"display_name": "YunQi AI(New API 网关)",
"status": "active",
"base_url": YUNQI_BASE_URL,
"api_key": "", # 留空:运行时从 .env(YUNQI_API_KEY)解析,密钥不进库
"metadata": {"kind": "relay", "api_version": API_VERSION},
},
)
meta = yunqi.metadata or {}
meta["api_version"] = API_VERSION # edits 端点必带,否则 404
yunqi.metadata = meta
if not yunqi.base_url:
yunqi.base_url = YUNQI_BASE_URL
yunqi.status = "active" # get_default_model 要求 provider__status=active
yunqi.save(update_fields=["metadata", "base_url", "status"])
ModelConfig.objects.update_or_create(
provider=yunqi,
name="gpt-image-2",
capability="image",
defaults={
"display_name": "GPT-Image-2(参考图)",
"endpoint": "images/generations", # 文生图端点;图生图由 provider.image_edit 走 images/edits
"unit_price": "2.0000",
"status": "active",
"metadata": {"family": "gpt", "supports_reference": True, "recommended": True},
},
)
# 切到 yunqi 唯一 active 图像模型:禁用所有非 yunqi 图像模型(含 tokenssr、火山 Seedream)。
# 否则 created_at 更早且 active 的模型会被 get_default_model(order_by created_at)顶替默认。
ModelConfig.objects.filter(capability="image").exclude(provider__name="yunqi").update(status="disabled")
def revert(apps, schema_editor):
# 反向:停用 yunqi 图像,复活 tokenssr:gpt-image-2(回到 0006 后的状态)
ModelConfig = apps.get_model("ai", "ModelConfig")
ModelConfig.objects.filter(provider__name="yunqi", capability="image").update(status="disabled")
ModelConfig.objects.filter(
provider__name="tokenssr", capability="image", name="gpt-image-2"
).update(status="active")
class Migration(migrations.Migration):
dependencies = [("ai", "0006_seed_tokenssr_models")]
operations = [migrations.RunPython(switch, revert)]