feat(core): AI 生成 Agent 化 — 多模型流式脚本 agent + 可插拔 Provider + gpt-image-2 参考图 + 模特库

- 后端·可插拔 Provider 层:通用 OpenAICompatibleProvider(tokenssr 等中转站,base_url+api_key,零改代码换站)+ ModelProvider.api_key
- 后端·脚本 agent:结构化 ScriptDraft 契约 + 加载电商 skill + 出稿/改稿一体对话 agent(3模式/多模型)+ 流式 SSE 端点(DRF SSE renderer)
- 后端·图像:gpt-image-2 参考图出图 + 故事板 @图1@图2@图3 多锚点合成(锁脸锁商品);Seedance 打开 generate_audio
- 后端·模特库:gpt-image-2 生成器(9:16氛围图→16:9白底三视图)+ seed_demo_models 管理命令
- DB·迁移:tokenssr 中转站 + 多模型 seed(豆包/GPT-5.5/Gemini + gpt-image-2);ScriptSegment 结构化字段
- 前端·脚本趴:接真 SSE(工具卡 + 思考流)+ 模型下拉 + 3模式 + 改稿;agentScriptStream
- skills/ecommerce-video-script 电商脚本技能(运行时依赖)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
seaislee1209
2026-06-17 03:12:03 +08:00
co-authored by Claude Opus 4.8
parent a0ffb6fc8e
commit 6464001f84
32 changed files with 9294 additions and 54 deletions
@@ -0,0 +1,18 @@
# Generated by Django 5.1.15 on 2026-06-16 17:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ai', '0004_seed_tts_model'),
]
operations = [
migrations.AddField(
model_name='modelprovider',
name='api_key',
field=models.CharField(blank=True, max_length=255),
),
]
@@ -0,0 +1,82 @@
"""Seed tokenssr 中转站 provider + 多模型(文本 GPT-5.5 / Gemini-3-Pro,图像 gpt-image-2 支持参考图)。
可插拔:tokenssr provider 的 base_url 存 DB(换站改这里),api_key 留空 → 运行时回退 settings.PROVIDER_KEYS(.env)。
图像主力切到 tokenssr:gpt-image-2(images/edits 多图参考),停用只能纯文生图的 yunqi:gpt-image-2。
幂等:全部 get_or_create / update_or_create,可重复 apply。
"""
from django.db import migrations
TOKENSSR_BASE_URL = "https://king.tokenssr.com/v1"
TEXT_MODELS = [
# name, display_name, metadata
("gpt-5.5", "GPT-5.5", {"family": "gpt", "recommended": True}),
("gemini-3-pro-preview", "Gemini 3 Pro", {"family": "gemini"}),
]
IMAGE_MODELS = [
("gpt-image-2", "GPT-Image-2(参考图)", {"family": "gpt", "supports_reference": True, "recommended": True}),
("gemini-2.5-flash-image", "Gemini 2.5 Flash Image", {"family": "gemini", "supports_reference": True}),
]
def seed(apps, schema_editor):
ModelProvider = apps.get_model("ai", "ModelProvider")
ModelConfig = apps.get_model("ai", "ModelConfig")
tokenssr, _ = ModelProvider.objects.get_or_create(
name="tokenssr",
defaults={
"display_name": "tokenssr 中转站",
"status": "active",
"base_url": TOKENSSR_BASE_URL,
"api_key": "", # 留空:运行时从 .env(PROVIDER_KEYS)解析,密钥不进库
"metadata": {"kind": "relay", "note": "一把 key 通吃 ~90 模型"},
},
)
if not tokenssr.base_url:
tokenssr.base_url = TOKENSSR_BASE_URL
tokenssr.save(update_fields=["base_url"])
for name, display, meta in TEXT_MODELS:
ModelConfig.objects.update_or_create(
provider=tokenssr,
name=name,
capability="text",
defaults={
"display_name": display,
"endpoint": "chat/completions",
"unit_price": "1.0000",
"status": "active",
"metadata": meta,
},
)
for name, display, meta in IMAGE_MODELS:
ModelConfig.objects.update_or_create(
provider=tokenssr,
name=name,
capability="image",
defaults={
"display_name": display,
"endpoint": "images/generations",
"unit_price": "2.0000",
"status": "active" if name == "gpt-image-2" else "disabled",
"metadata": meta,
},
)
# 图像主力切到 tokenssr:gpt-image-2;停用只能纯文生图的 yunqi:gpt-image-2(参考图分镜要靠 tokenssr)
ModelConfig.objects.filter(provider__name="yunqi", capability="image").update(status="disabled")
def unseed(apps, schema_editor):
# 反向:停用 tokenssr 模型并复活 yunqi 图像(不删数据,保守)
ModelProvider = apps.get_model("ai", "ModelProvider")
ModelConfig = apps.get_model("ai", "ModelConfig")
ModelConfig.objects.filter(provider__name="tokenssr").update(status="disabled")
ModelConfig.objects.filter(provider__name="yunqi", capability="image").update(status="active")
ModelProvider.objects.filter(name="tokenssr").update(status="disabled")
class Migration(migrations.Migration):
dependencies = [("ai", "0005_modelprovider_api_key")]
operations = [migrations.RunPython(seed, unseed)]