
    Oj9                        d Z ddlmZ ddlZddlmZmZ ddlmZm	Z	 ddl
mZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ  ej2                  e      ZdZdZ G d dee      Zy)u  앞쪽 **판별** 스텝 (13.68) — 「무엇이 참조 사진을 사야 하는가」.

    13.5  entity_merge        ← 결속할 엔티티가 여기서 정해진다
      ↓
    13.65 grounding_plan      route 확정 (기존 축)
      ↓
    13.68 ★grounding_screen   **판별만** — `era_research.assess_plan_cached`
      ↓
    13.7  entity_filter       판별이 연 것을 **안 지운다** + 없는 행은 만든다
      ↓
    19.x  중앙 획득           ★사는 곳은 **여기 하나**

★이 스텝은 **참조를 사지 않는다.** 검색·이미지 검색·VLM 호출 0.
사는 것은 중앙 19.x 한 곳이다 — 두 곳에서 사면 같은 대상을 두 번 산다.

★`generation_difficulty` 를 **근거로 쓰지 않는다.** 같은 유료 판단을 두 번
사지 않기로 확정됐다 — 판별 SOT 는 `era_research.assess_subjects` 다.

★결속(`binding`)은 `grounding_carry` 가 SOT 다. 이 스텝은 그 장부를 읽을 뿐
이름을 다시 짝짓지 않는다.
    )annotationsN)AnyDict)GROUNDING_MODE_LEGACYresolve_grounding_mode)
StepRunner)_EntityStepMixin)grounding_carry)grounding_entity_contract)grounding_screen)era_research)SUBJECT_ID_CONTRACT_VERSIONassess_cachezscreen_partial.jsonc                  X    e Zd ZdZdddZd ZddZddZdddd	 	 	 	 	 	 	 dd	Zdd
Z	y)GroundingScreenStepu;   참조 획득 **의무**를 정한다. 사지는 않는다.c                 
    t         j                        }|t        k(  r)t        j	                  d        j                  |i d      S ddlm}  ||      r^ j                  d      }|xs i j                  d      xs i }|sddl
m}  |d	d
d      |}t        |j                  d      xs g       }ng j                  d      }	|	xs i j                  d      xs i } j                  d      }
|
xs i j                  d      xs i j                  d      xs g }t        j                  | j                   j                   d|      }|j                  d      xs i }t#        |j                  d      xs i       }|j%                  |j                  d      xs g D ci c]=  }t'        |j                  d      xs d      t'        |j                  d      xs d      ? c}       t)        j*                  |d   ||      }ddlm}  j                  d      } ||      }|j1                         sddl
m}  |ddd       j                   j2                        xs i j                  d      xs i j                  t4              xs i }t7        |t"              rt#        |      ni j%                   j9                                t;               }fd, fd} ||      rt)        j<                  ||      }|j                  d      xs g D ci c]t  }|j                  d      xs i j                  d       rNt'        |j                  d      xs d      t'        |j                  d      xs i j                  d       xs d      v }} j                  |||!      S t)        j>                  |||fd"fd# j                   jA                         | j                  xs i j                  d$      |%
      }t        j	                  d&tC        |      |d'   |d(   |d)          t)        jD                  |      }|rt        jG                  d*tC        |             |j                  d      xs g D ci c]t  }|j                  d      xs i j                  d       rNt'        |j                  d      xs d      t'        |j                  d      xs i j                  d       xs d      v }} j                  |||+      S c c}w c c}w c c}w )-Nu9   grounding_screen: mode=legacy — 아무것도 안 한다T)screenedskippedr   )uses_chunk_producergrounding_chunkdata)AppErrorzstep.no_inputu\   grounding_chunk 산출 없음 — C(c) 판에서 판별은 그 producer 의 줄로만 선다i  )codemessagestatus_codegrounding_candidatesentity_mergegrounding_a0
candidates)
project_id
episode_idsource_stepa0_candidatescandidate_ledgersubject_dispositionsrowsresearch_subject_id dispositionsubjects)build_world_facts_blockvisual_world_rulesz&grounding_screen.missing_world_contextuN   visual_world_rules 가 비었다 — 시대 기준 없이 판별할 수 없다c                (    j                  |       y N)_save_partial)_row_plan_cacheselfs      Z/Users/manta/Documents/Projects/TheRoad-I1/backend/app/core/steps/grounding_screen_step.py
_flush_rowz0GroundingScreenStep._execute.<locals>._flush_row   s     v&    )dispositionsproducer_linkfinal_id)r   	final_idsc                h    t        j                  |       t              rj                  |       S d S r.   )
isinstancegetdict)kcaches    r4   <lambda>z.GroundingScreenStep._execute.<locals>.<lambda>   s1    $.uyy|T$B "'1 "MHL"Mr6   c                (    j                  | |      S r.   )__setitem__)r?   vr@   s     r4   rA   z.GroundingScreenStep._execute.<locals>.<lambda>   s    5#4#4Q#:r6   grounding_screen_max_assess)	r7   world_facts_block	cache_get	cache_putproject_configopik_metadatafailed_memo
max_assesson_rowuH   grounding_screen: 대상 %d개 · 판별 호출 %d · %s · 갈래별 %sassess_callscountsby_owneruF   grounding_screen: producer 가 없어 미룬 의무 %d건 — §2-6.5a)r   r@   r:   )returnNone)$r   rI   r   loggerinfo_wrapapp.core.grounding_moder   _load_prev_checkpointr=   app.core.errorsr   list_carrybuild_subjectsr    r!   r>   updatestr_screenbuild_populationapp.core.world_contextr+   stripstep_id	CACHE_KEYr<   _load_partialsetproject_from_producerscreen_subjectsbuild_opik_metadatalenfacet_obligationswarning)r3   modeg_moder   chunk_cpr   r   entitiesa0_candsmerge_cpa0_cpbuiltledgerr7   rr*   r+   rules_cpworldprevrK   r5   	projectedfinalsout
facet_debtr@   s   `                         @r4   _executezGroundingScreenStep._execute1   s   '(;(;<** KKST::fr4:@@ 	@v&112CDHN''/52D4(A #	% %
 HDHH%;<BCH11.AH B++F39rH..~>E"))&17R<< $!#  %%T__&h@ -.4"
 EII&<=CDjj(.B.
. +,23S}9M9SQS5TT.
 	 ++*x7 	C--.BC'1{{}0 =h  ++DLL9?RDDVL ss9~,)+ 	.8t.DT
" 	T'')*5+0 	' v&55|5I
 !'

6 2 8b 8I !9!%%06B;;JG	 !%% 56<"=AEE/28b==jI  !! !9  I
 ::fyF:KK%%%#N:..224#++1r66-/
 	VM3~.Hs:	P ..s3
 NNXJ! #JJv.4"4E4aeeO,277
C aee128b9o.4"99*EL"MN4 	 E zz&3evzNNs
\INEs    ATA9T1A9Tc                (    | j                   t        z  S r.   )_cp_dirPARTIAL_NAME)r3   s    r4   _partial_pathz!GroundingScreenStep._partial_path   s    ||l**r6   c                   dd l }| j                         }|j                         si S 	 |j                  |j	                  d            }t        |t              r|S i S # t
        $ r t        j                  d       i cY S w xY w)Nr   utf-8encodinguD   grounding_screen: 중간 저장이 깨졌다 — 무시하고 간다)
jsonr   existsloads	read_text	ExceptionrS   rk   r<   r>   )r3   r   pgots       r4   rd   z!GroundingScreenStep._load_partial   sx     xxzI	**Q[['[:;C !d+s33  	NNabI	s   !A  B ?B c                    dd l }dd l}| j                         }|j                  j	                  dd       |j                  d      }|j                  |j                  |d      d       |j                  ||       y )	Nr   T)parentsexist_okz.tmpF)ensure_asciir   r   )	r   osr   parentmkdirwith_suffix
write_textdumpsreplace)r3   r@   r   r   r   tmps         r4   r/   z!GroundingScreenStep._save_partial   se     	td3mmF#tzz%ez<wO 	

3r6   NF)r@   r   r:   c               >   |j                  d      xs g }|j                  d      xs i }|j                  t        j                  d      |j                  t        j                  d      z   }t	        |      |z
  }	|t        j
                  t	        |      ddd|j                         D 
ci c]  \  }
}|
dk7  s|
| c}}
t        j                  |      t        |xs i       d}|	||t        <   |rd|d<   |	t	        |      || j                         |d	S c c}}
w )
Nr&   rO   r   )rl   contract_versionsubject_countsearch_callsimage_callsr   )r|   final_id_by_subjectTr   )completed_countapplicable_countfailed_countconfig_hashr   )r=   r^   SCREEN_UNRESOLVEDSCREEN_CAPPEDri   SCREEN_CONTRACT_VERSIONitemsrj   r>   rc   _config_hash)r3   rm   r   r@   r   r:   r&   rO   faileddoner?   rD   payloads                r4   rU   zGroundingScreenStep._wrap   s)    ||F#)rh'-2 **W66:JJw44a894y6! ' ? ? Y#
 !) 0L 01A9K4Kq!t 0L#
 "33H=
 $(	R#8#
 !&GI!%GI# #D	",,.
 	
 Ms   +D9Dc                   ddl }ddl}ddlm} |j	                   || j
                        t        j                  t        j                  t        j                  t        t        j                  t        j                         t        j                          t        j"                  t        j$                  t        j"                        d
dd      }|j'                  |j)                  d            j+                         dd	 S )
u   ★판별 계약·팩 바이트를 지문에 접는다.

        안 접으면 팩이나 모델을 바꿔도 resume 이 **옛 판별을 그대로 건너뛴다**.
        r   N)compute_config_hash)
basescreen_contractcarry_contractproducer_contractsubject_contract
era_policyera_packera_pack_hashassess_modelassess_model_physicalTF)	sort_keysr   r      )hashlibr   app.core.step_runnerr   r   rI   r^   r   rZ   CARRY_CONTRACT_VERSION_ecPRODUCER_CONTRACT_VERSIONr   _eraERA_RESEARCH_POLICY_VERSIONresolve_era_packera_pack_content_hashASSESS_MODELresolve_model_physicalsha256encode	hexdigest)r3   r   r   r   raws        r4   r   z GroundingScreenStep._config_hash   s    
 	<jj'(;(;<&>>$;;
 "%!>!> ;::--/!779 -- &*%@%@!!&##
& '  /( ~~cjj12<<>sCCr6   )resume)rl   r]   rQ   Dict[str, Any])rQ   r   )r@   r   rQ   rR   )rm   r]   r   r   r@   zDict[str, Any] | Noner   boolr:   zDict[str, str] | NonerQ   r   )rQ   r]   )
__name__
__module____qualname____doc__r}   r   rd   r/   rU   r    r6   r4   r   r   .   sX    EGOR+4
 .2#15#
*#
#
 /#
 ;I#
JDr6   r   )r   
__future__r   loggingtypingr   r   rV   r   r   r   r   app.core.steps.entity_stepsr	   app.modules.pipeliner
   rZ   r   r   r   r^   r   r   &app.modules.pipeline.grounding_subjectr   	getLoggerr   rS   rc   r   r   r   r6   r4   <module>r      sb   * #  = + 8 : A < 5 N			8	$ 	 %mD*J mDr6   