
    |jI                        d Z ddlm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 ddlmZ ddlmZmZmZmZmZmZmZmZmZmZmZmZ  ej8                  e      ZdZd	Z  G d
 de      Z!y)u  W21B-wave-4 C2: ShotProjectionCardStep.

opt-in (default OFF) producer of the per-shot projection card — the
common shot-observation SOT that BG (``background_prompt`` vNext, C4) and
the final shot t2i (``scene_detail`` vNext, C5) both consume so they
follow the same contract (design brief §3; wiring brief §3).

Per ``(bg_id, shot_id)`` target (one card per shot in the background's
``applies_to_shots``; anchor / plate grouping is deferred to the plan
vNext, C3) the step:
  - Assembles the marker inventory + union registry from the overlay
    payload (base ∪ transient ∪ ignored), and the source_hashes
    provenance from the upstream checkpoints.
  - Computes a projection-card VLM output. With the real-provider
    selector OFF (default) it uses the synthetic fixture — a
    non-authoritative placeholder that NEVER auto-passes the gate, NO
    real VLM call.
  - Builds the card envelope and runs the v0 deterministic gate
    (pass / needs_review / blocked).
  - Records per-card envelope + validation in the manifest.

Gates (all must be true):
  - ``settings.background_mode`` ∈ {"on", "floor_plan_anchored"}.
  - ``settings.shot_projection_card_enabled`` = True.
  - ``settings.base_location_dossier_enabled`` = True.

Anything else → ``not_applicable`` with a byte-stable empty payload.

The semantic readback (W21B-wave-4) is an OPTIONAL gate, not a hard dep:
when its checkpoint is present the per-fp ``gate_state`` is carried into
the card; otherwise the card records ``semantic_gate_state=not_available``
and the gate does not treat semantic as a hard precondition (design brief
§10 decision 1). LLM / image / VLM API call is 0 unless the real-provider
selector is explicitly flipped (or a mock provider is injected in tests).
DB / ImageAsset write 0.
    )annotationsN)Path)AnyCallableDictOptional)
StepRunner)PROVIDER_MODELPROVIDER_NAMEProjectionCardErrorbuild_card_envelopebuild_shot_contextbuild_union_registrycompute_card_cache_keycompute_projection_cardcompute_source_hashesiter_card_targetsresolve_pack_versionvalidate_shot_projection_card   1c                  t    e Zd ZU dZded<   	 	 	 	 ddZddZddZddZddZ	e
	 	 	 	 	 	 dd	       Zddd
Zy)ShotProjectionCardStepN'Optional[Callable[..., Dict[str, Any]]]_vlm_provider_overridec                    || _         y)z7Mocking helper. Production callers must NOT touch this.N)r   )selfproviders     ^/Users/manta/Documents/Projects/TheRoad-I1/backend/app/core/steps/shot_projection_card_step.pyset_vlm_provider_for_testingz3ShotProjectionCardStep.set_vlm_provider_for_testingH   s     '/#    c                |    ddl m} | j                  | j                  S t        t	        |dd            syddlm} |S )u  Resolve the projection VLM provider for this run.

        Resolution order (only one path active per run):
          1. ``set_vlm_provider_for_testing`` injection — bypasses
             settings (mock-provider tests).
          2. ``settings.shot_projection_card_real_provider_enabled`` True
             → the production-adjacent ``litellm_projection_card_provider``.
          3. Default → ``None``; the core emits the synthetic fixture.
             Zero external API calls.
        r   settingsN*shot_projection_card_real_provider_enabledF) litellm_projection_card_provider)app.core.configr$   r   boolgetattr2app.modules.pipeline.shot_projection_card_providerr&   )r   r$   r&   s      r   _resolve_vlm_providerz,ShotProjectionCardStep._resolve_vlm_providerN   sF     	-&&2...HJER
 	
 0/r!   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)
u   Read a prior step's ``manifest.json`` from the checkpoint dir.

        Mirrors the W21B-wave-4 semantic / W20A2 geometry helpers —
        StepRunner does not provide this, so the step defines it.
        r   r#   checkpointsepisodeszmanifest.jsonutf-8)encodingz)shot_projection_card: %s parse failed: %sN)r'   r$   r   projects_dir
project_id
episode_idexistsjsonloads	read_text	Exceptionloggerwarning)r   step_idr$   cpexcs        r   _load_prev_checkpointz,ShotProjectionCardStep._load_prev_checkpointf   s     	- &&'oo  oo	
   	 99;zz",,,"@AA
 	  ?#  	s   $A9 9	B#BB#c                   ddl m} |j                  t        |j                        t        t        |dd            t        t        |dd            t        t        |dt                    t        t        d}t        j                  t        j                  |d	      j                  d
            j                         d d S )Nr   r#   shot_projection_card_enabledFr%   #shot_projection_card_prompt_version)background_modebase_location_dossier_enabledr@   r%   rA   schema_versionprompt_versionT)	sort_keysr/      )r'   r$   rB   r(   rC   r)   strPROMPT_VERSIONSCHEMA_VERSIONhashlibsha256r5   dumpsencode	hexdigest)r   r$   payloads      r   _config_hashz#ShotProjectionCardStep._config_hash   s    ,  (77-166. -1"@%H- ;?JE;
 47"GX4 -,#
& ~~JJw$/66w?

)+cr 	r!   c                8    dddt         | j                         i dS )Nr   applicable_countcompleted_countfailed_countrD   config_hashdata)rJ   rQ   )r   s    r   _not_applicablez&ShotProjectionCardStep._not_applicable   s'     ! ,,,.
 	
r!   c                    | sy| j                  d      xs i j                  d      xs i }|j                  |      xs i }|j                  d      }t        |t              r|r|S dS )zAOptional semantic gate carry: per-fp gate_state or not_available.not_availablerX   per_fp
gate_state)get
isinstancerH   )semantic_cpfp_idr\   entrystates        r   _semantic_gate_for_fpz,ShotProjectionCardStep._semantic_gate_for_fp   sd    
 "//&)/R44X>D"

5!'R		,'"5#.5uMoMr!   c                b  >? ddl m} |j                  dvr| j                         S t	        t        |dd            s| j                         S t	        |j                        s| j                         S | j                  d      }| j                  d      }|xs i j                  di       xs i }|xs i j                  di       xs i }t        ||	      }|s| j                         S | j                  d
      }| j                  d      }	| j                  d      }
| j                  d      }| j                  d      }| j                  d      }| j                  d      }t        t        |dt                    }t        |      }t        }t        }|xs i j                  di       xs i }|xs i j                  di       xs i }| j                         ?i }d}d}d}d}d}d}|j                  d      xs i }|xs i j                  di       xs i }|xs i j                  di       xs i }|	xs i j                  di       xs i }|xs i j                  di       xs i } |
xs i j                  di       xs i }!|D ]  }"|"d   }#|"d   }$|"d   }%|"d   }&|# d|$ }'dg>d }(?>?fd})|)}(	 t!        |&      \  }*}+| j#                  |
|%      },|j                  di       j                  |%i       }-|-rdnd}.|j                  di       j                  |%i       }/d }0|/j                  d      xs g D ]  }1|1j                  d      |#k(  s|1}0 n | j                  d      xs i j                  |%      }2|j                  d      xs i j                  |%      }3d }4|-sd}4n|0d }4n	|2sd!}4n|3sd"}4|4|#|$|%d#|4|4ddd$||'<   |d%z  }|d%z  }Ft%        |$||&      }5d'D 6ci c]'  }6|6|j                  |#      xs i j                  |6      ) c}6|5d(<   t'        |-|-|&|j                  di       j                  |%      |
r!|!j                  di       j                  |%      nd |0|5|t(        ||||2)      }7t+        |7|#|$*      }8t-        |*|%|#|$|(|-j                  d+      |+|0|5j                  d,      |5j                  d-      |5|j                  |#      xs i |||d.	/      }9t/        |8t(        ||||#|$|%|,d0|.|7|9|+1      }:t1        |:|72      };|(>d   nd}<||<z  }|#|$|%|:|;|;d3   |;d4   t	        |<      |<d5	||'<   |d%z  }|;d3   d#k(  r|d%z  }n|;d3   d6k(  r|d%z  }n|d%z  } |rd%nd||t(        | jA                         ||||d=|ddd>d?S c c}6w # t2        $ rV}=t4        j7                  d7|'|=       |(>d   nd}<||<z  }|#|$|%t        |=      d d8 t	        |<      |<d9||'<   |d%z  }Y d }=~=Yd }=~=wt8        $ rO}=t4        j;                  d:|'|=       |#|$|%d;t=        |=      j>                   d<|= d d8 ddd9||'<   |d%z  }Y d }=~=d }=~=ww xY w)@Nr   r#   >   onfloor_plan_anchoredr@   Ffloor_plan_overlay_payloadbackground_master_planrX   )overlay_payloadmaster_planbase_location_dossierfloor_plan_geometry_readbackfloor_plan_semantic_readbackfloor_plan_promptfloor_plan_rendershot_staging
scene_saverA   background_catalogbg_idshot_idra   ov_entryz::c                 .    dxx   dz  cc<    di | S )Nr   r    rx   )kw_counterbase_providers    r   countedz0ShotProjectionCardStep._execute.<locals>.counted   s    QK1$K(.2..r!   floor_plansokmissingcamera_recommendationsdossiersr\   fp_render_missingcamera_recommendation_missingdossier_missinggeometry_missingblocked)rt   ru   ra   
card_statevalidator_statefallback_reasonreal_vlm_callreal_vlm_call_countr   )ru   rq   rr   )surface_rolesub_location_labelstate_label_rawapplies_to_shotsbg_meta)	fp_render	substrateoverlaygeometrysemantic
camera_recshot_contextrE   rD   modelr   pack_versiondossier)source_hashesrt   ru   png_pathshot_intentscene)	marker_registrycamera_recommendationr   r   r   r   r   r   r   )	inventoryra   rt   ru   vlm_providerfp_image_pathprompt_contextdetailed_fp)card_idrD   rE   r   r   rt   ru   ra   semantic_gate_statesubstrate_kindsubstrate_statusr   
vlm_outputr   )cardcurrent_source_hashesr   r   )	rt   ru   ra   r   
validationr   r   r   r   needs_reviewzshot_projection_card %s: %si,  )rt   ru   ra   errorr   r   z&shot_projection_card %s unexpected: %szunexpected: z: )passr   r   )cardscard_state_countsr   image_api_call_countllm_call_countrS   )!r'   r$   rB   rY   r(   r)   rC   r>   r^   r   rH   rI   r   r   r
   r+   r   rd   r   r   rJ   r   r   r   r   r   r9   r   r8   	exceptiontype__name__rQ   )@r   moder$   
overlay_cp	master_cprj   rk   targets
dossier_cpgeometry_cpr`   fp_prompt_cpfp_render_cpshot_staging_cpscene_save_cprE   r   provider_name
model_nameshot_staging_datascene_save_datar   	completedfailedr   r   passedvlm_call_totalcatalogfp_render_datafp_prompt_datageometry_datadossier_datasemantic_datatgtrt   ru   ra   rv   card_keycounted_providerr|   r   registryr   fp_render_entryr   fp_prompt_entryr   crdossier_entrygeometry_entryprecondition_reasonr   kr   r   r   envelopegateper_vlmr=   rz   r{   s@                                                                 @@r   _executezShotProjectionCardStep._execute   s   ,##+HH''))GH&DeLM''))H::;''))//0LM
../GH	%+00<B B++FB7=2#+
 ''))//0GH
001OP001OP112EF112EF44^D22<@HC^T

 ,N;%#
,277CIr(.B33FB?E2224+-	??#78>B&,"11&"=C&,"11&"=C$*//;Ar"(b--fb9?R$*//;ArCLE)nGLE:H7),HsHHL(/ $+ q&:8&D#	8&*&@&@'# #&&}b9==eRH   ,;4	 "&&}b9==eRH   "
)--.FGM2MBvvg%/%'
 N ".!1!1*!=!C H H O"/"3"3H"="C!H!H!O '+#&*=''*I'&*;''*<'&2!&#*!&&/+>+>)./0	'E(O NIqLG  2#!2. + E*0b55a88+Y' !6--$*..x<@@G ' &))(B7;;EB!)!-#1#1$*!-)#!& 1"/ug 5'#!1"1"5"5j"A+31;'3'7'7'F!-!1!1'!:(4#*;;u#5#;(4!+$1
$
& /##1#1$*#(;#0%5"/)$,  5! *:)E(1+1')"&"$"&"&|"4'+,='>%)']+2
#h Q	%2qLG,'>9 A%LaKFM F &+(",,,."$0&&
 (6()"#

 	
i+r ' :HcJ)9)E(1+1')"&" Xds^%)']+2#h !   <h #&"+DI,>,>+?r#GM%*+,#h !sG   )BU9A<U9 U9,U4>E
U94U99	X.AWX.AX))X.)r   r   returnNone)r   r   )r;   rH   r   Optional[Dict[str, Any]])r   rH   )r   Dict[str, Any])r`   r   ra   rH   r   rH   )resume)r   rH   r   r   )r   
__module____qualname__r   __annotations__r    r+   r>   rQ   rY   staticmethodrd   r   rx   r!   r   r   r   C   sr     GKCJ/?/	/0044
 	N-	N69	N		N 	NJ
r!   r   )"__doc__
__future__r   rK   r5   loggingpathlibr   typingr   r   r   r   app.core.step_runnerr	   )app.modules.pipeline.shot_projection_cardr
   r   r   r   r   r   r   r   r   r   r   r   	getLoggerr   r9   rJ   rI   r   rx   r!   r   <module>r      se   #H #     0 0 +    
		8	$w
Z w
r!   