
    Oj7                       U d Z ddlmZ ddlZddlmZmZmZmZm	Z	m
Z
 ddlmZmZmZ dZdZdZd	Zd
ZdZefZdZdZdZeefZefdefdeefdiZded<    G d de      Zd3dZd4dZd5dZ dd	 	 	 	 	 d6dZ!d7dZ"dZ#dZ$e#e$fZ%d8dZ&d9d Z'	 	 d:d!Z(d"d#	 	 	 	 	 	 	 	 	 	 	 d;d$Z)	 	 	 	 d<d%Z*d=d&Z+d'Z,d(Z-d>d)Z.d?d*Z/	 	 	 	 d@d+Z0d"d#	 	 	 	 	 	 	 	 	 	 	 dAd,Z1	 	 	 	 dBd-Z2 G d. d/e      Z3dCd0Z4dDd1Z5dEd2Z6y)Fu  `location_part` 참조 **묶음** — 맥락 + 상세. ★inert. 유료 0.

## 무엇인가

사용자 확정 (2026-08-31): 「1960년대 이발소 해서 하나로 검색 조사해야 해 …
아님 **둘을 찾아서 참조 두 개 이상으로** … 참조 이미지 여러 개로 해도 돼!」

`location_part` 하나가 **논리 멤버 둘**을 갖는다 —

    맥락(context)  그 장소   — 「1960년대 이발소」
    상세(detail)   그 부분   — 「이발소 회전 간판」

**같은 사진이면 한 장으로 합치고**, 다르면 둘 다 기존 `background` 자리에
붙인다. ★합쳐도 **논리 멤버 둘의 처분·신원·계보는 따로 남는다**.

## 세 가지를 고쳤다 (Codex 재현 2026-08-31)

    ①하류 SOT 가 뒤집혀 있었다 — raw `status` 를 봤다.
      정본은 **`outcome`**(`selected` | `reference_unavailable`)이고 raw
      status 는 **감사용**이다. `outcome` 이 없거나 모르는 값이면 **조용히
      건너뛰지 않고 선다** — 그러면 `selected` 의무가 사라진다.
    ②exact 열쇠에 **실제 획득 신원**이 빠졌다. `grounding:LP01:detail` 만으로는
      **옛 자산**이 신원 바뀐 새 의무를 **만족시킨다**(재현됨).
    ③「같은 bytes 면 합친다」가 **구현이 없었다.** 같은 사진을 넣으면 2장이
      나오고 역할 차례도 뒤집혔다.

## 왜 새 슬롯을 안 만드나

야외 직행 경로가 **이미** 사진+지도 두 장을 같은 `background` kind 로 넣는다.
그 모양을 그대로 쓴다 — 네 평행 목록에 **덧붙인다**.
★실측: **맥락·상세는 기존 배경판을 대신하지 못한다**(안 붙이면
`StaleUpstreamError`). 그래서 이것은 **더하는** 설계다.

## 아직 안 쓰인다

`app/` 어디서도 부르지 않는다. D 원자적 cutover 에서 general scene 경로가
**RPC sidecar 가 있을 때만** 이것을 부른다.
    )annotationsN)AnyDictListOptionalSequenceTuple)PURPOSE_CONTEXTPURPOSE_DETAILPURPOSESz2.202609020800
backgroundref_kindref_rolecharacter_outlookoutfit_ref_explicit	groundingselectedreference_unavailablebackground_generalgrounding_part_detail_refgrounding_context_detail_refzDict[Tuple[str, ...], str]ROLE_BY_PURPOSESc                      e Zd ZdZy)BundleContractErroruC   묶음이 계약을 어겼다. ★조용히 넘어가지 않는다.N__name__
__module____qualname____doc__     e/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/grounding_reference_bundle.pyr   r   h   s    Mr!   r   c                    t        | D ch c]  }t        |       c}      }|D ]  }|t        vst        d|dt                |st        d      t	        |      S c c}w )uG   쓰임들의 **정본 차례**. ★집합을 결정적으로 만든다.u   모르는 쓰임 u    — u   쓰임이 없다)sortedstrr   r   tuple)purposespgots      r"   _purposes_keyr*   l   sj    
(+(Q#a&(+
,CH%(9!eH:&NOO  !"455: ,s   Ac                    t        | j                  d      xs d      j                         }|t        vrt	        d|dt         d      |S )u  멤버의 **처분**. ★없거나 모르는 값이면 **선다**.

    ★★★조용히 건너뛰면 `selected` 의무가 사라진다 (Codex 재현 2026-08-31).
    앞 판은 raw `status` 를 봤고, `outcome` 이 비면 「아닌가 보다」로 지나갔다.
    outcome u
   처분이 	    다 — uX    중 하나여야 한다. (raw status 는 감사용이고 의무를 정하지 않는다))r%   getstripOUTCOMESr   )mr)   s     r"   _outcome_ofr3   w   s\     aeeI$"
%
+
+
-C
(!y
 3M NO 	O Jr!   c                    t        j                         }| D ]<  }|j                  t        |      j	                  d             |j                  d       > |j                         d d S )Nzutf-8       )hashlibsha256updater%   encode	hexdigest)partshr(   s      r"   _foldr>      sR    A	Qw'(	  ;;="r!   r    )covered_final_idsc                   t        | t              rt        |       gn!t        | D ch c]  }t        |       c}      }|D cg c]  }|j                         s| }}|st	        d      t        |D ch c]+  }t        |xs d      j                         s!t        |      - c}      }|st	        d      t        |      }t        |D ch c]+  }t        |xs d      j                         s!t        |      - c}xs t        |            }t         ddj                  |       ddj                  |       dt        |       dt        |       	S c c}w c c}w c c}w c c}w )u  `attached_meta` 에 실을 값. ★**출처·쓰임·신원·덮는 의무**를 다 나른다.

    ★★★신원이 빠지면 **옛 자산이 새 의무를 만족시킨다** (Codex 재현) —
    획득 계약이 바뀌어 다시 사야 하는데 옛 사진이 붙어 있으면 통과했다.

    Args:
        subject_final_id: 이 참조의 **주인**. 부모 장소 맥락이면 `L01`,
            그 장소 부분 상세면 `LP01` 이다.
        covered_final_ids: 이 한 장이 **덮는 의무들**. 부모 장소 맥락 한 장이
            형제 `LP01`·`LP02` 의 맥락 의무를 같이 덮는다 — 그때 여기에 둘 다
            적힌다. ★★앞 판은 `subject_final_id` 밖에 없어서 형제마다 **같은
            사진을 따로** 넣게 되어 있었다 (Codex BLOCK · 09-01): 모델에 같은
            사진이 중복되고 참조 장수 상한도 낭비한다.
            ★비면 주인 자신만 덮는다.
        `subject_final_id` 가 비었다r-   uJ   획득 신원이 없다 — 무엇으로 샀는지가 값에 안 실린다:+)

isinstancer%   r$   r0   r   r*   setSOURCE_PREFIXjoinr>   )	subject_final_idr'   member_identitiesr?   xsubjectsidspscovs	            r"   
meta_valuerO      sV   * .4 %&'-=>-=SV-=>?  $18aqwwy8H1!"DEE
"3L"3Qs17|7I7I7K#a&"3L
MC!XZ 	Z	x	 B
"3L"3Qs17|7I7I7K#a&"3L "M#CoQsxx12!CHHRL>Szl!E#J<) * ?1 M
 Ms)   EEE5"EE"E$0E$c                   t        | xs d      j                  d      }t        |      dk7  s|d   t        k7  ry	 t	        |d   j                  d            }|d   j                  d      t        |      |d	   |d
   dS # t
        $ r Y yw xY w)u   `attached_meta` 값을 되읽는다. 조사 것이 아니면 `None`.

    ★이름을 해석하지 않는다 — **우리가 만든 값**의 모양만 읽는다.
    r-   rB      r   N   rC            )subject_final_idsr'   identity_foldcovered_fold)r%   splitlenrF   r*   r   list)valuer<   rM   s      r"   parse_meta_valuer]      s    
 ""3'E
5zQ%(m358>>#./ "'q!4$r("1XuQxA A  s   B   	BBassetfilec                   t        | j                  d      xs d      j                         }|t        vrt	        d|dt         d      |t
        k(  r>t        | j                  d      xs d      j                         }|st	        d      ||dS t        | j                  d	      xs d      j                         }|st	        d
      ||dS )u:  멤버 하나의 **좌표**. ★갈래를 명시로 받는다.

        {"source": "asset", "asset_id": "<UUID>"}   ← DB/service 가 읽는다
        {"source": "file",  "path": "<정본 경로>"}   ← 파일에서 읽는다.
                                                      ★asset_id 를 **주장 안 한다**

    ★`ref_image_map` 의 열쇠는 **참조 map key** 지 ImageAsset UUID 가 아니다
    (실측). 그것을 쓰려면 `ref_map_key` 로 **따로** 부른다 — `asset_id` 라고
    부르면 읽지 않은 UUID 가 계보로 남는다.
    sourcer-   u   좌표 갈래가 r.   uN    중 하나여야 한다. `asset_id or path` 로 모호하게 두지 않는다asset_idu+   `source=asset` 인데 `asset_id` 가 없다)ra   rb   pathu&   `source=file` 인데 `path` 가 없다)ra   rc   )r%   r/   r0   SOURCESr   SOURCE_ASSET)r2   srcvs      r"   coordinate_ofrh      s     aeeHo#
$
*
*
,C
'!wiy 9C CD 	D lj!'R(..0%&STT1--AEE&MR &&(A!"JKK1%%r!   c                    t        | j                  d      xs d      t        | j                  d      xs | j                  d      xs d      fS )Nra   r-   rb   rc   )r%   r/   )cs    r"   
_coord_keyrk      sD    h%2&j!8QUU6]8b9; ;r!   c                2   i }| D ]  }t        |      }t        |j                  d      xs d      }|st        d      t        |j                  d      xs d      }t	        |g       |j                  d      du}|t
        k7  s|rt        |j                  d      xs d      }|st        | d| d	      t        |j                  d
      xs d      j                         }|st        | d| d      t        |      }	t        |j                  t              xs t              }
t        |j                  t              xs d      xs d}|
t        v r
|
 d| d| n|
 d| }|j                  |g |g g g g g g |
|d
      }|d   |k7  rt        | d|d   d|      |d   j                  |       |d   j                  |	       |d   j                  |       |d   j                  |       |d   j                  d |j                  d      xs |fD               |j                  d      r |d   j                  t        |d                |j                  d      sa|d   j                  t        |d                 g }g }t        |      D ]n  }||   }t	        |d         }t        t!        |d               }t#        |||d   |d         }|d   xs t$        j                  |      }|t        d| dt        t$                     t        |d   D ci c]  }t'        |      | c}j)                               }|j                  |t+        |      |||d
   |D cg c]  \  }}|	 c}}dj-                  t        t!        |d                     xs dt        t!        |d               t        t!        |d               t        t!        |d               |d    d!       |j                  |d    |f       q ||fS c c}w c c}}w )"ut  논리 멤버들 → **붙일 자리들** + **정확히 붙어야 하는 짝**.

    ★★★**bytes 를 안 받는다** — CP 는 JSON 이라 bytes 를 못 담는다.
    durable sidecar 는 **좌표**(`source`+`asset_id|path`)와
    `content_sha256` 을 적고, 소비하는 쪽이 읽어 **해시를 확인**한다.

    ★★★**같은 사진이면 한 장**으로 합치되 — 합쳐도 **논리 멤버 둘의
    좌표·신원·계보를 전부 보존**한다 (Codex 재현 2026-08-31: 앞 판은
    `setdefault` 가 **첫 좌표만** 잡아 둘째 자산 계보를 잃었고,
    `plan_bundle(AB) != plan_bundle(BA)` 였다).

    ★서로 다른 좌표가 **같은 sha** 를 말하면 둘 다 남기고, **어느 것으로
    읽을지는 결정적으로** 고른다(`coordinates[0]`). 읽은 뒤 해시를 확인하므로
    거짓 주장은 그때 걸린다.
    rH   r-   rA   purposenot_attachedNmember_identity/u!    에 `member_identity` 가 없다content_sha256ub    가 `selected` 인데 `content_sha256` 이 없다 — 사진이 맞는지 확인할 길이 없다 )
rV   rq   coordinatesr'   rI   labelsparent_final_idsr?   r   r   r   u5    의 역할이 같은 자리 안에서 갈린다 — z vs rV   rs   r'   rI   r?   c              3  n   K   | ]-  }t        |xs d       j                         s!t        |       / yw)r-   N)r%   r0   ).0rJ   s     r"   	<genexpr>zplan_bundle.<locals>.<genexpr>8  s-      )P5q#ag2,:L:L:NCF5s   "55coverslabelrt   parent_final_idru   )rH   r'   rI   r?   u   쓰임 조합 u!    에 맞는 역할이 없다 — u    · r   )rV   r'   rolerO   rq   rs   rz   rI   ru   r?   r   )r3   r%   r/   r   r*   OUTCOME_SELECTEDr0   rh   MEMBER_KIND_KEYBUNDLE_KINDMEMBER_ROLE_KEYSINGLE_SUBJECT_KINDS
setdefaultappendextendr$   rE   rO   r   rk   itemsr[   rG   )membersby_hashr2   r,   lprm   deliberately_noneidentshacoordkindr|   keyslotplanrequiredsrM   rK   valrj   coords_ks                          r"   plan_bundler      s   " *,Ga.)*0b1%&HIIaeeI&,"-wi  EE.1=&&*; AEE+,23%$ay ABD D!%%()/R0668%$ay !B BC C a  155)8[9155)/R08D .. vbTu-7;fF3%4H 	!!#!#s2Bbr$	(
  
t#%$K
#&d4(45 5 	 !((,]""5)Z( !((/ 	 !(( )PUU8_55)P 	P55>N!!#aj/255"##$++C2C0D,EFG J "$D&(HgCL1Z=)#a 3456(R+,-@+A+,-@+AC
 }8 0 4 4R 8<% $E*+,./ /
 "#M"24"2Q $A)"2449EG=!)tBx 01+126%"aA62[[AhK(8!9:@b!'A.A,B(C!D &s1-?+@'A B!'A.A,B(C!D*
 	 	:,-? @ >!4 3s   #P
,Pr-   )wherec                  t        |      }d}t        |      D ]j  }	t        |	j                  d      xs d      }
|
s&t        dj	                  |	d          d|	d    d      t        |	j                  d	      xs d
      }d}i }g }t        |
      D ]  \  }} ||      \  }}|s)t        dj	                  |	d          d|	d    d| d      t        j                  |      j                         }||k7  r/t        dj	                  |	d          d|	d    d| d|d|d
      |j                  t        |xs i              |dk(  s|t        |xs i       }} |J | j                  dt        |	j                  d      xs d
      |f       |j                  dt        |	j                  d      xs t              |	d   f       |j                  d|	d          |	d   t        |	d         t        |	d         t        |	d         t        |	j                  d      xs d      t        |	d         |	d	   t        |
      ||d
}|	j                  d      rt        |	d         |d<   |r||d<   |j                  d|       |dz  }m |S )u  `plan_bundle` 이 낸 **좌표**로 사진을 읽어 네 목록에 **덧붙인다**.

    Args:
        load_bytes: `(coordinate) -> (bytes, resolved)`.
            ★반환이 **bytes 만이면 안 된다** — 그러면 읽지도 않은 자산 UUID 가
            계보로 남는다.

    ★★★**좌표를 전부 읽어 전부 확인한다** (Codex 재현 2026-08-31).
    앞 판은 `coordinates[0]` 만 확인하면서 metadata 에는 **좌표 전부**를
    계보로 적었다 — 둘째 좌표가 **다른 사진인데 같은 해시를 주장**해도
    통과하고 그 경로가 계보로 남았다. 즉 **확인하지 않은 자산이 계보**가 됐다.
    읽는 것은 **무료**이므로 전부 읽고, 하나라도 없거나 해시가 다르면
    **provider 앞에서 선다**. 쓸 것은 **첫 좌표**로 결정적으로 고른다.

    ★기존 것을 안 지운다. 차례는 `plan_bundle` 이 정한 정본을 지킨다.
    r   rs   r    rC   rV   rp   r'   u    에 좌표가 없다rq   r-   Nu    의 사진을 못 읽었다 (u<   ) — 계보에 적을 좌표는 **전부** 읽혀야 한다u    의 좌표 u9    가 **다른 사진**을 가리킨다 — 적힌 해시 u    · 읽은 것 u8   . 확인 안 한 자산을 계보로 남기지 않는다rz   r   rO   r|   r?   rI   )
pipeline_roler'   rV   grounding_subject_idsr?   rI   rq   rs   verified_sourcesresolved_sourceru   r   rS   )r[   reversedr/   r   rG   r%   	enumerater7   r8   r;   r   dictinsertr   )labeled_refsattached_meta	ref_rolesref_role_metadatar   
load_bytesr   r)   nr2   r   want	first_rawfirst_resolvedcheckedirj   rawresolvedr   metas                        r"   insert_bundle_refsr   e  s   ( t*C	Ac]aeeM*0b1%88A1234Aa
m_DYZ\ \155)*0b1%)	)+(*f%DAq&qMMC)xx"5 678!J- IsVXY Y ..%//1Cd{)xx"5 678!J-UVTW XKKO(RagUWX X NN4B/0Av,/hn"1E>	 & $$$AAEE'N$8b 99EFQQUU:%6%E+!F!"<!2 	3AfI&vYQz]+!%a(;&<!=%)!,?*@%A!%aee,?&@&FB!G!%a(;&<!= 01< '- 
 55#$'+A.@,A'BD#$!DM  D)	Qa b Hr!   c                &   |D ch c]  \  }}t        |      t        |      f }}}| D cg c]3  \  }}t        |      t        |      f|vrt        |      t        |      f5 }}}|rt        dt        |       d| d      yc c}}w c c}}w )u  **durable 로 적힌** 요구가 실제로 붙었나. ★안 붙었으면 선다.

    ★★`required` 는 **RPC 에 적힌 것**을 넘긴다 — 붙이는 쪽이 쓴 목록을
    그대로 다시 넘기면 **둘 다 빠뜨려도 통과한다**(Codex).

    ★기존 plate/space/outdoor 면제는 이것을 **대신 만족시키지 못한다** —
    값이 `grounding:<lp>:<purposes>:<신원>` 이라 다른 것과 안 겹친다.
    u   조사가 고른 참조 u   개가 안 붙었다: uS    — 기존 배경판이나 클로즈업 면제가 이것을 대신하지 못한다N)r%   r   rZ   )r   r   krg   havemissings         r"   assert_grounding_attachedr     s     *77ASVSVD7,4 0HDAqq63q6"$. AAHG 0!&s7|n4J7) T] ]^ 	^  80s
    B8Bc                    t        |       }t        t        j                               t        hz  }t	        |D ch c]	  }||vs| c}      S c c}w )u   이 묶음이 쓰는 역할 중 **아직 선언 안 된 것**.

    ★`REF_ROLE_VALUES` 는 **닫힌 목록**이라 모르는 역할이면 즉시 선다.
    D cutover 에서 이 목록이 **비어야** 켤 수 있다.
    )rE   r   valuesOUTLOOK_ROLEr$   )knownr   usedrs       r"   undeclared_rolesr     sK     u:D&&()\N:Dd4datm1d4554s   	AAgrounding_bundle_membersgrounding_required_attachmentsc                f   t        | t              syt        | vry| t           }t        |t              s)t	        dt         dt        |      j                   d      |D ]3  }t        |t              rt	        dt        |      j                          |D cg c]  }t        |       c}S c c}w )uN  RPC 에서 묶음 멤버를 꺼낸다. ★**키가 없을 때만** `None`(옛 길).

    ★★★앞 판은 `rpc.get(...)` 이 `None` 을 주면 **키가 없는 것과 똑같이**
    옛 길로 접었다 (Codex 재현 2026-08-31): `{members: None}` 이 조용히
    통과했다. schema marker 에 쓴 것과 **같은 원칙**을 여기도 쓴다 —

        키 없음              → `None`. 옛 CP 라 옛 길이 맞다
        키 있고 목록         → 그 목록 (빈 목록은 **명시 「없음」**)
        키 있는데 목록 아님   → **선다** (`None` 포함)
    N`%   ` 가 있는데 목록이 아니다: u4    — 모르는 것을 옛 것으로 읽지 않는다u   멤버가 dict 가 아니다: )rD   r   RPC_MEMBERS_KEYr[   r   typer   )rpcr)   rJ   s      r"   members_from_rpcr     s     c4 c!
o
Cc4 !  ECy!!""VXY 	Y !T"%0a1A1A0BCE E  !!SDGS!!!s   B.c           	     @   t        | t              syt        | vry| t           }t        |t              s(t	        dt         dt        |      j                         g }|D ]  }t        |t              rL|j                  t        |j                  d      xs d      t        |j                  d      xs d      f       _t        |t        t        f      r:t        |      dk(  r,|j                  t        |d         t        |d	         f       t	        d
|       |S )ua  RPC 에 **적힌** 요구를 읽는다. ★**키가 없으면 `None`** — 「없음」과 다르다.

    ★★★앞 판은 `rpc.get(...) or []` 라 **키 없음**과 **명시 빈 목록**을
    못 갈랐고, 잘못된 타입(`{}` 등)도 조용히 삼켰다 (Codex 재현).
    그러면 만드는 쪽이 요구를 안 적었는데 「없다고 적었다」로 읽힌다.

        키 없음              → `None`  (만드는 쪽이 안 적었다)
        키 있고 목록         → 그 목록 (빈 목록은 **명시 「요구 없음」**)
        키 있는데 목록 아님   → **선다**
    Nr   r   r   r-   r\   rR   r   rS   u   요구 모양이 이상하다: )rD   r   RPC_REQUIRED_KEYr[   r   r   r   r   r%   r/   r&   rZ   )r   r)   outrJ   s       r"   required_from_rpcr     s    c4 s"

Cc4 ! !!FCy!!"$% 	% "$CaJJAEE&M/R0#aeeGn6J2KLMD%=)c!fkJJAaD	3qt9-.%(Gu&MNN  Jr!   c                *   ddl }|D cg c]  }t        |       }}	 |j                  |d       t        |      \  }}|| t        <   |D 	cg c]
  \  }}	||	d c}	}| t        <   |S c c}w # t        $ r}t	        d| d      |d}~ww xY wc c}	}w )u9  **만드는 쪽**이 members 와 required 를 **한 번에** 적는다. ★원자적.

    ★★★canary 나 시험이 sidecar 를 **손으로 두 벌** 만들면, 그 둘이 어긋난
    채로 굳는다 (Codex 2026-08-31). 그래서 **이 함수 하나**가 —

        ①JSON 으로 담을 수 있는 모양인지 본다 (bytes 가 섞이면 선다)
        ②`plan_bundle` 로 요구를 낸다
        ③members 와 required 를 **같은 RPC 에 같이** 적는다

    ★소비하는 쪽은 **이 함수를 안 부른다** — 읽고 대조만 한다.
    r   NF)ensure_asciiu    멤버를 CP 에 못 담는다 (uE   ) — durable sidecar 는 bytes 가 아니라 **좌표**를 적는다)r   r\   )jsonr   dumps	TypeErrorr   r   r   r   )
r   r   r   rJ   r)   exc_planr   r   rg   s
             r"   write_sidecarr     s     #
$Gq47GC
$:

3U
+
 "#&OE8CAIJAa!4JCO %  :!.se 40 0169	:: Ks"   A)A. B.	B7BBc          	        t        |      }|yt        |      \  }}	t        |      }
|
t        dt         d      t        |
      t        |	      k7  r#t        dt        |
       dt        |	             t        | ||||||      S )u  RPC 에 sidecar 가 **있을 때만** 붙인다. ★없으면 **아무것도 안 한다**.

    ★★★**요구를 쓰지 않는다** — 읽고 **대조만** 한다. 쓰면 SOT 가 아니게
    되고, 조립이 이 호출을 빠뜨리면 쓰기도 같이 빠져 거짓 통과한다.

        멤버 키 없음                  → 0. 목록 한 글자도 안 바뀐다
        멤버 키 있는데 목록 아님       → **선다**
        멤버는 있는데 요구 키가 없음   → **선다**
        요구가 다시 낸 것과 **다름**   → **선다**
        맞으면                        → 좌표로 읽어 붙인다(해시 확인)
    r   u   sidecar 는 있는데 `u{   ` 키가 **없다** — 만드는 쪽이 둘을 같이 적어야 한다(`write_sidecar`). 소비하는 쪽은 안 만든다uJ   적힌 요구와 멤버에서 다시 낸 것이 **다르다**: 적힌 것 u    · 다시 낸 것 )r   r   )r   r   r   r   r   r$   r   )r   r   r   r   r   r   r   r   r   expected	persisteds              r"   attach_from_rpcr   4  s     s#G )ND(!#&I!%&6%7 80 01 	1 iF8,,! +,,?x@P?QST 	T lM9/)35B Br!   c                8    t        |       }|syt        ||       y)u   RPC 에 **적힌** 요구가 붙었나. ★조립·검사 **뒤에** 부른다.

    ★기존 `validate_attached_refs` 의 클로즈업·plate 면제와 **별개**다 —
    조사가 고른 것은 그 면제로 대신 채워지지 않는다.
    N)r   r   )r   r   r   s      r"   assert_from_rpcr   U  s     !%Hh6r!   c                      e Zd ZdZy)ReferenceCountRefuseduV   참조 장수가 provider 가 받는 범위 밖이다. ★보내기 **전에** 선다.Nr   r    r!   r"   r   r   m  s    `r!   r   c                    t        | dd      }t        |      s!t        t        |       j                   d       |       }t        |t              s!t        dt        |      j                         t        |      S )u   client 에게 **직접 묻는다**. ★registry 를 베끼지 않는다.

    ★없으면 **선다** — 「아마 다 받겠지」로 지나가면 잘린 참조가 **그림에서만**
    드러난다.
    reference_input_capabilityNuf    에 `reference_input_capability()` 가 없다 — 몇 장을 받는지 모르는 채로 안 보낸다u%   능력 계약이 dict 가 아니다: )getattrcallabler   r   r   rD   r   )clientfnr)   s      r"   capability_ofr   q  s     
5t	<BB<#F|$$% &M MN 	N $Cc4 #3DI4F4F3GHJ 	J9r!   c                x   | j                  d      }| j                  d      }| j                  dd      s|rt        | j                  d       d      |1|t        |      k  r#t        | j                  d       d| d	| d
      |2|t        |      kD  r#t        | j                  d       d| d| d      yy)u  참조 `n` 장이 이 provider 에 맞나. ★**보내기 전에** 본다.

        `max_images` 가 수    → `n <= max` 를 **본다**
        `max_images` 가 None  → 「**모른다**」다. ★상한을 **발명하지 않는다** —
                                지나가되 부르는 쪽이 observed n 을 **기록**한다
        `min_images`          → 모자라면 선다

    ★넘으면 **조용히 자르지 않는다** — 잘린 참조는 그림에서만 드러난다.
    
min_images
max_imagessupports_labeled_refsTprovideru-    는 이름표 붙은 참조를 안 받는다Nu    는 참조 u   장 이상이 필요한데 u	   장이다u   장까지인데 u/   장이 왔다 — 조용히 자르지 않는다)r/   r   int)
capabilityr   lohis       r"   assert_reference_countr     s     
	%B		%B>>148Q#~~j)**WXZ 	Z	~!c"g+#~~j)*,rd:Uc 	 
~!c"g+#~~j)*,rd:J1# N7 78 	8 &~r!   c                    t        | j                  d      xs d      t        | j                  d      xs d      t        | j                  dd            | j                  d      | j                  d      dS )	uD  지문에 접을 것 — **provider class/model + 능력**.

    ★provider 를 바꾸면 같은 문안·같은 참조라도 **다른 산출**이다. 능력이
    바뀌면 보낼 수 있는 장수가 달라진다. 둘 다 지문에 들어가야 옛 산출이
    「지금 계약의 것」으로 통하지 않는다.
    r   r-   modelr   Tr   r   )r   r   r   r   r   )r%   r/   bool)r   s    r"   capability_lockr     si     
z28b9Z^^G,23!%NN2D9"; nn\2 nn\2 r!   )r'   Sequence[str]returnzTuple[str, ...])r2   Dict[str, Any]r   r%   )r<   r   r   r%   )r'   r   rI   r   r?   r   r   r%   )r\   r%   r   zOptional[Dict[str, Any]])r2   r   r   Dict[str, str])rj   r   r   zTuple[str, str])r   Sequence[Dict[str, Any]]r   z2Tuple[List[Dict[str, Any]], List[Tuple[str, str]]])r   r   r   r   r   r   r   r   r   r   r   r   r   r%   r   r   )r   Sequence[Tuple[str, str]]r   r   r   None)r   r   r   z	List[str])r   r   r   zOptional[List[Dict[str, Any]]])r   r   r   zOptional[List[Tuple[str, str]]])r   r   r   r   r   zList[Tuple[str, str]])r   r   r   r   r   r   r   r   r   r   r   r   r   r%   r   r   )r   r   r   r   r   r   )r   r   r   r   )r   r   r   r   r   r   )r   r   r   r   )7r   
__future__r   r7   typingr   r   r   r   r   r	   +app.modules.pipeline.grounding_host_contextr
   r   r   BUNDLE_CONTRACT_VERSIONr   r~   r   OUTLOOK_KINDr   r   rF   r}   OUTCOME_UNAVAILABLEr1   r   __annotations__RuntimeErrorr   r*   r3   r>   rO   r]   re   SOURCE_FILErd   rh   rk   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r    r!   r"   <module>r      s#  %L #  = =C C +    #$ %    - 12 ,2n%'E0 , N, N 35"*"/"*"/"*9<"*JA$ 
%&6;zGz@ 68G"&G;?G5G $'G 03G =@GT^-F^#'^(6. -3 "6>38M< @BB#B8<BB-0B9<BFIBB
7#<
7AE
70aL a$82r!   