
    Oj6                       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Zdddddd	Zdd
Z	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZ eh d      Z	 	 	 	 	 	 ddZddZ e       ZddddZ	 	 	 	 	 	 	 	 ddZdd	 	 	 	 	 	 	 ddZy)u9  GROUNDING-V2 §2-3.5 — A0 후보를 추출 입력에 **합류**시킨다.

설계: 계획 §1.8.

★A0 를 sidecar 로만 두면 **결속할 엔티티가 아예 없다.** ``entity_all`` 은
**shot description 만** 읽고(``entity_lister.py:170``) shot schema 는
``characters`` 가 필수가 아니라, 한 번만 크게 나오는 대상은 볼 기회조차 없다.
그래서 A0 산출이 **다음 단계 입력에 합류**해야 한다.

★**legacy 는 한 글자도 안 바뀐다** — 후보가 없으면 빈 목록을 돌려주고
호출부는 아무것도 안 붙인다.

★후보를 **넣기만 하고 끝나지 않는다.** 뒤 단계가 그것을 또 거를 수 있으므로
``missing_candidates`` 로 **완전성**을 확인한다 — 하나라도 사라지면
「미확정」이지 「통과」가 아니다.
    )annotationsN)AnyDictIterableListSequenceOptional   prop	characterlocation)r   r   r   location_partoutlookc                    dj                  t        j                  d| xs d      j                         j	                               S )uX   비교용 정규화 — NFKC + 소문자 + 공백 축약. ★뜻으로 묶지 않는다. NFKC )joinunicodedata	normalizelowersplit)texts    \/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/grounding_overlay.py_normr   %   s4    88K))&4:2?EEGMMOPP    c                z    | D cg c]+  }t         j                  |j                  d            |k(  r|- c}S c c}w )uR   그 갈래가 받아야 할 후보만. ★owner 를 억지로 바꾸지 않는다.
owner_type)_OWNER_TO_ENTITYget)
candidatesentity_typecs      r   candidates_forr$   *   sG     " Iz!##AEE,$78KG z I I Is   08c                    t        | |      }|sg S ddg}|D ]_  }|j                  d|j                  dd       d|j                  dd       d	|j                  d
d       d|j                  dd              a |S )u  추출 입력에 덧붙일 줄. ★후보가 없으면 **빈 목록** — legacy 불변.

    ★「이미 제외 규칙에 걸릴 만한 것들이니 빼지 마라」를 함께 적는다.
    후보만 나열하면 모델이 기존 제외 규칙을 그대로 적용해 다시 지운다.

    ★**제외 기준 뒤에 놓아야 한다.** 앞에 놓으면 프롬프트가
    「빼지 마라 … 빼라」 순서가 되어 자기모순이고, 뒤에 오는 목록이 이긴다.
    조립 순서는 호출부 시험으로 잠근다.
    ug   [원문에서 먼저 건진 고증 후보 — ★위 제외 기준보다 이 목록이 우선합니다]u   아래는 원문을 읽고 **거르기 전에** 건진 것입니다. 위에 적힌 제외 기준 중 무엇에 걸리더라도 **이 목록의 대상은 빼지 마세요.**z- surface_formr   u    (×planned_occurrences   z, source_anchoru   ) — why_candidate)r$   appendr    )r!   r"   minelinesr#   s        r   build_overlay_linesr.   2   s     *k2D	q	rE
 ~b)* +%%-q12"QUU?25N4O P55,-/	
  Lr   c           	        |D cg c]  }t        |j                  dd             }}g }t        | |      D ]F  }t        |j                  dd            s!t        fd|D              r6|j	                  |       H |S c c}w )u;  후보 중 **산출에 안 남은 것**. ★없어진 것을 「없다」로 읽지 않는다.

    이름이 그대로 살아남는다는 보장은 없으므로 **정규화한 표면형이
    산출 이름 안에 들어 있는지**로 본다 — 「고무줄로 묶인 회수권 뭉치」가
    「회수권 뭉치」로 줄어도 잡는다.

    ★이것은 **글자로 뜻을 판단하는 것이 아니다.** 「같은 대상인가」를 묻는 게 아니라
    「내가 넣은 말이 산출에 남았는가」를 묻는 것이다 — 완전성 확인이다.
    namer   r&   c              3  2   K   | ]  }|v xs |v   y wN ).0nwants     r   	<genexpr>z%missing_candidates.<locals>.<genexpr>c   s"     95a419)T	)5   )r   r    r$   anyr+   )r!   	survivingr"   enamesoutr#   r6   s          @r   missing_candidatesr>   O   s     099y!U155$%yE9 "CJ4QUU>2./9599JJqM 5 J :s   "B>   designresearch
unresolvedc                   | D ch c]J  }|j                  d      t        v r1t        |j                  d      xs |j                  d      xs d      L }}|j                  d       t	               }|j                         D ]s  }|D ]l  }|j                  d      xs dj                         }|s)t        |j                  dd            sGt        fd|D              s\|j                  |       n u |S c c}w )u  조사·저작·미확정 대상의 ``short_id``. ★**기존 보호 통로에 union 한다.**

    계획 §1.8: 「새 필터 기구는 만들지 않는다 — 기존 ``protected_short_ids`` 통로에
    research 확정 후보를 union 한다」.

    ★``skip`` 은 보호하지 않는다. 넣으면 저빈도 필터가 아무것도 못 지우고,
    그러면 이 필터를 없앤 것과 같다.

    ★``unresolved`` 는 **보호한다** — 「모른다」를 「빼도 된다」로 읽으면
    계약 §3 을 어긴다.
    route_surface_formr&   r   short_idr0   c              3  2   K   | ]  }|v xs |v   y wr2   r3   )r4   wr0   s     r   r7   z&protected_short_ids.<locals>.<genexpr>   s"     ADqAI22Dr8   )	r    PROTECTED_ROUTESr   discardsetvaluesstripr9   add)	decidedentities_by_typedr6   r=   rowsr;   sidr0   s	           @r   protected_short_idsrS   m   s    " A55>-- 	aeeO$Cn(=CD 	 
 	LLuC '')A55$*113Cvr*+DADAA  * Js   AC:c                 `    ddl m}  i }| j                         D ]  \  }}|||<   ||| d<    |S )u   ★접두는 **계약 모듈**에서 오고, **복수 alias 만** 코드로 만든다.

    여기 다섯 갈래를 다시 적으면 그것이 두 벌이다 (Codex).
    r   )OWNER_PREFIXs).app.modules.pipeline.grounding_entity_contractrU   items)rU   r=   ownerpres       r   _build_entity_prefixr[      sD    
 LC"((*
sE
ugQK + Jr   )
characters	locationspropsc                   ddl m} |D ch c](  } ||      rt        |j                  d      xs d      * }}|j	                  d       |si S ddlm} i }|j                         D ]n  \  }}	t        j                  |      }
|
st        j                  ||      }||vr:t        | |	|      D cg c]$  }t        |j                  d      xs d      |v r|& }}|sw|	D ch c]   }t        |j                  d      xs d      " }}d}|D ]  }	 |dz  }|
 |d	}||vrn|j                  |       |j                  |g       j                  |t        |j                  d
d            dt        |j                  d      xs d      |j                  d      xs d|j                  d      xs dt        |j                  d      xs g       d        q |S c c}w c c}w c c}w )u  ★★★**어려운 단발 대상을 실제 엔티티 행으로 만든다.**

    왜 필요한가 — `protected_short_ids` 는 **이미 있는 행**의 이름을 찾아
    보호할 뿐이고, `promoted` subject 는 `short_id` 가 **없다**
    (`grounding_carry`: 「엔티티가 없어서 온 것이다」). 그래서 지금 통로는
    「이미 등록된 저빈도 행을 안 지움」까지만 하고, 사용자가 말한
    **「어려운 단발 대상을 엔티티에 등록」은 못 한다.**

    그러면 옛 화폐가 **조사 대상에는 있는데 엔티티·카드에는 없는** 반쪽이 된다.

    ★대상은 **`generation_difficulty` 가 여는 것만**이다. `route` 로 고르면
    안 된다 — 실측에서 아홉 축이 **전부** `research` 였다.

    ★facet(`location_part`·`outlook`)은 **안 올린다** — 계약
    (`GENERIC_PROMOTION_OWNERS`)이 정한다. LP 는 부모 없이 만들면 **고아**가
    되고, outlook 은 `OutlookSyncService` 가 SOT 다. 이미 등록된 `LP##` 행을
    **스캔·결속**하는 것은 다른 축이고 그쪽은 열려 있다.

    Returns:
        갈래 → **새로 만든 행들**. 기존 행은 안 건드린다.
    r   )needs_reference_acquisitionresearch_subject_idr   )GENERIC_PROMOTION_OWNERSrE   Tr(   02dr&   r)   r*   scene_indices)rE   r0   grounding_materializedra   r)   r*   rd   )&app.modules.pipeline.grounding_plannerr`   strr    rI   rW   rb   rX   _ENTITY_PREFIX_KEY_TO_ENTITY_TYPEr>   rM   
setdefaultr+   r   list)r!   rN   surviving_by_typer`   rP   want_idsrb   r=   keyrQ   prefixetyper#   goner;   usedr5   rR   s                     r   materialize_missing_entitiesrs      s   4 S !D A$?$B AEE/06B7   DR	" ,.C&,,.	T##C(#''S1 00-j$F HFaquu239r:hF F H 7;;dAEE*%+,d;AQ3(d?	 
 HHSMNN3#**aeeNB78 +/'*1551F+G+M2'N!"!7!=2!"!7!=2!%aeeO&<&B!C
, 
 + /N JcD.H <s   -G0)G "%G%)promoted_idsc               $   t        |       }|xs dD ch c]+  }t        |xs d      j                         s!t        |      - }}i }i }|j                         D ]P  \  }}	t	        ||	|      }
|
r|
||<   |
D cg c]$  }t        |j                  d      xs d      |vr|& }}|sL|||<   R |D cg c])  }t        j                  |j                  d            (|+ }}|D cg c])  }t        j                  |j                  d            (|+ }}t        t        |      t        |      |t        |      |t        d |j                         D              |t        d |j                         D              | d
S c c}w c c}w c c}w c c}w )u  갈래별 완전성. ★하나라도 사라지면 통과가 아니다.

    ``deferred`` 는 **잃은 것이 아니다** — 이 단계에 받을 갈래가 없을 뿐이다
    (outlook 은 §2-6.5). 「없다」와 「여기서 못 받는다」를 갈라 센다.

    ★★``promoted_ids`` 도 **잃은 것이 아니다.** 엔티티에 없어서 A0 신원 그대로
    승격된 것들이라, 「엔티티 목록에 있나」로 물으면 당연히 없다 — 그런데 그건
    **사라진 것이 아니라 다른 자리로 간 것**이다. 이 인자가 없던 판에서는
    승격한 7개가 그대로 `missing` 으로 세어져 스텝이 `partial` 이 되고 하류가
    막혔다. 승격이 반쪽이었다.

    ★**명시로 받는다.** 후보 목록에서 「엔티티에 없으면 승격됐겠지」로 유추하면
    승격 안 된 것(얽혀서 `unresolved` 인 것)까지 통과시킨다.
    r3   r   ra   r   c              3  2   K   | ]  }t        |        y wr2   lenr4   vs     r   r7   z&completeness_report.<locals>.<genexpr>(  s     #L4KqCF4K   c              3  2   K   | ]  }t        |        y wr2   rw   ry   s     r   r7   z&completeness_report.<locals>.<genexpr>*  s     >-=SV-=r{   )
contract_versioncandidate_countchecked_countdeferredpromoted_countentity_missingentity_missing_countmissingmissing_countcomplete)rk   rg   rL   rX   r>   r    r   OVERLAY_CONTRACT_VERSIONrx   sumrK   )r!   rl   rt   candsxkeptr   r   rp   rQ   no_rowr#   rq   r   checkeds                  r   completeness_reportr      s   ( E)/R/I/qCRL4F4F4HCF/DI/1G 79N(..0t#E47$*N5!! H6aquu239r:$F 6 H!GEN 1 !V5a$4$8$8|9L$M$U5HVY%Q#3#7#7l8K#L#Xq%GY4u:Wd)( ##LN4I4I4K#L L>W^^-=>>K % JH WYs(   "E>E>8)F1)FF%)FF)r   rg   returnrg   )r!   Iterable[Dict[str, Any]]r"   rg   r   List[Dict[str, Any]])r!   r   r"   rg   r   z	List[str])r!   r   r:   zSequence[Dict[str, Any]]r"   rg   r   r   )rN   r   rO   #Dict[str, Sequence[Dict[str, Any]]]r   rJ   )r   zDict[str, str])r!   r   rN   r   rl   r   r   zDict[str, List[Dict[str, Any]]])r!   r   rl   r   rt   zOptional[Iterable[str]]r   zDict[str, Any])__doc__
__future__r   r   typingr   r   r   r   r   r	   r   r   r   r$   r.   r>   	frozensetrH   rS   r[   rh   ri   rs   r   r3   r   r   <module>r      sR    #  @ @   	 Q
I(I7:II(7::('  	6 AB %9 	L &' &1z &( M(M%M ;M %	Mh -1	4(4:4 *	4
 4r   