feat: 角色重跑参考当前立绘+提示词,保持人物一致 (row42)
原「重跑」立绘是纯文生图,每次重抽成另一个随机人。改为:角色已有当前立绘时, 重跑传该立绘作参考图 → 后端走 image_edit(参考立绘+提示词),保持同一人物相貌/发型/身份一致。 - generate_base_asset 新增 reference_asset_id;person 有参考图则 image_edit + build_person_portrait_prompt_refs - run_base_asset_task image_edit size 按 kind 区分(person 立绘竖 1024x1536,商品三视图横) - 全链路透传 reference_asset_id:pipeline.regenPortrait(传 viewPortraitAsset)→ genBaseAsset → onGenerateBaseAsset → api.generateBaseAsset → generate-base-asset 端点 场景重跑不受影响(仍文生图);需部署生效(后端)。 构建: tsc=0/build=0; py_compile OK; projects+assets 测试无新增回归(基线 4 fail 2 err)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -965,9 +965,10 @@ export function App() {
|
||||
}
|
||||
onAdoptVideoVersion={(segmentId, versionId) => action(() => api.adoptVideoVersion(pipelineProject.id, { video_segment_id: segmentId, version_id: versionId }), "已采用该版本")}
|
||||
onGenerateVoiceover={(payload) => action(() => api.generateVoiceover(pipelineProject.id, payload), "配音已生成")}
|
||||
onGenerateBaseAsset={async (kind, prompt, label) => {
|
||||
onGenerateBaseAsset={async (kind, prompt, label, referenceAssetId) => {
|
||||
// 异步:提交→轮询出图→刷新;返回新资产 id 供「立绘→三视图」链式生成
|
||||
const assetId = await submitAndPollAsset(() => api.generateBaseAsset(pipelineProject.id, { kind, prompt, label }), "基础资产已生成");
|
||||
// referenceAssetId:角色重跑时传当前立绘 → 后端走 image_edit 参考它,保持人物一致
|
||||
const assetId = await submitAndPollAsset(() => api.generateBaseAsset(pipelineProject.id, { kind, prompt, label, reference_asset_id: referenceAssetId }), "基础资产已生成");
|
||||
return assetId ? { adopted_asset: assetId } : null;
|
||||
}}
|
||||
onGenerateStoryboard={(prompt) =>
|
||||
|
||||
@@ -374,7 +374,7 @@ export const api = {
|
||||
return request<{ voiceover: VoiceoverInfo }>(`/api/projects/${projectId}/generate-voiceover/`, { method: "POST", body: JSON.stringify(payload) });
|
||||
},
|
||||
// 异步:提交后秒回 RESERVED 任务,再用 generateImageStatus 轮询取结果(慢出图在 worker 跑,Web 不被占住)
|
||||
generateBaseAsset(projectId: string, payload: { kind: "product" | "person" | "scene"; prompt: string; label?: string }) {
|
||||
generateBaseAsset(projectId: string, payload: { kind: "product" | "person" | "scene"; prompt: string; label?: string; reference_asset_id?: string }) {
|
||||
return request<{ task: { id: string; status: string } }>(`/api/projects/${projectId}/generate-base-asset/`, { method: "POST", body: JSON.stringify(payload) });
|
||||
},
|
||||
adoptBaseAsset(projectId: string, payload: { group_id: string; asset_id: string }) {
|
||||
|
||||
@@ -468,7 +468,7 @@ export function PipelinePage(props: {
|
||||
onSaveProjectMeta?: (meta: Record<string, unknown>) => Promise<unknown>;
|
||||
onAdoptVideoVersion: (segmentId: string, versionId: string) => Promise<unknown>;
|
||||
onGenerateVoiceover: (payload: { items: Array<{ index: number; text: string }>; voice_type?: string }) => Promise<unknown>;
|
||||
onGenerateBaseAsset: (kind: "product" | "person" | "scene", prompt: string, label?: string) => void | Promise<unknown>;
|
||||
onGenerateBaseAsset: (kind: "product" | "person" | "scene", prompt: string, label?: string, referenceAssetId?: string) => void | Promise<unknown>;
|
||||
onAdoptBaseAsset: (groupId: string, assetId: string) => void | Promise<unknown>;
|
||||
onSetAdoptState: (groupId: string, state: "adopted" | "unadopted") => void | Promise<unknown>;
|
||||
onDeleteBaseAsset: (groupId: string) => void | Promise<unknown>;
|
||||
@@ -757,11 +757,11 @@ export function PipelinePage(props: {
|
||||
const addBusy = (k: string) => setGenBusy((s) => { const n = new Set(s); n.add(k); return n; });
|
||||
const delBusy = (k: string) => setGenBusy((s) => { const n = new Set(s); n.delete(k); return n; });
|
||||
type GenResult = { id?: string; adopted_asset?: string | null } | null;
|
||||
async function genBaseAsset(kind: "product" | "person" | "scene", prompt: string, label: string | undefined, busyKey: string): Promise<GenResult> {
|
||||
async function genBaseAsset(kind: "product" | "person" | "scene", prompt: string, label: string | undefined, busyKey: string, referenceAssetId?: string): Promise<GenResult> {
|
||||
if (genBusy.has(busyKey)) return null; // 同一按钮防连点(不同按钮可并发)
|
||||
addBusy(busyKey);
|
||||
try {
|
||||
return (await onGenerateBaseAsset(kind, prompt, label)) as GenResult;
|
||||
return (await onGenerateBaseAsset(kind, prompt, label, referenceAssetId)) as GenResult;
|
||||
} finally {
|
||||
delBusy(busyKey);
|
||||
}
|
||||
@@ -3973,8 +3973,10 @@ export function PipelinePage(props: {
|
||||
const busyTri = isBusy(`addet-tri:${entity.key}`) || isBusy(`${pBK}:tri`);
|
||||
async function regenPortrait() {
|
||||
const prompt = adPrompt.trim() || grp.prompt || `${entity!.name},9:16 竖屏`;
|
||||
// 重跑立绘 → 只追加新立绘候选并采用;三视图改手动(用「生成三视图」按钮),不再链式自动出
|
||||
await genBaseAsset(isPerson ? "person" : "scene", prompt, entity!.name, pBK);
|
||||
// 重跑立绘 → 只追加新立绘候选并采用;三视图改手动(用「生成三视图」按钮),不再链式自动出。
|
||||
// 角色重跑:若该角色已有当前立绘,传它作参考图 → 后端走 image_edit 参考立绘+提示词,保持同一人物一致(不重抽随机人)。
|
||||
const ref = isPerson && viewPortraitAsset ? viewPortraitAsset : undefined;
|
||||
await genBaseAsset(isPerson ? "person" : "scene", prompt, entity!.name, pBK, ref);
|
||||
setAdPortraitId(null); setAdTriId(null);
|
||||
}
|
||||
async function regenTri() {
|
||||
|
||||
Reference in New Issue
Block a user