fix: make AI polish intent-aware
This commit is contained in:
352
api/main.py
352
api/main.py
@@ -5517,55 +5517,282 @@ _PERSON_INTENT_RE = re.compile(
|
||||
re.I,
|
||||
)
|
||||
|
||||
_OBJECT_INTENT_RE = re.compile(
|
||||
r"("
|
||||
r"产品|商品|物体|物件|道具|设备|机器|仪器|建筑|房子|屋顶|椅子|桌子|汽车|飞船|"
|
||||
r"\b(?:product|object|item|device|machine|gadget|building|house|roof|chair|table|car|vehicle|spaceship)\b"
|
||||
r")",
|
||||
re.I,
|
||||
)
|
||||
|
||||
def _prompt_has_person_intent(*parts: str) -> bool:
|
||||
text = "\n".join(part for part in parts if part).strip()
|
||||
if not text or _NO_PERSON_INTENT_RE.search(text):
|
||||
return False
|
||||
return bool(_PERSON_INTENT_RE.search(text))
|
||||
_SCENE_INTENT_RE = re.compile(
|
||||
r"("
|
||||
r"场景|街道|房间|室内|室外|空间|城市|森林|海边|天空|太空|夜景|摊位|"
|
||||
r"\b(?:scene|street|room|interior|exterior|space|city|forest|beach|sky|night|stall|booth)\b"
|
||||
r")",
|
||||
re.I,
|
||||
)
|
||||
|
||||
_ANIMAL_INTENT_RE = re.compile(
|
||||
r"("
|
||||
r"动物|猫|狗|鸟|马|鱼|龙|"
|
||||
r"\b(?:animal|cat|dog|bird|horse|fish|dragon)\b"
|
||||
r")",
|
||||
re.I,
|
||||
)
|
||||
|
||||
_SKG_RE = re.compile(r"\bskg\b", re.I)
|
||||
|
||||
_NO_PERSON_CLAUSE_RE = re.compile(
|
||||
r"\b(?:no|without)\s+people[^.。!?!?]*(?:[.。!?!?]|$)|"
|
||||
r"\bdo\s+not\s+(?:introduce|add|include)\s+people[^.。!?!?]*(?:[.。!?!?]|$)",
|
||||
re.I,
|
||||
)
|
||||
|
||||
_PREVIOUS_POLISH_BOILERPLATE_PATTERNS = [
|
||||
re.compile(
|
||||
r"\bDetailed visual prompt,\s*clear main subject,\s*coherent composition,\s*"
|
||||
r"natural lighting,\s*refined color palette,\s*high-quality details\.?",
|
||||
re.I,
|
||||
),
|
||||
re.compile(
|
||||
r"\bSmooth camera movement,\s*clear subject continuity,\s*stable composition,\s*"
|
||||
r"natural motion,\s*coherent lighting,\s*no subtitles,\s*no watermark\.?",
|
||||
re.I,
|
||||
),
|
||||
re.compile(
|
||||
r"\bPreserve the original object-only,\s*scene-only,\s*or product-only composition;?\s*"
|
||||
r"do not introduce people,\s*faces,\s*bodies,\s*hands,\s*avatars,\s*characters,\s*"
|
||||
r"crowds,\s*bystanders,\s*or human silhouettes\.?",
|
||||
re.I,
|
||||
),
|
||||
re.compile(
|
||||
r"\bUse a fully fictional synthetic AI character,\s*not based on any real person,\s*"
|
||||
r"celebrity,\s*public figure,\s*or identifiable private individual\.?",
|
||||
re.I,
|
||||
),
|
||||
re.compile(
|
||||
r"\bNot based on any real person,\s*celebrity,\s*public figure,\s*or identifiable private individual\.?",
|
||||
re.I,
|
||||
),
|
||||
re.compile(
|
||||
r"\bThe subject is a fictional synthetic AI character,\s*not based on any real person\.?",
|
||||
re.I,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def _prompt_person_guard(req: PromptPolishReq) -> str:
|
||||
if req.mode not in {"image", "video", "general"}:
|
||||
@dataclass(frozen=True)
|
||||
class PromptIntent:
|
||||
raw_text: str
|
||||
cleaned_text: str
|
||||
person_requested: bool
|
||||
no_person_requested: bool
|
||||
subject_kind: str
|
||||
skg_requested: bool
|
||||
|
||||
|
||||
def _strip_previous_polish_boilerplate(text: str) -> str:
|
||||
raw = (text or "").strip()
|
||||
if not raw:
|
||||
return ""
|
||||
if _prompt_has_person_intent(req.text, req.system_prompt):
|
||||
return (
|
||||
"The user requested a person, portrait, model, or character subject. "
|
||||
"Describe any such subject as a fully fictional synthetic AI character or virtual avatar, "
|
||||
"not based on any real person, celebrity, public figure, or identifiable private individual. "
|
||||
"Avoid real-person likeness, biometric identity, endorsement, or impersonation.\n"
|
||||
)
|
||||
return (
|
||||
"The user did not request a person or character subject. Preserve the original object-only, "
|
||||
"scene-only, or product-only composition. Do not introduce people, faces, bodies, hands, "
|
||||
"avatars, characters, crowds, bystanders, or human silhouettes.\n"
|
||||
cleaned = raw
|
||||
for pattern in _PREVIOUS_POLISH_BOILERPLATE_PATTERNS:
|
||||
cleaned = pattern.sub(" ", cleaned)
|
||||
cleaned = re.sub(r"\s+", " ", cleaned).strip()
|
||||
cleaned = re.sub(r"\s+([,.;:!?])", r"\1", cleaned)
|
||||
cleaned = re.sub(r"(?:[.。]\s*){2,}", ". ", cleaned)
|
||||
cleaned = re.sub(r"^[,.;:!?,。!?\s]+", "", cleaned)
|
||||
cleaned = re.sub(r"[,;:,;:\s]+$", "", cleaned)
|
||||
return cleaned or raw
|
||||
|
||||
|
||||
def _classify_prompt_intent(text: str) -> PromptIntent:
|
||||
cleaned = _strip_previous_polish_boilerplate(text)
|
||||
person_requested = bool(_PERSON_INTENT_RE.search(cleaned))
|
||||
no_person_requested = bool(_NO_PERSON_INTENT_RE.search(cleaned)) and not person_requested
|
||||
if person_requested:
|
||||
subject_kind = "person"
|
||||
elif _ANIMAL_INTENT_RE.search(cleaned):
|
||||
subject_kind = "animal"
|
||||
elif _OBJECT_INTENT_RE.search(cleaned):
|
||||
subject_kind = "object"
|
||||
elif _SCENE_INTENT_RE.search(cleaned) or no_person_requested:
|
||||
subject_kind = "scene"
|
||||
else:
|
||||
subject_kind = "unknown"
|
||||
return PromptIntent(
|
||||
raw_text=text,
|
||||
cleaned_text=cleaned,
|
||||
person_requested=person_requested,
|
||||
no_person_requested=no_person_requested,
|
||||
subject_kind=subject_kind,
|
||||
skg_requested=bool(_SKG_RE.search(cleaned)),
|
||||
)
|
||||
|
||||
|
||||
def _prompt_polish_fallback(req: PromptPolishReq) -> PromptPolishResp:
|
||||
text = req.text.strip()
|
||||
base = _ensure_english(text) if req.target_language == "en" else text
|
||||
def _remove_no_person_phrases(text: str) -> str:
|
||||
return _NO_PERSON_INTENT_RE.sub(" ", _NO_PERSON_CLAUSE_RE.sub(" ", text or ""))
|
||||
|
||||
|
||||
def _output_mentions_person_subject(text: str) -> bool:
|
||||
return bool(_PERSON_INTENT_RE.search(_remove_no_person_phrases(text or "")))
|
||||
|
||||
|
||||
def _clean_prompt_output(text: str) -> str:
|
||||
out = (text or "").strip()
|
||||
out = re.sub(r"^```(?:text)?\s*", "", out, flags=re.I).strip()
|
||||
out = re.sub(r"\s*```$", "", out).strip()
|
||||
out = re.sub(r'^[\'"「『]+|[\'"」』]+$', "", out).strip()
|
||||
out = _strip_previous_polish_boilerplate(out)
|
||||
return out
|
||||
|
||||
|
||||
def _ensure_fictional_person_subject(text: str) -> str:
|
||||
out = (text or "").strip()
|
||||
if not out:
|
||||
return out
|
||||
if re.search(r"\b(?:fictional|synthetic|virtual avatar|AI character|not based on any real person)\b", out, re.I):
|
||||
return out
|
||||
out = re.sub(
|
||||
r"\b(?:a|an|the)?\s*(?:person|human|model|woman|man|girl|boy|actor|actress|character|avatar)\b",
|
||||
"a fully fictional synthetic AI character",
|
||||
out,
|
||||
count=1,
|
||||
flags=re.I,
|
||||
)
|
||||
if not re.search(r"\b(?:fictional|synthetic|virtual avatar|AI character)\b", out, re.I):
|
||||
out = f"{out}. The subject is a fictional synthetic AI character, not based on any real person."
|
||||
return out
|
||||
|
||||
|
||||
def _basic_polished_prompt(req: PromptPolishReq, intent: PromptIntent) -> str:
|
||||
base = intent.cleaned_text or req.text.strip()
|
||||
base = _ensure_english(base) if req.target_language == "en" else base
|
||||
base = re.sub(r"\s+", " ", base).strip()
|
||||
base = re.sub(r"[。.!!??]+$", "", base).strip()
|
||||
person_intent = _prompt_has_person_intent(req.text, req.system_prompt)
|
||||
person_guard = (
|
||||
" Use a fully fictional synthetic AI character, not based on any real person, celebrity, public figure, or identifiable private individual."
|
||||
if person_intent
|
||||
else " Preserve the original object-only, scene-only, or product-only composition; do not introduce people, faces, bodies, hands, avatars, characters, crowds, bystanders, or human silhouettes."
|
||||
)
|
||||
if intent.person_requested:
|
||||
base = _ensure_fictional_person_subject(base)
|
||||
if req.mode == "video":
|
||||
polished = (
|
||||
f"{base}. Smooth camera movement, clear subject continuity, stable composition, "
|
||||
f"natural motion, coherent lighting, no subtitles, no watermark.{person_guard}"
|
||||
f"{base}. Cinematic motion, clear subject continuity, coherent camera movement, "
|
||||
"natural lighting transition, stable composition, detailed environmental interaction, "
|
||||
"no subtitles, no watermark."
|
||||
)
|
||||
elif req.mode in {"general", "chat"}:
|
||||
polished = base
|
||||
else:
|
||||
polished = (
|
||||
f"{base}. Detailed visual prompt, clear main subject, coherent composition, "
|
||||
f"natural lighting, refined color palette, high-quality details.{person_guard}"
|
||||
f"{base}. Clear main subject, coherent composition, natural lighting, refined color palette, "
|
||||
"detailed textures, cinematic camera framing, high-quality visual detail."
|
||||
)
|
||||
return PromptPolishResp(model="fallback", text=polished[:1800])
|
||||
if intent.no_person_requested:
|
||||
polished = f"{polished} No people, faces, bodies, hands, crowds, or human silhouettes."
|
||||
return re.sub(r"\s+", " ", polished).strip()
|
||||
|
||||
|
||||
def _polished_prompt_issue(intent: PromptIntent, output: str) -> str:
|
||||
out = output or ""
|
||||
if not intent.skg_requested and _SKG_RE.search(out):
|
||||
return "introduced SKG without user input"
|
||||
if intent.person_requested and _NO_PERSON_INTENT_RE.search(out):
|
||||
return "person prompt contains a no-person prohibition"
|
||||
if not intent.no_person_requested and _NO_PERSON_INTENT_RE.search(out):
|
||||
return "added a no-person prohibition that the user did not request"
|
||||
if not intent.person_requested and _output_mentions_person_subject(out):
|
||||
return "introduced a person or character subject that the user did not request"
|
||||
return ""
|
||||
|
||||
|
||||
def _sanitize_polished_prompt(req: PromptPolishReq, intent: PromptIntent, output: str) -> str:
|
||||
out = _clean_prompt_output(output)
|
||||
if not out:
|
||||
out = _basic_polished_prompt(req, intent)
|
||||
if not intent.skg_requested:
|
||||
out = re.sub(r"\bSKG\b[-\s]*(?:branded|brand|product|device|campaign|ad)?", "", out, flags=re.I)
|
||||
if intent.person_requested:
|
||||
out = _remove_no_person_phrases(out)
|
||||
out = _ensure_fictional_person_subject(out)
|
||||
elif intent.no_person_requested:
|
||||
if _output_mentions_person_subject(out):
|
||||
out = _basic_polished_prompt(req, intent)
|
||||
else:
|
||||
out = _remove_no_person_phrases(out)
|
||||
if _output_mentions_person_subject(out):
|
||||
out = _basic_polished_prompt(req, intent)
|
||||
out = re.sub(r"\s+", " ", out).strip()
|
||||
out = re.sub(r"\s+([,.;:!?])", r"\1", out)
|
||||
if out and "a" <= out[0] <= "z":
|
||||
out = out[0].upper() + out[1:]
|
||||
return out[:1800]
|
||||
|
||||
|
||||
def _prompt_has_person_intent(*parts: str) -> bool:
|
||||
text = "\n".join(part for part in parts if part).strip()
|
||||
return _classify_prompt_intent(text).person_requested
|
||||
|
||||
|
||||
def _prompt_person_guard(req: PromptPolishReq) -> str:
|
||||
if req.mode not in {"image", "video", "general"}:
|
||||
return ""
|
||||
intent = _classify_prompt_intent(req.text)
|
||||
if intent.person_requested:
|
||||
return (
|
||||
"The input explicitly requests a person, portrait, model, or character subject. "
|
||||
"Keep that subject, but describe them as a fully fictional synthetic AI character or virtual avatar. "
|
||||
"Do not imply a real person, celebrity, public figure, private individual, endorsement, or copied likeness.\n"
|
||||
)
|
||||
if intent.no_person_requested:
|
||||
return (
|
||||
"The input explicitly requests a no-person composition. Keep the output free of people, faces, bodies, "
|
||||
"hands, avatars, characters, crowds, bystanders, and human silhouettes.\n"
|
||||
)
|
||||
return (
|
||||
"The input does not explicitly request a person or character. Do not force a person into the rewrite, "
|
||||
"and do not add a no-person prohibition. If the source input or an attached/reference image already contains "
|
||||
"a person or character, preserve that visible subject conditionally as a fictional AI-generated synthetic "
|
||||
"character; otherwise use neutral wording such as 'main subject' when the subject is unclear.\n"
|
||||
)
|
||||
|
||||
|
||||
def _prompt_polish_fallback(req: PromptPolishReq) -> PromptPolishResp:
|
||||
intent = _classify_prompt_intent(req.text)
|
||||
return PromptPolishResp(model="fallback", text=_sanitize_polished_prompt(req, intent, _basic_polished_prompt(req, intent)))
|
||||
|
||||
|
||||
def _repair_polished_prompt(req: PromptPolishReq, intent: PromptIntent, output: str, *, allow_llm: bool = False) -> str:
|
||||
out = _sanitize_polished_prompt(req, intent, output)
|
||||
issue = _polished_prompt_issue(intent, out)
|
||||
if not issue or not allow_llm or not LLM_API_KEY:
|
||||
return out
|
||||
repair_prompt = (
|
||||
"Repair the rewritten generation prompt so it follows the source input exactly.\n"
|
||||
f"Issue to fix: {issue}.\n"
|
||||
"Hard rules:\n"
|
||||
"- Do not introduce SKG or any brand unless it appears literally in the source input.\n"
|
||||
"- Do not introduce products, platforms, ad framing, sales language, slogans, hashtags, or claims unless present in the source input.\n"
|
||||
"- If the source requests a person, keep the person only as a fully fictional synthetic AI character.\n"
|
||||
"- If the source mentions a reference, uploaded, first-frame, last-frame, or current image that may already contain a person, preserve that visible subject conditionally as a fictional AI-generated synthetic character.\n"
|
||||
"- If neither the source nor a referenced image requests or shows a person, do not add a person or character.\n"
|
||||
"- Return one clean prompt only, no explanation.\n\n"
|
||||
f"Source input:\n{intent.cleaned_text[:1800]}\n\n"
|
||||
f"Current rewritten prompt:\n{out[:1800]}"
|
||||
)
|
||||
try:
|
||||
resp = llm().chat.completions.create(
|
||||
model=REWRITE_MODEL,
|
||||
messages=[
|
||||
{"role": "system", "content": "You repair generation prompts by removing contradictions and preserving only source intent."},
|
||||
{"role": "user", "content": repair_prompt},
|
||||
],
|
||||
temperature=0.15,
|
||||
max_tokens=700,
|
||||
)
|
||||
repaired = _sanitize_polished_prompt(req, intent, resp.choices[0].message.content or "")
|
||||
return repaired if not _polished_prompt_issue(intent, repaired) else out
|
||||
except Exception as e:
|
||||
print(f"[prompt polish repair fallback] {e}", flush=True)
|
||||
return out
|
||||
|
||||
|
||||
@app.post("/prompt/polish", response_model=PromptPolishResp)
|
||||
@@ -5573,6 +5800,7 @@ def polish_prompt(req: PromptPolishReq) -> PromptPolishResp:
|
||||
text = req.text.strip()
|
||||
if not text:
|
||||
raise HTTPException(400, "text required")
|
||||
intent = _classify_prompt_intent(text)
|
||||
if not LLM_API_KEY:
|
||||
return _prompt_polish_fallback(req)
|
||||
|
||||
@@ -5588,47 +5816,54 @@ def polish_prompt(req: PromptPolishReq) -> PromptPolishResp:
|
||||
"chat": "a professional response to the user's request",
|
||||
}.get(req.mode, "an image-generation prompt")
|
||||
user_system = req.system_prompt.strip()
|
||||
structure_hint = (
|
||||
"For image prompts, write one polished paragraph covering subject, action/state, setting, composition, camera framing, lighting, material/detail, color mood, and production quality. "
|
||||
if req.mode != "video"
|
||||
else "For video prompts, write one polished paragraph covering opening state, subject motion, camera movement, continuity, environmental interaction, lighting transition, and ending state. "
|
||||
)
|
||||
prompt = (
|
||||
f"Rewrite the user's input into {mode_hint} in {target_label}.\n"
|
||||
"Preserve the user's actual subject, brand, product, place, style, and intent.\n"
|
||||
"Do not add SKG, health-tech, massage products, TikTok ad framing, product sales language, hashtags, captions, or any brand/product not explicitly present in the input or user-selected guidance.\n"
|
||||
"Do not add medical, wellness, or advertising claims unless the user asked for them.\n"
|
||||
"Improve concrete visual details, composition, lighting, camera language, materials, mood, and quality.\n"
|
||||
"Return only the rewritten prompt. No markdown, labels, JSON, quotes, explanation, or alternatives.\n"
|
||||
"Preserve only the subject, brand, product, place, platform, style, action, and intent explicitly present in the source input.\n"
|
||||
"Do not introduce SKG or any other brand unless the source input literally includes it.\n"
|
||||
"Do not introduce products, platforms, ad framing, sales language, slogans, hashtags, captions, or marketing claims unless explicitly present in the source input.\n"
|
||||
"Improve visual specificity, composition, lighting, camera language, materials, motion, mood, and production quality without changing the subject.\n"
|
||||
"Do not add a no-person prohibition unless the source input explicitly asks for no people.\n"
|
||||
"If the source input mentions uploaded images, reference images, first frames, last frames, or current images, keep any existing visible person in those references as a fictional AI-generated synthetic character; do not invent people for references that have none.\n"
|
||||
f"{structure_hint}"
|
||||
"Return only the rewritten prompt. No markdown, labels, JSON, quotes, explanation, alternatives, or meta-instructions.\n"
|
||||
f"{_prompt_person_guard(req)}"
|
||||
)
|
||||
if req.mode == "chat":
|
||||
prompt = (
|
||||
f"Answer or rewrite the user's request professionally in {target_label}.\n"
|
||||
"Follow the user-selected guidance when provided.\n"
|
||||
"Do not add SKG, health-tech, massage products, TikTok ad framing, product sales language, hashtags, captions, or any brand/product not explicitly present in the input or user-selected guidance.\n"
|
||||
"Do not add SKG or any brand/product/platform not explicitly present in the source input or user-selected guidance.\n"
|
||||
"Do not add medical, wellness, or advertising claims unless the user asked for them.\n"
|
||||
"Return only the final content in the format requested by the guidance. No markdown fences, labels, explanation, or alternatives unless explicitly requested.\n"
|
||||
)
|
||||
if req.mode == "video":
|
||||
prompt += (
|
||||
"For video, describe motion, timing, camera movement, continuity, and what changes over time. "
|
||||
"Do not add people for scale, atmosphere, lifestyle context, or background decoration unless the input explicitly asked for people.\n"
|
||||
"Do not add people for scale, atmosphere, lifestyle context, or background decoration unless the input or reference image already contains or requests people.\n"
|
||||
)
|
||||
if user_system:
|
||||
prompt += f"\nUser-selected polishing guidance:\n{user_system[:1000]}\n"
|
||||
prompt += f"\nInput:\n{text[:2500]}"
|
||||
prompt += f"\nSource input:\n{intent.cleaned_text[:2500]}"
|
||||
|
||||
try:
|
||||
resp = llm().chat.completions.create(
|
||||
model=REWRITE_MODEL,
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a neutral professional prompt editor. You preserve intent and never inject unrelated brands or products."},
|
||||
{"role": "system", "content": "You are a neutral professional prompt editor. Preserve source intent exactly and never inject SKG or unrelated brands, products, platforms, people, or marketing context."},
|
||||
{"role": "user", "content": prompt},
|
||||
],
|
||||
temperature=0.45,
|
||||
max_tokens=900,
|
||||
)
|
||||
out = (resp.choices[0].message.content or "").strip()
|
||||
out = re.sub(r"^```(?:text)?\s*", "", out, flags=re.I).strip()
|
||||
out = re.sub(r"\s*```$", "", out).strip()
|
||||
out = re.sub(r'^[\'"「『]+|[\'"」』]+$', "", out).strip()
|
||||
return PromptPolishResp(model=REWRITE_MODEL, text=(out or _prompt_polish_fallback(req).text)[:1800])
|
||||
out = _clean_prompt_output(resp.choices[0].message.content or "")
|
||||
if not out:
|
||||
out = _prompt_polish_fallback(req).text
|
||||
return PromptPolishResp(model=REWRITE_MODEL, text=_repair_polished_prompt(req, intent, out, allow_llm=True))
|
||||
except Exception as e:
|
||||
print(f"[prompt polish fallback] {e}", flush=True)
|
||||
return _prompt_polish_fallback(req)
|
||||
@@ -8197,6 +8432,24 @@ def _storyboard_video_prompt(scene: StoryboardScene, seed: int | None = None) ->
|
||||
return "\n".join([p for p in parts if p.strip()])
|
||||
|
||||
|
||||
_REFERENCE_IMAGE_SYNTHETIC_PERSON_GUARD = (
|
||||
"Reference images may be AI-generated visual assets. If any person, face, body, hand, avatar, or character "
|
||||
"appears in the provided reference image(s), treat the visible subject as a fully fictional AI-generated "
|
||||
"synthetic character, not a real person, celebrity, public figure, private individual, or copied likeness. "
|
||||
"Preserve the reference subject and composition without identifying, impersonating, or implying endorsement "
|
||||
"by any real person."
|
||||
)
|
||||
|
||||
|
||||
def _append_reference_image_person_guard(prompt: str, has_reference: bool) -> str:
|
||||
out = (prompt or "").strip()
|
||||
if not has_reference or not out:
|
||||
return out
|
||||
if "Reference images may be AI-generated visual assets" in out or "fully fictional AI-generated synthetic character" in out:
|
||||
return out
|
||||
return f"{out}\n\n{_REFERENCE_IMAGE_SYNTHETIC_PERSON_GUARD}"
|
||||
|
||||
|
||||
class ProductFusionDescriptionReq(BaseModel):
|
||||
shots: list[ProductFusionShot] = Field(default_factory=list)
|
||||
|
||||
@@ -8287,8 +8540,9 @@ def _video_public_error(raw: object) -> str:
|
||||
"人脸",
|
||||
)):
|
||||
return (
|
||||
"视频生成失败:参考图里有清晰人物或疑似真实人脸,视频模型出于肖像/隐私风控拒绝生成。"
|
||||
"请换成无可识别人脸的首帧,或先裁掉/模糊人物脸,再重新生成视频。"
|
||||
"视频生成失败:参考图被视频模型判定为疑似真实人脸或肖像隐私。"
|
||||
"系统会按 AI 生成的虚拟角色提交,但上游仍可能误判;请尝试换更低识别度的首帧,"
|
||||
"或裁掉/弱化脸部后重新生成视频。"
|
||||
)
|
||||
|
||||
if any(token in lower for token in (
|
||||
@@ -8687,6 +8941,7 @@ def _enqueue_storyboard_videos(job: Job, frame: KeyFrame, req: GenerateStoryboar
|
||||
source_ref = req.source_ref
|
||||
if source_ref and source_ref.kind == "source_video" and not source_ref.url:
|
||||
source_ref = None
|
||||
has_visual_reference = bool(ref_path.exists() or last_ref_path or reference_ref_paths)
|
||||
items: list[GeneratedVideo] = []
|
||||
ids: list[str] = []
|
||||
queued_tasks: list[tuple[str, tuple]] = []
|
||||
@@ -8695,6 +8950,7 @@ def _enqueue_storyboard_videos(job: Job, frame: KeyFrame, req: GenerateStoryboar
|
||||
ids.append(local_id)
|
||||
variant_seed = (req.seed + i) if req.seed is not None else random.randint(100000, 999999)
|
||||
variant_prompt = _ensure_english(f"{prompt}\n\nCreate variation {i + 1} of {count}. Variation seed: {variant_seed}. Keep the same compact row meaning but vary camera motion, gesture timing, and composition.")
|
||||
variant_prompt = _append_reference_image_person_guard(variant_prompt, has_visual_reference)
|
||||
items.append(GeneratedVideo(
|
||||
id=local_id,
|
||||
provider_id="",
|
||||
|
||||
Reference in New Issue
Block a user