
    ֓j                       U 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mZmZ  ej                  e      Z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<    ee      j3                         j4                  j4                  j4                  j4                  j4                  dz  dz  dz  ez  Zded<    eh d      Zded<    G d de      Zdd	 	 	 	 	 d1dZ d2dZ!d3d Z"	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d4d!Z#	 	 	 	 	 	 d5d"Z$	 	 	 	 	 	 	 	 d6d#Z%d$Z&d%ed&<   d7d'Z' eh d(      Z(ded)<    eh d*      Z)ded+<   	 	 	 	 	 	 	 	 	 	 d8d,Z*	 	 	 	 	 	 	 	 	 	 	 	 	 	 d9d-Z+	 	 	 	 d:d.Z,deeed/	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d;d0Z-y)<uB  W20B: real-LLM planner provider for shot_aware_bg_render_plan.

Production-adjacent helper that, **when explicitly resolved by the step
wrapper**, performs a single ``litellm.completion`` call to drive the
dwelling-scoped reference-graph DAG planner. The output is the parsed
graph dict ready for ``build_render_plan_for_fp`` consumption.

Why ``litellm.completion`` directly rather than the project's
``call_structured`` router: the router applies retry / fallback /
temperature defaults and re-shapes ``response_format`` in ways that
trip OpenAI's strict ``json_schema`` mode (notably forwarding
``temperature`` to a gpt-5.5 route that rejects unknown tunable params
and forwarding the prompt-pack schema with constraint keys OpenAI
strict mode rejects). This module is the single, lock-stable entry
point that:

  - reads the prompt pack itself (system / user_template / schema),
  - deep-copies + recursive-strips the schema for OpenAI strict mode,
  - performs every preflight (arg shape, env, prompt pack, litellm
    capability) before any network call,
  - issues exactly one ``litellm.completion`` call with
    ``num_retries=0`` and no temperature,
  - applies fail-closed response guards,
  - validates against the ORIGINAL pack schema (local jsonschema is
    the SOT — sanitized copy is only the request shape),
  - hands off to the production ``validate_llm_output`` (DAG / refs /
    camera / etc.) and fails-closed on validator diagnostics.

LLM / image / VLM API call 0 in tests (every test monkeypatches
``litellm.completion``). The default production code path keeps
``settings.shot_aware_bg_render_plan_real_provider_enabled = False``,
so the step never resolves to this helper.

The module's only external dependency is ``litellm``, lazy-imported
inside the function body — callers that never flip the selector pay
zero import cost.
    )annotationsN)Path)AnyDict	FrozenSetListOptionalTuplezopenai/gpt-6-astrastrPLANNER_MODEL_DEFAULTi>  intMAX_COMPLETION_TOKENS_DEFAULT   TIMEOUT_SECONDS_DEFAULTshot_aware_bg_render_plan_v2RESPONSE_SCHEMA_NAMEz3.202607231435PROMPT_VERSIONprompts_baseshot_aware_bg_render_planr   _PROMPT_PACK_DIR>   notallOfanyOfoneOfformatmaximumminimumpatterncontainsmaxItemsminItems	maxLength	minLength
multipleOfmaxContainsminContainsuniqueItemsexclusiveMaximumexclusiveMinimumpatternPropertiesFrozenSet[str]_OPENAI_UNSUPPORTED_SCHEMA_KEYSc                       e Zd ZU dZdZded<   y)"ShotAwareBgRenderPlanProviderErrorut  Fail-closed signal for the W20B planner provider path.

    W20F10 (Codex 조건 5): retry 경로에서 실제 completion 호출이 2회였던
    실패는 ``completion_call_count`` 속성으로 실비용을 보존한다 — caller
    (build_render_plan_for_fp 실패 감사)가 real_api_call_counts.llm 에
    반영. 미설정=1 (기존 단일 호출 semantics).
       r   completion_call_countN)__name__
__module____qualname____doc__r1   __annotations__     q/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/shot_aware_bg_render_plan_llm_provider.pyr/   r/   _   s     "#3"r8   r/   r0   completion_callsc               *    t        |       }||_        |S N)r/   r1   )msgr;   errs      r9   _provider_errorr@   k   s     -S
1C 0CJr8   c                   t        | t              ri }| j                         D ]  \  }}|t        v rt	        |      ||<    |j                  d      dk(  r>t        |j                  d      t              rt        |d   j                               |d<   |S t        | t              r| D cg c]  }t	        |       c}S | S c c}w )aB  Deep-copy + recursive strip of OpenAI strict-mode unsupported keys.

    Returns a fresh tree; the input is not mutated.

    Additionally enforces the OpenAI Structured Outputs strict-mode
    contract that **every object property must be listed in
    ``required``**. The original prompt-pack schema marks some fields
    optional (e.g. ``selected_refs.items.space_description``,
    ``camera_decision.framing_notes``) so local jsonschema validation
    can accept LLM responses that omit them. The request-side sanitized
    copy must close that gap so strict mode does not reject the
    ``response_format`` before the model is even invoked.

    The fill keeps the original ``properties`` insertion order (Python
    dict ordering is the language guarantee since 3.7) so the request
    payload is deterministic across runs.
    typeobject
propertiesrequired)
isinstancedictitemsr-   "_sanitize_schema_for_openai_strictgetlistkeys)schemaoutkeyvalueitems        r9   rI   rI   x   s    $ &$  ,,.JC559%@CH ) 776?h&:GGL!4,
 #3|#4#9#9#;<C
O
&$EKLVT248VLLM Ms   *Cc                    t         } | j                         r| j                         st        d|       | dz  }| dz  }| dz  }| dz  }||||fD ]0  }|j                         r|j	                         r$t        d|       	 |j                  d      }|j                  d      }|j                  d      }|j                  d      }	|j                         st        d      |j                         st        d      |	j                         st        d      	 t        j                  |      }t        |t              s"t        dt        |      j                   d      ||||	dS # t        $ r*}
t        d	t        |
      j                   d
|
       |
d}
~
ww xY w# t        t        f$ r}
t        d|
       |
d}
~
ww xY w)u  Load system / user_template / schema from the prompt pack dir.

    Fail-closed if any artifact is missing, unreadable, empty, or — for
    ``schema.json`` — not valid JSON. Caller treats this as a preflight
    failure (completion call count stays 0).
    zprompt pack dir missing: z	system.mdzuser_template.mdzschema.jsonzgraph_anchor_repair.mdzprompt pack file missing: zutf-8)encodingzprompt pack read failed: : Nzprompt pack system.md is emptyz%prompt pack user_template.md is emptyz+prompt pack graph_anchor_repair.md is emptyz(prompt pack schema.json not valid JSON: z3prompt pack schema.json must be a JSON object (got ))system_promptuser_templaterM   graph_anchor_repair_template)r   existsis_dirr/   is_file	read_text	ExceptionrB   r2   stripjsonloads	TypeError
ValueErrorrF   rG   )pack_dirsys_pathusr_pathschema_pathrepair_pathprV   rW   
schema_rawrX   excrM   s               r9   _load_prompt_packrk      s     H??HOO$50'|4
 	
 +%H,,H]*K55K+{;xxz4,QE2  <

 **G*< **G*< **G*<
'2'<'< (= (
$  0,
 	
  03
 	
 (--/09
 	
J'
 fd#0L))*!-
 	

 '&(D	 7  0'S	(:(:';2cUC
	$ z" 06se<
	s1   AE+ F! +	F4%FF!G0F??Gc                   |xs i j                  d      xs i }t        |t              r)|j                         D 	ci c]  \  }}	|dk7  r||	 c}	}n|}
|xs i j                  d      xs i }|xs i j                  d      xs i |xs i j                  d      xs i d}| j	                  ||t        j                  |
d      t        j                  |d      t        j                  |d      t        j                  |d      t        j                  |d      t        j                  t        |      d            S c c}	}w )	u>  Substitute the user_template placeholders with structured JSON blocks.

    The substitutions carry no scenario-specific tokens — every block is
    a verbatim ``json.dumps(..., sort_keys=True)`` of upstream data.

    W20F8: ``camera_candidates_block`` 별도 섹션에 per-unit camera/look_at
    candidate set 만 verbatim 추출. LLM 이 exact-copy 위반으로
    coordinate 를 invent 하지 않도록 prompt 안에서 후보 set 을 두 번
    노출 (geometry_block 안 + 별도 W20F8 섹션). **이 중복은 의도된 것이라
    prompt-diet 대상이 아니다.**

    2026-08-03 prompt-diet 2단계: ``per_bg_render_facts_by_bg_id`` 는
    바로 아래 ``per_bg_facts_block`` 으로 따로 실리는데 ``dossier_block``
    안에도 그대로 들어 있어 같은 JSON 이 한 콜에 두 번 나갔다. 템플릿의
    두 섹션(``## Base location dossier`` / ``## Per-BG render facts``)이
    이미 분리돼 있고 dossier 섹션이 facts 포함을 말하지 않으므로,
    dossier 사본에서만 그 키를 빼도 전달 내용의 손실이 없다.
    per_bg_render_facts_by_bg_idper_bgcamera_cell_candidates_per_unit look_at_cell_candidates_per_unitro   rp   T	sort_keys)fp_idreadback_statusdossier_blockgeometry_blockcamera_candidates_blockper_bg_facts_blockshot_staging_blockcandidate_catalog_block)rJ   rF   rG   rH   r   r_   dumpsrK   )rW   rt   ru   dossiergeometryshot_readinesscandidate_catalogper_bg_factskvdossier_for_blockper_bg_stagingcamera_candidatess                r9   _build_user_promptr      sC   8 Mr&&'EFL"L gt$ %]]_	
,TQ22 qD_	
 +2  %*//9?RN ^  !BCIr ^  !CDJ 'jj!2dCzz(d; $

+< M::ldC::nE $

"#t!
    	
s   Ec                    d| dd|dgS )Nsystem)rolecontentuserr7   rV   user_prompts     r9   _build_messagesr     s     m4K0 r8   c                F   |xs i j                  d      xs i }|xs i j                  d      xs i }t        j                  ||dd      }t        j                  | j                  d      xs i d      }dj                  d |xs g D              xs d	}d
| d| d| dS )uu  W20F9 — camera validator-fail targeted repair prompt.

    Codex 명시 (2026-05-28): retry 입력 = validator diagnostic + camera
    candidate block + 기존 graph/reference 보존, camera_decision 의
    camera_cell/look_at_cell/lens/fov 만 후보 set 에서 verbatim copy 로 고쳐라.
    empty/parse/shape/graph/anchor/ref/dedup retry 금지 — 본 helper 는 caller
    가 camera-only failure 분류한 후에만 호출된다.

    Output 은 schema 동일 (전체 graph 다시 emit) — LLM 이 camera_decision 만
    바꿔서 동일 graph 를 다시 emit 한다.

    2026-08-03 prompt-diet 1단계: 말미에 붙이던
    ``## Original input bundle (for context)`` + 최초 user prompt 전문을
    제거했다. 이 분기는 ``_is_camera_only_failure`` 가 camera 축을 뺀 11개
    검증축 전부 통과를 요구하므로 graph/anchor/reference 는 이미 확정돼
    있고, 고쳐야 할 카메라 좌표는 아래 candidate 배열 안에서만 고른다 —
    원문 번들이 기여할 자리가 없다. 실측 절감 = 그 콜의 user 대부분
    (최대 콜 기준 114k 중 원문 재첨부분).
    ro   rp   rq   Trr   graph
c              3  &   K   | ]	  }d |   ywz- Nr7   .0ds     r9   	<genexpr>z3_build_camera_repair_user_prompt.<locals>.<genexpr>C  s     H.FRs8.F   - (none)u  # W20F9 — camera validator repair

Your previous response was structurally correct (DAG / anchor / references / graph completeness / rationale all passed), but the code-side validator rejected one or more `camera_decision.camera_cell` / `look_at_cell` entries because the coordinate is NOT a member of the supplied candidate set.

## Validator diagnostics (camera_in_candidates only)

z<

## Camera coordinate candidates (verbatim copy required)

z

## Previous graph you emitted (PRESERVE everything except camera_decision.camera_cell / look_at_cell / lens_enum / fov_deg)

u/  

## What to emit

Re-emit the SAME JSON object matching the schema, with the SAME graph node ordering, the SAME `is_dwelling_identity_anchor`, the SAME `mode`, the SAME `reference_decision`, the SAME `render_guidance`, and the SAME `rationale`. ONLY fix the `camera_decision` fields (camera_unit / camera_cell / look_at_unit / look_at_cell / lens_enum / fov_deg) so that:
- `camera_cell` is a verbatim integer-pair copy of one entry in `camera_cell_candidates_per_unit[camera_unit]`.
- `look_at_cell` is a verbatim integer-pair copy of one entry in `look_at_cell_candidates_per_unit[camera_unit]`.
- `lens_enum` and `fov_deg` follow the system prompt rule 6.
Do NOT change any other field. Do NOT invent any new node. Do NOT compute or interpolate a coordinate — use ONLY values from the candidate arrays above.
)rJ   r_   r|   join)previous_parsedcamera_diagnosticsr~   	cam_cands
look_candsrx   prev_graph_block
diag_blocks           r9    _build_camera_repair_user_promptr     s    2 R$$%FGM2I.b%%&HIORJ"jj/80:	
  zzG$*d H.@.FB.FHHVJJ	C , G"
# $P 
 (	(r8   )required_shape_okgraph_completeness_oknode_index_order_okdag_okmax_refs_per_bg_oktwo_refs_distinct_spaces_oksame_fp_only_okanchor_exactly_one_ok anchor_in_clean_candidate_set_okrationale_and_mode_ok#synthetic_readback_production_clearzTuple[str, ...]_W20F9_REPAIR_KEYS_MUST_PASSc                l    | j                  d      duryt        D ]  }| j                  |      dus y y)uN   W20F9: retry 가능 조건 = camera_in_candidates 만 fail + 다른 모두 ok.camera_in_candidates_okFT)rJ   r   )
validatorsr   s     r9   _is_camera_only_failurer   s  s:    ~~/0=)>>!D( * r8   >   r   r   r   _W20F10_EXEMPT_EMPTY_NODES>   r   r   r   _W20F10_EXEMPT_NONEMPTY_NODESc                   ||z  sy|st         nt        }d}| j                         D ]-  \  }}t        |t              s|dk(  r||v r|du rd}(|dus- y |S )u&  W20F10: 교정 retry 가능 조건 — validator boolean+노드 구조로 판정.

    diagnostic 문자열 검색 금지(Codex 조건 1). 상류 renderable/clean-anchor
    후보가 비어 있거나 **교집합이 0**이면(그래프=renderable 전수 ∧ anchor
    ∈ clean set 을 동시에 만족할 노드가 존재 불가) LLM 이 복구할 수
    없으므로 즉시 fail-closed (Codex TEST-GAP-3 경계). camera_in_
    candidates_ok 는 면제 집합에 없으므로 camera 실패가 섞이면 False —
    W20F9 와 상호 배타.
    Fall_validators_passedT)r   r   rH   rF   bool)r   nodesrenderable_bg_idsclean_anchor_candidate_bg_idsexemptany_exempt_failedrO   rP   s           r9   _is_graph_anchor_only_failurer     s       ==*/"*   &&(
U%&))&=~$(! ) r8   c           	     R   ddl }dj                  d |xs g D              xs d}|t        j                  t	        |      d      t        j                  t	        |      d      ||d|j                  d	d
j                  t        |j                              z   dz   fd|       S )u  W20F10 교정 입력 조립 — 팩 스템({{token}} 치환, 하드 프롬프팅 금지).

    치환은 **템플릿 원문 단일 패스** 정규식 callback (Codex 리뷰
    NARROW-2: 순차 str.replace 는 먼저 삽입된 데이터(이전 LLM 응답 등)
    속의 후속 토큰을 재치환해 '이전 응답 전문' 을 변조한다 — 삽입 값은
    다시 스캔되지 않아야 byte-preserved). patch 가 아니라 전체 schema
    문서 재반환 계약은 스템이 소유.
    r   Nr   c              3  &   K   | ]	  }d |   ywr   r7   r   s     r9   r   z9_build_graph_anchor_repair_user_prompt.<locals>.<genexpr>  s      ,Q"QC,r   r   Trr   )diagnostics_blockrenderable_bg_ids_blockclean_anchor_candidates_blockprevious_response_blockoriginal_user_promptz\{\{(|z)\}\}c                ,    | j                  d         S )Nr0   )group)mmappings    r9   <lambda>z8_build_graph_anchor_repair_user_prompt.<locals>.<lambda>  s    '!''!*%r8   )rer   r_   r|   sortedsubmapescape)	repair_templater   previous_contentdiagnosticsr   r   r   r   r   s	           @r9   &_build_graph_anchor_repair_user_promptr     s    "  &,",  	  (#'::$%$7)-01T*C#3 4G 66388C		7344x?% r8   c                    dt         d| ddS )Njson_schemaT)namestrictrM   )rB   r   )r   sanitized_schemas    r9   _build_response_formatr     s     (&
 r8   )r   modeltimeout_secondsmax_completion_tokensc           	     Z   t        | t              r| st        d| d      t        |t              s"t        dt	        |      j
                   d      t        |t              s"t        dt	        |      j
                   d      t        |t              s"t        dt	        |      j
                   d      |g }t        |t              s"t        dt	        |      j
                   d      dd	lm}  |       st        d
      t               }		 ddl
}
ddlm} 	 t        |
j                  |            }|st        d|d      	 |
j                  |d      xs g }d|vrt        d|d      |j!                  d      }t        |t              r|r|nd}t#        |	d   | |||||      }t%        |	d   |      }t'        |	d         }t)        |      }	  ||||||d      }t+        |d!d      xs g }|st        d"      |d   }t+        |d#d      }|t        d$      t+        |d%d      }|rt        d&t        |      dd'       t+        |d(d      }|st        d)      t+        |d*d      }|d+k7  rt        d,|d-      	 t-        j.                  |      }t        |t              s"t        d/t	        |      j
                   d      	 ddl}	 |j7                  |	d         j9                  |       dd4lm }m!} tE        |j!                  d5      xs i jG                               } |xs i j!                  d6      }!t        |!t        tH        f      rtE        d7 |!D              }"n7tE        d8 |xs i j!                  d9      xs i jK                         D              }"tE        d: |j!                  d;      xs i j!                  d<      xs g D              }#|j!                  d=      xs i j!                  d>      xs g }$ || |$| |||"|#?      }%|%d@   su|%j!                  dA      xs g }&tM        |%      r7|&D 'cg c]  }'dB|'v sdC|'v sdD|'v sdE|'v r|' }(}'tO        ||(|F      })t%        |	d   |)      }*	  |||*|||d      }+t+        |+d!d      xs g },|,st        dH      |,d   }-t+        |-d#d      }.|.t        dI      t+        |.d%d      }/|/rt        dJt        |/      dd'       t+        |.d(d      }0|0st        dK      t+        |-d*d      }1|1d+k7  rt        dL|1d-      	 t-        j.                  |0      }2t        |2t              s"t        dNt	        |2      j
                   d      	 |j7                  |	d         j9                  |2       |2j!                  d=      xs i j!                  d>      xs g }3 || |3| |||"|#?      }4|4d@   stM        |4      rT ||3|P      \  }5}6|6rE || |5| |||"|#?      }7|7d@   r1|5|2d=   d><   dQd|6ddR tQ        |6      tQ        |6      dRkD  |(ddS dT|2dU<   |2S dVjS                  |4j!                  dA      xs g       ddW }8t        dX|8       dQdQ|(ddS dY|2dU<   |2S tU        |%|$|"|#Z      rtW        |	d[   |t        |      t        |&      |"|#\      })t%        |	d   |)      }*	  |||*|||d      }9t+        |9d!d      xs g }:|:stY        d`d^_      |:d   };t+        |;d#d      }<|<tY        dad^_      t+        |<d%d      }=|=rtY        dbt        |=      dd' d^_      t+        |<d(d      }>|>stY        dcd^_      t+        |;d*d      }?|?d+k7  rtY        dd|?d-d^_      	 t-        j.                  |>      }@t        @t              s$tY        dft	        @      j
                   dd^_      	 |j7                  |	d         j9                  @       @j!                  d=      xs i j!                  d>      xs g }A || |A| |||"|#?      }B|Bd@   s7dVjS                  Bj!                  dA      xs g       ddW }CtY        dh|C d^_      dQdQt        |&      ddS di@dj<   |@S dVjS                  |&ddk       }Dt        dl|DddW        |S # t        $ r*}t        dt	        |      j
                   d|       |d}~ww xY w# t        $ r*}t        dt	        |      j
                   d|       |d}~ww xY w# t        $ r*}t        dt	        |      j
                   d|       |d}~ww xY w# t        $ r*}t        d t	        |      j
                   d|       |d}~ww xY w# t0        t2        f$ r*}t        d.t	        |      j
                   d|       |d}~ww xY w# t        $ r*}t        d0t	        |      j
                   d|       |d}~ww xY w# |j:                  $ r*}t        d1t        |j<                        dd2        |d}~wt        $ r*}t        d3t	        |      j
                   d|       |d}~ww xY wc c}'w # t        $ r*}t        dGt	        |      j
                   d|       |d}~ww xY w# t0        t2        f$ r*}t        dMt	        |      j
                   d|       |d}~ww xY w# |j:                  $ r*}t        dOt        |j<                        dd2        |d}~ww xY w# t        $ r,}tY        d]t	        |      j
                   d| d^_      |d}~ww xY w# t0        t2        f$ r,}tY        det	        |      j
                   d| d^_      |d}~ww xY w# |j:                  $ r,}tY        dgt        |j<                        dd2  d^_      |d}~ww xY w)mu  Real-LLM planner via a single ``litellm.completion`` call.

    Returns the parsed graph/output dict ready for
    ``build_render_plan_for_fp`` consumption. Fail-closed at every
    preflight joint — the completion call counter stays 0 whenever this
    function fails before issuing the network call.

    Flow:

      1. Arg-shape guards (no env / network access).
      2. Env preflight: ``OPENAI_API_KEY`` non-empty.
      3. Prompt pack preflight: load system / user_template / schema;
         fail-closed on missing / empty / malformed.
      4. Lazy ``import litellm``.
      5. Litellm capability preflight:
         ``supports_response_schema(model)`` True AND
         ``response_format`` listed in
         ``get_supported_openai_params(model, custom_llm_provider="openai")``.
      6. Assemble messages (system + structured user prompt with the
         JSON-dumped upstream blocks).
      7. Single ``litellm.completion`` call. Strict ``json_schema``
         response_format using the SANITIZED schema, ``num_retries=0``,
         no temperature, ``max_completion_tokens`` + ``timeout`` locked
         to module-level defaults.
      8. Response guards: non-empty choices, message present, no
         truthy refusal, non-empty content, ``finish_reason == "stop"``
         exactly.
      9. Parse JSON content; reject non-object roots.
     10. Local ``jsonschema.Draft202012Validator`` against the ORIGINAL
         pack schema (keeps minLength / minItems / etc. enforcement).
     11. Production ``validate_llm_output`` — DAG / refs / camera /
         anchor / same-fp / rationale; fail-closed on diagnostics.

    Returns the parsed dict on full success; raises
    ``ShotAwareBgRenderPlanProviderError`` on any failure.
    z$fp_id must be non-empty string (got rU   zdossier must be dict (got zgeometry must be dict (got z!shot_readiness must be dict (got Nz$candidate_catalog must be list (got r   )has_openai_keyz9OPENAI_API_KEY missing or empty; refusing to call litellm)llm_completionzlitellm import failed: rT   )r   z)litellm.supports_response_schema raised: zmodel zH does not advertise response_schema support in litellm; refusing to callopenai)r   custom_llm_providerz,litellm.get_supported_openai_params raised: response_formatzK does not list response_format in supported openai params; refusing to callru   unknownrW   )rW   rt   ru   r}   r~   r   r   rV   r   rM   r   )r   messagesr   timeoutr   num_retrieszlitellm.completion raised: choiceszresponse.choices is emptymessagez&response.choices[0].message is missingrefusalzLLM emitted a refusal:    r   z,response.choices[0].message.content is emptyfinish_reasonstopz*finish_reason must be 'stop' exactly (got z$); length/content_filter fail closedz$response content is not valid JSON: z+response content is not a JSON object (got zjsonschema import failed: z$local jsonschema validation failed: i,  zlocal jsonschema setup failed: )snap_camera_cells_to_candidatesvalidate_llm_outputrm   r   c              3  B   K   | ]  }t        |t              s|  y wr=   rF   r   )r   bs     r9   r   z=litellm_shot_aware_bg_render_plan_provider.<locals>.<genexpr>  s      &
%!As);A~s   c              3  h   K   | ]*  \  }}t        |t              r|j                  d       r| , yw)okN)rF   rG   rJ   )r   bgidentrys      r9   r   z=litellm_shot_aware_bg_render_plan_provider.<locals>.<genexpr>  s6      &
 e %&599T?	  s   02rn   c              3  D   K   | ]  }t        |t              r|r|  y wr=   r   )r   bids     r9   r   z=litellm_shot_aware_bg_render_plan_provider.<locals>.<genexpr>  s-      	>  c3C 	s    anchor_selection_metadatacandidate_bg_idsr   r   )rt   r   same_fp_bg_idsr~   ru   r   r   r   r   camera_celllook_at_cell	lens_enumfov_deg)r   r   r~   zgproduction validate_llm_output failed (camera-only first pass); W20F9 retry litellm.completion raised: z%W20F9 retry response.choices is emptyz2W20F9 retry response.choices[0].message is missingz#W20F9 retry LLM emitted a refusal: z8W20F9 retry response.choices[0].message.content is emptyz.W20F9 retry finish_reason must be 'stop' (got z$W20F9 retry content not valid JSON: z+W20F9 retry content not a JSON object (got z0W20F9 retry local jsonschema validation failed: )r   r~   r0      
   ) camera_validator_retry_attempted camera_validator_retry_succeededcamera_candidate_snap_repairs#camera_candidate_snap_repairs_total'camera_candidate_snap_repairs_truncatedfirst_pass_camera_diagnostics_w20f9_retry_metadataz; i  z9W20F9 retry production validate_llm_output still failed: )r  r  r  )r   r   r   rX   )r   r   r   r   r   r   ziproduction validate_llm_output failed (graph/anchor first pass); W20F10 retry litellm.completion raised:    r:   z&W20F10 retry response.choices is emptyz3W20F10 retry response.choices[0].message is missingz$W20F10 retry LLM emitted a refusal: z9W20F10 retry response.choices[0].message.content is emptyz/W20F10 retry finish_reason must be 'stop' (got z%W20F10 retry content not valid JSON: z,W20F10 retry content not a JSON object (got z1W20F10 retry local jsonschema validation failed: z:W20F10 retry production validate_llm_output still failed: )&graph_anchor_validator_retry_attempted&graph_anchor_validator_retry_succeededfirst_pass_diagnostics_graph_anchor_retry_metadata   z'production validate_llm_output failed: )-rF   r   r/   rG   rB   r2   rK   app.core.openai_keysr   rk   litellmr   r]   r   supports_response_schemaget_supported_openai_paramsrJ   r   r   rI   r   getattrr_   r`   ra   rb   
jsonschemaDraft202012ValidatorvalidateValidationErrorr   .app.modules.pipeline.shot_aware_bg_render_planr   r   	frozensetrL   tuplerH   r   r   lenr   r   r   r@   )Ert   r}   r~   r   r   r   r   r   r   packr  _llm_completionrj   supports_oksupported_paramsreadback_status_rawru   r   r   r   r   responser   choicer>   r   r   r   parsedr  r   r   r   renderable_rawr   r   r   r   	diag_listr   	cam_diagsrepair_user_promptrepair_messagesretry_responseretry_choicesretry_choice	retry_msgretry_refusalretry_contentretry_finishretry_parsedretry_nodesretry_validatorssnapped_nodessnap_repairssnapped_validators
retry_diagga_response
ga_choices	ga_choicega_msg
ga_refusal
ga_content	ga_finish	ga_parsedga_nodesga_validatorsga_diagdiagsE                                                                        r9   *litellm_shot_aware_bg_render_plan_providerrC    s   ` eS!025)1=
 	
 gt$0(g)?)?(@B
 	
 h%0)$x.*A*A)B!D
 	
 nd+0(112!5
 	
  '.0*+445Q8
 	
 40G
 	

 D	
7;;%;HI 0UI 3 4
 	
// 0   	 	  000UI 8 9
 	
 #,,'89 )3/4G 	 
 %?+'%+K ?+H :$x.I,)O"+#"7
 h	406BG0'
 	
 QZF
&)T
*C
{04
 	
 c9d+G0%c'l4C&8%;<
 	
 c9d+G0:
 	
 FOT:M08CE
 	
G$ fd#0L))*!-
 	

	''X7@@H 	3	4	:@@BN %*//0CDN.4-0% &
%&
 
 & &
  %2**84:eg &
 
 5> 	> [[45;@@"  	> 	5! ZZ &B++G4:E$%'+&CJ -. NN=17R	":.$$a!Q&%*"a' A~ 9   "B &#,!"
 ."?3.O!0,$3+*? !"$ $NItDJM 8;  )+Li>I 8H  $Iy$?M89=)$3/24  $Iy$?M 8N  #<$GLv%8D#&&JL #zz-8 lD18 .778; //X?HHV (++G4:??HNBK2!-! /"3.K  $$;< ++;<2Q)H3/M< $-@"'"/+9%-,;.? =
.* ..EF=JL1':DEDE$0"$5GJ$0H" %($5$:AJ3BEL)@A $0/!YY$((7=23
 9)l,  56451:3B5L01
  (/*G	
 "H $%C D%0!$W O"3.K" ."?3.O-,$3+*? !  !i>D"J%<%&  #1IY	48F~%I%&  !D9J%::t,/1%& 
 !D9J%%& 
  	?DAIF"%E m#GI%& 
 JJz2	 i.% O445Q8%& 
	//N(9% "g.4"99'BHbH/-! /"3.KM !!89 ))!%%m4:3 &&i)%&  ;<:;*.y/#2*>9I45
 yy2A'05d4Cj\B
 	
 MO  0%d3i&8&8%9C5A
	  07Cy!!""SE+
 	"  0:Cy!!""SE+
 	b  0)$s)*<*<)=RuE
	J z" 02Cy!!""SE+
 	  0(c););(<BseD
	 %% 023s{{3CDS3I2JK
	  0-d3i.@.@-AC5I
	~6  8KCy))*"SE3 	P z* 8:Cy))*"SE3  -- 8F3;;'-.0 \  %LCy))*"SE3 &'	
 X z* %;Cy))*"SE3%& 	  -- %G3;;'-.0%& 	sJ  
b c 	c; d1 
e' f# #g =i7i 'j /#j= 7k9 1l1 ;#m/ 	c%b==c	c8%c33c8;	d.%d))d.1	e$:%ee$'f 6%ff #	g,%ggi(%hi%h>>i	i>%i99i>j:%j55j:=k6%k11k69	l.'l))l.1m, 'm''m,/n*>'n%%n*)r>   r   r;   r   returnr/   )rM   r   rD  r   )rD  Dict[str, Any])rW   r   rt   r   ru   r   r}   rE  r~   rE  r   rE  r   	List[Any]rD  r   )rV   r   r   r   rD  zList[Dict[str, Any]])r   rE  r   	List[str]r~   rE  rD  r   )r   rE  rD  r   )
r   rE  r   rF  r   r,   r   r,   rD  r   )r   r   r   r   r   r   r   rG  r   r,   r   r,   rD  r   )r   rE  rD  rE  )rt   r   r}   rE  r~   rE  r   rE  r   zOptional[List[Any]]r   r   r   r   r   r   rD  rE  ).r5   
__future__r   r_   loggingospathlibr   typingr   r   r   r   r	   r
   	getLoggerr2   loggerr   r6   r   r   r   r   __file__resolveparentr   r  r-   r]   r/   r@   rI   rk   r   r   r   r   r   r   r   r   r   r   rC  r7   r8   r9   <module>rR     s  $J #   	  > >			8	$ 2 s 1%* s *"  ": c : ' & 	N##**1188??78:HI $  3< = 3  	# 	# *+	#&'D<~99 9 	9
 9 9 #9 !9 	9x(+C#C "C 	C
 	CL1 o  .7 8 . N 
 1: ; 1 ~ "" " &	"
 $2" 
"J## # 	#
 # &# $2# 	#L
'

0 .2&2!>@	@	 @	 	@	
 #@	 +@	 @	 @	 @	 @	r8   