
    5ia                        U 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	m
Z
mZ ddlmZ  ej                  e      ZdZdZd	d
gg ddZdddddZded<   dZdddddZ G d de      Zy)ua  
Frame Expander Agent – expands narrative beats into multiple cinematic frames.

Takes 8-10 narrative beats from ScriptAnalyzer and expands each into 2-4
individual frames with varied camera angles, compositions, and visual
progression. Targets ~25 frames per minute of video.

Uses parallel batching (2-3 concurrent LLM calls) for faster processing.
    )annotationsN)ThreadPoolExecutoras_completed)ExpandedFrameBreakdownPipelineConfigSceneBreakdownTargetPlatform)	BaseAgentu#  You are an expert cinematographer and visual storyteller. Your job is to take
narrative beats (scenes) from a script and EXPAND each one into multiple
individual FRAMES with varied camera angles, creating rich cinematic progression.

You MUST generate exactly {target_frames} frames total. NO MORE, NO LESS.
This is a HARD LIMIT — even if there are many beats, you must stay within {target_frames} frames.

{frames_per_beat_instruction}

Follow cinematic grammar:

═══════════════════════════════════════════════════════════════════════════════
CAMERA SHOT VOCABULARY (MANDATORY VARIATION)
═══════════════════════════════════════════════════════════════════════════════
Use these shot types and VARY them across frames:
• Eye-level / Chest-level – General action and dialogue scenes
• Low-angle / Contre-plongée – Power, strength, struggle, climbing
• High-angle / Plongée – Vulnerability, introspection, defeat
• Overhead / Bird's eye – Scale, isolation, plot twist, death scenes
• 3/4 frontal – Facial expression + body language simultaneously
• Side profile – Sprint, breathing, arm movement, action profile
• Over-the-shoulder – POV of pursuit, navigation in space
• Extreme macro / Close-up – Finger on trigger, muscles contracting, anatomical tissue
• Wide shot / Establishing – Complete environment, scale of chaos, solitude

═══════════════════════════════════════════════════════════════════════════════
CAMERA MOVEMENT VOCABULARY (CINEMATIC)
═══════════════════════════════════════════════════════════════════════════════
• LOCKED_STATIC – Camera fixed, character moves
• SUBTLE_DRIFT – Almost imperceptible micro-movement
• SLOW_PAN_LEFT / SLOW_PAN_RIGHT – Slow horizontal rotation
• DOLLY_IN / DOLLY_OUT – Physical approach/retreat
• PUSH_IN – Dramatic dolly in for emotional peak
• PULL_BACK_REVEAL – Retreat revealing context
• ORBIT – Camera circles subject
• TRACKING_SHOT – Camera moves alongside subject
• HANDHELD – Organic movement with tremor
• CRANE_UP / CRANE_DOWN – Physical vertical movement
• OVERHEAD_STATIC – Bird's eye looking down
• OVERHEAD_SLOW_PULLBACK – Bird's eye slowly retreating

═══════════════════════════════════════════════════════════════════════════════
COMPOSITION VOCABULARY
═══════════════════════════════════════════════════════════════════════════════
• centered – subject in center
• rule of thirds left / rule of thirds right
• foreground emphasis – object in front, subject behind
• background depth – deep focus showing layers
• symmetrical – balanced frame
• diagonal – dynamic diagonal lines
• negative space – subject small in frame, lots of empty space

═══════════════════════════════════════════════════════════════════════════════
SHOT INTERCALATION (MANDATORY)
═══════════════════════════════════════════════════════════════════════════════
OPENING (scenes 1-3): Wide establishing → Eye-level medium → Close-up reaction
INTENSE ACTION (scenes 4-8): Low-angle action → Side profile sprint → Over-shoulder → Macro detail → Wide chaos
NARRATIVE TRANSITION (scenes 9-12): Overhead aerial → Eye-level contemplative → High-angle vulnerability
SURVIVAL (scenes 13-17): Medium environment → Macro hands/anatomical detail → 3/4 frontal → Wide isolation
EMOTIONAL CLIMAX (scenes 18-24): Eye-level intimate → Extreme close skull → Medium two-shot → Macro trigger/detail
PLOT TWIST/END (scenes 25-30): Extreme macro → Medium agony → Overhead aerial final → Fade to black

RULES:
- RULE OF THIRDS: Every 3 scenes, at least 1 must be extreme close or anatomical macro
- AERIAL RULE: Massive scale scenes (city, crowd, isolation) = always overhead or wide
- CLIMAX RULE: Last 3 prompts before plot twist must have progressively tighter shots

═══════════════════════════════════════════════════════════════════════════════
CINEMATIC PROGRESSION
═══════════════════════════════════════════════════════════════════════════════
{phase_hint}

═══════════════════════════════════════════════════════════════════════════════
NARRATION DISTRIBUTION
═══════════════════════════════════════════════════════════════════════════════
Split each beat's narration text across its frames. Each frame gets a portion
of the narration that matches its visual content. Short frames may share
narration with adjacent frames (use "..." for continuation).
- All narration_text MUST remain in Brazilian Portuguese (pt-BR)
- When splitting narration across frames, keep the natural pt-BR rhythm and phrasing

═══════════════════════════════════════════════════════════════════════════════
SYNC WORD RULE
═══════════════════════════════════════════════════════════════════════════════
For EACH frame, extract the EXACT WORD or SHORT PHRASE (1-5 words) from the
narration text that this frame best represents. This is the editing anchor —
the moment in the audio where this image should cut in.

═══════════════════════════════════════════════════════════════════════════════
ANATOMICAL HIGHLIGHT RULE (REAL ANATOMY)
═══════════════════════════════════════════════════════════════════════════════
If the narration mentions a BODY PART, ORGAN, HEALTH CONDITION, PAIN, or
PHYSICAL SYMPTOM, set anatomical_highlight to describe the REAL ANATOMICAL
structure with medical accuracy:
- Muscles with fibers, fascicles, tendons with correct anatomical names
- Organs with structure, lobes, organic texture
- NEVER use generic "glow" — always real anatomical visualization

═══════════════════════════════════════════════════════════════════════════════
GOLDEN RULES
═══════════════════════════════════════════════════════════════════════════════
• Generate EXACTLY {target_frames} frames
• NEVER repeat the same camera shot type in consecutive frames
• NEVER use more than 2 "static" camera movements in a row
• NEVER repeat identical poses in back-to-back frames
• Consecutive scenes MUST CONTRAST in camera type
• Every 3 scenes: at least 1 extreme close or anatomical macro
• Each frame must advance the visual story
• Visual focus should shift between character, environment, and details
• Every frame MUST have narration_text (never empty)
• Every frame MUST have sync_word (1-5 words from narration)
• Vary environments across frames for visual dynamismud  Full story arc — maintain this progression:
1. HOOK (first 2 frames): STRIKING wide/establishing shot, then dramatic close-up
2. BUILDING (frames 3-60%): Alternate medium shots and close-ups, vary angles
3. CLIMAX (60-85%): Extreme close-ups, dutch angles, rapid angle changes
4. RESOLUTION (final 15%): Wider shots, slower movements, stable compositionsu   OPENING half — HOOK + BUILD:
Start with a STRIKING establishing shot, then dramatic close-up.
Build with alternating medium shots and close-ups, varied angles.u   CLOSING half — CLIMAX + RESOLVE:
Use extreme close-ups, dutch angles, rapid changes for intensity.
End with wider shots, slower movements for emotional landing.)u   OPENING section — HOOK + EARLY BUILD:
Start with a STRIKING establishing shot, then dramatic close-up.
Begin building with varied medium shots.u   MIDDLE section — LATE BUILD + CLIMAX:
Intensify with more close-ups, low/high angles.
Push toward visual climax with extreme angles and rapid cuts.u   CLOSING section — LATE CLIMAX + RESOLVE:
Peak intensity then gradually return to wider, calmer shots.
End with stable compositions for emotional landing.)            2   )tiktokinstagram_reelsyoutube_shortsyoutubezdict[str, int]PLATFORM_FRAME_TARGETSu(  You are an expert cinematographer creating COVERAGE SHOTS for cinematic storytelling.

COVERAGE SHOTS means generating MULTIPLE FRAMES of the EXACT SAME SCENE from DIFFERENT CAMERA ANGLES.
This technique allows editors to cut between angles for dynamic visual storytelling.

You MUST generate exactly {total_coverage_frames} frames total.
There are {num_beats} narrative beats, and each beat gets {angles_per_beat} camera angles.

═══════════════════════════════════════════════════════════════════════════════
COVERAGE RULES (CRITICAL)
═══════════════════════════════════════════════════════════════════════════════

For EACH narrative beat, generate {angles_per_beat} frames showing the EXACT SAME ACTION 
from DIFFERENT CAMERA ANGLES.

1. ALL frames in a coverage group MUST share:
   - IDENTICAL environment description (verbatim)
   - IDENTICAL character pose/action (verbatim)
   - IDENTICAL lighting conditions
   - SAME sync_word
   - SAME coverage_group_id

2. ONLY these elements change between coverage frames:
   - camera_shot (wide → medium → close-up)
   - camera_movement
   - composition
   - visual_focus

3. COVERAGE ANGLE SEQUENCE (for {angles_per_beat} angles):
{angle_sequence}

═══════════════════════════════════════════════════════════════════════════════
CAMERA SHOT VOCABULARY
═══════════════════════════════════════════════════════════════════════════════
• Wide shot / Establishing – Full scene, character + environment
• Medium-wide shot – Knees up, shows body language
• Medium shot – Waist up, balanced view
• Medium close-up – Chest up, emotional connection
• Close-up – Face or specific detail
• Extreme close-up – Eyes, hands, texture detail
• Low angle – Looking up (power, dominance)
• High angle – Looking down (vulnerability)
• Side profile – Lateral view, action profile
• 3/4 frontal – Face + body language
• Over-the-shoulder – POV perspective

═══════════════════════════════════════════════════════════════════════════════
OUTPUT FORMAT
═══════════════════════════════════════════════════════════════════════════════

For each frame, include:
- frame_number: Sequential number starting at 1
- parent_scene: The beat number this frame belongs to
- coverage_group_id: "coverage_X" where X is the beat number
- coverage_angle_index: 0 for master, 1 for medium, 2 for close-up, etc.
- coverage_angle_type: "master", "medium", "close-up", "reverse", or "insert"
- camera_shot, camera_movement, composition
- environment_detail, pose_action, mood, visual_focus
- narration_text: The narration for this beat (same for all angles in group)
- sync_word: Key word from narration (same for all angles in group)

CONSISTENCY CHECK:
If Frame 1 shows "skeleton sitting on couch in living room"
Then Frame 2 MUST show "skeleton sitting on couch in living room"
And Frame 3 MUST show "skeleton sitting on couch in living room"
ONLY the camera angle changes.z~   - Angle 0 (MASTER): Wide/establishing shot, shows full scene
   - Angle 1 (CLOSE-UP): Detail shot (face, hands, anatomical)z   - Angle 0 (MASTER): Wide/establishing shot, shows full scene
   - Angle 1 (MEDIUM): Different angle (lateral, 3/4, over-shoulder)
   - Angle 2 (CLOSE-UP): Detail shot (face, hands, anatomical)z   - Angle 0 (MASTER): Wide/establishing shot, shows full scene
   - Angle 1 (MEDIUM): Medium shot from different angle
   - Angle 2 (CLOSE-UP): Detail shot (face, hands)
   - Angle 3 (REVERSE): Over-the-shoulder or reverse anglea&     - Angle 0 (MASTER): Wide/establishing shot, shows full scene
   - Angle 1 (MEDIUM): Medium shot from lateral angle
   - Angle 2 (CLOSE-UP): Close-up of face/expression
   - Angle 3 (INSERT): Detail insert (hands, object, anatomical)
   - Angle 4 (REVERSE): Over-the-shoulder or reverse angle)r   r         c                     e Zd ZdZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZddZddZ		 	 	 	 	 	 	 	 ddZ
	 	 	 	 	 	 	 	 dd	Z	 	 	 d	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 	 	 ddZe	 	 	 	 	 	 dd       Zy
)FrameExpanderAgenta   Expands narrative beats into cinematic frames with camera variety.

    Uses parallel batching: splits beats into 2-3 groups and generates frames
    for each group concurrently, then merges results. This halves or thirds
    the generation time compared to a single monolithic call.
    c                    t        |dd      r6t        |dd      }|dk(  r| j                  ||      S | j                  ||      S | j                  ||      S )Nenable_coverage_shotsFcoverage_modeallsmart)getattr_expand_with_smart_coverage_expand_with_coverage_expand_standard)self	breakdownconfigr   s       >/root/.openclaw/workspace/visionaryfx/agents/frame_expander.pyexpandzFrameExpanderAgent.expand  sb     62E:#FOUCM'77	6JJ11)VDD $$Y77    c                n   |j                   }|j                  }t        |      }|dk  s|dk  r,| j                  |||t        d      }| j                  ||      S |dk\  rdnd}||z  }||z  }	g }
d}t        |      D ]*  }|||	k  rdndz   }|
j                  ||||z           ||z  }, ||z  }||z  }t        |      D cg c]  }|||k  rdndz    }}t        |   }g }d}|D ]  }|j                  |       ||z  } t        j                  d	|||       d
g|z  }t        |      5 }i }t        t        |
|||            D ]-  \  }\  }}}}|||j                  | j                  |||||      <   / t        |      D ]4  }||   }|j!                         ||<   t        j                  d|dz   |       6 	 d
d
d
       g }|D ]  }|j#                  |j$                          t'        |d   j(                  |d   j*                  t        |      |      }| j                  ||      S c c}w # 1 sw Y   vxY w)z0Standard frame expansion without coverage shots.r      r   )
phase_hintframe_offset   r   r      z;Expanding %d beats into %d frames using %d parallel batchesN)max_workerszBatch %d/%d completetitlesummarytotal_framesframes)frames_per_minutesceneslen_expand_batchFULL_ARC_HINT_enforce_frame_limitrangeappendPHASE_HINTSloggerinfor   	enumeratezipsubmitr   resultextendr3   r   r0   r1   )r"   r#   r$   target_framesbeats	num_beatsrB   num_batches
chunk_size	remainderchunksstartisize
frames_per	frame_remframe_targetshintsoffsetsoffsetftresultspoolfutureschunkhintofffuture
all_framesrmergeds                                  r%   r!   z#FrameExpanderAgent._expand_standard"  s    00  J	 >]b0''y-( ( F
 ,,V]CC %>aq+-
+	{# 	Aa)m;DMM%edl34TME	 #k1
!K/	 ;'
 q9}!!4
 

 K(  	BNN6"bLF	 	I}k	
 9=v7KK8 	HDG-6FM5':. ))E2tS
  &&y"dC  'w/ HFO#]]_
2AE;GH	H 
 	(Aahh'	( (!*""AJ&&Z	
 ((??]
*	H 	Hs    H&)BH++H4c                   |j                   }t        |      }t        |dd      }|j                  }t	        d||z        }t        ||      }||z  }||k  r<||z  }	t        |      D 
cg c]  }
t        |
|	z         }}
|D 
cg c]  }
||
   	 }}
n|}t        j                  d||||       t        j                  |t        d         }t        j                  ||||      }g }t        |      D ]g  \  }}|j                  d|dz    d|dz    d|j                    d	|j"                   d
|j$                   d|j&                   d|j(                   d| d       i d|j*                   d|j,                   d| d| d| ddj/                  |      z   }t        dt	        d|dz              }| j1                  ||t2        d|      }| j5                  |||      }|S c c}
w c c}
w )u   Generate coverage shots - multiple camera angles per scene.
        
        Respects frames_per_minute: if user requests 6 frames with 3 angles,
        we generate 2 scenes × 3 angles = 6 frames total.
        coverage_angles_per_scener   r-   zNCoverage mode: Using %d of %d beats, generating %d frames (%d angles per beat))total_coverage_framesrF   angles_per_beatangle_sequenceBeat z (coverage_group_id: coverage_z):
  Environment: 
  Pose/Action: 	
  Mood: 
  Narration: 

  Notes: u   
  → Generate z8 frames with DIFFERENT camera angles for this SAME sceneVIDEO TITLE: 

SUMMARY: z
TOTAL FRAMES TO GENERATE: z
ANGLES PER BEAT: 
NARRATIVE BEATS (z selected beats):



i   i    i  ffffff?system_promptuser_promptresponse_modeltemperature
max_tokens)r5   r6   r   r4   maxminr:   intr=   r>   COVERAGE_ANGLE_SEQUENCESgetCOVERAGE_SYSTEM_PROMPTformatr?   r;   environmentpose_actionmoodnarration_textnarration_notesr0   r1   joincallr   _ensure_coverage_metadata)r"   r#   r$   rE   rF   rb   rD   num_scenes_to_user2   steprL   selected_indicesselected_beatsrc   system
beats_textidxsuserrs   rB   s                        r%   r    z(FrameExpanderAgent._expand_with_coveragev  ss      J	!&*EqI00  =O#CD   19= )?: y(00D7<=N7OP!AHPP0@A1eAhANA"N\y,	
 255$Q'

 (..".'+)	 / 
 
/ 		FCay >sQwi H""#-- 1""#-- 166( #  ! 0 01 2--. /""1!22jl		 IOO, -!))* +))5 7  /0 1  122G	I
 kk*%& 	 D,*< =>
 1!  
 //8I?[u  QAs   (GGc           	     @   |j                   }t        |      }t        |dd      |j                  }t        j                  d|       g }|D ]U  }t        |dd      }t        |dd      }	|s"| j                  |      }| j                  |      }	|j                  |||	d       W |j                  d	 d
       t        fd|D              }
|
|kD  r| j                  ||      }n|
|k  r| j                  ||      }|j                  d        g }d}d}|D ]  }|d   }|d   rdt              D ]P  }g d}|t        |t        |      dz
           }| j                  ||d| ||      }|j                  |       |dz  }R |dz  }q| j                  ||      }|j                  |       |dz  } t        j                  dt        |      t        d |D              t        d |D                     t!        |j"                  |j$                  t        |      |      S )zSmart Coverage: AI decides which scenes need multi-angle coverage.
        
        Analyzes each beat's mood, action, and narrative importance to determine
        if it benefits from multiple camera angles.
        r`   r   zCSmart Coverage mode: Analyzing %d beats to determine coverage needsneeds_coverageFcoverage_priorityr   )beatr   priorityc                    | d   S )Nr    xs    r%   <lambda>z@FrameExpanderAgent._expand_with_smart_coverage.<locals>.<lambda>  s
    a
m r'   T)keyreversec              3  0   K   | ]  }|d    rnd  ywr   r-   Nr   ).0drb   s     r%   	<genexpr>zAFrameExpanderAgent._expand_with_smart_coverage.<locals>.<genexpr>  s&      #
  !!12O9#
   c                     | d   j                   S )Nr   )scene_numberr   s    r%   r   z@FrameExpanderAgent._expand_with_smart_coverage.<locals>.<lambda>
  s    ai.D.D r'   )r   r-   r   mastermediumclose-upr   insert	coverage_)coverage_group_idcoverage_angle_indexcoverage_angle_typezHSmart Coverage: Generated %d frames (%d beats with coverage, %d without)c              3  ,   K   | ]  }|d    s	d  ywr   r   r   r   s     r%   r   zAFrameExpanderAgent._expand_with_smart_coverage.<locals>.<genexpr>+  s     Ea3C1DE   
c              3  ,   K   | ]  }|d    r	d  ywr   r   r   s     r%   r   zAFrameExpanderAgent._expand_with_smart_coverage.<locals>.<genexpr>,  s     IaQ7G5HIr   r/   )r5   r6   r   r4   r=   r>   _should_have_coverage_calculate_coverage_priorityr;   sortsum_trim_coverage_to_fit_expand_coverage_to_fitr:   ru   _create_frame_from_beatr   r0   r1   )r"   r#   r$   rE   rF   rD   coverage_decisionsr   	needs_covr   frames_with_coverager\   frame_numbercoverage_groupdecision	angle_idxangle_types
angle_typeframerb   s                      @r%   r   z.FrameExpanderAgent._expand_with_smart_coverage  s      J	!&*EqI00Q	
   	D&6>It%8!<H  66t<	<<TB%%"+$' 	  	$;TJ  # #
'#
  
  -/!%!;!;"M?" "M1!%!=!="M?"
 	$DE 
* 	"HF#D()!&!7 &I"WK!,SC<Lq<P-Q!RJ 88l,5n5E*F-6,6	 9 E %%e, A%L& !# 44T<H!!%(!+	". 	V
OE-EEI-II		
 &//%%Z	
 	
r'   c                    t        |dd      xs dj                         }t        |dd      xs dj                         }g d}g d}|D ]  }||v s y |D ]  }||v s y y)zEHeuristic to determine if a beat needs coverage based on mood/action.r}    r|   )intensedramatictense	climactic	explosivefearful
triumphant	desperateangry	terrifiedshockedecstaticfurious	agonizing	thrilling)runningfightingfallingjumping
strugglingconfronting	attacking	defendingescapingchasing
collapsing	explodingtransforming	screamingcryingTF)r   lower)r"   r   r}   actioncoverage_moodscoverage_actionsmas           r%   r   z(FrameExpanderAgent._should_have_coverage6  s    fb)/R668$r28b??A

   	ADy	
 " 	AF{	 r'   c                   t        |dd      xs dj                         t        |dd      xs dj                         d}t        fddD              rd}n-t        fdd	D              rd
}nt        fddD              rd}t        fddD              rt        |d      }n t        fddD              rt        |d      }t        fddD              rt	        |d      }t        fddD              rt	        |d      }|S )z-Calculate priority score (1-10) for coverage.r}   r   r|   r   c              3  &   K   | ]  }|v  
 y wNr   r   r   r}   s     r%   r   zBFrameExpanderAgent._calculate_coverage_priority.<locals>.<genexpr>]  s     EQqDyE   )r   r   peak
   c              3  &   K   | ]  }|v  
 y wr   r   r   s     r%   r   zBFrameExpanderAgent._calculate_coverage_priority.<locals>.<genexpr>_  s     EqdEr   )r   r   r   r,   c              3  &   K   | ]  }|v  
 y wr   r   r   s     r%   r   zBFrameExpanderAgent._calculate_coverage_priority.<locals>.<genexpr>a  s     KqdKr   )	emotionalpowerfulr      c              3  &   K   | ]  }|v  
 y wr   r   r   r   r   s     r%   r   zBFrameExpanderAgent._calculate_coverage_priority.<locals>.<genexpr>e  s     KqqF{Kr   )r   r   r   	   c              3  &   K   | ]  }|v  
 y wr   r   r   s     r%   r   zBFrameExpanderAgent._calculate_coverage_priority.<locals>.<genexpr>g  s     HfHr   )r   r   r   c              3  &   K   | ]  }|v  
 y wr   r   r   s     r%   r   zBFrameExpanderAgent._calculate_coverage_priority.<locals>.<genexpr>k  s     SQqDySr   )calmpeacefulneutralcontemplativer   c              3  &   K   | ]  }|v  
 y wr   r   r   s     r%   r   zBFrameExpanderAgent._calculate_coverage_priority.<locals>.<genexpr>m  s     EqqF{Er   )standingsittinglyingr   )r   r   anyrt   ru   )r"   r   scorer   r}   s      @@r%   r   z/FrameExpanderAgent._calculate_coverage_priorityU  s    fb)/R668$r28b??A E"DEEEE$DEEEK$JKKE K$JKKqMEH&GHHqME S"RSSqMEE$DEEqMEr'   c                    t        fd|D              }t        t        |      dz
  dd      D ]#  }||k  r |S ||   d   sd||   d<   |dz
  z  }% |S )zERemove coverage from lowest priority beats to fit target frame count.c              3  0   K   | ]  }|d    rnd  ywr   r   r   r   angless     r%   r   z;FrameExpanderAgent._trim_coverage_to_fit.<locals>.<genexpr>w  #      
56a()Fq0
r   r-   r   F)r   r:   r6   )r"   	decisionstargetr  current_framesrL   s      `  r%   r   z(FrameExpanderAgent._trim_coverage_to_fitr  s    
  
:C
 

 s9~)2r2 	/A'  |,-16	!-.6A:.	/ r'   c                    t        fd|D              }|D ])  }||k\  r |S |d   r|dz
  z   |k  sd|d<   |dz
  z  }+ |S )z7Add coverage to more beats to reach target frame count.c              3  0   K   | ]  }|d    rnd  ywr   r   r  s     r%   r   z=FrameExpanderAgent._expand_coverage_to_fit.<locals>.<genexpr>  r  r   r   r-   T)r   )r"   r  r  r  r  r   s      `  r%   r   z*FrameExpanderAgent._expand_coverage_to_fit  s}      
:C
 

  	3A'  %&!VaZ0F:*.A&'"vz2N	3 r'   Nc                   ddl m} dddddd}|j                  |d      } |d i d	|d
|j                  d|d|dk(  rdndd|dk(  rdndd|j                  d|j
                  d|j                  d|j
                  r|j
                  j                         d   ndd|j                  d|j                  ddddd|d|d|S )!z0Create a Frame object from a SceneAnalysis beat.r   )Framez	wide shotzmedium shotr   zover-the-shoulderzextreme close-upr   r   parent_scenecamera_shotcamera_movementr   staticzsubtle driftcompositioncenteredzrule of thirdsenvironment_detailr|   r}   visual_focus	characterr~   r   	sync_wordr   anatomical_highlightr   r   r   r   )
modelsr  rx   r   r{   r|   r}   splitr~   r   )	r"   r   r   r   r   r   r  camera_shotsr  s	            r%   r   z*FrameExpanderAgent._create_frame_from_beat  s3    	! "#"*(
 #&&':MJ 
%
**
 $
 )<x(GH^	

 ':X&E
K[
  $//
 ((
 
 9=8H8H))//1!4k
  ..
 !00
 
 "$
 0
 "6
  !4!
 	
r'   c                Z   g d}t        |j                        D ]t  \  }}||z  }||z  }|j                  sd|dz    |_        ||_        |j                  r|j                  dk(  r |t        |t        |      dz
           |_        |dz   |_        v t        |j                        |_        |S )z1Ensure all frames have correct coverage metadata.r   r   r-   r   )	r?   r3   r   r   r   ru   r6   r   r2   )	r"   rB   rF   rb   r   rL   r   beat_idxr   s	            r%   r   z,FrameExpanderAgent._ensure_coverage_metadata  s     L!&--0 	'HAuO+HO+I **,5hl^*D')2E&,,0I0IR0O,7Is;GWZ[G[8\,]) "#QE	'  "&--0r'   c                   t        |      }t        d|t        |d      z        }|dk  rd| d| d| d}n/|dk  r
d| d| d	}n t        d|dz
        }	|dz   }
d
|	 d|
 d| d	}t        j                  |||      }g }|D ]a  }|j	                  d|j
                   d|j                   d|j                   d|j                   d|j                   d|j                          c d|j                   d|j                   d| d|dz    dt        |       ddj                  |      z   }t        dt        d|dz              }| j                  ||t         d|      S ) z&Generate frames for a subset of beats.r-   z
There are z beats but only z# frames allowed. You MUST pick the ug    most visually impactful moments — some beats will get 1 frame, others may get 0. Prioritize variety.r   z%Create 1-2 frames per beat (average ~zB). Some beats get 1 frame, key beats get 2. Total MUST be exactly .z For EACH narrative beat, create -zN frames using DIFFERENT camera angles and compositions. Total MUST be exactly )rD   r*   frames_per_beat_instructionrd   z:
  Environment: re   rf   rg   rh   ri   rj   z
TARGET FRAMES: z#
FRAME NUMBERING: Start from frame rk   z
 beats):

rl   i @  i   iX  rm   rn   )r6   rt   SYSTEM_PROMPTrz   r;   r   r{   r|   r}   r~   r   r0   r1   r   ru   r   r   )r"   rE   r#   rD   r*   r+   rF   avg_per_beatfpb_instructionlohir   r   r   r   batch_max_tokenss                   r%   r7   z FrameExpanderAgent._expand_batch  s
    J	1ms9a/@@A1YK'7 G%%2O 4UV 
 Q7~ F))6q:  Qq()B!B22$at <IIVWXZ 
 %%'!(7 & 
 
 	A' (""#-- 1""#-- 166( #  ! 0 01 2--.0	 IOO, -!))* ++_ -11=1A0B C  #E
|<	9
 kk*%& 	 uc$0C&DEyy 1'  
 	
r'   c                   | j                   }t        |      |k  r/t        |      D ]  \  }}|dz   |_         t        |      | _        | S t
        j                  dt        |      |       t        |      |z  }g }t        |      D ]$  }t        ||z        }|j                  ||          & t        |      D ]  \  }}|dz   |_         t        | j                  | j                  ||      S )z?Hard-truncate frames to target count and renumber sequentially.r-   u7   LLM generated %d frames but target is %d — truncatingr/   )r3   r6   r?   r   r2   r=   warningr:   rv   r;   r   r0   r1   )rB   r  r3   rL   fr   selectedr   s           r%   r9   z'FrameExpanderAgent._enforce_frame_limit!  s     v;& !&) '1!"Q'"%f+FMEK	

 6{V#v 	)Aa$h-COOF3K(	)
 h' 	#DAqUAN	# &,,NN	
 	
r'   )r#   r   r$   r   returnr   )r*  bool)r*  rv   )r  listr  rv   r  rv   r*  r,  )Nr   r   )r   rv   r   strr   rv   r   r-  )rB   r   rF   rv   rb   rv   r*  r   )rE   r,  r#   r   rD   rv   r*   r-  r+   rv   r*  r   )rB   r   r  rv   r*  r   )__name__
__module____qualname____doc__r&   r!   r    r   r   r   r   r   r   r   r7   staticmethodr9   r   r'   r%   r   r   
  s   8!8 8 
 	8 R@!R@ R@ 
 	R@hV!V V 
 	Vpf
!f
 f
 
 	f
P>:'*47	('*47	, "&$%#%	$
"%$
$
 "$
 !	$
L&  	
 
 8C
C
 "C
 	C

 C
 C
 
 C
J !
&!
!
 
 !
 !
r'   r   )r1  
__future__r   loggingconcurrent.futuresr   r   r  r   r   r   r	   agents.base_agentr
   	getLoggerr.  r=   r   r8   r<   r   __annotations__ry   rw   r   r   r'   r%   <module>r9     s    #  ?  (			8	$qfQ	L	H	
. 	*  C LBB>> $y
 y
r'   