
    )j,                    v   d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	 ej
                            d e ee                                          j                             ddlmZmZmZmZmZmZmZmZ h dZddZddddZd dZd dZd!dZd"d#dZe dk    r ej!         e                       dS dS )$u  
extract_schema.py — Analyze a ComfyUI API-format workflow and extract
controllable parameters.

Improvements over v1:
  - Catalogs live in `_common.py`, shared with `check_deps.py`
  - Coverage expanded for Flux / SD3 / Wan / Hunyuan / LTX / IPAdapter / rgthree
  - Symmetric duplicate-name resolution: ALL duplicates get a node-id suffix
    (instead of "first wins, second renamed"), so callers see consistent names
  - Negative prompt detected by tracing `KSampler.negative` connections back to
    the source CLIPTextEncode (more reliable than meta-title heuristic)
  - Embedding references in prompt text are extracted as model dependencies
  - Detects Primitive nodes that drive other nodes' inputs (and surfaces them
    as the user-facing parameter)
  - Reroutes are followed when tracing connections

Usage:
    python3 extract_schema.py workflow_api.json
    python3 extract_schema.py workflow_api.json --output schema.json

Stdlib-only. Python 3.10+.
    )annotationsN)Path)Any)OUTPUT_NODESPARAM_PATTERNSPROMPT_FIELDSis_linkiter_embedding_refsiter_model_deps
iter_nodesunwrap_workflow>   KSampler	CFGGuiderBasicGuiderDualCFGGuiderSamplerCustomKSamplerAdvancedSamplerCustomAdvancedvaluer   returnstrc                   t          | t                    rdS t          | t                    rdS t          | t                    rdS t          | t                    rdS t          | t
                    rdS t          | t                    rdS dS )Nboolintfloatstringlinkobjectunknown)
isinstancer   r   r   r   listdict)r   s    H/home/wildlama/.hermes/skills/creative/comfyui/scripts/extract_schema.py
infer_typer$   0   s    % v% u% w% x% v% x9       )max_hopsworkflowr"   r   r!   r'   r   
str | Nonec                  t          |          sdS |d         }t                      }t          |          D ]}|||v r|c S |                    |           |                     |          }t          |t                    s dS |                    dd          }|dv rS|                    di           pi }t          d |                                D             d          }	|	|c S |	d         }|c S |S )a  Follow a [node_id, slot] link, hopping through Reroute / Primitive nodes
    if needed, to find the *upstream* node id that holds the actual value/input.

    Bounded by both `max_hops` AND a visited-set to prevent infinite loops on
    pathological graphs.
    Nr   
class_type >   easy showAnythingNoteReroutePrimitiveNodeinputsc              3  8   K   | ]}t          |          |V  d S N)r	   ).0vs     r#   	<genexpr>z trace_to_node.<locals>.<genexpr>W   s-      GGAGAJJGaGGGGGGr%   )	r	   setrangeaddgetr    r"   nextvalues)
r(   r   r'   nidvisited_nodeclsr1   	next_links
             r#   trace_to_noderC   @   s    4== t1gCG8__  ;#..JJJC||C  $%% 	44hh|R((KKKXXh++1rFGGGGGNNI 


A,C


Jr%   c                   t          |           D ]\  }}|d         t          vr|                    di           pi }|                    d          }t          |          sRt	          | |          }|rat          |                     |          t                    r9| |                             dd          }|                    d          s|dv r|c S dS )zDTrace `negative` input of a sampler back to the source text encoder.r+   r1   negativer,   CLIPTextEncode>   BNK_CLIPTextEncodeAdvancedsmZ CLIPTextEncodeNr   SAMPLER_NODE_FAMILYr:   r	   rC   r    r"   
startswith)r(   r=   r@   r1   negsrcrA   s          r#   find_negative_prompt_noderN   `   s    ))  	T%888(B''-2jj$$s|| 	Hc** 	:hll3//66 	3-##L"55C~~.// 3:n3n3n


4r%   c                   t          |           D ]\  }}|d         t          vr|                    di           pi }|                    d          }t          |          sRt	          | |          }|rat          |                     |          t                    r9| |                             dd          }|                    d          s|dv r|c S d S )Nr+   r1   positiver,   rF   >   rG   rH   rI   )r(   r=   r@   r1   posrM   rA   s          r#   find_positive_prompt_noderR   q   s    ))  	T%888(B''-2jj$$s|| 	Hc** 	:hll3//66 	3-##L"55C~~.// 3:n3n3n


4r%   c                H     g }t                     }t                     }g }t                     D ]\  }}|d         }|                    di           pi }|t          v r|                    |           t          D ]\  }	}
}||	k    r|
|vr||
         }t          |          }|dk    r0|}|dk    rr||k    r	||k    rd}nc||k    rd}nZ|                    d          pi                     dd                                           t           fd	d
D                       rd}|                    |||
|||d           ǐi }|D ]1}|
                    |d         g                               |           2i }|                                D ]\  }}t          |          dk    r2|d         }|d         |d         |d         |d         |d         d||<   J|                    d            |D ]9}| d|d          }|d         |d         |d         |d         |d         |d||<   :t          t                               }g }t!                      }t#                     D ]\  }}||f}||v r|                    |                                |i           }|                    di           pi }d}d}|                                D ]5\  }}t'          |t(                    r|t*          v r||v r|}|dd         } n6|                    ||||dd           t          |          t          |          t          |          t          |          d|v d|v pt          d |D                       t           fd|D                       d}|||||d S )!a  Extract controllable parameters from a workflow.

    Returns:
        {
          "parameters": { friendly_name: {node_id, field, type, value, ...} },
          "output_nodes": [node_id, ...],
          "model_dependencies": [{node_id, class_type, field, value, folder}],
          "embedding_dependencies": [{node_id, embedding_name, found_in_field, value_excerpt}],
          "summary": {...}
        }
    r+   r1   r   promptnegative_prompt_metatitler,   c              3      K   | ]}|v V  	d S r3    )r4   t_
meta_titles     r#   r6   z!extract_schema.<locals>.<genexpr>   s(      ]]2+]]]]]]r%   )rE   rL   z-promptanti)	name_hintnode_idfieldtyper   r+   r]      r   r^   r_   r`   r   )r^   r_   r`   r   r+   c                b    t          | d                                       d          | d         fS )Nr^   r&   r_   )r   zfill)xs    r#   <lambda>z extract_schema.<locals>.<lambda>   s)    AiL(9(9(?(?(B(BAgJ'O r%   )keyr?   )r^   r_   r`   r   r+   alias_ofNx   
embeddings)r^   embedding_namer_   value_excerptfolderseedc              3  @   K   | ]}|                     d           V  dS )seed_N)rK   )r4   ps     r#   r6   z!extract_schema.<locals>.<genexpr>  s.      /Z/Z!W0E0E/Z/Z/Z/Z/Z/Zr%   c              3  p   K   | ]0}                     |i                                d d          dv V  1dS )r+   r,   >   	SaveVideoSaveAnimatedPNGSaveAnimatedWEBPVHS_VideoCombineN)r:   )r4   nr(   s     r#   r6   z!extract_schema.<locals>.<genexpr>  sa       !
 !
  LLB##L"55 : !
 !
 !
 !
 !
 !
r%   )parameter_countoutput_node_countmodel_dep_countembedding_dep_counthas_negative_prompthas_seedis_video_workflow)
parametersoutput_nodesmodel_dependenciesembedding_dependenciessummary)rR   rN   r   r:   r   appendr   r$   lowerany
setdefaultitemslensortr!   r   r7   r
   r9   r    r   r   )!r(   r   pos_nodeneg_node
raw_paramsr^   r@   rA   r1   p_classp_fieldfriendlyr   tactual_nameby_namerr~   nameentries	full_name
model_depsembedding_depsseen_embr=   emb_namerf   found_fieldexcerptfnamefvalr   r[   s!   `                               @r#   extract_schemar      s    !L )22H(22H
  J#H-- ' '< (B''-2,((( +9 	 	&GWhg~~f$$7OE5!!AF{{"K 8##h&&8x+?+?"3KK(("*KK #'((7"3"3"9r!>!>w!K!K!Q!Q!S!SJ]]]]6\]]]]] 8&7(" !     1	H &(G 9 91[>2..55a8888"$J   gw<<1
AY<!G*&	AgJo   Jt LLOOLPPP  #44a	l44	 |ajfI'
"#L/ $	) )
9%% oh//00J "$N%(UUH,X66  XHo(??S||C$$(B''-2!<<>> 	 	KE4$$$ -)?)?HPTDTDT#tt*& $"
 
 	 	 	 	 z?? ..z??">220J>j(ZC/Z/Zz/Z/Z/Z,Z,Z  !
 !
 !
 !
 $!
 !
 !
 
 
 G !$("0  r%   argvlist[str] | Nonec                x   t          j        d          }|                    dd           |                    ddd           |                    d	d
d           |                    |           }t	          |j                                                  }|                                s!t          d| dt          j
                   dS 	 |                                5 }t          j        |          }d d d            n# 1 swxY w Y   t          |          }nl# t          $ r)}t          d| t          j
                   Y d }~dS d }~wt          j        $ r)}t          d| t          j
                   Y d }~dS d }~ww xY wt#          |          }|j        rt          j        |d         d          }	nt          j        |dt(                    }	|j        rKt	          |j                                      |	           t          d|j         t          j
                   nt          |	           dS )Nz7Extract controllable parameters from a ComfyUI workflow)descriptionr(   zPath to workflow API JSON file)helpz--outputz-ozOutput file (default: stdout)z--summary-only
store_truezOnly print the summary block)actionr   zError: z
 not found)filera   u   Error: invalid JSON — r      )indent)r   defaultzSchema written to r   )argparseArgumentParseradd_argument
parse_argsr   r(   
expanduserexistsprintsysstderropenjsonloadr   
ValueErrorJSONDecodeErrorr   summary_onlydumpsr   output
write_text)
r   rp   argswf_pathfpayloadr(   eschemaouts
             r#   mainr     s   ,efffANN:$DNEEENN:t*INJJJNN#L6  8 8 8<<D4=!!,,..G>> ++++#*====q	\\^^ 	#qillG	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	#"7++   mmm#*----qqqqq   ,,,3:>>>>qqqqq H%%F 8j	*1555j3777{ T[$$S)))04;00szBBBBBc


1sH   D "D7D DD 
DD 
F(EFFF__main__)r   r   r   r   )r(   r"   r   r!   r'   r   r   r)   )r(   r"   r   r)   )r(   r"   r   r"   r3   )r   r   r   r   )"__doc__
__future__r   r   r   r   pathlibr   typingr   pathinsertr   __file__resolveparent_commonr   r   r   r	   r
   r   r   r   rJ   r$   rC   rN   rR   r   r   __name__exitrY   r%   r#   <module>r      s   . # " " " " "   



             33ttH~~--//677 8 8 8                           BC      @   "    N N N Nb% % % % %P zCHTTVV r%   