
    +j7                        S r SSKrSSKJr  SSKJr   " S S5      rS\4S jr\	" 1 S	k5      r
\	" 1 S
k5      rS\S\4S jrS rS\S\4S jrSrS\S\4S jrS\S\\\   \\   4   4S jrS\S\S\4S jrS\S\S\4S jrS,S\S\S\S\4S jjrS\S\\\\   4   4S jrS\\   S \S!\S\\   4S" jrS\S#\S$\S%\S\\   4
S& jr      S-S#\S$\S%\S'\\\      S(\\   S \S!\S)\\   S*\S\\\   \4   4S+ jjrg).zp
Job utilities for the /api/jobs endpoint.
Provides normalization and helper functions for job status tracking.
    N)Optional)
prune_dictc                   :    \ rS rSrSrSrSrSrSrSr	\\\\\	/r
Srg	)
	JobStatus   zJob status constants.pendingin_progress	completedfailed	cancelled N)__name__
__module____qualname____firstlineno____doc__PENDINGIN_PROGRESS	COMPLETEDFAILED	CANCELLEDALL__static_attributes__r       4/home/wildlama/comfy/ComfyUI/comfy_execution/jobs.pyr   r      s.    GKIFIKFI
>Cr   r   returnc                     [        U [        5      (       d!  [        S[        U 5      R                   35      e[        [
        R                  " U 5      5      U :w  a  [        S5      eU $ )u  Validate a client-supplied job (prompt) id.

Job ids must be UUIDs in the canonical lowercase hyphenated form. The id
is stored and compared verbatim everywhere downstream — history keys,
websocket events, and /interrupt matching — so accepting another spelling
would silently rewrite the client's id and then miss every exact-match
lookup. Rejecting loudly beats that.

Returns the id unchanged. Raises ValueError when the value is not a
string in canonical UUID form.
zjob id must be a string, got z<job id must be a UUID in canonical lowercase hyphenated form)
isinstancestr
ValueErrortyper   uuidUUIDvalues    r   validate_job_idr&      sV     eS!!8e9M9M8NOPP
499U%WXXLr   >   3dtextaudiovideoimages>   .fbx.glb.obj.gltf.usdzfilenamec                 V   ^ U R                  5       m[        U4S j[         5       5      $ )Nc              3   F   >#    U  H  nTR                  U5      v   M     g 7fNendswith).0extlowers     r   	<genexpr>#has_3d_extension.<locals>.<genexpr>3   s     A.@su~~c"".@   !)r9   anyTHREE_D_EXTENSIONS)r1   r9   s    @r   has_3d_extensionr?   1   s!    NNEA.@AAAr   c                     U c  g[        U [        5      (       a  [        U 5      (       a  U SSSS.$ g[        U [        5      (       a  U $ g)zNormalize a single output list item for the jobs API.

Returns the normalized item, or None to exclude it.
String items with 3D extensions become {filename, type, subfolder} dicts.
Noutput r'   )r1   r!   	subfolder	mediaType)r   r   r?   dict)items    r   normalize_output_itemrG   6   sL     |$D!! $hRVZ[[$r   outputsc                 \   0 nU R                  5        H  u  p#[        U[        5      (       d  X1U'   M   0 nUR                  5        H[  u  pVUS:X  d  [        U[        5      (       d  XdU'   M&  / nU H)  nUc  M  [	        U5      n	UR                  U	b  U	OU5        M+     XtU'   M]     XAU'   M     U$ )zNormalize raw node outputs for the jobs API.

Transforms string 3D filenames into file output dicts and removes
None items. All other items (non-3D strings, dicts, etc.) are
preserved as-is.
animated)itemsr   rE   listrG   append)
rH   
normalizednode_idnode_outputsnormalized_node
media_typerK   normalized_itemsrF   norms
             r   normalize_outputsrU   G   s     J!(,--".w!-!3!3!5JZ'z%/F/F.3
+!<,T2 ''0@dK	 
 +;J' "6 .7! "1" r   i   r%   c                 H    [        U 5      [        ::  a  SU 0$ U S[         SS.$ )zwCreate a text preview dict with optional truncation.

Returns:
    dict with 'content' and optionally 'truncated' flag
contentNT)rW   	truncated)lenTEXT_PREVIEW_MAX_LENGTHr$   s    r   _create_text_previewr[   f   s5     5z,,5!!112 r   
extra_datac                     U R                  S5      nU R                  S0 5      nUR                  S0 5      R                  S5      nX4$ )zeExtract create_time and workflow_id from extra_data.

Returns:
    tuple: (create_time, workflow_id)
create_timeextra_pnginfoworkflowidget)r\   r^   r_   workflow_ids       r   _extract_job_metadatare   t   sH     ../KNN?B7M##J377=K##r   rR   rF   c                 &  ^ U [         ;   a  gUR                  SS5      nU(       a-  UR                  S5      (       d  UR                  S5      (       a  gUR                  SS5      R                  5       m[	        U4S j[
         5       5      (       a  gg)	aU  
Check if an output item is previewable.
Matches frontend logic in ComfyUI_frontend/src/stores/queueStore.ts
Maintains backwards compatibility with existing logic.

Priority:
1. media_type is 'images', 'video', 'audio', or '3d'
2. format field starts with 'video/' or 'audio/'
3. filename has a 3D extension (.obj, .fbx, .gltf, .glb, .usdz)
TformatrB   zvideo/zaudio/r1   c              3   F   >#    U  H  nTR                  U5      v   M     g 7fr4   r5   )r7   r8   r1   s     r   r:   !is_previewable.<locals>.<genexpr>   s!     
@-?c8S!!-?r<   F)PREVIEWABLE_MEDIA_TYPESrc   
startswithr9   r=   r>   )rR   rF   fmtr1   s      @r   is_previewablerm      sx     ,, ((8R
 C
x((CNN8,D,D xx
B'--/H

@-?
@@@r   statusc           	      L    U u  p#pEn[        U5      u  pg[        UUUUSUS.5      $ )znConvert queue item tuple to unified job dict.

Expects item with sensitive data already removed (5 elements).
r   )ra   rn   priorityr^   outputs_countrd   )re   r   )rF   rn   rp   	prompt_id_r\   r^   rd   s           r   normalize_queue_itemrt      sA    
 -1)H4Z@K""  r   rr   history_iteminclude_outputsc                 d   US   nUu  pEpgn[        U5      u  pUR                  S0 5      n
U
(       a  U
R                  S5      OSnUR                  S0 5      n[        U5      u  pSnSnSnSnU
(       a  U
R                  S/ 5      nU H  n[        U[        [
        45      (       d  M   [        U5      S:  d  M1  US	   US
   nn[        U[        5      (       d  MR  US:X  a  UR                  S5      nMk  US;   d  Ms  UR                  S5      nUS:X  a  UnM  US:X  d  M  SnM     US:X  a  [        R                  nO>US:X  a(  U(       a  [        R                  O[        R                  nO[        R                  n[        U UUUUUUUUU	S.
5      nU(       a  [        U5      US'   U
US'   UUS.US'   U$ )zConvert history item dict to unified job dict.

History items have sensitive data already removed (prompt tuple has 5 elements).
promptrn   
status_strNrH   Fmessages   r      execution_start	timestamp)execution_successexecution_errorexecution_interruptedr   r   Tsuccesserror)
ra   rn   rp   r^   execution_start_timeexecution_end_timer   rq   preview_outputrd   execution_status)rx   r\   r`   )re   rc   get_outputs_summaryr   rL   tuplerY   rE   r   r   r   r   r   rU   )rr   ru   rv   prompt_tuplerp   rs   rx   r\   r^   rd   status_infory   rH   rq   r   r   r   r   was_interruptedrz   entry
event_name
event_datarn   jobs                            r   normalize_history_itemr      s   
  )L)5&HQ4Z@K""8R0K2=.4Jy"-G$7$@!MOO??:r2E%$//CJ!O).q58J
j$//!%66/9~~k/J,#'hh-7^^K-H*%)::.8O'+BB.2O  Y$$	w	(7$$Y=M=M$$
" 40*&(" C *73I"-$
J
 Jr   c                    SnSnSnU R                  5        GH'  u  pE[        U[        5      (       d  M  UR                  5        H  u  pgUS:X  d  [        U[        5      (       d  M"  U H  n[        U[        5      (       dh  [	        U5      n	U	cX  US:X  aP  US-  nUcH  [        U[
        5      (       a  U(       a  US   OSn
O[        U5      n
[        U
5      n0 UEUUS.EnUc  UnM~  U	nUS-  nUb  M  [        Xh5      (       d  M  0 UESU0EnS	U;  a  XlS	'   UR                  S
5      S:X  a  UnM  Ub  M  UnM     M     GM*     X=(       d    U4$ )z
Count outputs and find preview in a single pass.
Returns (outputs_count, preview_output).

Preview priority (matching frontend):
1. type="output" with previewable media
2. Any previewable media
r   NrJ   r(   r|   rB   )nodeIdrD   r   rD   r!   rA   )
rK   r   rE   rL   rG   r   r   r[   rm   rc   )rH   countr   fallback_previewrO   rP   rR   rK   rF   rN   
text_valuetext_previewenricheds                r   r   r      s|    EN!(,--!-!3!3!5JZ'z%/F/F!$--!6t!<J!)%/!QJE-5#-dE#:#:<@abJ14TJ/CJ/O,"&2,".51;,"
 $4#;7?$4 %D
!-!*33   ' H #$.0:-xx'83)1)1+3(Q  "6 "1d 4$444r   jobssort_by
sort_orderc                 :    US:H  nUS:X  a  S nOS n[        XUS9$ )z,Sort jobs list by specified field and order.descexecution_durationc                 r    U R                  SS5      nU R                  SS5      nU(       a  U(       a  X!-
  $ S$ )Nr   r   r   rb   )r   startends      r   get_sort_key#apply_sorting.<locals>.get_sort_key6  s5    GG2A6E''.2C"%%3;6Q6r   c                 &    U R                  SS5      $ )Nr^   r   rb   )r   s    r   r   r   ;  s    77=!,,r   )keyreverse)sorted)r   r   r   r   r   s        r   apply_sortingr   1  s.    V#G&&	7
	- $'::r   runningqueuedhistoryc                     X;   a  [        XU    SS9$ U H(  nUS   U :X  d  M  [        U[        R                  5      s  $    U H(  nUS   U :X  d  M  [        U[        R                  5      s  $    g)a5  
Get a single job by prompt_id from history or queue.

Args:
    prompt_id: The prompt ID to look up
    running: List of currently running queue items
    queued: List of pending queue items
    history: Dict of history items keyed by prompt_id

Returns:
    Job dict with full details, or None if not found
T)rv   r|   N)r   rt   r   r   r   )rr   r   r   r   rF   s        r   get_jobr   A  sv     %i1CUYZZ7i'i.C.CDD  7i'i.?.?@@  r   status_filterrd   limitoffsetc	                 (   / n	Uc  [         R                  n[         R                  U;   a2  U  H,  n
U	R                  [	        U
[         R                  5      5        M.     [         R
                  U;   a2  U H,  n
U	R                  [	        U
[         R
                  5      5        M.     [         R                  [         R                  [         R                  1nU[        U5      -  nU(       aL  UR                  5        H8  u  p[        X5      nUR                  S5      U;   d  M'  U	R                  U5        M:     U(       a)  U	 Vs/ s H  nUR                  S5      U:X  d  M  UPM     n	n[        XU5      n	[        U	5      nUS:  a  XS n	Ub  U	SU n	U	U4$ s  snf )a-  
Get all jobs (running, pending, completed) with filtering and sorting.

Args:
    running: List of currently running queue items
    queued: List of pending queue items
    history: Dict of history items keyed by prompt_id
    status_filter: List of statuses to include (from JobStatus.ALL)
    workflow_id: Filter by workflow ID
    sort_by: Field to sort by ('created_at', 'execution_duration')
    sort_order: 'asc' or 'desc'
    limit: Maximum number of items to return
    offset: Number of items to skip

Returns:
    tuple: (jobs_list, total_count)
Nrn   rd   r   )r   r   r   rM   rt   r   r   r   r   setrK   r   rc   r   rY   )r   r   r   r   rd   r   r   r   r   r   rF   history_statusesrequested_history_statusesrr   ru   r   jtotal_counts                     r   get_all_jobsr   \  s]   8 D!-DKK,T93H3HIJ  M)DKK,T93D3DEF  "++Y-=-=y?R?RS!1C4F!F!'.}}#I(ACwwx $>>C  (7
 G4a155#7;#F4G
3Dd)KzG}FU|+ Hs   =FF)F)NN
created_atr   Nr   )r   r"   typingr   comfy_api.internalr   r   r   r&   	frozensetrj   r>   boolr?   rG   rE   rU   rZ   r[   r   intre   rm   rt   r   r   rL   r   r   r   r   r   r   <module>r      s  
   )? ?c ( $$NO  IJ Bs Bt B
"t  8    	$d 	$uXc]HSM5Q/R 	$s $ 4 8u c d $>c > >PT >ae >B?5 ?5%Xd^0C*D ?5D;T
 ;S ;c ;d4j ; s T 4 $ 8TX> > *.!%=== = DI&	=
 #= = = C== = 4:s?=r   