
    j&                     h   d Z ddlZddlZddlmZ ddlmZ ddlmZ ddl	m
Z
  ej                  e      Zdddd	d
id	d
id	d
id	d
id	d
idddgdddddd	d
id	d
id	d
idddgdddd	d
id	d
id	d
id	d
iddd	d
id	d
idddgddddd	d
iddg dddZdZg g ddddg g dZdedefdZ G d de
      Zy)u   기획서 분석 step — PDF를 LLM에 직접 전달하여 구조화된 섹션 추출.

PDF 파일이 있으면 multimodal (base64)로 직접 분석.
없으면 DB의 planning_doc_text로 fallback.
    N)Path)Dict)settings)
StepRunnerobjectarraytypestring)namedescriptionvisual_traits
age_genderroler   r   F)r	   
propertiesrequiredadditionalProperties)r	   items)r   r   r   )
charactersrelationshipr   r   )r   	locationsworld_setting	tone_mood	story_arcvisual_conceptskey_relationshipsavailable_sectionsus  당신은 영화/드라마 기획서 분석 전문가입니다.
주어진 기획서(PDF 또는 텍스트)에서 구조화된 정보를 추출하세요.
이미지, 표, 레이아웃 등 모든 시각적 정보도 참고하세요.

## 추출 규칙
- 기획서에 해당 정보가 **있으면** 상세히 추출
- 기획서에 해당 정보가 **없으면** 빈 문자열("") 또는 빈 배열([])
- available_sections: 실제로 의미 있는 내용이 추출된 섹션 이름만 나열

## 인물(characters)
- 기획서에 인물 설명이 있으면 추출
- visual_traits: 외형 특징 (키, 체형, 머리, 특징적 외모 등) — 있으면 추출, 없으면 ""
- age_gender: 나이/성별 — 있으면 추출
- role: 역할 (주인공, 조연, 악역 등)

## 장소(locations)
- 기획서에 장소·공간 설명이 있으면 추출 (없으면 빈 배열)
- visual_traits: 그 공간의 외형 특징 (구조, 재질, 색감, 조명, 놓인 것들 등)

## 세계관(world_setting)
- 시대, 장소, 세계 규칙 등 종합 설명

## 톤/분위기(tone_mood)
- 작품의 톤, 분위기, 비주얼 방향성

## 줄거리(story_arc)
- 전체 스토리 요약 (스포일러 포함 OK)

## 비주얼 컨셉(visual_concepts)
- 아트 디렉션, 색감, 촬영 스타일, 컨셉아트 설명 등

## 인물 관계(key_relationships)
- 주요 인물 간 관계 (가족, 연인, 적대 등) resultreturnc                 
   g }dD ]%  }| j                  |      s|j                  |       ' dD ]7  }| j                  |      xs dj                         s'|j                  |       9 d}|D cg c]	  }||v s| c}S c c}w )u  어떤 절이 **실제로 쓸 만하게** 왔는지 — 분석 결과 하나만 보고 정한다.

    ★이 규칙이 두 곳에 있었다 (스텝 하나 · 업로드 서비스 하나). 2026-09-18 에
     장소 칸을 더하면서 스텝만 고쳤더니, **정상 업로드 경로(서비스)가 장소를
     지웠다**(Codex BLOCK). 규칙을 한 자리로 모은다 — 새 절을 더할 때 고칠
     곳도 여기 하나다.
    )r   r   r   )r   r   r   r   r   )r   r   r   r   r   r   r   )getappendstrip)r   outkeyorderks        V/Users/manta/Documents/Projects/TheRoad-I1/backend/app/core/steps/planning_doc_step.pycompute_available_sectionsr)   |   s     C?::c?JJsO @ NJJsO!r((*JJsO NBE)u!SAu)))s   /	B 9B c                       e Zd ZdZddefdZy)PlanningDocAnalysisStepux  기획서를 LLM으로 분석 — PDF multimodal 우선, 텍스트 fallback.

    기획서 업로드 시점에 ``planning_doc_analysis_service`` 가 project-level
    checkpoint 를 미리 만들어둠. 본 step 은 episode 분석 cascade 호환을 위해
    유지되지만, project-level 결과가 있으면 그대로 mirror 하고 LLM 재호출은
    하지 않음.
    r   c                 v   ddl m} ddlm}  || j                  | j
                        }|6t        j                  d| j                  d d |j                  dg              |S | j
                  j                  |      j                  |j                  | j                  k(        j                         }t        t        j                        | j                  z  dz  d	z  j!                         }|r|j"                  nd |s9rt%        j'                               d
k  rt        j                  d       t(        S ddlm} fd}fd}	d }
|rA |       \  }}t        j                  d|       	  |dt.        |t0        | j2                  d      }
|
^r\t%        j'                               d
k\  r@ |	       \  }}t        j                  d|        |dt.        |t0        | j2                  d      }
|
t        j7                  d       t(        S t9        |
      }||
d<   t        j                  dt%        |
j                  dg             t%        |
j                  dg             t%        |
j                  dg             ||rd       |
S d       |
S # t4        $ r!}t        j7                  d|       Y d }~#d }~ww xY w)Nr   )ProjectRegistry)load_project_checkpoint)dbzPplanning_doc_analysis: project-level checkpoint mirror (project=%s, sections=%s)   r   assetszplanning_doc.pdfd   u6   planning_doc_analysis: 기획서 없음 — 빈 결과)call_structuredc                      j                         } t        j                  |       j                  d      }dddddd| idgt	        |       fS )	Nzutf-8textuI   이 기획서 PDF를 분석하여 구조화된 정보를 추출하세요.)r	   r5   	image_urlurlzdata:application/pdf;base64,)r	   r6   )
read_bytesbase64	b64encodedecodelen)	pdf_bytespdf_b64pdf_paths     r(   _build_pdf_promptz;PlanningDocAnalysisStep._execute.<locals>._build_pdf_prompt   sg     ++-I&&y188AG)tu$9'C4 
 9~     c                  ,    d  t         xs d      fS )Nu_   다음 기획서를 분석하여 구조화된 정보를 추출하세요.

## 기획서 전문

r   )r<   )planning_texts   r(   _build_text_promptz<PlanningDocAnalysisStep._execute.<locals>._build_text_prompt   s)    **7:=&B'( (rA   z0planning_doc_analysis: PDF multimodal (%d bytes)planning_doc_analysisg?)stepsystem_promptuser_promptresponse_schemaproject_configtemperaturez/PDF multimodal failed, falling back to text: %sz/planning_doc_analysis: text fallback (%d chars)z/planning_doc_analysis: both PDF and text failedzZplanning_doc_analysis: %d characters, %d locations, %d relationships, sections=%s, mode=%sr   r   r   pdfr5   )app.models.catalogr-   *app.services.planning_doc_analysis_servicer.   
project_idr/   loggerinfor!   queryfilteridfirstr   r   projects_direxistsplanning_doc_textr<   r#   _EMPTY_RESULTapp.modules.llm.llm_clientr3   _SYSTEM_PROMPT_ANALYSIS_SCHEMArJ   	Exceptionwarningr)   )selfmoder-   r.   project_dataprojecthas_pdfr3   r@   rD   r   rH   sizeexccomputed_availabler?   rC   s                  @@r(   _executez PlanningDocAnalysisStep._execute   s   6	
 /t477K#KK,#  !5r:	  ''--077$//1

%' 	
 --.@8KN``//#5<11$]5H5H5J1Kc1QKKPQ  >		(  1 3KKKJDQ
W(0"0 +$4#'#6#6 # >mM4G4G4I0Jc0Q 2 4KKKI4P$,,' 0#22F >NNLM   8?'9#$5

<,-

;+,

.34E	
  #)	
 A  WPRUVVWs   0 J 	J8J33J8N)resume)__name__
__module____qualname____doc__r   rg    rA   r(   r+   r+      s    ` `rA   r+   )rl   r9   loggingpathlibr   typingr   app.core.configr   app.core.step_runnerr   	getLoggerri   rP   r\   r[   rY   listr)   r+   rm   rA   r(   <module>ru      s       $ +			8	$   #X.$*H#5&,h%7#)8"4#X. $]3(-
&  #X.$*H#5&,h%7
 $]3(-	
 !(+h'h'"H- #)8"4%+X$6 *>:(-
 h'
a4j( "u; z ;F 	*t * **ij irA   