
    CPj%                         d Z ddlZddlZddlmZ ddlmZmZmZ ddl	m
Z
  ej                  e      ZdZdZ G d d	e
      Zy)
u  background_chain_planning StepRunner — set_design 교체용 신규 step (PR #3, 2026-04-27).

on_demand: 자동 run-all에서 실행 X. 사용자가 명시적으로 step을 트리거할 때만 동작.

Note: shot_validator 체크포인트는 shot_extract와 동일한 `data.scenes[].shots[]`
스키마를 보존한다 (shot_validator_step.py:7-8 contract). 따라서 본 step에서
`shot_extract_data` 인자에 shot_validator 결과를 그대로 전달해도 안전하다.
    N)Path)AnyDictOptional)
StepRunner   5c                       e Zd ZdZdefdZdedeeeef      fdZ	deeeef      fdZ
deeef   fdZddedeeef   fd	Zy
)BackgroundChainPlanningStepu7   Step: location별 배경 이미지 chain 트리 설계.returnc                     ddl }ddl}ddlm} |j                  |j
                  t        t        d}|j                  |j                  |d      j                  d            j                         dd S )	uT   SCHEMA_VERSION + PROMPT_VERSION 포함한 config_hash. resume 시 stale plan 회피.r   Nsettings)background_modemodelschema_versionprompt_versionT)	sort_keysutf-8   )hashlibjsonapp.core.configr   r   openai_modelSCHEMA_VERSIONPROMPT_VERSIONsha256dumpsencode	hexdigest)selfr   _jsonr   payloads        c/Users/manta/Documents/Projects/TheRoad-I1/backend/app/core/steps/background_chain_planning_step.py_config_hashz(BackgroundChainPlanningStep._config_hash   se    %,'77**,,	
 ~~KK4K077@

)+cr 	    step_idc                     ddl m} t        |j                        | j                  z  dz  dz  | j
                  z  |z  dz  }|j                         r%t        j                  |j                  d            S y )Nr   r   checkpointsepisodeszmanifest.jsonr   )encoding)
r   r   r   projects_dir
project_id
episode_idexistsr   loads	read_text)r!   r'   r   cps       r$   _load_prev_checkpointz1BackgroundChainPlanningStep._load_prev_checkpoint-   s    ,&&'oo  oo	
   	 99;::bllGl<==r&   c                    | j                  d      }|sy|j                  d      xs i }|j                  d      xs g }|j                  d      xs g }|syi }|D ]  }|j                  dd      }|s|||<    |t        |      dS )	u  background_planner 체크포인트에서 chain_bg_groups + chain_bg_order 로드.

        Phase 5: planner cp가 있고 chain_bg_groups가 비어있지 않으면 planner-driven
        path로 진입한다. 없으면 None → legacy `_phase0_group_by_location` fallback
        (Phase 4 회귀 보장).

        Returns:
            None — planner cp가 없거나 chain_bg_groups가 비어있을 때
            {"groups": {gid: spec}, "order": [gid, ...]} — planner-driven 입력
        background_plannerNdatachain_bg_groupschain_bg_orderid )groupsorder)r3   getlist)r!   r2   r6   r7   r8   groups_by_idggids           r$   _load_planner_groupsz0BackgroundChainPlanningStep._load_planner_groups<   s     ''(<=vvf~#((#45;"239r') A%%b/C$%S! ! 'n1EFFr&   c                    | j                  d      }|si S |j                  di       xs i }i }|j                  d      xs i }t        |t              r|j	                         D ]  }t        |t              s|j                  d      dk7  r)|j                  d      xs dj                         }|sO|j                  d      xs g D ]  }t        |t              s|s|||<     |s^|j                  d	g       xs g D ]E  }|j                  d      dk7  r|j                  d      xs dj                         }|s>|||d
   <   G |S )ul  location_floor_plan checkpoint에서 status='ok' + non-empty prompt_text 매핑.

        Phase 4: chain_bg_planning user_prompt에 도면 prompt를 prepend하여 LLM이
        floor plan 인식한 chain bg prompt 생성. mode=off / chain_only 시 빈 dict.

        Phase 5: building_group의 모든 멤버 location_id에 동일 prompt를 매핑.
        (fp_rooftop_unit이 L05+L04+L10+L11을 커버하면 4 키 모두 같은 텍스트.)
        외부/서브룸 location의 chain_bg_planning에도 [FLOOR PLAN] 블록이 prepend되어
        outdoor skip 룰 회피. data.floor_plans (신 shape) 우선, 없으면 data.locations
        (legacy compat alias)로 fallback.

        ⚠️ 순서 의존성: chain_bg_planning(order 19.6)이 location_floor_plan(order 21.5)
        보다 작은 order이지만, 둘 다 applicability=on_demand이므로 사용자가 명시
        트리거 시점에는 location_floor_plan 체크포인트가 이미 존재한다 (run-all →
        사용자 트리거 순서). chain_bg_planning의 applicability를 always로 바꾸면 이
        prepend가 silently 깨질 수 있다.
        location_floor_planr6   floor_plansstatusokprompt_textr:   location_ids	locationsr9   )r3   r=   
isinstancedictvaluesstripstr)	r!   r2   r6   outrE   fptextlidlocs	            r$   _load_floor_plan_promptsz4BackgroundChainPlanningStep._load_floor_plan_promptsW   sH   $ ''(=>Ivvfb!'R  hh}-3k4(!((*!"d+66(#t+}-3::<66.17R7C!#s+#'C 8 + xxR06B6778$,.4";;=!%CI 7 
r&   modec                 \   ddl m} | j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d      }| j                  d	      }	d|fd|fd|fd|ff}
|
D ]&  \  }}|r|j                  d
      r |d| dd       ddlm} | j                         }| j                         } ||j                  d
i       |r|j                  d
i       ni |r|j                  d
i       ni |r|j                  d
i       ni |r|j                  d
i       ni |r|j                  d
i       nd |	r|	j                  d
i       nd | j                         ||
      }|j                  d      }|,t        |      }t        d |j                         D              }n=|j                  di       }t        |      }t        d |j                         D              }|j                  dd      }|||t        | j                         |dS )Nr   )AppErrorshot_validatorshot_selectionshot_stagingscene_directorentity_mergeentity_detailvisual_world_rulesr6   zstep.no_inputu    체크포인트 없음 — background_chain_planning은 shot_validator/shot_selection/scene_director/entity_merge 4개를 필수로 요구합니다. 분석 단계를 먼저 실행하세요.i  )codemessagestatus_code)run_background_chain_planning)
shot_extract_datashot_selection_datashot_staging_datadirector_dataentity_merge_dataentity_detail_dataworld_rules_dataopik_metadatafloor_plan_promptsplanner_groupsr;   c              3   F   K   | ]  }|j                  d       dv rd  ywrF   )rG   skipped   Nr=   .0vs     r$   	<genexpr>z7BackgroundChainPlanningStep._execute.<locals>.<genexpr>   s(      *a55?&77 ?   !rJ   c              3   F   K   | ]  }|j                  d       dv rd  ywro   rr   rs   s     r$   rv   z7BackgroundChainPlanningStep._execute.<locals>.<genexpr>   s)      -a55?&77 -rw   _failed_count)completed_countapplicable_countfailed_countr   config_hashr6   )app.core.errorsrX   r3   r=   .app.modules.pipeline.background_chain_planningrc   rU   rB   build_opik_metadatalensumrM   popr   r%   )r!   rV   rX   shot_cpsel_cp
staging_cpdirector_cpmerge_cp	detail_cprules_cprequirednamer2   rc   rl   rm   resultr;   
applicableok_countrJ   faileds                         r$   _executez$BackgroundChainPlanningStep._execute   sQ   , ,,-=>++,<=//?
001AB--n=..?	--.BC w'v&{+X&	
 !HD"RVVF^(& !^ _ !$  !	

 "::<224.%kk&"5:@

62 6b<FjnnVR8B9D+//&"5":Bhll626<Ey}}VR849AX\\&"5t2241)
 H%VJ !==? H
 

;3IYJ $++- H OQ/  ( *",,,.
 	
r&   N)resume)__name__
__module____qualname____doc__rO   r%   r   r   r   r3   rB   rU   r    r&   r$   r   r      s    Ac S Xd38n5M GhtCH~&> G60$sCx. 0dP
S P
S#X P
r&   r   )r   r   loggingpathlibr   typingr   r   r   app.core.step_runnerr   	getLoggerr   loggerr   r   r   r   r&   r$   <module>r      sL       & & +			8	$
  }
* }
r&   