- 后端·可插拔 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>
93 lines
4.0 KiB
Python
93 lines
4.0 KiB
Python
"""模特库生成:用 gpt-image-2 预生成电商真人模特。
|
|
|
|
流程(与用户定的 SOP 一致):
|
|
1) 先出 9:16 竖屏氛围正面图(image_generation);
|
|
2) 以正面图为参考,出 16:9 白底三视图(image_edit,提示词锁角色一致性)。
|
|
两张都落 TOS,存为 person 类 Asset(metadata.kind="model"),进模特库/资产库。
|
|
|
|
可被管理命令(seed_demo_models)或后端业务复用。模型走默认 image 模型(当前 = tokenssr:gpt-image-2)。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import uuid
|
|
from io import BytesIO
|
|
|
|
from apps.ai.models import ModelConfig
|
|
from apps.ai.services import _asset_preview_url, build_provider, get_default_model
|
|
from apps.assets.models import Asset, AssetFile
|
|
from apps.assets.storage import TosStorage
|
|
|
|
THREE_VIEW_PROMPT = (
|
|
"参考图1角色,生成角色三视图,从左往右依次为:胸像特写,全身正面,全身侧面,全身背面,白色背景"
|
|
)
|
|
|
|
|
|
def _store_model_asset(*, team, user, media: str, name: str, brief: str, view: str) -> Asset:
|
|
"""把生成的图片(url/base64)落 TOS,存为 person 类模特资产(非项目维度)。"""
|
|
from apps.ai.providers import VolcanoArkProvider # media_to_bytes 复用
|
|
|
|
fileobj, content_type = VolcanoArkProvider.media_to_bytes(media)
|
|
suffix = ".png"
|
|
if "jpeg" in (content_type or ""):
|
|
suffix = ".jpg"
|
|
elif "webp" in (content_type or ""):
|
|
suffix = ".webp"
|
|
asset_id = uuid.uuid4()
|
|
object_key = f"teams/{team.id}/models/{asset_id}{suffix}"
|
|
raw = fileobj.getvalue()
|
|
stored = TosStorage().upload_fileobj(fileobj=BytesIO(raw), object_key=object_key, content_type=content_type or "image/png")
|
|
asset = Asset.objects.create(
|
|
id=asset_id,
|
|
team=team,
|
|
created_by=user,
|
|
name=name,
|
|
asset_type=Asset.Type.IMAGE,
|
|
source=Asset.Source.AI_GENERATED,
|
|
category=Asset.Category.PERSON,
|
|
metadata={"kind": "model", "brief": brief, "view": view},
|
|
)
|
|
AssetFile.objects.create(
|
|
asset=asset,
|
|
object_key=stored.object_key,
|
|
bucket=stored.bucket,
|
|
content_type=stored.content_type,
|
|
size_bytes=stored.size_bytes,
|
|
is_primary=True,
|
|
)
|
|
return asset
|
|
|
|
|
|
def generate_model(*, team, user, brief: str, name: str | None = None) -> dict:
|
|
"""生成一个电商模特(9:16 氛围正面图 + 16:9 白底三视图)。返回 {"frontal":Asset,"three_view":Asset}。"""
|
|
model_config = get_default_model(ModelConfig.Capability.IMAGE)
|
|
if model_config is None:
|
|
raise ValueError("没有可用的图像模型(image capability)")
|
|
provider = build_provider(model_config)
|
|
if not hasattr(provider, "image_edit"):
|
|
raise ValueError(f"当前图像模型 {model_config.provider.name}:{model_config.name} 不支持参考图三视图(image_edit)")
|
|
|
|
label = name or brief[:16]
|
|
# 1) 9:16 氛围正面图
|
|
frontal_prompt = f"{brief},电商真人模特,9:16竖屏氛围正面半身,自然妆容,柔和影棚光,真实质感,单人,简洁背景"
|
|
resp = provider.image_generation(model=model_config.name, prompt=frontal_prompt, size="1024x1536")
|
|
frontal = _store_model_asset(
|
|
team=team, user=user, media=provider.extract_first_media_url(resp),
|
|
name=f"{label}·正面氛围", brief=brief, view="frontal",
|
|
)
|
|
# 2) 16:9 白底三视图(以正面图为参考,锁角色一致性)
|
|
frontal_url = _asset_preview_url(frontal)
|
|
resp2 = provider.image_edit(model=model_config.name, prompt=THREE_VIEW_PROMPT, images=[frontal_url], size="1536x1024")
|
|
three_view = _store_model_asset(
|
|
team=team, user=user, media=provider.extract_first_media_url(resp2),
|
|
name=f"{label}·三视图", brief=brief, view="three_view",
|
|
)
|
|
return {"frontal": frontal, "three_view": three_view}
|
|
|
|
|
|
# 演示用默认模特画像(电商常用人设)
|
|
DEFAULT_MODEL_BRIEFS = [
|
|
"26岁都市白领女性,知性温柔,黑色及肩直发,米色针织衫",
|
|
"30岁阳光运动男性,短发,健康肤色,浅灰色休闲卫衣",
|
|
"22岁元气学生女生,马尾,清透妆,浅蓝色衬衫",
|
|
]
|