全能创作:定妆/台词/换款与剧情方向硬约束,资源可下载

修角色定妆修改不重出图、角色图混入商品、宠物旁白腔、换款同点复读、剧情反转方向漂移,并补会话资源下载与外观/空消息闸门。
This commit is contained in:
Azmat@qq.com
2026-09-20 14:52:51 +08:00
parent f2e8a5f3c1
commit fb3b2904ae
10 changed files with 1149 additions and 142 deletions
+95 -14
View File
@@ -258,23 +258,43 @@ def _plot_twist_direction_continuation(
)
if isinstance(direction, dict):
title = str(direction.get("title") or choice).strip()
conflict = str(direction.get("conflict") or "").strip()
product_role = str(direction.get("product_role") or "").strip()
reversal = str(direction.get("reversal") or "").strip()
tone = str(direction.get("tone") or "").strip()
detail = ";".join(
str(direction.get(key) or "").strip()
for key in ("conflict", "product_role", "reversal", "tone")
if str(direction.get(key) or "").strip()
part for part in (conflict, product_role, reversal, tone) if part
)
payload_dir = {
"id": str(direction.get("id") or "").strip(),
"title": title,
"conflict": conflict,
"product_role": product_role,
"reversal": reversal,
"tone": tone,
}
else:
title = choice.strip() or "用户自定义方向"
conflict = product_role = reversal = tone = ""
detail = title
payload_dir = {
"id": "",
"title": title,
"conflict": "",
"product_role": "",
"reversal": "",
"tone": "",
}
memory = dict(conversation.memory or {})
memory["plot_twist_story_direction"] = title
memory["plot_twist_story_direction_detail"] = detail
memory["plot_twist_story_direction_payload"] = payload_dir
conversation.memory = memory
conversation.save(update_fields=["memory", "updated_at"])
return (
f"用户已选择剧情方向【{title}】。方向细节:{detail}。"
"现在只调用 write_strategy 写创作策略卡,必须沿用该冲突、商品作用与反转;"
"不要再展示方向卡、不要复述选择、不要直接写方案或出片。"
"现在只调用 write_strategy 写创作策略卡,策略的创作方向与后续方案/分镜必须严格沿用该冲突、商品作用与反转,"
"不得改写成另一套常见带货故事;不要再展示方向卡、不要复述选择、不要直接写方案或出片。"
)
@@ -2245,6 +2265,12 @@ class CreationConversationViewSet(TeamScopedViewSetMixin, ModelViewSet):
memory["product_info_resolved"] = True
if clean_ans and not any(kw in clean_ans for kw in ("按图片", "设计品牌", "不用再问")):
memory["product_brand_and_name"] = clean_ans
bm = re.search(r"品牌[::是为]?\s*([^\n,,。!!]+)", clean_ans)
nm = re.search(r"(?:品名|商品名|产品名)[::是为]?\s*([^\n,,。!!]+)", clean_ans)
if bm:
memory["product_brand"] = bm.group(1).strip()
if nm:
memory["product_name"] = nm.group(1).strip()
for r in (conversation.pinned_refs or []):
if isinstance(r, dict) and r.get("type") in ("asset", "product"):
r["name"] = clean_ans
@@ -2255,8 +2281,9 @@ class CreationConversationViewSet(TeamScopedViewSetMixin, ModelViewSet):
continuation_instruction = "用户要求直接根据图片内容设计品牌与品名推进创作。直接基于图片外观特征推进方案,不要重复追问品牌。"
conversation.memory = memory
conversation.save(update_fields=["memory", "pinned_refs", "updated_at"])
text = ""
record_user_message = False
# 保留用户原文气泡,便于回看刚填的品牌/品名;不要清空 text,
# 否则会落到「消息不能为空」且对话里看不到这次回答。
force_creative_turn = True
else:
continuation_instruction = (
"用户刚用输入框回答了你上一句问题。直接基于这条真实回答继续原任务;"
@@ -2265,26 +2292,78 @@ class CreationConversationViewSet(TeamScopedViewSetMixin, ModelViewSet):
if params_changed:
continuation_instruction += " 会话参数已更新,旧方案作废,按新参数重新产出。"
# 用户对刚生成的角色给出确认/重刷反馈
# 用户对刚生成的角色给出确认/重刷/外观修改反馈
clean_text = text.strip()
if re.search(r"^(使用这[个位只]角色|就用这[个位只]角色|就用[她他它]|满意|确认使用|合适|可以)[继续创作。!! ]*$", clean_text) or clean_text in ("使用这个角色继续创作", "使用这个宠物角色继续创作"):
memory_now = dict(conversation.memory or {})
person_confirm_pending = bool(memory_now.get("person_confirm_pending"))
is_platform_person = memory_now.get("person_source") == "platform_generate"
confirmed_person = (
bool(re.search(
r"^(使用这[个位只]角色|就用这[个位只]角色|就用[她他它]|满意|确认使用|合适|可以)[继续创作。!! ]*$",
clean_text,
))
or clean_text in (
"使用这个角色继续创作",
"使用这个宠物角色继续创作",
)
or (person_confirm_pending and clean_text in ("继续", "继续创作"))
)
regenerate_button = bool(re.search(
r"^(重新生成|再生成|重刷|换一个|换位|不满意).{0,6}(角色|人物|模特|宠物)?[。!! ]*$",
clean_text,
))
upload_person = bool(re.search(
r"(上传|发一张|发个).{0,8}(人物|角色|模特|宠物)|我上传",
clean_text,
))
if confirmed_person:
if person_confirm_pending:
memory_now["person_confirm_pending"] = False
conversation.memory = memory_now
conversation.save(update_fields=["memory", "updated_at"])
force_creative_turn = True
continuation_instruction = (
"用户已确认使用当前生成的角色出镜。直接基于该角色继续推进创作方案"
"(若尚未选商品则确定商品,已选好商品则开始写创作策略和方案),不要再次追问角色来源。"
)
elif re.search(r"^(重新生成|再生成|重刷|换一个|换位|不满意).{0,6}(角色|人物|模特|宠物)?[。!! ]*$", clean_text) and (conversation.memory or {}).get("person_source") == "platform_generate":
person_prompt = str((conversation.memory or {}).get("person_prompt") or "").strip()
elif is_platform_person and (regenerate_button or (person_confirm_pending and clean_text and not upload_person)):
prev_prompt = str(memory_now.get("person_prompt") or "").strip()
if regenerate_button:
appearance_prompt = prev_prompt
elif prev_prompt:
appearance_prompt = f"{prev_prompt};按用户最新要求调整:{clean_text}"
else:
appearance_prompt = clean_text
# 卸掉上一张平台定妆锁定,避免新旧角色叠在 pinned_refs 里
old_model_id = str(memory_now.get("person_model_id") or "").strip()
if old_model_id:
conversation.pinned_refs = [
ref for ref in (conversation.pinned_refs or [])
if not (
isinstance(ref, dict)
and ref.get("type") in {"model", "character"}
and str(ref.get("id") or "") == old_model_id
)
]
memory_now["person_confirm_pending"] = False
memory_now.pop("person_source_ready", None)
memory_now.pop("person_model_id", None)
conversation.memory = memory_now
conversation.save(update_fields=["memory", "pinned_refs", "updated_at"])
try:
user_msg = append_message(conversation, role="user", text=clean_text)
generating = submit_generated_person_reference(
conversation=conversation,
user=request.user,
appearance_prompt=person_prompt,
appearance_prompt=appearance_prompt,
)
return JsonResponse({
"conversation_id": str(conversation.id),
"agent_status": conversation.agent_status,
"messages": [CreationMessageSerializer(generating).data],
"messages": [
CreationMessageSerializer(user_msg).data,
CreationMessageSerializer(generating).data,
],
}, status=202)
except Exception:
pass
@@ -2775,7 +2854,9 @@ class CreationConversationViewSet(TeamScopedViewSetMixin, ModelViewSet):
)
if params_changed:
continuation_instruction += " 会话参数已经更新,旧方案作废,按新参数重新产出方案。"
elif not text and not refs:
elif not text and not refs and not (force_creative_turn or continuation_instruction):
# 追问卡(品牌品名/点选闸门等)会把正文清空、改走 continuation;
# 这时没有 text/refs 也是合法推进,不能误报「消息不能为空」。
return JsonResponse({"detail": "消息不能为空"}, status=400)
# 同一账号同时只允许一个整理方案 turn(含本会话已在 planning 的二次发送)