
    j/                        U d Z ddlZddlmZmZmZmZ  ej                  e      Z	dZ
dddiddiddidg dd	d
Zeeef   ed<   dddddddddddddZeeeeef   f   ed<   ddddZdZdddd Zeeef   ed!<   d"Zd#Zd$eeeef      d%efd&Zd$eeef   d%efd'Zd(ed$eeeef      d%efd)Zd*ed%efd+Z G d, d-      Zy).u  T2I 프롬프트 안전 정책 수정 모듈 — 거절된 프롬프트를 LLM 클라이언트로 수정.

prompt_loader 통합 (problems.md #14): 기존 hard-coded ``v1`` 디렉토리 직접 read
제거 → ``prompt_loader.load_prompt`` 경유하여 DB prompt_template 우선 + numeric-
aware version 정렬 + version pack drift detection (#6) + local jsonschema
validation (#13) 의 통합 정책을 적용 받는다.
    N)AnyDictListOptionalprompt_sanitizerobjecttypestring)sanitized_promptchangesstrategyF)r	   
propertiesrequiredadditionalPropertiesSANITIZE_SCHEMAu   영화 프리비즈u+   촬영 전 컨셉아트 형태로 재구성u$  This is a pre-visualization concept art for a film production. Storyboard-quality still frame for the director's shot planning. Cinematic composition, dramatic lighting, professional film production context. NO gore, NO explicit violence, NO blood — focus on dramatic tension and emotion.

)namedescriptionprefixu   영화 포스터u8   감정과 분위기 중심의 키 비주얼로 재구성u   A cinematic movie poster key visual for a feature film. Focus on the emotional core — characters' faces, dramatic poses, atmospheric lighting. Professional film marketing quality. Show mood and tension, not violence.

u   직후 정적 장면u5   액션 직후의 정적인 순간으로 시점 변경u   The moment AFTER the action — a quiet, contemplative still frame. Characters process what just happened. Focus on facial expressions, body posture, environmental details. Cinematic, thoughtful, no active violence.

)film_prevismovie_poster	aftermath
STRATEGIESr   r   r   )         SEMANTIC OVERRIDE:zEDo not rewrite as alive, unharmed, asleep, or emotionally recovering.zKDo not rewrite as conscious, reacting, emotionally processing, or standing.zCDo not rewrite as unharmed, recovered, active, or standing/walking.)deadunconsciousseverely_injured_STATE_GUIDANCEa  SEMANTIC OVERRIDE:
Preserve the fixed pose and body position for affected subjects.
Do not introduce a new gesture, standing pose, walking pose, active reaction, or changed limb position.
This override takes priority over the sanitizer strategy prefix.
Affected entities: {entity_list}.zSEMANTIC OVERRIDE:
This is a REFERENCE image that defines one character's identity.
Exactly ONE figure is in frame: {subject}, alone, against a plain neutral background.
This override takes priority over the sanitizer strategy prefix.constraintsreturnc                 n   | sydd| j                  dd       g}| j                  d      rL|j                  d       t        | d   j                               D ]  \  }}|j                  d| d	|         d
D ])  }| j                  |      s|j                  d| d       + dj	                  |      S )uU   user.md {semantic_constraints_block} slot 렌더. constraints=None → 빈 문자열. uW   SEMANTIC CONSTRAINTS (이 섹션이 전략 프리픽스와 충돌 시 우선합니다):z- semantic_mode: semantic_modeunknownsource_statesu"   - 보존 대상 entity 와 상태:z  - z: )preserve_posepreserve_subject_stateforbid_state_polarity_rewriteforbid_unharmed_rewriteforbid_active_reactionsingle_subject_only- z: True
)getappendsorteditemsjoin)r!   linessidstatekeys        R/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/prompt_sanitizer.py"_render_semantic_constraints_blockr:   e   s    a
KOOOYGHIE '9: _!=!C!C!EFJCLL4uBug./ G ??3LL2cU&)* 99U    c           	          | j                  d      xs i }g d}t        |      D ]7  }||   }t        j                  |d      }|j                  d| d| d|        9 dj	                  |      S )Nr'   )r   zEPreserve each affected subject's immobilized state and fixed posture.z@This override takes priority over the sanitizer strategy prefix.r$   zPer-entity state contracts:z5Preserve the structured source state; do not rewrite.r.   z (z): r/   )r0   r2   r    r1   r4   )r!   r'   r5   r6   r7   guidances         r9   _build_immobilized_blockr>      sz    OOO4:ME m$c""&&u.efr#bs8*56 % 99Ur;   promptc                    |r|j                  d      s| S |j                  d      }|dk(  rt        |      }n|dk(  r?dj                  |j                  d      xs g       xs d}t        j	                  |      }n|d	k(  r|j                  d
      xs d}t
        j	                  |      }t        |j                  d      xs i j                               D ]+  \  }}t        j                  |      }|s|d| d| d| z  }- n| S | j                  t              }	|	dk\  r| d|	 j                         }
|
dz   |z   S | j                         dz   |z   S )u	  final sanitized_prompt 끝에 deterministic SEMANTIC OVERRIDE block append.

    중복 append 차단: scene_image_pipeline 의 moderation retry 루프 (line 261)
    는 ``current_prompt = sanitize_result.get("sanitized_prompt", ...)`` 로 갱신
    하므로, 다음 retry 의 sanitize() 입력 prompt 에 이미 override block 이 포함
    돼 있다. exact literal marker 기반 검색 (의미 판단 아님) 으로 기존 block
    tail 을 잘라낸 후 새 block append — retry 마다 1 block 만 유지.
    override_strategy_prefixr%   immobilizedpose_locked, 
entity_idsz(none))entity_listreference_identitysubjectzthe character described above)rH   r'   r/   z stays in the z state. r   Nz

)r0   r>   r4   _POSE_LOCKED_BLOCKformat_REFERENCE_IDENTITY_BLOCKr2   r3   r    find_SEMANTIC_OVERRIDE_MARKERrstrip)r?   r!   modeblockrF   rH   _sid_stateguideidxheads              r9   _apply_semantic_overriderV      s[    koo.HI???+D}(5		ii = CDP"))k)B	%	%//),O0O)000A #17R>>@BLD&#''/E2dV>&%II	B 
++/
0C
axds|""$f}u$$==?V#e++r;   filenamec                 X    ddl m} | j                  d      r| dd n| } |t        |      S )u  ``prompts/_base/prompt_sanitizer`` 모듈에서 stem 로드 (problems.md #14).

    DB prompt_template 우선 → 파일 fallback (numeric-aware version 정렬). 기존
    ``filename`` 인자 (e.g. ``"sanitize_system.md"``) 호환 위해 ``.md`` 확장자
    자동 제거.

    Raises:
        FileNotFoundError: 모듈/stem 미존재.
        RuntimeError: ``PROMPT_VERSION_PACK_STRICT=true`` 환경에서 stem 이 latest
            version pack 에 없는 경우 (#6 strict mode). caller 가 처리.
    r   )load_promptz.mdN)app.modules.prompt_loaderrY   endswith_MODULE)rW   rY   stems      r9   _load_promptr_      s.     6$--e48CR=(Dw%%r;   c                   x    e Zd ZdZddeeeef      ddfdZ	 	 ddedede	e   d	e
d
eeeef      deeef   fdZy)PromptSanitizeruI   LLM 클라이언트로 T2I 거절된 프롬프트를 수정하는 모듈.Nproject_configr"   c                     || _         y )N)_project_config)self
llm_clientrb   s      r9   __init__zPromptSanitizer.__init__   s
    -r;   original_promptblock_reasonblock_categoriesattemptsemantic_constraintsc                 \   ddl m} t        j                  |d      }t        |   }t        d      }	t        d      j                  |||rdj                  |      nd||d   |d	   |d
   t        |            }
 |d|	|
t        | j                  d      }|j                  dd      }|j                  |d
   j                         dd       s|d
   |z   }t        ||      }||d<   ||d<   t        j                  d|||r|xs i j                  d      nd|j                  dd      dd        |S )u  Sanitize a blocked T2I prompt.

        Args:
            original_prompt: The original prompt that was blocked.
            block_reason: Reason for the block (e.g. SAFETY, HARM).
            block_categories: List of safety categories that triggered the block.
            attempt: Sanitization attempt number (1=film_previs, 2=movie_poster, 3=aftermath).
            semantic_constraints: Optional dict from semantic_contract_router. When
                provided, renders a SEMANTIC CONSTRAINTS section into the sanitize LLM's
                user prompt (Layer 1) and appends a SEMANTIC OVERRIDE block to the final
                sanitized_prompt (Layer 2). None → existing behavior unchanged.

        Returns:
            Dict with keys: sanitized_prompt, changes, strategy
        r   )call_structuredr   zsanitize_system.mdzsanitize_user.mdrD   zN/Ar   r   r   )rh   ri   rj   rk   strategy_namestrategy_descriptionstrategy_prefixsemantic_constraints_blockprompt_sanitize)stepsystem_promptuser_promptresponse_schemarb   schema_namer   r$   N(   r   zGPrompt sanitized: attempt=%d, strategy=%s, semantic_mode=%s, changes=%sr%   noner   d   )app.modules.llm.llm_clientrn   ATTEMPT_TO_STRATEGYr0   r   r_   rJ   r4   r:   r   rd   
startswithstriprV   loggerinfo)re   rh   ri   rj   rk   rl   rn   strategy_keyr   ru   rv   result	sanitizeds                r9   sanitizezPromptSanitizer.sanitize   s^   . 	?*..wDl+$%9:"#56==+%<LTYY'78RW"6*!)-!8$X.'IJ^'_ > 	
 !"'#+//)
 JJ126	##HX$6$<$<$>s$CD *Y6I -Y8LM	%.!" *zUAU!'R,,_=[aJJy"%ds+	
 r;   )NN)r   N)__name__
__module____qualname____doc__r   r   strr   rg   r   intr    r;   r9   ra   ra      s    S.c3h8P .\` . 9=EE E s)	E
 E 'tCH~6E 
c3hEr;   ra   )r   loggingtypingr   r   r   r   	getLoggerr   r   r]   r   r   __annotations__r   r}   rM   r    rI   rK   r:   r>   rV   r_   ra   r   r;   r9   <module>r      s    , ,			8	$
 #X.H%X&
 <!	#c3h 	 &De		 #Q\	 'Nb	))
Dd38n$% @   1  `e]#c3h ( $G HT#s(^4L QT :$sCx. S  ",S ",xS#X7O ",TW ",J&3 &3 &$L Lr;   