
    Ojv                       d Z ddlmZ ddlZddlZddlZddlZddlmZ ddl	m
Z
mZmZmZmZ ddlmZ ddlmZ ddlmZmZmZmZmZmZmZ  ej6                  e      Zd	Zd
Zddl m!Z"  ejF                  d      Z$g dZ% G d de      Z&y)u  W21B (2026-06-08): DwellingZoneMapStep — FP-image + VLM zone grouping.

opt-in (default OFF) producer of a per-dwelling **zone map** that bypasses the
edge-judge (``bg_space_partition``), which cannot group same-dwelling bgs framed
from different camera angles. For each interior dwelling (bgs grouped by their
floor-plan fp_id) the step:

  1. assembles a per-bg structural description (priority: shot_projection_card
     plate prose → freshness-verified background_prompt t2i → master-plan
     catalog labels; dossier structural facts attached as enrichment when
     present);
  2. (real provider) distils the base-set rooms (gpt-5.5), writes a clean B&W
     2D floor-plan prompt (gpt-5.5), renders it (gpt-image-2 text-to-image — a
     NEW clean plan, the distorted ``floor_plan_render`` PNG is NOT reused),
     then a gpt-5.5 VISION call maps each bg to its plan zone + grid focus;
  3. deterministically joins that into the zone-map contract (zones + bg/shot
     zone assignments), draws a SEPARATE annotated FP (shot-number overlay,
     ``must_not_be_used_for_render``) for mapping verification only, and
     records a diagnostic comparison against the edge-judge grouping.

The CLEAN render ref and the ANNOTATED (numbered) ref are kept strictly apart
so no downstream i2i anchor can grab the numbered image (number-leak guard).
This step does NOT yet feed any renderer — it only produces + validates the
contract (Phase 1); the background_render zone-1-plate wiring is Phase 2.

Gates (all true, else ``not_applicable`` with byte-stable empty data):
  - ``settings.background_mode`` ∈ {"on", "floor_plan_anchored"}.
  - ``settings.dwelling_zone_map_enabled`` = True.
  - ``settings.floor_plan_prompt_version`` ∈ {"6", "7"}.

When enabled but ``dwelling_zone_map_real_provider_enabled`` is False, every
dwelling gets the non-authoritative synthetic fixture (no LLM/VLM/image call) —
the plumbing path. A per-dwelling provider failure falls back to synthetic for
that dwelling only (never a step failure).
    )annotationsN)Path)AnyCallableDictListOptional)
StepRunner)capture_artifact)assemble_zone_map%build_analysis_from_numbered_elementsbuild_analysis_from_space_modelcompare_to_edge_judgecompute_synthetic_zone_mapselect_dwelling_targetsvalidate_vlm_zone_output   1) V6_COMPATIBLE_FP_PROMPT_VERSIONSz^[a-z0-9][a-z0-9_]*$))   (   r   )r   x   r   )r      P   )         )   <   r   )r      r    c                     e Zd ZU dZded<   dZded<   dZded<   dZded<   dZd	ed
<   dZ	ded<   dddddd	 ddZ
d dZd!dZd"dZe	 	 	 	 	 	 d#d       Zed$d       Zdd	 	 	 	 	 	 	 	 	 	 	 d%dZd Zd&dZ	 	 	 	 	 	 	 	 d'dZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d(dZd)d*dZe	 	 	 	 d+d       Ze	 	 	 	 d,d       Ze	 	 	 	 d-d       Ze	 	 	 	 	 	 d.d       Zy)/DwellingZoneMapStepNz'Optional[Callable[..., Dict[str, Any]]]_analyze_overridezOptional[Callable[..., str]]_fp_prompt_override_render_overridez-Optional[Callable[..., List[Dict[str, Any]]]]_vlm_overridez&Optional[Callable[..., Optional[str]]]_annotate_overrider   _client)analyze	fp_promptrendervlmannotatec               J    || _         || _        || _        || _        || _        y)zaMocking helpers for the experimental LLM/VLM/image path. Production
        must NOT touch these.N)r#   r$   r%   r&   r'   )selfr)   r*   r+   r,   r-   s         [/Users/manta/Documents/Projects/TheRoad-I1/backend/app/core/steps/dwelling_zone_map_step.pyset_overrides_for_testingz-DwellingZoneMapStep.set_overrides_for_testingV   s,    
 ")#,  & "*    c                L   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 # t        $ r!}t        j                  d||       Y d }~y d }~ww xY w)	Nr   settingscheckpointsepisodeszmanifest.jsonutf-8)encodingz&dwelling_zone_map: %s parse failed: %s)app.core.configr5   r   projects_dir
project_id
episode_idexistsjsonloads	read_text	Exceptionloggerwarning)r/   step_idr5   cpexcs        r0   _load_prev_checkpointz)DwellingZoneMapStep._load_prev_checkpointb   s    ,&&'$//9MI??+-457FG 	 99;Wzz",,,"@AA   WGRUVVWs   $A9 9	B#BB#c                   ddl m} ddlm}m} |j
                  |j                  t        t        |dd            t        t        |dd            t        |dd      t        |d	d      t        |d
d      t        t        ||d}t        j                  t        j                  |d      j                  d            j                         d d S )Nr   r4   )PROMPT_VERSIONPROVIDER_VERSIONdwelling_zone_map_enabledF'dwelling_zone_map_real_provider_enableddwelling_zone_map_text_model dwelling_zone_map_vision_modeldwelling_zone_map_render_model)background_modefloor_plan_prompt_versionenabledreal_provider
text_modelvision_modelrender_modelschema_versionprompt_versionprovider_versionprovider_prompt_versionT)	sort_keysr8      )r:   r5   /app.modules.pipeline.dwelling_zone_map_providerrJ   rK   rR   rS   boolgetattrSCHEMA_VERSIONhashlibsha256r?   dumpsencode	hexdigest)r/   r5   PROVIDER_PROMPT_VERSIONrK   payloads        r0   _config_hashz DwellingZoneMapStep._config_hasho   s    ,	

  (77)1)K)KGH.I5QR!"KUSU!(,JBO#H.NPRS#H.NPRS,, 0'>
 ~~JJw$/66w?

)+cr 	r2   c                8    dddt         | j                         i dS )Nr   applicable_countcompleted_countfailed_countrY   config_hashdata)rb   rj   )r/   s    r0   _not_applicablez#DwellingZoneMapStep._not_applicable   s'     ! ,,,.
 	
r2   c                v   t        | t              si S | j                  d      xs i }|j                  d      }|r||k7  ri S i }|j                  d      xs i j                         D ]O  \  }}|xs i j                  d      }t        |t              s,|j                         s=|j                         ||<   Q |S )u  background_prompt t2i prompts, but ONLY when the checkpoint is FRESH
        for the current catalog (``consumed_bg_catalog_hash`` == master_plan
        ``bg_catalog_hash``). background_prompt is order 21.60 — AFTER this step
        — so on a fresh run it is absent, and on a re-run a STALE checkpoint may
        linger; either way it must never be a blind primary source (Codex W21B
        review #2). When the hash cannot be confirmed fresh, returns ``{}``.rq   consumed_bg_catalog_hashbackgrounds
t2i_prompt)
isinstancedictgetitemsstrstrip)bg_prompt_cpmaster_bg_catalog_hashrq   consumedoutbidspecprompts           r0   _fresh_t2i_by_bgz$DwellingZoneMapStep._fresh_t2i_by_bg   s     ,-I'-28867%5K)KI ((=17R>>@ICjb%%l3F&#&6<<>!<<>C A 
r2   c                (   t        | t              syi }| j                         D ]g  \  }}t        |t              s t        |t              rt        |t              s|||<   <t        |t
              sM|sPt        d |D              sc|||<   i |xs dS )u4  Pure structural (numeric/bool) subset of a dossier ``per_bg_render
        _facts`` entry — marker-number lists, counts, flags. Type-filtered only
        (no field-name hardcoding, no prose, no semantic inference) so it serves
        as generic structural context the VLM can use to disambiguate a plate.Nc              3  b   K   | ]'  }t        |t              xr t        |t                ) y w)N)rw   intr`   ).0xs     r0   	<genexpr>z8DwellingZoneMapStep._structural_facts.<locals>.<genexpr>   s-      3HI1
1c">:a+>'>>s   -/)rw   rx   rz   r`   r   listall)factsr   kvs       r0   _structural_factsz%DwellingZoneMapStep._structural_facts   s     %& KKMDAq!T"z!S'9*QPTBUAAt$s 3HI3 0 A " {dr2   )dossier_factsc               r   i }t        |t              r|j                  d      xs i j                  d      xs i }|j                         D ]  \  }}	t        |	t              s|	j                  d      }
|	j                  d      xs i }|j                  d      }t        |
t              s_t        |t              sp|j                         s|j                  |
g       j                  |j                                 g }|D ]  }||v rdj                  ||         d}}nK||v r||   d}}n?|j                  |      xs i d	j                  fd
dD              j                         }d}|||d}| j                  |xs i j                  |            }|r||d<   |dz   |d<   |j                  |        |S )u  Per-bg structural text for the VLM bg→zone mapping. Primary =
        shot_projection_card plate prose (a real upstream dep, freshness-safe at
        this order); secondary = FRESHNESS-VERIFIED background_prompt t2i;
        fallback = master-plan catalog labels. When base_location_dossier ran,
        each bg's pure structural facts are attached as ``structure_facts`` (the
        provider serializes them into the VLM prompt) — real enrichment, never
        the primary signal. The VLM is told to ignore transient events, so even
        the event-bearing catalog text is usable as a degraded last resort.
        (Codex W21B review #2: background_prompt is never a blind primary.)rq   cardsbg_id
vlm_outputbg_plate_visible_descriptionz / projection_cardbackground_prompt_fresh c              3  Z   K   | ]"  }t        j                  |      xs d        $ yw)rO   N)r{   ry   )r   r   es     r0   r   z@DwellingZoneMapStep._assemble_bg_descriptions.<locals>.<genexpr>   s'       1ZACaB'1Zs   (+)sub_location_labelstate_label_rawcatalog_label)r   descriptionsourcestructure_factsz+dossierr   )
rw   rx   ry   rz   r{   r|   
setdefaultappendjoinr   )r/   bg_idscatalogprojection_cpfresh_t2i_by_bgr   plate_by_bgr   _cidcardr   r,   descblocksr   srcblockstructr   s                     @r0   _assemble_bg_descriptionsz-DwellingZoneMapStep._assemble_bg_descriptions   s   $ -/mT*#''/52::7CIrE#kkm
d!$-hhw'hh|,2ww=>c3'JtS,Adjjl**33::4::<H , (*E#!JJ{5'9:<Mc/)+E24McKK&,"xx  1Z  %'  &.3DTW$XE++]-@b,E,Ee,LMF+1'("%
"2hMM% ! " r2   c                R    ddl m} ddlm}  |t	        |j
                              S )Nr   )openai_clientr4   )timeout)app.core.openai_keysr   r:   r5   floatllm_timeout_image_gen)r/   r   r5   s      r0   _resolve_openai_clientz*DwellingZoneMapStep._resolve_openai_client   s#    6,(889
 	
r2   c          	     ,   | j                   | j                  |||      S ddlm} ddlm} | j
                  | j                         | _         || j
                  t        |dd      ||g |d      }|j                  |j                  |j                  d	S )
Nr   out_pathfp_idr   r4   )render_one_floor_planrQ   zgpt-image-2.5-sunburst	1536x1024)r   image_modelr   r   	ref_pathsr   size)statuspng_patherror)r%   r:   r5   &app.modules.pipeline.floor_plan_renderr   r(   r   ra   r   r   r   )r/   r   r   r   r5   r   ress          r0   _render_clean_fpz$DwellingZoneMapStep._render_clean_fp   s      ,((QV(WW,P<<668DL#,,*JLde
 **#,,SSr2   c                  | j                   | j                  |||      S 	 ddlm}m} 	 |j                  |      j                  d      }|j                  \  }}	|j                  |      }
i }|j                  d      xs i j                  d	d
      xs d
}|j                  d      xs i j                         D ]  \  }}|j                  d      xs i }t        |j                  d      t        t        f      sA|j                  d      xs d}||vr%t         t#        |      t#        t               z     ||<   ||   }|d   |z  |z  }|d   |z  |	z  }d}|
j%                  ||z
  ||z
  ||z   ||z   g|dd       |
j'                  |dz
  |dz
  f|dd d        |j(                  j+                  dd       |j-                  |       t/        |j1                         ddddi       t3        |      S # t        $ r }t
        j                  d|       Y d}~yd}~ww xY w# t        $ r }t
        j                  d|       Y d}~yd}~ww xY w)u   Draw a SEPARATE numbered overlay (one circle per bg at its grid
        focus, coloured by zone) — verification only, never a render anchor.N	clean_pngr   zone_mapr   )Image	ImageDrawz7dwelling_zone_map: PIL unavailable, skip annotation: %sRGBz+dwelling_zone_map: annotate open failed: %sgridr   d   bg_zone_assignments
grid_focusr   zone_id?yr^   )   r   r      )filloutlinewidth      )r   Tparentsexist_okdwelling_zone_annotated_fp
diagnosticverification_only)roledispositionpipeline_metadata)r'   PILr   r   rB   rC   rD   openconvertr   Drawry   rz   rw   r   r   _ZONE_PALETTElenellipsetextparentmkdirsaver   
read_bytesr{   )r/   r   r   r   r   r   rG   fpwhdraw
zone_colorr   r   agzidcr   r   rads                        r0   _annotate_fpz DwellingZoneMapStep._annotate_fp  sP   
 "".**#h + K K	,	I&..u5B ww1~~b!%'
V$*//<C!&;<BIIKHE1l#)rAaeeCj3,7%%	")cC*$"/J#mBT0T"U
33A#!A#!ACLL!c'1s7AGQW=  CIIq2vq1uouRSzIH L 	dT:
 	!-$2D9		
 8}I  	NNTVYZ	
  	NNH#N	s.   G3  H 3	H<HH	I(IIc               @   ddl m}	 ddlm}
 ddlm} t        |	d|j                        }t        |	d|j                        }t        |t              xr, t        t        |t              r|j                  d      nd      }|st        |      nd	g i}t        |j                  d	            }| j                  | j                  ||
      }n&|rt        |      }n|r|}n|j!                  ||      }|j                  d	      xs g D cg c]6  }t        |t              r$|j                  d      xs |j                  d      8 }}| j"                  | j#                  ||      }n|j%                  |r|nd||      }|| dz  }| j'                  |||      }|j                  d      dk7  s-|j                  d      rt)        |d         j+                         st-        d|j                  d            t)        |d         } |
t/        |            }| j0                  | j1                  t/        |      |      }n=|j3                  t/        |      ||D cg c]  }t        |t.              s| c}|      }t5        ||      }|d   st-        d|d   dd        t7        ||d   ||d|      }|d   }|| d z  }dd!lm}  || j<                  | j>                  d"#      5  | jA                  |||$      }ddd       r |
|      nd} | |d%   d&<   tC        ||'      |d(   d)<   tE        d* |j                  d      xs i jG                         D              }!|!|d(   d+<   |rd,n|rd-nd.tI        |      |||D "ci c]#  }"|"j                  d/      |"j                  d0      % c}"|D "cg c]%  }"|"j                  d1      s|"j                  d/      ' c}"||t        |tJ              rtI        |      ndd2	}#||#d3S c c}w c c}w # 1 sw Y   xY wc c}"w c c}"w )4a4  Returns ``{"zone_map", "provenance"}`` or raises on any provider miss
        (caller falls back to synthetic for this dwelling). ``bg_blocks`` already
        carry any dossier ``structure_facts`` (attached in
        ``_assemble_bg_descriptions``); the provider serializes them into the VLM
        prompt.r   r4   )to_relative_image_path)dwelling_zone_map_providerrN   rP   zonesNrooms)	bg_blocksspace_model)r  modellabelname)r  space_analysis)r  r  r  z
_clean.pngr   r   okr   zclean FP render failed: r   )fp_image_pathr  )r
  r  room_labelsr  )rawexpected_bg_idszVLM zone output invalid: blockers   
normalized)r   vlm_normalizedbg_shot_mapclean_fp_refannotated_fp_refr  r   z_annotated.png)generation_contextdwelling_zone)stager   r  ref)r   partition_plandiagnosticsedge_judge_comparisonc              3  L   K   | ]  \  }}|j                  d       sg s|  yw)structure_cuesN)ry   )r   r   zs      r0   r   z9DwellingZoneMapStep._run_real_dwelling.<locals>.<genexpr>  s+      4CFCEE*+r Cs   "$zones_without_structure_cuesr  numbered_elementsbg_blocks_fallbackr   r   r   )	structure_sourcespace_room_countspace_roomsr*   bg_description_sourcesdossier_enriched_bgsrV   rW   vlm_raw_count)r   
provenance)&r:   r5   app.core.file_pathsr   app.modules.pipeliner   ra   TEXT_MODEL_DEFAULTVISION_MODEL_DEFAULTrw   rx   r`   ry   r   r#   r   analyze_dwelling_spacer$   build_clean_fp_promptr   r   r>   RuntimeErrorr{   r&   map_bgs_to_zonesr   r   "app.services.image_capture.contextr  r<   r=   r   r   sortedrz   r   r   )$r/   r   r   r  r  r  r   r  out_dirr5   r   provrV   rW   used_space_modelnumbered_analysisused_numberedanalysisrr  r*   
clean_pathr+   r   	clean_refr  val	assembledr   
annot_pathr  	annot_abs	annot_ref
cues_emptybr(  s$                                       r0   _run_real_dwellingz&DwellingZoneMapStep._run_real_dwelling8  sw   " 	->KX'EtG^G^_
x)I4KdKde &k48 ST)3K)F[__W%D>S $ 22CD*12 	 .227;<!!---	{-[H6{CH(H22Yj2YH ll7+1r1
1!T" EE'N+aeeFm+1 	 
 ##/00[Ya0bI22+;K'z 3 ;I
 %
33
&&i*TY&Z::h4'

:0F4PVWaPbKcKjKjKl!9&**W:M9PQRR
+,	*3y>:	 )$$3y>Y$WC''!)n	(3J1z!S7IQJ" ( $C '3G4y!:3z?2A;N:OPQQ &L(9{"T(T	 Z( %77
IOOT__O
 ))I
]e)fI
 :C*95	.7#$U+ <Qn<> 78  4'||G4:AAC4 4
 CM >? "2,9() #K 0&"9B'D9BAgh/'D )2%O(11QUU;L5Mg	%O$()3C)>SXA

  %J??a
8 K&
 
2'D%Os0   ;O?P.P'P	?(P-PP	Pc                   ddl m} |j                  dvr| j                         S t	        t        |dd            s| j                         S |j                  t        vr| j                         S | j                  d      }|xs i j                  d      xs i }|j                  d      xs i }|s| j                         S |j                  d	      }t        |
      }|j                         D 	ci c]  \  }}	|	d   s||	 }
}}	|
s| j                         S t	        t        |dd            }|r| j                  d      nd }| j                  |      }| j                  |      }| j                  |r| j                  d      nd       }|r| j                  d      nd }| j                  |r| j                  d      nd |      }| j                  d      }| j!                  ||      }t#        |j$                        | j&                  z  dz  dz  | j(                  z  dz  dz  }|r|j+                  dd       i }i }d}d}d}t-        |
j                               D ].  \  }}|d   }|D ci c]4  }|t/        |j                  |      xs i j                  d      xs g       6 }}|st1        |||      ||<   |dz  }dt2        j5                  |      s-t6        j9                  d|       t1        |||      ||<   |dz  }| j;                  |||||D ci c]  }||v s|||    c}      }	 | j=                  |||||j                  |      |j                  |      |j                  |      |      } | d   ||<   | d    ||<   |dz  }1 tG        |      tG        |      dtH        | jK                         |||||||j                         D 	ci c]  \  }}	|	d   s||	d&    c}	}d'd(S c c}	}w c c}w c c}w # t>        $ r]}!t6        jA                  d!||!       t1        |||      }"tC        |!      jD                   d"|! d d# |"d$   d%<   |"||<   |dz  }Y d }!~!d }!~!ww xY wc c}	}w ))Nr   r4   >   onfloor_plan_anchoredrL   Fbackground_master_planrq   background_catalogbg_catalog_hash)rH  
applicablerM   floor_plan_promptbase_location_dossiershot_projection_cardbackground_promptbg_space_partitionr6   r7   dwelling_zone_map
fp_rendersTr   r   applies_to_shots)r   r   r  r   u0   dwelling_zone_map: unsafe fp_id %r — synthetic)r   r   r   r   r   )r   r   r  r  r  r   r  r3  r   r(  z.dwelling_zone_map: real path failed for %s: %sz: r   r  fallback_reasonnot_applicable_reason)per_dwellingr(  rU   
real_countsynthetic_countfallback_countnot_applicable_dwellingsrl   )&r:   r5   rR   rr   r`   ra   rS   _SUPPORTED_FP_PROMPT_VERSIONSrH   ry   r   rz   _space_models_by_fp_numbered_elements_by_fp_dossier_facts_by_bgr   _partition_plans_by_fpr   r;   r<   r=   r   r2  r   r   _SAFE_FP_REmatchrC   r   r   rC  rB   rD   type__name__r   rb   rj   )#r/   moder5   mp_cpmp_datar   r~   targetsr   trJ  realfp_prompt_cpspace_modelsnumbered_by_fpr   r   	fresh_t2ipartition_cppartition_plansr3  rU  r(  rV  rW  rX  r   targetr   rB  r  r  resultrG   zms#                                      r0   _executezDwellingZoneMapStep._execute  s   ,##+HH''))GH&A5IJ''))--5RR''))**+CD;B##F+1r++239r''))!(->!?)WE)0LAAlOb!e
L''))GH&OQVWX KOt112EFTX//=66|D11CGD&&'>?TSNR223IJX\))?CD&&':;"$	 112FG55lGL &&'$//9MI??+-@ACOP 	 
 MM$M6')%'

#J$4$4$67ME6H%F  A 4Q-2223EFL"MM   &@K'IU#1$$$U+OQVW&@K'IU#!#66wm )<BYFqa=FXq-"22FY 7 [I$00K'\5E5Ee5L&4&8&8&?#2#6#6u#=w	 1 P
 '-Z&8U#$*<$8
5!a
; 8R !$L 1"<0,,,. ,(!%(#2"0 ")-!0A, 122!0-
 	
W MN$ Z  $OQVX[\/KI:>s):L:L9MRPSu7UVZWZ7[=!"34&(U#!#$.-s>   .O(<O('9O.	O3O3+AO8Q!8	QAQQc                D   i }t        | t              s|S | j                  d      xs i j                  d      xs i }|j                         D ]P  \  }}|xs i j                  d      }t        |t              s,t        |j                  d      t              sL|||<   R |S )u   Per-fp ``space_model`` from the floor_plan_prompt checkpoint — the
        canonical dwelling structure SOT (every zone of the dwelling).rq   floor_plansr  r  rw   rx   ry   rz   r   )ri  r   rt  r   r   sms         r0   r[  z'DwellingZoneMapStep._space_models_by_fp+  s     *,,-J$((06B;;MJPb&,,.KE4*"!!-0B"d#
266'?D(IE
 / 
r2   c                
   i }t        | t              s|S | j                  d      xs i j                  d      xs i }|j                         D ]3  \  }}|xs i j                  d      }t        |t              s,|s/|||<   5 |S )aX  Per-fp ``numbered_elements`` from the floor_plan_prompt checkpoint.

        The fp prompt packs emit no ``space_model``; ``numbered_elements`` is
        the structure SOT they DO emit, so this is the production structure
        source. ``_space_models_by_fp`` stays the higher-priority path for any
        future pack that adds space_model.rq   rt  r   ru  )ri  r   rt  r   r   nes         r0   r\  z,DwellingZoneMapStep._numbered_elements_by_fp;  s     02,-J$((06B;;MJPb&,,.KE4*"!!"56B"d#E
 / 
r2   c                D   i }t        | t              s|S | j                  d      xs i j                  d      xs i }t        |t              rS|j                         D ]@  \  }}|xs i j                  d      xs i }t        |t              s0|j	                  |       B |S )zPer-bg structural render facts from base_location_dossier (enrichment
        for the VLM context). Empty when the dossier step did not run.rq   dossiersper_bg_render_facts_by_bg_id)rw   rx   ry   rz   update)
dossier_cpr   rz  _fpdossierr   s         r0   r]  z(DwellingZoneMapStep._dossier_facts_by_bgO  s     !*d+J^^F+1r66zBHbh% ( 0W B++,JKQreT*JJu% !1 
r2   c                >   t        | t              si S | j                  d      xs i }|j                  d      xs |j                  d      xs i }t        |t              r7|r5|j                         D ci c]  \  }}t        |t              s|| c}}S i S c c}}w )u   Map each fp_id → its ``space_partition_plan`` (per-fp) from the
        bg_space_partition checkpoint, for the diagnostic comparison only.rq   plans_by_fpspace_partition_plans)rw   rx   ry   rz   )rm  r   rq   plansr   ps         r0   r^  z*DwellingZoneMapStep._partition_plans_by_fp`  s     ,-I'-2'R4884K+LRPReT"u',{{}L}eb!
1d8KBE}LL	 Ms   5BB)returnNone)rE   r{   r  Optional[Dict[str, Any]])r  r{   )r  Dict[str, Any])r}   r  r~   Optional[str]r  Dict[str, str])r   r   r  r  )r   	List[str]r   r  r   r  r   r  r   r  r  List[Dict[str, Any]])r   r{   r   r   r   r{   r  r  )r   r   r   r   r   r  r  r  )r   r{   r   r  r  zDict[str, List[str]]r  r  r  r  r   zOptional[List[Dict[str, Any]]]r  r  r3  r   r  r  )resume)rc  r{   r  r  )ri  r  r  Dict[str, Dict[str, Any]])ri  r  r  zDict[str, List[Dict[str, Any]]])r}  r  r  r  )rm  r  r   r  r  r  )rb  
__module____qualname__r#   __annotations__r$   r%   r&   r'   r(   r1   rH   rj   rr   staticmethodr   r   r   r   r   r   rC  rr  r[  r\  r]  r^   r2   r0   r"   r"   L   s;   AE>E8<5<@D=DCGM@GAE>EGS dt	+		+0
 .HU	 ,  0 370 0  	0
 00 (0 00 
0f
T$. .,0.<J.	.b}@ }@ 	}@
 *}@ (}@ .}@ :}@ 1}@ }@ 
}@@q
f .	"  .	( & ,	   .9G	" r2   r"   )'__doc__
__future__r   rc   r?   loggingrepathlibr   typingr   r   r   r   r	   app.core.step_runnerr
   app.services.image_capture.sinkr   &app.modules.pipeline.dwelling_zone_mapr   r   r   r   r   r   r   	getLoggerrb  rC   rb   rJ   app.core.fp_prompt_compatr   rZ  compiler_  r   r"   r  r2   r0   <module>r     s   "F #    	  6 6 + <   
		8	$
 bjj01`* `r2   