/projects/:id/pipeline).이미지는 단일 테이블 image_asset(backend/app/models/project.py:150)에 asset_type 디스크리미네이터로 구분된다.
| asset_type | 의미 | 핵심 키 |
|---|---|---|
floor_plan | FP(평면도) | entity_id=location |
reference | 인물/소품 레퍼런스 | entity_id |
chain_bg / background_chain_node | 배경 플레이트/체인 노드 | episode_id, (location) |
composite | 합성 | source_image_id |
scene | 씬 스틸 | still_id, episode_id |
참고: "스케치/포즈 가이드/구도 가이드"는 별도 asset_type이 아니라 파이프라인 단계 산출물이다. 이미지 자산으로 저장되는 한 위 타입 중 하나(주로 reference/체인 노드)로 들어오거나 image-time 생성물이다. 실제 저장 여부는 구현 시 확인 후 노드 포함 규칙에 반영한다.
| 소스 | 형태 | 신뢰도 |
|---|---|---|
source_image_id / parent_image_id (self-FK) | 이미지 UUID → 이미지 UUID | 정확(i2i/편집 체인) |
image_asset.reference_image_ids (lineage) | 이미지 UUID 배열(등장 인물/소품 ref) | "의도된" 참조 — 실제 attach와 다를 수 있음 |
llm_call_log (project.py:281) | 실제 attach 라벨 + 정확한 프롬프트(user_prompt) | SOT(실제 호출) — 단 ⚠️ output 이미지 FK 없음, 참조가 텍스트 라벨 |
핵심 난점. 파이프라인의 본체인 FP→배경플레이트→씬스틸 체인은 reference_image_ids(인물/소품만)에 담기지 않는다. 또 llm_call_log는 실제 attach의 SOT지만 (a) 산출 이미지로의 직접 FK가 없고(metadata_json의 still_id/entity_id로 조인), (b) 참조가 이미지 UUID가 아니라 텍스트 라벨("BACKGROUND chain reference" 등)로 저장된다. 따라서 "실제 사용된 연결고리"를 정확히 그리려면 백엔드에서 구조적 조인으로 엣지를 복원해야 한다. (사용자 결정: 정확 복원 채택)
| A. 백엔드 정확 복원 채택 | B. lineage만(프론트only) | C. 생성시 UUID 로깅 추가 | |
|---|---|---|---|
| 엣지 정확도 | 높음(구조적 조인+llm_call_log 검증) | 낮음(배경/FP 체인 누락) | 매우 높음(미래 데이터만) |
| 백엔드 변경 | 신규 read-only 엔드포인트 1개 | 없음 | 이미지 파이프라인 코어 수정 |
| 기존 데이터 | 복원 가능 | 제한적 | 소급 불가 |
| 리스크 | read-only, 메인 파이프라인 무접촉 | 오해 소지(가짜 정확도) | 핵심 경로 회귀 위험 |
채택: A. 읽기 전용 신규 엔드포인트로 기존 데이터에서 엣지를 구조적으로 복원하고 llm_call_log로 실제 attach 여부를 플래그한다. 메인 파이프라인을 건드리지 않아 회귀 위험이 없고, 정확도와 작업량의 균형이 가장 좋다. (C는 추후 별도 개선으로 분리 가능.)
GET /api/v1/projects/{project_id}/pipeline-graph?episode_id=<eid>
→ PipelineGraphResponse { nodes[], edges[], episodes[] }
신규 파일: backend/app/services/pipeline_graph_service.py(조립 로직, read-only), backend/app/schemas/pipeline.py(스키마). 라우트는 기존 backend/app/api/v1/images.py 또는 신규 pipeline.py 라우터(프로젝트 prefix 재사용).
{
id, asset_type, status,
entity_id, entity_short_id, entity_name, entity_type, // 그룹/색상용
still_id, scene_index, shot_index, episode_id,
variant_label, width, height, created_at,
thumb_url: /api/v1/projects/{pid}/images/{id}/file?thumb=1,
full_url: /api/v1/projects/{pid}/images/{id}/file,
prompt: image_asset.prompt_used // 노드 표시용
}
| kind | 복원 규칙(구조 키) | source→target |
|---|---|---|
i2i | source_image_id / parent_image_id 직접 | 부모 이미지 → 파생 이미지 |
reference | 씬 노드의 reference_image_ids(lineage, 자산 UUID) | 인물/소품 ref → 씬 |
background | (episode_id, location, scene/still) 구조 조인으로 그 씬의 배경 플레이트 자산 매칭 [구현시 background_chain_render_step 키 확인] | 배경 플레이트 → 씬 |
fp | 같은 location(entity_id)+episode의 floor_plan → chain_bg | FP → 배경 |
prev_scene | scene_still.dependent_scene_id → 직전 씬 대표 이미지 | 이전 씬 → 현재 씬 |
각 엣지에 actual_attached: bool 플래그를 붙인다 — llm_call_log를 metadata_json(still_id/entity_id)+operation_type으로 조인해 실제 호출에 attach됐는지 검증. 미확인 엣지는 점선/저채도로 구분 표시(가짜 정확도 방지, 정직성). 엣지별 prompt_authoritative(= llm_call_log.user_prompt)는 노드 상세에서 선택적으로 노출.
규칙 준수 라벨 텍스트의 글자/substring 매칭으로 의미를 추출하지 않는다. 오직 구조 키(episode_id·entity_id·still_id·location short_id·asset_type·created_at 순서)로 조인한다. llm_call_log.reference_image_ids 라벨은 검증 플래그 용도로만 쓰고 엣지 생성의 1차 소스로는 쓰지 않는다.
@xyflow/react (React Flow v12) 추가 — 노드-링크 캔버스, 팬/줌, 커스텀 이미지 노드, 선택/하이라이트/디밍을 노드·엣지 스타일로 제어. React 19 호환. 레이아웃 좌표 계산은 dagre(레이어드 DAG)로 1회 계산. (대안: SVG+d3-zoom 수제 — 팬/줌/드래그/레이아웃 재구현 부담 커서 비채택.)
레이어드 DAG(좌→우 또는 상→하): FP/ref(소스) → 배경 플레이트 → 씬 스틸 → 합성(싱크). dagre로 좌표 계산 후 React Flow에 주입.
썸네일(?thumb=1, lazy-load) + 타입 배지 + 엔티티/씬 라벨. 디밍 상태 = opacity 0.15.
| 파일 | 변경 |
|---|---|
frontend/src/pages/PipelineCanvas.tsx | 신규 페이지 |
frontend/src/hooks/api/usePipelineGraph.ts | 신규 react-query 훅 |
frontend/src/components/pipeline/PipelineNode.tsx, NodeDetailPanel.tsx, layout.ts | 신규 |
frontend/src/App.tsx | 라우트 추가(/projects/:id/pipeline) |
frontend/src/components/layout/Sidebar.tsx | 프로젝트 메뉴에 항목 추가 |
frontend/src/i18n/ko.json | nav.project.pipeline 라벨 |
frontend/package.json | @xyflow/react, dagre(+@types/dagre) 추가 |
llm_call_log 실제 attach와 교차 확인.image_asset.prompt_used와 일치, 패널에서 펼침 동작.python -m http.server --bind 0.0.0.0)로 산출물 공유 — open 금지.pipeline_graph_service + 스키마 + 엔드포인트.Codex가 설계서 + 관련 코드(load_background_chain_bg_map, scene_generation_coordinator.build_scene_attached_refs, background_render_step._register_image_assets 등)를 직접 추적해 A안을 승인하고 다음을 보강했다. 아래 내용이 §2·§4·§5·§6 대비 우선한다.
소비자 SOT는 load_background_chain_bg_map. 우선순위: background_render Phase 7 → background_chain_render Phase 5 → legacy.
data.groups[bg_id] = {status, png_path, shot_ids[], location_id}. shot_ids는 Sxx_Shotyy 형식 → loader가 sceneIndex_shotIndex 키로 펼침.build_scene_attached_refs(batch 포함)가 _bc_key = f"{scene_index}_{shot_index}"로 background_chain_bg_map 조회 → attach 시 attached_meta.insert(0,("background",bg_id)), ref_roles.insert(0,"background_chain_ref"), ref_role_metadata.insert(0,{"bg_id":bg_id}). bg_id는 구조 필드.background_render_step._register_image_assets → ImageAsset(asset_type="chain_bg", episode_id, entity_id=<location canon id>, variant_type=bg_id, file_path, prompt_used). Phase 5 planner도 동일(variant_type=group_id). legacy는 background_chain_node+node_id → fallback 분기.엣지 복원 순서(확정):
background_render.data.groups를 1차 SOT로 bg_id → png_path/location_id/shot_ids 읽기.image_asset에서 asset_type='chain_bg' AND episode_id AND variant_type=bg_id로 노드 매칭. 없으면 png_path 기반 virtual node(node_origin='checkpoint_virtual' 표기 — DB sync miss 숨기지 않음).shot_ids로 scene still (scene_index, shot_index)에 bg→scene 엣지.background_chain_render.data.groups → legacy data.locations[loc_id].shot_backgrounds[] fallback.entity_id == bg.location canon으로 연결. 실제 render가 floor_plan ref를 썼는지 catalog/ref_used 있으면 표시, 없으면 inferred.주의(과장 방지): bg_map 매핑이 있어도 close framing / dep-scene continuity로 실제 attach에서 background_chain_ref가 skip될 수 있다. bg→scene 엣지는 planned_by_bg_map=true와 verification_status를 분리하고, planned-only는 점선으로 표시.
각 엣지에 두 축의 메타를 단다 — actual_attached=true 같은 단정 대신 정직한 표기 사용.
direct_fk | checkpoint_structural | lineage_reference | call_log_label_seen | planned_only.structural_inferred | label_seen | unverified | conflict.llm_call_log는 actual_labels_seen / prompt_authoritative(user_prompt) / operation_type / still_id·entity_id 대조에만 사용. 라벨→정확한 ImageAsset UUID는 항상 증명 불가하므로 verified_by_call_log/label_seen_in_call 같은 약한 표현 사용.reference_image_ids 엣지는 character/prop lineage로만 명확히 라벨링 + UI에 "lineage(의도된 참조)" 작은 배지.중요 보정. 가이드/스케치류(outdoor composition guide, registered pose guide, visual_continuity_anchor sketch 등)는 image_asset row가 아니라 checkpoint 경로에만 존재할 수 있다. "모든 노드=이미지"(FP·스케치 등 전부) 요구를 만족하려면 노드 소스를 이중으로 둔다:
image_asset 기반(scene/reference/chain_bg/floor_plan/composite).node_origin='checkpoint_virtual' + png_path로 노드화.MVP에서는 ImageAsset 노드를 먼저 완성하고, virtual 노드(가이드/스케치)는 곧바로 후속 단계로 포함한다 — 단 설계상 이중 소스를 처음부터 전제한다(서비스 인터페이스가 두 소스를 합쳐 nodes[]를 만들도록).
background_render/background_chain_render.data.groups[bg_id].shot_ids 중심 — §9.1 반영 완료.chain_bg.variant_type=bg_id DB 매칭 + checkpoint virtual fallback — §9.1/9.3 반영 완료.