
    4AjM                        d Z ddlmZ ddlZddlZddlmZmZ ddlmZ ddl	m
Z
 ddlmZmZ ddlmZ  G d	 d
ee          Z G d de          Z G d de          Z G d d          ZdS )aj  Cost tracker core: estimate, reserve, reconcile, and persist to cost_log.json.

Implements the budget governance rules from the spec:
- Every paid operation produces a preflight estimate
- The orchestrator reserves estimated budget before execution
- Budget overruns trigger pauses (in warn/cap mode)
- Actual spend is reconciled when the tool finishes or fails
    )annotationsN)datetimetimezone)Enum)Path)AnyOptional)
BudgetModec                  "    e Zd ZdZdZdZdZdZdS )EntryStatus	estimatedreserved	completedfailedrefundedN)__name__
__module____qualname__	ESTIMATEDRESERVED	COMPLETEDFAILEDREFUNDED     A/app/working/workspaces/default/OpenMontage/tools/cost_tracker.pyr   r      s'        IHIFHHHr   r   c                      e Zd ZdZdS )BudgetExceededErrorz=Raised when an operation would exceed the budget in cap mode.Nr   r   r   __doc__r   r   r   r   r      s        GGDr   r   c                      e Zd ZdZdS )ApprovalRequiredErrorz?Raised when an operation needs user approval before proceeding.Nr   r   r   r   r"   r"   #   s        IIDr   r"   c                  $   e Zd ZdZddddej        dfd8dZed9d            Zed9d            Z	ed9d            Z
ed9d            Zd:dZd;dZd<d Zd=d!Zd>d?d$Zd<d%Zd@d+ZdAd0ZdBd1ZdCd2ZdCd3ZdDd5ZedEd6            ZedEd7            ZdS )FCostTrackerzDTracks estimated, reserved, and actual costs for a pipeline project.g      $@皙?      ?TNbudget_total_usdfloatreserve_pctsingle_action_approval_usd"require_approval_for_new_paid_toolboolmoder
   cost_log_pathOptional[Path]returnNonec                    || _         || _        || _        || _        || _        || _        g | _        t                      | _        |r*|	                                r| 
                                 d S d S d S N)r'   r)   r*   r+   r-   r.   entriesset_approved_toolsexists_load)selfr'   r)   r*   r+   r-   r.   s          r   __init__zCostTracker.__init__+   s     !1&*D'2T/	*-/), 	]1133 	JJLLLLL	 	 	 	r   c                >    t          d | j        D                       S )Nc              3  x   K   | ]5}|d          t           j        j        k    |                    dd          V  6dS )statusreserved_usd        N)r   r   valueget.0es     r   	<genexpr>z2CostTracker.budget_reserved_usd.<locals>.<genexpr>D   sQ       
 
{k2888 EE.#&&8888
 
r   sumr4   r9   s    r   budget_reserved_usdzCostTracker.budget_reserved_usdB   2     
 
\
 
 
 
 
 	
r   c                >    t          d | j        D                       S )Nc              3     K   | ]D}|d          t           j        j        t           j        j        fv ,|                    dd          V  EdS )r=   
actual_usdr?   N)r   r   r@   r   rA   rB   s     r   rE   z/CostTracker.budget_spent_usd.<locals>.<genexpr>L   s\       
 
{{4:K<N<TUUU EE,$$UUUU
 
r   rF   rH   s    r   budget_spent_usdzCostTracker.budget_spent_usdJ   rJ   r   c                0    | j         | j        z
  | j        z
  S r3   )r'   rN   rI   rH   s    r   budget_remaining_usdz CostTracker.budget_remaining_usdR   s    $t'<<t?WWWr   c                P    | j         | j        z  }t          d| j        |z
            S )z"Budget minus the reserve holdback.r?   )r'   r)   maxrP   )r9   holdbacks     r   usable_budget_usdzCostTracker.usable_budget_usdV   s,     (4+;;31H<===r   dict[str, float]c                    t          | j        d          t          | j        d          t          | j        d          dS )N   )total_spent_usdtotal_reserved_usdrP   )roundrN   rI   rP   rH   s    r   cost_snapshotzCostTracker.cost_snapshot\   sA    $T%:A>>"'(@!"D"D$)$*CQ$G$G
 
 	
r   toolstr	operationestimated_usdc                    |                                  }| j                            |||t          j        j        t          |d          dd|                                 d           |                                  |S )z%Record an estimate. Returns entry ID.rW   r?   )idr\   r^   r=   r_   r>   rM   	timestamp)	_new_idr4   appendr   r   r@   rZ   _now_save)r9   r\   r^   r_   entry_ids        r   estimatezCostTracker.estimatee   sw    <<>>"!+1"=!44	
 	
 		 		 		 	

r   rg   c                L   |                      |          }|d         }|| j        k    r1| j        t          j        k    rt          d|dd| j        d          | j        rC|dk    r=|d         | j        vr.| j        t          j        k    rt          d|d         d          || j        k    r1| j        t          j	        k    rt          d	|dd
| j        d          t          j        j        |d<   ||d<   |                                 |d<   |                                  dS )zReserve budget for an estimated entry.

        Raises BudgetExceededError in cap mode, or ApprovalRequiredError
        when the action exceeds the single-action approval threshold.
        r_   zAction costs $z.2fz#, exceeds single-action threshold $r   r\   zFirst paid use of tool z requires approvalzReservation of $z exceeds usable budget $r=   r>   rb   N)_findr*   r-   r
   OBSERVEr"   r+   r6   rT   CAPr   r   r   r@   re   rf   )r9   rg   entryr   s       r   reservezCostTracker.reserveu   sn    

8$$/*	 t666yJ...+VY> V V040OUV V   2 	y1}}V}D$8889
 222/U%-UUU  
 t---yJN**)5yM 5 5.45 5  
 &.4h )n!YY[[k

r   c                :    | j                             |           dS )z,Mark a tool as approved for paid operations.N)r6   add)r9   r\   s     r   approve_toolzCostTracker.approve_tool   s      &&&&&r   rM   successc                   |                      |          }|rt          j        j        nt          j        j        |d<   t          |d          |d<   d|d<   |                                 |d<   |                                  dS )z,Reconcile actual spend after tool execution.r=   rW   rM   r?   r>   rb   N)rj   r   r   r@   r   rZ   re   rf   )r9   rg   rM   rr   rm   s        r   	reconcilezCostTracker.reconcile   ss    

8$$9@^+/55kFXF^h#J22l #n!YY[[k

r   c                    |                      |          }t          j        j        |d<   d|d<   |                                 |d<   |                                  dS )z'Cancel a reservation without executing.r=   r?   r>   rb   N)rj   r   r   r@   re   rf   r9   rg   rm   s      r   refundzCostTracker.refund   sO    

8$$%.4h #n!YY[[k

r   video_analysis_briefdicttarget_duration_secondsint	tool_planc                   |                     di           }|                     di           }|                     di           }|                     di                                dd          }|                     dd          }|                     d	d
          }	|dk    r	|	|dz  z  }
nd}
dd
dddd}|                     |d          }t          |
|dz  z            }t          ||          }|                     dd          }|dk    r|dk    r	||z  dz  }nd}t          ||dz  z            }|                     dg           }|                     |||          \  }}|dk    r t          dt          ||z                      nd}||z
  }|                     di           }|r|                     dd          nd}||z  }|r t          |t          ||z                      nd}d}|dv rdnd}t          |t          ||z                      }g }g }|                    | d|
dd| d            |                    |           |                     d!i           } | rt          ||z            }!|                      d"d#          }"|                    d!|                      d$d%          |!|"t          |!|"z  d&          d'|d(d)| d*t          |dz
  d+z             d,d-           |r|dk    rt          ||z            }#|                     d"d.          }"|                    d|                     d$d%          |#|"t          |#|"z  d&          |d(d/| d0| d1d-           |                    t          |d+z             d2|d(d3| d4| d5           |                     d6i           }$|$r|dk    r|$                     d7d8          }%t          ||%z  d&          }&|                    d9|$                     d$d%          ||%|&d:t          |           d;| d<d-           |                    d:t          |           d;| d=| d>           |                     d?i           }'|'rF|'                     d@dA          }(|                    d?|'                     d$d%          d|(|(dBd-           t          t          dC |D                       d&          })t          |)|z  d&          }*t          |)dDz  d&          }+dE},|,t          |d          z  }-t          |)|-z  d&          }.|r|                     dd          dk    rdF}/n!|s|                     dd          dk    rdG}/ndH}/||)|*|+dI|.|/|||||t          |dE          t          |
d          |dJS )Kaf  Estimate production cost based on reference analysis + target duration.

        Args:
            video_analysis_brief: The VideoAnalysisBrief artifact from video analysis
            target_duration_seconds: How long the output video should be
            tool_plan: Which tools will be used for each asset type, e.g.:
                {
                    "image_generation": {"tool": "flux_fal", "cost_per_unit": 0.05},
                    "video_generation": {"tool": "kling_fal", "cost_per_unit": 0.30,
                                         "clip_duration_seconds": 5},
                    "tts": {"tool": "elevenlabs_tts", "cost_per_word": 0.00003},
                    "music": {"tool": "music_gen", "cost_per_track": 0.10},
                }

        Returns:
            Itemized cost breakdown with line items, total, sample cost, and assumptions.
        structure_analysispacing_profilenarration_transcriptsourceduration_seconds<   pacing_stylesteady_educationaltotal_scenes   r   g      @
            
rapid_firedynamic_socialr   slow_contemplativevariable
word_count   scenes)rx   scenes_listr      video_generationclip_duration_secondsg?)r   r   g       @g      ?z scenes (reference has z.1fz cuts/min, pacing: )image_generationcost_per_unitg?r\   unknownrW   ~z.0fz images/scene x z
 scenes + d   z% retry buffer)categoryproviderquantityunit_cost_usd	total_usdbasisg333333?zs of motion / z
s clips = z clips + retry bufferu   % motion ratio → zs needs z clips (zs each)ttscost_per_wordgiUMu>tts_narrationzNarration at z WPM = ~z wordsz words for z secondsmusiccost_per_trackr?   z1 background music trackc              3  &   K   | ]}|d          V  dS )r   Nr   )rC   items     r   rE   z6CostTracker.estimate_from_reference.<locals>.<genexpr>g  s'      FF4T+.FFFFFFr   gffffff?   highmediumlow)r   r   )
line_itemsr   total_range_usdsample_cost_usd
confidenceassumptionsestimated_scenesestimated_imagesestimated_clipsestimated_wordsmotion_ratiocuts_per_minuterz   )rA   rZ   rR   _estimate_motion_ratiord   rG   )0r9   rx   rz   r|   	structurepacing	narrationref_durationr   
ref_scenesr   min_scenes_by_pacing
min_scenesdensity_based_scenesr   ref_word_count
actual_wpmr   r   r   motion_basisestimated_motion_scenesestimated_still_scenesvid_planclip_durationmotion_secondsclips_needed_for_coverageretry_multiplierimages_per_scener   r   r   img_plan	img_count	unit_cost
clip_counttts_planr   tts_cost
music_plan
music_costsubtotal	low_total
high_totalsample_scenessample_fractionsample_costr   s0                                                   r   estimate_from_referencez#CostTracker.estimate_from_reference   s   . ),,-A2FF	/44(,,-CRHH	+//"==AABTVXYYzz.2FGG ]]>155
!(L2,=>OO!O "#"# 
  
 *--lA>>
  %_8ORT8T%UVVz+?@@ #|Q77! 2 2(<72=JJJ
.E.J KLL  mmHb11%)%@%@!5#% &A &
 &
"l a 5)L899::: 	 
 "24K!K ==!3R88DLS%<a@@@RS0<? %C#.=011%
 %
 %
  	"  #/2R"R"R33X["%5566
 
 
 ' 'X ' '#' ' '	
 	
 	
 	<((( ==!3R88 	.1AABBI _d;;I.$LL;;%!*"9y#8!<<M(X M M>N M M 01 4;<<M M M
 
 
 
 
  	1A558;KKLLJ _d;;I.$LL;;&!*":	#91==%R H H H H0H H H
 
 
 
 
 +,, + +!Q+ +/H+ +!+ + +   ==++ 	",,$LL'BBM_}<a@@H+$LL;;+!.%[z):):[[O[[[     9j 1 1 9 9? 9 9.9 9 9   ]]7B//
 		#(8#>>J#&NN69==!+'3     FF:FFFFFJJ (%55q99	8d?A..
 '#.>*B*BBH6::  	9==q99A==JJ 	IMM,::Q>>!JJJ %!'0*EE*$& 0 08.!,22$_a88'>
 
 	
r   r   list[dict[str, Any]]r   tuple[float, str]c                 
 ddddddddddd


fd	|D             }|rt          |          t          |          z  }t          d
t          |          t          |          z
            }|rB|                     ||          \  }}t          |          ||z  z   t          |          z  }d}	nd}	t	          t          t          |d          d          d          |	fS |                     ||          S )z=Estimate how much of the target treatment truly needs motion.g      ?g?333333?g?ffffff?g      ?皙?)
	animationb_rollstock_footageproduct_shot
transitionscreen_recordingtalking_headdiagramchart	text_cardc                R    g | ]#}|                     d           xv          $S )visual_type)rA   )rC   scenemotion_weightsr   s     r   
<listcomp>z6CostTracker._estimate_motion_ratio.<locals>.<listcomp>  sD     
 
 
$yy777NJJ ;'JJJr   r   )rx   r   zemotion ratio blended from classified scene types and reference-style fallback for unclassified scenesz0motion ratio derived from classified scene typesr?   ffffff?r   )rG   lenrR   _fallback_motion_ratiorZ   min)r9   rx   r   r   classified_weightsratiounknown_countfallback_ratio_r   r   r   s             @@r   r   z"CostTracker._estimate_motion_ratio  sd      $ 
 

 
 
 
 
$
 
 

  	?*++c2D.E.EEE3{#3#3c:L6M6M#MNNM K$($?$?)=!- %@ % %!
 +,,~/MM+&&' 
G 
 KS__d33Q77>>**!5% + 
 
 	
r   c               
   |                     di                                dd          }|                     di           }t          |                     d                    }|                     dd          }ddd	d
dd}|                     |d          }|dv rt          |d          }|rt          |d          }|dk    rt          |d          }t          t	          t          |d          d          d          }d}	||	fS )zCFallback heuristic for motion ratio before scene vision enrichment.r   type replication_guidancemotion_requiredsuggested_pipelineg?g?r   r   r&   r   )shorts	instagramtiktokgffffff?r   	cinematicg?r%   r   r   z]motion ratio inferred from pacing/style because scene visual types have not been enriched yet)rA   r,   rR   rZ   r   )
r9   rx   r   source_typereplicationr   r   base_by_pacingr   r   s
             r   r   z"CostTracker._fallback_motion_ratio  s!    +..x<<@@LL*../ErJJ{/@AABB(__-A2FF ""&"%
 
 ""<55;;;sOOE 	$sOOE,,t$$Ec#eS//400!44) 	 e|r   c                T   | j         d S d| j        t          | j        d          t          | j        d          | j        d}| j         j                            dd           t          | j         d          5 }t          j
        ||d           d d d            d S # 1 swxY w Y   d S )	Nz1.0rW   )versionr'   rI   rN   r4   T)parentsexist_okwr   )indent)r.   r'   rZ   rI   rN   r4   parentmkdiropenjsondump)r9   datafs      r   rf   zCostTracker._save  s    %F $ 5#()A1#E#E %d&;Q ? ?|
 
 	!''t'DDD$$c** 	)aIdAa((((	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	)s   8BB!$B!c                    t          | j                  5 }t          j        |          }d d d            n# 1 swxY w Y   |                    dg           | _        |                    d| j                  | _        d S )Nr4   r'   )r  r.   r  loadrA   r4   r'   )r9   r  r  s      r   r8   zCostTracker._load  s    $$%% 	 9Q<<D	  	  	  	  	  	  	  	  	  	  	  	  	  	  	 xx	2.. $);T=R S Ss   6::dict[str, Any]c                \    | j         D ]}|d         |k    r|c S t          d|d          )Nra   zCost entry z
 not found)r4   KeyErrorrv   s      r   rj   zCostTracker._find  sJ    \ 	 	ET{h&& ';X;;;<<<r   c                 B    t          j                    j        d d         S )N   )uuiduuid4hexr   r   r   rc   zCostTracker._new_id  s    z||$$r   c                 b    t          j        t          j                                                  S r3   )r   nowr   utc	isoformatr   r   r   re   zCostTracker._now  s     |HL))33555r   )r'   r(   r)   r(   r*   r(   r+   r,   r-   r
   r.   r/   r0   r1   )r0   r(   )r0   rU   )r\   r]   r^   r]   r_   r(   r0   r]   )rg   r]   r0   r1   )r\   r]   r0   r1   )T)rg   r]   rM   r(   rr   r,   r0   r1   )rx   ry   rz   r{   r|   ry   r0   ry   )rx   ry   r   r   r   r]   r0   r   )rx   ry   r   r]   r0   r   )r0   r1   )rg   r]   r0   r  )r0   r]   )r   r   r   r    r
   WARNr:   propertyrI   rN   rP   rT   r[   rh   rn   rq   rt   rw   r   r   r   rf   r8   rj   staticmethodrc   re   r   r   r   r$   r$   (   s       NN #'!,037%?(,    . 
 
 
 X
 
 
 
 X
 X X X XX > > > X>

 
 
 
    $ $ $ $L' ' ' '       W
 W
 W
 W
r0
 0
 0
 0
d! ! ! !J) ) ) )T T T T= = = = % % % \% 6 6 6 \6 6 6r   r$   )r    
__future__r   r  r  r   r   enumr   pathlibr   typingr   r	   lib.config_modelr
   r]   r   	Exceptionr   r"   r$   r   r   r   <module>r,     sW    # " " " " "   ' ' ' ' ' ' ' '                             ' ' ' ' ' '    #t   	 	 	 	 	) 	 	 	
	 	 	 	 	I 	 	 	
\6 \6 \6 \6 \6 \6 \6 \6 \6 \6r   