fix: isolate subject reference generation
This commit is contained in:
98
api/main.py
98
api/main.py
@@ -5547,7 +5547,7 @@ def _subject_assets_background_worker(
|
||||
req: GenerateSubjectAssetsReq,
|
||||
queued: list[tuple[SubjectView, str, str]],
|
||||
) -> None:
|
||||
if req.reconstruction_mode == "similar" and not req.source_subject_brief.strip():
|
||||
if not req.source_subject_brief.strip() and _subject_source_indices(req, idx):
|
||||
try:
|
||||
req.source_subject_brief = _describe_source_subject(job_id, _subject_source_indices(req, idx))
|
||||
except Exception as e:
|
||||
@@ -5738,21 +5738,35 @@ def _generate_subject_assets_sync(job_id: str, idx: int, element_id: str, req: G
|
||||
frame_reference_paths = [p for p in (_source_frame_path(job_id, i) for i in source_indices) if p.exists()]
|
||||
source_subject_brief = (
|
||||
_ensure_english(req.source_subject_brief.strip())
|
||||
if similar_mode and req.source_subject_brief.strip()
|
||||
else (_describe_source_subject(job_id, source_indices) if similar_mode else "")
|
||||
if req.source_subject_brief.strip()
|
||||
else (_describe_source_subject(job_id, source_indices) if source_indices else "")
|
||||
)
|
||||
source_subject_clause = (
|
||||
f"Source video role brief from selected keyframes: {source_subject_brief}. "
|
||||
"Use this brief to preserve role category, creator-ad energy, camera readability, and broad styling, while creating a new non-identical subject. "
|
||||
+ (
|
||||
"Use this brief as secondary text evidence while preserving the same visible source subject from the attached reference image(s). "
|
||||
if req.reconstruction_mode == "same"
|
||||
else "Use this brief to preserve role category, creator-ad energy, camera readability, and broad styling, while creating a new non-identical subject. "
|
||||
)
|
||||
if source_subject_brief else
|
||||
"Source video role brief unavailable; create a new non-identical ad subject guided by the user direction, template brief, and requested view. "
|
||||
(
|
||||
"Source video role brief unavailable; use the attached source reference image(s) as primary evidence for the same visible subject. "
|
||||
if req.reconstruction_mode == "same"
|
||||
else "Source video role brief unavailable; create a new non-identical ad subject guided by the user direction, template brief, and requested view. "
|
||||
)
|
||||
)
|
||||
if not similar_mode:
|
||||
if similar_mode:
|
||||
if character_reference_paths:
|
||||
remaining = max(0, 10 - len(character_reference_paths))
|
||||
model_src = character_reference_paths + frame_reference_paths[:remaining]
|
||||
elif frame_reference_paths:
|
||||
model_src = frame_reference_paths[:10]
|
||||
else:
|
||||
model_src, tmp_focus = _focus_source_for_element(job_id, idx, el)
|
||||
if character_reference_paths:
|
||||
remaining = max(0, 10 - len(character_reference_paths))
|
||||
model_src = character_reference_paths + frame_reference_paths[:remaining]
|
||||
elif len(frame_reference_paths) > 1:
|
||||
elif frame_reference_paths:
|
||||
model_src = frame_reference_paths[:10]
|
||||
|
||||
try:
|
||||
@@ -5823,14 +5837,25 @@ def _generate_subject_assets_sync(job_id: str, idx: int, element_id: str, req: G
|
||||
"If the reference outfit is useful, inherit its broad wardrobe category and color family, but redraw it as a new non-identical clean commercial outfit. "
|
||||
)
|
||||
pack_bible_clause = (
|
||||
"PACK BIBLE - this exact bible applies to every view in this generated set. "
|
||||
"Subject bible: one newly designed commercial wellness-ad subject; inherit only broad non-identifying casting traits from the source such as gender presentation, regional/ethnic appearance category, skin-tone family, age range, body-proportion category, hair-length family, posture energy, and neck/shoulder readability. "
|
||||
"Do not copy the source person's biometric identity, exact face, exact hairstyle, marks, tattoos, captions, logos, or watermarks. "
|
||||
"Keep the same new face design, same head shape, same hair color and hair silhouette, same skin tone, same body proportions, same height impression, and same character age across front, side, three-quarter, and back views. "
|
||||
"Wardrobe bible: if the user direction names a specific outfit, use that one outfit uniformly across every view. Otherwise use one clean SKG wellness-ad activewear outfit for the entire pack: fitted short-sleeve performance top with a visible neck/collarbone area, slim athletic pants, and low-profile sneakers. "
|
||||
"Lock the exact top color, bottom color, shoe color, neckline shape, sleeve/strap structure, seams, trim, fabric finish, fit, and accessories before rendering the first view, then repeat those same clothing decisions in every other view. "
|
||||
"Never add or remove a jacket, blazer, hoodie, coat, dress, skirt, scarf, hat, bag, jewelry, logo, stripe pattern, or extra layer in only one view. "
|
||||
"Back and side views must show the same garment wrapping around the same body, not a redesigned outfit. "
|
||||
(
|
||||
"PACK BIBLE - source-locked mode. "
|
||||
"Subject bible: use the attached source frame(s) as the primary identity and wardrobe reference for one same visible subject. "
|
||||
"Preserve the visible gender presentation, regional/ethnic appearance category, skin-tone family, age range impression, body-proportion category, hair length/color/silhouette, face-structure impression, posture energy, neck/shoulder readability, outfit category, garment colors, material finish, and accessory logic across every generated view. "
|
||||
"Do not replace the source subject with a different actor, different body type, different ethnicity, different gender, different hairstyle, different outfit, or generic wellness model. "
|
||||
"Remove only source-video artifacts such as background, captions, watermarks, platform UI, compression noise, and accidental occlusion; redraw missing angles as the same subject. "
|
||||
"Lock the exact top color, bottom color, shoe color, neckline shape, sleeve/strap structure, seams, trim, fabric finish, fit, and accessories before rendering the first view, then repeat those same clothing decisions in every other view. "
|
||||
)
|
||||
if req.reconstruction_mode == "same" else
|
||||
(
|
||||
"PACK BIBLE - this exact bible applies to every view in this generated set. "
|
||||
"Subject bible: one newly designed commercial wellness-ad subject; inherit only broad non-identifying casting traits from the source such as gender presentation, regional/ethnic appearance category, skin-tone family, age range, body-proportion category, hair-length family, posture energy, and neck/shoulder readability. "
|
||||
"Do not copy the source person's biometric identity, exact face, exact hairstyle, marks, tattoos, captions, logos, or watermarks. "
|
||||
"Keep the same new face design, same head shape, same hair color and hair silhouette, same skin tone, same body proportions, same height impression, and same character age across front, side, three-quarter, and back views. "
|
||||
"Wardrobe bible: if the user direction names a specific outfit, use that one outfit uniformly across every view. Otherwise use one clean SKG wellness-ad activewear outfit for the entire pack: fitted short-sleeve performance top with a visible neck/collarbone area, slim athletic pants, and low-profile sneakers. "
|
||||
"Lock the exact top color, bottom color, shoe color, neckline shape, sleeve/strap structure, seams, trim, fabric finish, fit, and accessories before rendering the first view, then repeat those same clothing decisions in every other view. "
|
||||
"Never add or remove a jacket, blazer, hoodie, coat, dress, skirt, scarf, hat, bag, jewelry, logo, stripe pattern, or extra layer in only one view. "
|
||||
"Back and side views must show the same garment wrapping around the same body, not a redesigned outfit. "
|
||||
)
|
||||
)
|
||||
neck_product_clause = (
|
||||
"This subject pack is for SKG neck-and-shoulder wearable massage device videos. "
|
||||
@@ -5840,6 +5865,7 @@ def _generate_subject_assets_sync(job_id: str, idx: int, element_id: str, req: G
|
||||
)
|
||||
models = SUBJECT_ASSET_IMAGE_MODELS
|
||||
model_preference = _normalize_image_model_preference(req.image_model_preference)
|
||||
reference_image_count = len(model_src) if isinstance(model_src, list) else (1 if model_src else 0)
|
||||
generated: list[SubjectAsset] = []
|
||||
generation_errors: list[str] = []
|
||||
first_generation_error: RuntimeError | None = None
|
||||
@@ -5872,14 +5898,30 @@ def _generate_subject_assets_sync(job_id: str, idx: int, element_id: str, req: G
|
||||
if closeup_view and req.subject_kind == "living"
|
||||
else "The subject must be complete, centered, full body or full object, head-to-feet visible when applicable, not cropped by the canvas. Make the subject large and readable: it should occupy about 88-94% of the image height, with the head close to the top margin and feet close to the bottom margin. No tiny character, no miniature person, no distant full-body figure, no large empty white margins. "
|
||||
)
|
||||
reference_strategy_clause = (
|
||||
"Text-only generation mode: no source image is attached to this image request. Use only the written source/video/template briefs below as creative constraints. "
|
||||
"This is intentionally NOT image editing and NOT identity replication. "
|
||||
+ source_subject_clause
|
||||
+ template_brief_clause
|
||||
if similar_mode else
|
||||
"Use the reference image(s) only as visual evidence; do not crop, cut out, paste, trace, or extract pixels from the source. "
|
||||
)
|
||||
if similar_mode and reference_image_count:
|
||||
reference_strategy_clause = (
|
||||
f"Image-conditioned reference reconstruction mode: {reference_image_count} selected source reference image(s) are attached to this request. "
|
||||
"First read the attached frames and the written source brief, then generate a new similar but non-identical subject. "
|
||||
"Use the images as visual evidence for broad role, gender presentation, regional/ethnic appearance category, skin-tone family, body proportion, hair family, outfit category/color family, pose language, and creator-ad energy. "
|
||||
"Do not copy exact face, biometric identity, unique marks, source pixels, captions, watermarks, or background. "
|
||||
+ source_subject_clause
|
||||
+ template_brief_clause
|
||||
)
|
||||
elif similar_mode:
|
||||
reference_strategy_clause = (
|
||||
"Text-only generation mode: no source image is attached to this image request. Use only the written source/video/template briefs below as creative constraints. "
|
||||
"This is intentionally NOT image editing and NOT identity replication. "
|
||||
+ source_subject_clause
|
||||
+ template_brief_clause
|
||||
)
|
||||
else:
|
||||
reference_strategy_clause = (
|
||||
f"Source-locked image reference mode: {reference_image_count} selected source reference image(s) are attached and are the primary visual evidence. "
|
||||
"Preserve the visible source subject's identity impression, proportions, silhouette, material, colors, wardrobe, styling, and distinctive non-artifact details across all generated views. "
|
||||
"Do not crop, cut out, paste, trace, or extract pixels from the source; redraw a clean production-ready asset of the same visible subject. "
|
||||
+ source_subject_clause
|
||||
+ template_brief_clause
|
||||
)
|
||||
prompt = (
|
||||
reference_strategy_clause
|
||||
+
|
||||
@@ -5904,7 +5946,15 @@ def _generate_subject_assets_sync(job_id: str, idx: int, element_id: str, req: G
|
||||
+ transparent_character_clause
|
||||
)
|
||||
try:
|
||||
if similar_mode:
|
||||
if similar_mode and model_src is not None:
|
||||
print(
|
||||
f"[subject assets] reconstruction_mode=similar endpoint=/images/edits view={view} image_refs={reference_image_count} model_preference={model_preference}",
|
||||
flush=True,
|
||||
)
|
||||
img_bytes, _mode = _image_edit_call(model_src, prompt, models=models, fallback_text=False, max_attempts=3, max_side=1280, force_fallback_model=pack_force_fallback_model, image_model_preference=model_preference)
|
||||
if model_preference == "auto" and _mode.endswith(f":{IMAGE_FALLBACK_MODEL}"):
|
||||
pack_force_fallback_model = True
|
||||
elif similar_mode:
|
||||
print(
|
||||
f"[subject assets] reconstruction_mode=similar endpoint=/images/generations view={view} image_refs=0 model_preference={model_preference}",
|
||||
flush=True,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -653,10 +653,14 @@ function emptySubjectPromptMemory(): Record<SubjectReconstructionMode, string[]>
|
||||
return { realistic: [], cartoon: [], elements: [], custom: [] }
|
||||
}
|
||||
|
||||
function loadSubjectPromptMemory(): Record<SubjectReconstructionMode, string[]> {
|
||||
function subjectScopedStorageKey(baseKey: string, jobId: string) {
|
||||
return `${baseKey}:${jobId}`
|
||||
}
|
||||
|
||||
function loadSubjectPromptMemory(jobId: string): Record<SubjectReconstructionMode, string[]> {
|
||||
if (typeof window === "undefined") return emptySubjectPromptMemory()
|
||||
try {
|
||||
const parsed = JSON.parse(window.localStorage.getItem(SUBJECT_PROMPT_MEMORY_KEY) || "{}") as Partial<Record<SubjectReconstructionMode, string[]>>
|
||||
const parsed = JSON.parse(window.localStorage.getItem(subjectScopedStorageKey(SUBJECT_PROMPT_MEMORY_KEY, jobId)) || "{}") as Partial<Record<SubjectReconstructionMode, string[]>>
|
||||
const next = emptySubjectPromptMemory()
|
||||
for (const mode of Object.keys(next) as SubjectReconstructionMode[]) {
|
||||
next[mode] = Array.isArray(parsed[mode]) ? parsed[mode]!.filter(Boolean).slice(0, SUBJECT_PROMPT_MEMORY_LIMIT) : []
|
||||
@@ -667,25 +671,25 @@ function loadSubjectPromptMemory(): Record<SubjectReconstructionMode, string[]>
|
||||
}
|
||||
}
|
||||
|
||||
function saveSubjectPromptMemory(memory: Record<SubjectReconstructionMode, string[]>) {
|
||||
function saveSubjectPromptMemory(jobId: string, memory: Record<SubjectReconstructionMode, string[]>) {
|
||||
if (typeof window === "undefined") return
|
||||
try {
|
||||
window.localStorage.setItem(SUBJECT_PROMPT_MEMORY_KEY, JSON.stringify(memory))
|
||||
window.localStorage.setItem(subjectScopedStorageKey(SUBJECT_PROMPT_MEMORY_KEY, jobId), JSON.stringify(memory))
|
||||
} catch {
|
||||
/* localStorage may be unavailable */
|
||||
}
|
||||
}
|
||||
|
||||
function loadSubjectImageModelPreference(): SubjectImageModelPreference {
|
||||
function loadSubjectImageModelPreference(jobId: string): SubjectImageModelPreference {
|
||||
if (typeof window === "undefined") return "auto"
|
||||
const raw = window.localStorage.getItem(SUBJECT_MODEL_MEMORY_KEY)
|
||||
const raw = window.localStorage.getItem(subjectScopedStorageKey(SUBJECT_MODEL_MEMORY_KEY, jobId))
|
||||
return SUBJECT_IMAGE_MODEL_OPTIONS.some((item) => item.value === raw) ? raw as SubjectImageModelPreference : "auto"
|
||||
}
|
||||
|
||||
function saveSubjectImageModelPreference(value: SubjectImageModelPreference) {
|
||||
function saveSubjectImageModelPreference(jobId: string, value: SubjectImageModelPreference) {
|
||||
if (typeof window === "undefined") return
|
||||
try {
|
||||
window.localStorage.setItem(SUBJECT_MODEL_MEMORY_KEY, value)
|
||||
window.localStorage.setItem(subjectScopedStorageKey(SUBJECT_MODEL_MEMORY_KEY, jobId), value)
|
||||
} catch {
|
||||
/* localStorage may be unavailable */
|
||||
}
|
||||
@@ -1216,6 +1220,24 @@ function buildSimilarSubjectPrompt(
|
||||
return base.join(" ")
|
||||
}
|
||||
|
||||
function buildSourceLockedSubjectPrompt(subjectStyle: SubjectStyleMode) {
|
||||
const base = [
|
||||
"Source-locked subject replication from the selected reference frames.",
|
||||
"Use the attached reference frame(s) as the primary visual source for the same visible subject: preserve gender presentation, regional/ethnic appearance category, skin-tone family, body proportions, hair length/color/silhouette, face structure impression, wardrobe category, outfit colors, fit, and commercial role as closely as the model allows.",
|
||||
"Generate separate clean white-background multi-view assets of that same source subject, removing only source video background, platform UI, captions, watermarks, compression artifacts, and accidental occlusions.",
|
||||
"Do not invent a different actor, different ethnicity, different gender, different body type, different hair design, or different outfit when the reference evidence is visible.",
|
||||
"If multiple frames are supplied, treat them as evidence for one same subject and build one locked subject bible before rendering every view.",
|
||||
"Keep the neck, collarbone, shoulders, upper back, and side neck clean and usable for SKG neck-and-shoulder product placement.",
|
||||
]
|
||||
if (subjectStyle === "cartoon_subject") {
|
||||
base.push("If a cartoon style is requested, convert the same visible source subject into one consistent stylized character while preserving the reference's main appearance and outfit cues.")
|
||||
} else {
|
||||
base.push("The subject must remain a believable normal commercial ad actor, not a transparent or skeleton character.")
|
||||
}
|
||||
base.push("Output high-definition assets; each image is one requested view of the same unified subject.")
|
||||
return base.join(" ")
|
||||
}
|
||||
|
||||
function subjectAssetUrl(job: Job, asset: SubjectAsset) {
|
||||
if (!asset.url && asset.status && asset.status !== "completed") return ""
|
||||
return apiAssetUrl(asset.url) || resolveImageRefUrl(job.id, { kind: "asset", frame_idx: 0, element_id: asset.id })
|
||||
@@ -3287,8 +3309,8 @@ function SourceSubjectPipeline({
|
||||
const [activeDropMode, setActiveDropMode] = useState<SubjectReconstructionMode | null>(null)
|
||||
const [conversionFrameIndicesByMode, setConversionFrameIndicesByMode] = useState<Record<SubjectReconstructionMode, number[]>>(() => ({ ...EMPTY_RECONSTRUCTION_FRAME_MAP }))
|
||||
const [reconstructionDirections, setReconstructionDirections] = useState<Record<SubjectReconstructionMode, string>>(() => ({ ...DEFAULT_RECONSTRUCTION_DIRECTIONS }))
|
||||
const [subjectImageModelPreference, setSubjectImageModelPreference] = useState<SubjectImageModelPreference>(() => loadSubjectImageModelPreference())
|
||||
const [promptMemoryByMode, setPromptMemoryByMode] = useState<Record<SubjectReconstructionMode, string[]>>(() => loadSubjectPromptMemory())
|
||||
const [subjectImageModelPreference, setSubjectImageModelPreference] = useState<SubjectImageModelPreference>(() => loadSubjectImageModelPreference(job.id))
|
||||
const [promptMemoryByMode, setPromptMemoryByMode] = useState<Record<SubjectReconstructionMode, string[]>>(() => loadSubjectPromptMemory(job.id))
|
||||
const [cartoonStyle, setCartoonStyle] = useState<CartoonReconstructionStyle>("3d_animation")
|
||||
const [cartoonStyleOpen, setCartoonStyleOpen] = useState(false)
|
||||
const [subjectBusyFor, setSubjectBusyFor] = useState<{ jobId: string; jobLabel: string; mode: SubjectReconstructionMode; viewCount: number; sourceCount: number; profileLabel: string; modelLabel: string } | null>(null)
|
||||
@@ -3383,6 +3405,8 @@ function SourceSubjectPipeline({
|
||||
useEffect(() => {
|
||||
setConversionFrameIndicesByMode({ ...EMPTY_RECONSTRUCTION_FRAME_MAP })
|
||||
setReconstructionDirections({ ...DEFAULT_RECONSTRUCTION_DIRECTIONS })
|
||||
setSubjectImageModelPreference(loadSubjectImageModelPreference(job.id))
|
||||
setPromptMemoryByMode(loadSubjectPromptMemory(job.id))
|
||||
setLastSubjectProfile(null)
|
||||
setSubjectBusyFor(null)
|
||||
setSubjectAssetBusy(null)
|
||||
@@ -3392,12 +3416,12 @@ function SourceSubjectPipeline({
|
||||
}, [job.id])
|
||||
|
||||
useEffect(() => {
|
||||
saveSubjectImageModelPreference(subjectImageModelPreference)
|
||||
}, [subjectImageModelPreference])
|
||||
saveSubjectImageModelPreference(job.id, subjectImageModelPreference)
|
||||
}, [job.id, subjectImageModelPreference])
|
||||
|
||||
useEffect(() => {
|
||||
saveSubjectPromptMemory(promptMemoryByMode)
|
||||
}, [promptMemoryByMode])
|
||||
saveSubjectPromptMemory(job.id, promptMemoryByMode)
|
||||
}, [job.id, promptMemoryByMode])
|
||||
|
||||
useEffect(() => {
|
||||
if (expandedSubjectPackKey && !subjectAssetPacks.some((pack) => pack.key === expandedSubjectPackKey)) {
|
||||
@@ -3455,17 +3479,23 @@ function SourceSubjectPipeline({
|
||||
const sourceFrames = sourceIndices
|
||||
.map((index) => frames.find((frame) => frame.index === index))
|
||||
.filter((frame): frame is KeyFrame => !!frame)
|
||||
const rawDirection = reconstructionDirections[mode].trim()
|
||||
const sourceLockedReplication = mode === "custom" && !rawDirection
|
||||
if (!sourceFrames.length && mode !== "custom") {
|
||||
toast.warning(`先把参考帧拖到${reconstructionModeConfig(mode).label}。`)
|
||||
return
|
||||
}
|
||||
if (!sourceFrames.length && sourceLockedReplication) {
|
||||
toast.warning("自主描述没有文字时,需要先拖入参考帧用于形象复刻。")
|
||||
return
|
||||
}
|
||||
const baseFrame = sourceFrames[0] ?? frames[0]
|
||||
if (!baseFrame) {
|
||||
toast.warning("先完成抽帧,或从胶片加入至少一张参考帧。")
|
||||
return
|
||||
}
|
||||
const requestJobId = job.id
|
||||
const requestProfile = mode === "custom" && reconstructionDirections.custom.trim()
|
||||
const requestProfile = sourceLockedReplication || (mode === "custom" && rawDirection)
|
||||
? null
|
||||
: buildSubjectProfileForRequest()
|
||||
const subjectStyle = reconstructionSubjectStyle(mode)
|
||||
@@ -3502,13 +3532,15 @@ function SourceSubjectPipeline({
|
||||
const updated = await generateSubjectAssets(requestJobId, baseFrame.index, element.id, {
|
||||
subject_kind: "living",
|
||||
subject_style: subjectStyle,
|
||||
reconstruction_mode: "similar",
|
||||
reconstruction_mode: sourceLockedReplication ? "same" : "similar",
|
||||
background: "white",
|
||||
size: SUBJECT_ASSET_SIZE,
|
||||
source_frame_indices: sourceFrames.slice(0, RECONSTRUCTION_FRAME_LIMIT).map((frame) => frame.index),
|
||||
views: selectedSubjectViews,
|
||||
subject_profile: requestProfile?.payload ?? null,
|
||||
prompt: buildSimilarSubjectPrompt(subjectStyle, userDirection, null, requestProfile),
|
||||
prompt: sourceLockedReplication
|
||||
? buildSourceLockedSubjectPrompt(subjectStyle)
|
||||
: buildSimilarSubjectPrompt(subjectStyle, userDirection, null, requestProfile),
|
||||
image_model_preference: subjectImageModelPreference,
|
||||
replace_views: false,
|
||||
pack_label: `${reconstructionModeConfig(mode).label} ${new Date().toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit", hour12: false })}`,
|
||||
@@ -3563,13 +3595,19 @@ function SourceSubjectPipeline({
|
||||
const sourceIndices = asset.source_frame_indices?.length
|
||||
? asset.source_frame_indices
|
||||
: conversionFrameIndicesByMode[mode]
|
||||
const rawDirection = reconstructionDirections[mode].trim()
|
||||
const sourceLockedReplication = mode === "custom" && !rawDirection
|
||||
if (!sourceIndices.length && mode !== "custom") {
|
||||
toast.warning("转换层没有参考帧,不能重生。")
|
||||
return
|
||||
}
|
||||
if (!sourceIndices.length && sourceLockedReplication) {
|
||||
toast.warning("自主描述没有文字时,需要参考帧才能复刻重生。")
|
||||
return
|
||||
}
|
||||
setSubjectAssetBusy(`regen:${asset.id}`)
|
||||
try {
|
||||
const requestProfile = mode === "custom" && reconstructionDirections.custom.trim()
|
||||
const requestProfile = sourceLockedReplication || (mode === "custom" && rawDirection)
|
||||
? null
|
||||
: lastSubjectProfile ?? buildSubjectProfileForRequest()
|
||||
const subjectStyle = reconstructionSubjectStyle(mode)
|
||||
@@ -3577,18 +3615,20 @@ function SourceSubjectPipeline({
|
||||
const updated = await generateSubjectAssets(job.id, frame.index, element.id, {
|
||||
subject_kind: "living",
|
||||
subject_style: subjectStyle,
|
||||
reconstruction_mode: "similar",
|
||||
reconstruction_mode: sourceLockedReplication ? "same" : "similar",
|
||||
background: asset.background || "white",
|
||||
size: SUBJECT_ASSET_SIZE,
|
||||
source_frame_indices: sourceIndices.slice(0, RECONSTRUCTION_FRAME_LIMIT),
|
||||
views: [asset.view],
|
||||
subject_profile: requestProfile?.payload ?? null,
|
||||
prompt: buildSimilarSubjectPrompt(
|
||||
subjectStyle,
|
||||
buildReconstructionDirection(mode, reconstructionDirections[mode], cartoonStyle),
|
||||
null,
|
||||
requestProfile,
|
||||
),
|
||||
prompt: sourceLockedReplication
|
||||
? buildSourceLockedSubjectPrompt(subjectStyle)
|
||||
: buildSimilarSubjectPrompt(
|
||||
subjectStyle,
|
||||
buildReconstructionDirection(mode, reconstructionDirections[mode], cartoonStyle),
|
||||
null,
|
||||
requestProfile,
|
||||
),
|
||||
image_model_preference: subjectImageModelPreference,
|
||||
replace_views: true,
|
||||
pack_id: asset.pack_id ?? "",
|
||||
|
||||
Reference in New Issue
Block a user