
    AjM                       U d Z ddlmZ ddlmZmZmZmZmZ  e	h d      Z
ded<    e	h d      Zded<    e	h d	      Zded
<   ddg dddgdddidddg dddiddidg ddddidg ddddiddidddgiddid	dddddidddZded<    G d de      Zd'dZd(d Z	 	 	 	 	 	 	 	 d)d!Z	 	 	 	 	 	 d*d"Z	 	 	 	 d+d#Zd$d$d%	 	 	 	 	 	 	 d,d&Zy$)-uN  W21B-wave-4 Phase A: floor-plan marker SEMANTIC readback sidecar (core).

Pure, deterministic core for a readback that answers a question the
W20A2 geometry readback does **not**: *does the image content drawn at
marker N actually match the expected object class / label from the
dossier inventory?*

The W20A2 ``floor_plan_geometry_readback`` only observes marker
number / coarse cell / base-kind — it trusts that "marker N means the
object its label claims" because the metadata says so. The FP
comparison gallery (W21B-wave-4 brief) showed that this trust is
unsafe: a floor-plan PNG can drift from its own metadata (e.g., render
a larger, more furnished dwelling than the inventory describes). This
sidecar turns marker-label consistency into an explicit evidence-backed
gate.

Design boundaries (brief §3.1 — scenario leakage guard):
  - Code performs ONLY structural / exact-ID checks and reads the
    LLM-emitted ``semantic_match`` enum. It NEVER lexically inspects the
    free-text evidence fields (``observed_object_summary`` /
    ``mismatch_reason`` / ``reasoning_basis`` / ``source_ref``). There is
    no substring / regex / lexicon matching anywhere in this module —
    the only "meaning" signal is the structured enum the VLM emits.
  - ``expected_label`` / ``expected_layer`` are carried from the dossier
    inventory verbatim and validated by exact-string equality; the VLM
    must not re-label or re-classify a marker.
  - The gate fails closed: a synthetic-fixture readback, an unattested
    base marker, or an ``uncertain`` base marker never auto-passes. Only
    a real ``ok`` readback whose every base structural / persistent
    marker reads ``match`` passes.

This module performs NO LLM / image / VLM / DB / ImageAsset I/O. A real
VLM provider is injected via the ``vlm_provider`` slot of
``compute_semantic_readback`` (mirrors the W20A2 geometry pattern); the
provider itself lives in a separate module and is wired behind an
explicit opt-in selector in a later step of this wave. With
``vlm_provider=None`` (default) the synthetic-fixture path is used and
``fp_image_path`` is ignored.
    )annotations)AnyCallableDictListOptional>   matchmismatch	uncertain	frozensetSEMANTIC_MATCH_VALUES>   base_openingbase_structural_unitbase_persistent_fixturebase_persistent_furnitureBASE_STRUCTURAL_LAYERS>   notallOfanyOfoneOfformatmaximumminimumpatterncontainsmaxItemsminItems	maxLength	minLength
multipleOfmaxContainsminContainsuniqueItemsexclusiveMaximumexclusiveMinimumpatternProperties_OPENAI_UNSUPPORTED_SCHEMA_KEYSobjectFstatusfp_idobserved_marker_semanticsdiagnosticsstringok)typeenumr0   array	numberexpected_labelexpected_layerobserved_object_summarysemantic_matchmismatch_reason
source_ref
confidencereasoning_basisinteger)r   r   r   r   )r	   r
   r   r4   null)r0   additionalPropertiesrequired
properties)r0   itemsDict[str, Any]SEMANTIC_READBACK_SCHEMAc                      e Zd ZdZy)SemanticReadbackErrorz4Fail-closed signal for the semantic readback / gate.N)__name__
__module____qualname____doc__     g/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/floor_plan_semantic_readback.pyrF   rF      s    >rL   rF   c                H    t        | t              xr t        | t               S )N)
isinstanceintbool)values    rM   _is_intrS      s    eS!A*UD*A&AArL   c                R   i }| xs i j                  d      xs g D ]n  }t        |t              s	 t        |d         }|j                  d      }t        |t              sE|t        v sNt        |j                  dd            |d||<   p |S # t        t
        t        f$ r Y w xY w)u   Build ``{number: {"label": ..., "layer": ...}}`` for base markers.

    Only ``base_*`` markers are surfaced — overlay / transient markers
    are out of the FP self-fidelity authority set, exactly as in the
    geometry readback.
    base_marker_inventoryr4   base_layer_decisionlabel )rW   layer)	getrO   dictrP   KeyError	TypeError
ValueErrorstrr   )dossieroutentrynumdecisions        rM   _base_inventory_mapre      s     &(C-R$$%<=CC%&	eHo&C 9923h$5K)KUYYw34!CH D J )Z0 		s   BB&%B&c                
   g }t        | t              sddt        |       j                   dgddS | j	                  d      dk7  r$|j                  d| j	                  d      d	       | j	                  d
      |k7  r&|j                  d| j	                  d
      d|       t        |      }|s|j                  d       | j	                  d      }t        |t              s|j                  d       g }g }t        |      D ]  \  }}d| d}	t        |t              s|j                  |	 d       2d}
|
D cg c]	  }||vs| }}|r|j                  |	 d|        a|d   }t        |      s|j                  |	 d|d       ||v r|j                  |	 d| d       |j                  |       |d   }t        |t              r|t        vr&|j                  |	 d|dt        t                      dD ]3  }t        ||   t              r|j                  |	 d| d||   d       5 |d   }|=t        |t        t        f      rt        |t              r|j                  |	 d |d       ||vr|j                  |	 d| d!       ||   }|d"   |d#   k7  r"|j                  |	 d$|d"   d%|d#   d&|        |d'   |d(   k7  s|j                  |	 d)|d'   d*|d(   d&|         | j	                  d+      }t        |t              s|j                  d,       g }|rd|ddS d||D cg c]  }t        |d         t        |d"         t        |d'         t        |d-         t        |d         t        |d.         t        |d/         t        |d   t        t        f      r!t        |d   t              st        |d         ndt        |d0         d	 c}t        |      d1}d2g |dS c c}w c c}w )3uV  Pure JSON-shape + exact-ID validator for VLM semantic output.

    Returns ``{"ok": bool, "blockers": [str], "readback": dict | None}``.

    Code performs only structural / exact-ID checks plus the
    ``semantic_match`` enum check. No semantic / lexical inspection of
    the free-text evidence fields is performed (W21B-wave-4 §3.1).
    Fzoutput is not a dict ()N)r/   blockersreadbackr*   r/   zstatus must be 'ok' (got uV   ); the real-provider path only emits 'ok' — synthetic / failed are handled elsewherer+   zfp_id mismatch: provider=z vs dossier=zWdossier base_marker_inventory has zero base_* markers; the VLM has nothing to attest tor,   z(observed_marker_semantics must be a listzobserved_marker_semantics[]z not a dictr3   z missing required fields: r4   z.number must be int (got z.number=z duplicatedr8   z.semantic_match=z not in )r5   r6   r7   r9   r:   r<   .z must be a string (got r;   z(.confidence must be float|int|None (got z% not in dossier base_marker_inventoryr5   rW   z.expected_label=z disagrees with dossier label=z for marker #r6   rY   z.expected_layer=z, disagrees with dossier base_layer_decision=r-   zdiagnostics must be a listr7   r9   r:   r<   r)   T)rO   r[   r0   rG   rZ   appendre   list	enumeraterS   r_   r   sortedrP   floatrQ   )outputr`   r+   rh   	inventoryentriesseen_numbersidxrb   prefixr@   fmissingnr	   
text_fieldconfexpdiagseri   s                        rM   validate_semantic_outputr      s    Hfd#1$v,2G2G1HJK
 	
 zz(t#'

8(<'? @  !	

 zz'e#'

7(;'> ?i!	

 $G,I+	

 jj45Ggt$BC L(
U-cU!4%&OOvhk23

 '9h!5.1h9OOvh&@	JK(OqzOOvh&?uAFGOOvhhqc=>A&' %%6K)KOO(*5)8/013
J eJ/5ha
| ,!*-03
 \"4#u.*T42HOO(B4(!L IOO((1#%JK A,C%&#g,6h.u5E/F.I J447L3C D c#
 %&#g,6h.u5E/F.I JB7|&mA38_ )j JJ}%EeT"45tDD $ #&
"  ak*"%a(8&9":"%a(8&9":+.q1J/K+L"%a(8&9":#&q):';#<!!L/2 "!L/C<@&q= !L/* #&q):';#<  #&
& E{- H0 BH==K :`&
s   :	O;O;9B-P c                   | j                  d      }|dk(  r	dg g g dgdS |dk7  rt        d|d      t        |      }i }| j                  d	      xs g D ]3  }|j                  d
      }t        |      s |j                  d      ||<   5 g }g }g }	t	        |      D ]U  }|j                  |      }
|
|	j                  |       (|
dk(  r|j                  |       ?|
dk(  sE|j                  |       W |rd}n	|s|	rd}nd}||||	g dS )uC  Decide the FP self-fidelity gate state from a semantic readback.

    Gate states (fail-closed ordering):
      - ``synthetic_unverified`` — readback came from the synthetic
        fixture (no real VLM). Never authoritative.
      - ``needs_fix``  — at least one base marker reads ``mismatch``.
        FP render should retry (bounded) / be marked needs_fix for BG
        anchor consumption.
      - ``needs_review`` — no mismatch, but at least one base marker is
        ``uncertain`` or was never attested. Routes to manual visual
        review (brief §9) rather than a false pass.
      - ``pass`` — every base structural / persistent marker reads
        ``match``.

    The decision uses ONLY the structured ``semantic_match`` enum and the
    exact-ID join with the dossier base inventory — no text inspection.
    r*   synthetic_fixturesynthetic_unverifiedzvsemantic gate computed from a synthetic_fixture readback; placeholder verdicts, not a production fidelity attestation.)
gate_statemismatch_markersuncertain_markersunattested_base_markersr-   r/   zGsemantic gate requires readback.status in {ok, synthetic_fixture} (got rg   r,   r4   r8   r
   r   	needs_fixneeds_reviewpass)rZ   rF   re   rS   ro   rl   )ri   r`   r*   rr   verdict_by_numberrb   rc   r   r   r   verdictr   s               rM   compute_semantic_gater   h  sc   , \\(#F$$0 "!#')O	
 		
 ~#..4Zq:
 	

 $G,I(*9:@b@ii!3<%*YY/?%@c" A
 #%#%)+i #'',?#**3/
"##C(#$$S) !  
	5#

 !,.#: rL   c                   | j                  d      }t        |t              r|st        d|d      t	        |       }|st        d|d      g }t        |      D ](  }||   }|j                  ||d   |d   dd	ddd
dd	       * d||dgdS )uR  Return a synthetic_fixture-status semantic readback.

    Every base marker is recorded as ``uncertain`` — the fixture asserts
    nothing about real image content. It exists so downstream code
    (gate, step wrapper, tests) can be exercised before a real VLM
    provider is wired, mirroring the W20A2 geometry synthetic fixture.
    r+   z)dossier.fp_id missing or non-string (got rg   zdossier fp_id=z  has empty base_marker_inventoryrW   rY   rX   r   Nu9   synthetic_fixture placeholder — no real VLM observationr3   r   zsynthetic_fixture: semantic verdicts are deterministic placeholders, not real VLM observations. The gate must not promote this readback to a production fidelity pass.r)   )rZ   rO   r_   rF   re   ro   rl   )r`   r+   rr   rs   rc   r|   s         rM   "compute_synthetic_semantic_fixturer     s     KK EeS!#7yB
 	
 $G,I#UI%EF
 	
 %'Gi n"%g,"%g,+-"-#% "O	
 !& &%,C
		 	rL   N)fp_image_pathvlm_providerc                .   |t        |       S  || |      }t        |t              s"t        dt	        |      j
                   d      t        || | j                  d            }|d   s%d	j                  |d
         }t        d|dd        |d   S )u  Return a semantic readback for the dossier's fp_id.

    With ``vlm_provider=None`` (default) the synthetic_fixture path is
    used and ``fp_image_path`` is ignored. When a provider callable is
    passed, it is invoked as
    ``vlm_provider(dossier=..., fp_image_path=...)`` and its return value
    is run through the FULL ``validate_semantic_output`` contract
    (status / fp_id / shape / exact-ID joins / required fields). Any
    non-conforming return — including an output that merely *looks* ok at
    the top level but carries an unknown marker number, a re-labelled
    marker, or a missing field — raises ``SemanticReadbackError``. The
    dispatcher never returns an unvalidated provider payload.
    N)r`   )r`   r   z vlm_provider returned non-dict (rg   r+   )rq   r`   r+   r/   z; rh   z'vlm_provider output failed validation: i  ri   )	r   rO   r[   rF   r0   rG   r   rZ   join)r`   r   r   ra   resjoineds         rM   compute_semantic_readbackr     s    & 1'BB
wm
DCc4 #.tCy/A/A.B!D
 	
 #G7;;w+?C t93z?+#5fTcl^D
 	
 z?rL   )rR   r   returnrQ   )r`   rC   r   zDict[int, Dict[str, str]])rq   r   r`   rC   r+   r_   r   rC   )ri   rC   r`   rC   r   rC   )r`   rC   r   rC   )r`   rC   r   zOptional[str]r   z'Optional[Callable[..., Dict[str, Any]]]r   rC   )rJ   
__future__r   typingr   r   r   r   r   r   r   __annotations__r   r'   rD   	ExceptionrF   rS   re   r   r   r   r   rK   rL   rM   <module>r      s  &N # 6 6 $--O#P y P
 %.% 	  .7.  < !O#dV4(# (-
  &y1'-x&8 (!' 17/A ( B' )/'9#)8"4#)Hf+=">(.'9)$'&
R h'
W/	4, . 4n?I ?B6d>d> d> 	d>
 d>THH H 	H\22 2p $(<@	"" !" :	"
 "rL   