添加全能创作功能

This commit is contained in:
Azmat@qq.com
2026-09-03 13:11:46 +08:00
parent 22ed2833ad
commit 6a628b0ca7
53 changed files with 9115 additions and 108 deletions
+105
View File
@@ -279,3 +279,108 @@ class PromptTemplate(TimeStampedModel):
def __str__(self) -> str:
return f"prompt:{self.key}"
class CreationConversation(TeamOwnedModel):
"""全能创作的会话。一条会话 = 一次完整创作(可多轮改稿、多次出图/出片)。
和 ImageConversation 的区别:那张表只是「生图线程」,没有消息实体,历史靠 AITask 拼;
这里是真对话,消息落 CreationMessage。两者并存,互不影响(图片工作台仍走旧表)。
mode 发起时定死,会话内不可切(设计稿顶栏的模型/分辨率/比例跟着 mode 固定)。
pinned_refs 是「实体锁定」:本会话引用过的商品/角色/场景,每轮无条件带进上下文 ——
这是多次生成之间锁脸、锁商品的唯一手段,别省。
"""
class Mode(models.TextChoices):
VIDEO = "video", "视频创作"
IMAGE = "image", "图片创作"
class Status(models.TextChoices):
RUNNING = "running", "进行中"
COMPLETED = "completed", "已完成"
FAILED = "failed", "失败"
title = models.CharField(max_length=120, default="未命名创作")
mode = models.CharField(max_length=16, choices=Mode.choices, default=Mode.VIDEO)
preset = models.CharField(max_length=64, blank=True, default="") # "" = 自由创作
# 会话级参数:{model, resolution, ratio, duration} —— 设计稿顶栏 meta 就渲染它
params = models.JSONField(default=dict, blank=True)
# 实体锁定:[Ref],见契约 §1。每轮无条件带上
pinned_refs = models.JSONField(default=list, blank=True)
# 记忆:{summary, artifacts:[{msg_id,asset_id,prompt,kind}], turn_count}
memory = models.JSONField(default=dict, blank=True)
status = models.CharField(max_length=16, choices=Status.choices, default=Status.RUNNING)
last_active_at = models.DateTimeField(auto_now_add=True)
is_deleted = models.BooleanField(default=False)
purged_at = models.DateTimeField(null=True, blank=True)
class Meta:
indexes = [
# 创作历史页:按团队 + 最近活跃倒序
models.Index(fields=["team", "-last_active_at"]),
models.Index(fields=["team", "status", "-last_active_at"]),
models.Index(fields=["team", "is_deleted", "purged_at"]),
]
def __str__(self) -> str:
return f"creation:{self.mode}:{self.title}"
class CreationMessage(TimeStampedModel):
"""全能创作对话流里的一条消息。kind 决定前端渲染成哪种卡片(见契约 §2)。
text 只给 TEXT 用;其余 kind 的内容全在 payload 里 —— 前端按 kind 走不同组件,
别把结构化内容塞进 text 再让前端解析。
生成类消息(GENERATING / RESULT)挂 task:提交时先落一条 GENERATING,
轮询到终态后**原地改成 RESULT**(不新增消息),这样对话流不会被中间态刷屏。
"""
class Role(models.TextChoices):
USER = "user", "用户"
ASSISTANT = "assistant", "AI"
SYSTEM = "system", "系统"
class Kind(models.TextChoices):
TEXT = "text", "文字气泡"
ELICIT = "elicit", "追问卡" # AI 反问用户,带 单选/多选/填空/选素材 控件
STRATEGY = "strategy", "创作策略理解卡"
PLAN = "plan", "视频最终方案卡"
PROMPT_FILE = "prompt_file", "生成 Prompt 文件卡"
CONFIRM = "confirm", "确认闸门(带预计积分)"
GENERATING = "generating", "生成中"
RESULT = "result", "生成结果"
ERROR = "error", "错误"
conversation = models.ForeignKey(
CreationConversation, on_delete=models.CASCADE, related_name="messages"
)
role = models.CharField(max_length=16, choices=Role.choices)
kind = models.CharField(max_length=24, choices=Kind.choices, default=Kind.TEXT)
text = models.TextField(blank=True, default="")
payload = models.JSONField(default=dict, blank=True)
# 本条消息引用的实体:[Ref]。用 id 取事实与参考图,不许只存 "@商品名" 字符串
refs = models.JSONField(default=list, blank=True)
task = models.ForeignKey(
AITask,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="creation_messages",
)
# 会话内自增渲染序;并发插入靠 select_for_update 取 max+1
seq = models.PositiveIntegerField(default=0)
class Meta:
ordering = ["seq", "created_at"]
indexes = [
models.Index(fields=["conversation", "seq"]),
]
constraints = [
models.UniqueConstraint(
fields=["conversation", "seq"], name="uniq_creation_message_seq"
),
]
def __str__(self) -> str:
return f"msg:{self.kind}:{self.seq}"