
    +jt7                        U d Z ddlmZ ddlmZ ddlmZ ddlmZm	Z	m
Z
 ddlmZmZmZmZmZmZmZmZmZmZmZ ddlmZmZmZmZmZmZ dZed	         Z ed
           G d d                      Z  e ddddd           e ddddd           e ddddd           e dddddd           e dddd d           e d!ddd d           e d"dd#d$           e d%dd&d'           e d(dd)d*           e d+dd,d-           e d.dd/d0d1d           e d2dd3d4d1d           e d5d6d7dd8           e d9ddd:d8           e d;dd<d=           e d>dd?d@           e dAddBdCd1           e dDdd?d            e dEdFdGdH           e dIdJdKdL           e dMdJdKdL          gZ!e"e          e#dN<   dO e!D             Z$e%e&e f         e#dP<   g dQZ'g dRZ(dSe"fdTZ)dSe"fdUZ*dVedSe"fdWZ+dXe dSe"fdYZ,dXe dSe"fdZZ-dSe"ej.        j/                 fd[Z0d\edSe1d]z  fd^Z2dSe&fd_Z3d`e4ej5                 dXe dae"e
j6                 dSe"e         fdbZ7d`e4ej5                 dce"e
j8                 dSe"e         fddZ9dee&dfe"e         dSefdgZ:dhe&dee&dfe"e         dSe"e         fdiZ;dje&dhe&dee&dfe"e         dke<dle&d]z  dme&d]z  dSefdnZ=d\edSe&fdoZ> G dp dqej5                  Z? G dr dse	          Z@dSe@fdtZAd]S )uz.API Nodes for OpenRouter LLM chat completions.    )	dataclass)Literal)override)IOComfyExtensionInput)OpenRouterChatRequestOpenRouterChatResponseOpenRouterContentBlockOpenRouterImageContentOpenRouterImageUrlOpenRouterMessageOpenRouterReasoningConfigOpenRouterTextContentOpenRouterVideoContentOpenRouterVideoUrlOpenRouterWebSearchOptions)ApiEndpointget_number_of_imagessync_opupload_images_to_comfyapiupload_video_to_comfyapivalidate_stringz)/proxy/openrouter/api/v1/chat/completions)standard	reasoningfrontier_reasoning
perplexityperplexity_reasoningT)frozenc                   T    e Zd ZU eed<   eed<   eed<   eed<   dZeed<   dZ	eed<   dS )	
_ModelSpecslugprofileprice_in	price_outr   
max_images
max_videosN)
__name__
__module____qualname__str__annotations__Profilefloatr&   intr'        @/home/wildlama/comfy/ComfyUI/comfy_api_nodes/nodes_openrouter.pyr!   r!   %   s\         
IIIOOOJJr1   r!   zanthropic/claude-opus-4.7r   gh㈵>g-C6>   )r&   zopenai/gpt-5.5-progiUMu>g9̗'?zopenai/gpt-5.5zgoogle/gemini-3.5-flashr   gTqs*>g?̔>   )r&   r'   zx-ai/grok-4.20gh㈵>gh㈵>zx-ai/grok-4.3zdeepseek/deepseek-v4-progy;>1>gy;>1>zdeepseek/deepseek-v4-flashgC֔~>gC֔>zdeepseek/deepseek-v3.2g:;S>gXɔ]>zqwen/qwen3.6-max-previewgqr>g*#&,>zqwen/qwen3.6-plusgkNuϕ>gJ֗[>
   zqwen/qwen3.6-flashgTqs*>g?߲̔>zmistralai/mistral-large-2512r   gƠ>   zmistralai/mistral-medium-3-5giUMu>zz-ai/glm-4.6gn<Wۜ>gy;>1>z
z-ai/glm-5gv!>g+i)+>zmoonshotai/kimi-k2.6g_~>g+/\ʷF>zmoonshotai/kimi-k2-thinkingzperplexity/sonar-pror   gTqs*>giUMu>zperplexity/sonar-reasoning-pror   g>g>zperplexity/sonar-deep-researchMODELSc                     i | ]
}|j         |S r0   )r"   ).0ms     r2   
<dictcomp>r;   G   s    )D)D)D!&!)D)D)Dr1   _MODELS_BY_SLUG)offlowmediumhigh)r>   r?   r@   returnc                  V    t           j                            dt          ddd          gS )Nreasoning_effortr=   z4Reasoning effort. 'off' disables reasoning entirely.Toptionsdefaulttooltipadvanced)r   Combor   _REASONING_EFFORTSr0   r1   r2   _reasoning_extra_inputsrK   L   s3    
&J 	 	
 	
 r1   c                  V    t           j                            dt          ddd          gS )Nsearch_context_sizer?   zSHow much web search context to retrieve. Larger = more grounded but slower/pricier.TrD   )r   rI   r   _SEARCH_CONTEXT_SIZESr0   r1   r2   _perplexity_extra_inputsrO   X   s3    
!)i 	 	
 	
 r1   r#   c                     | dk    rg S | dv rt                      S | dk    rt                      S | dk    rt                      t                      z   S t          d|            )Nr   )r   r   r   r   zUnknown profile: )rK   rO   
ValueError)r#   s    r2   _profile_inputsrR   d   s|    *	555&(((,')))(((')),C,E,EEE
222
3
33r1   specc                    g }| j         dk    r|                    t          j                            dt          j                            t          j                            d          d t          d| j         dz             D             d          d| j          d	                     | j        dk    r|                    t          j                            d
t          j                            t          j	                            d          d t          d| j        dz             D             d          d| j         d	                     |S )Nr   imagesimagec                     g | ]}d | S )image_r0   r9   is     r2   
<listcomp>z!_media_inputs.<locals>.<listcomp>x       OOOA<A<<OOOr1      )namesminu&   Optional reference image(s) — up to z. Sent as URLs.)templaterG   videosvideoc                     g | ]}d | S )video_r0   rY   s     r2   r[   z!_media_inputs.<locals>.<listcomp>   r\   r1   u&   Optional reference video(s) — up to )
r&   appendr   Autogrowr   TemplateNamesImageranger'   Video)rS   extrass     r2   _media_inputsrl   p   sV   FK22HNN7++OOq$/A:M1N1NOOO 3  
 baaa   
	
 
	
 
	
 K22HNN7++OOq$/A:M1N1NOOO 3  
 baaa   
	
 
	
 
	
 Mr1   c                 J    t          | j                  t          |           z   S N)rR   r#   rl   )rS   s    r2   _inputs_for_modelro      s    4<((=+>+>>>r1   c                  $    d t           D             S )Nc                 r    g | ]4}t           j                            |j        t	          |                    5S r0   )r   DynamicComboOptionr"   ro   )r9   rS   s     r2   r[   z(_build_model_options.<locals>.<listcomp>   s5    ZZZ4BO""49.?.E.EFFZZZr1   )r7   r0   r1   r2   _build_model_optionsrt      s    ZZSYZZZZr1   responseNc                 ^    | j         r%| j         j        t          | j         j                  S d S rn   )usagecostr.   )ru   s    r2   _calculate_pricery      s.    ~ *(.-9X^()))4r1   c            
          g } t           D ]<}|j        dz  }|j        dz  }|                     d|j         d|dd|dd           =d                    |           }d| d	S )
Ni  z  "z": [z.8gz, ]z,
z(
  $rates := {
z
  };
  $r := $lookup($rates, widgets.model);
  $r ? {
    "type": "list_usd",
    "usd": $r,
    "format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" }
  } : {"type": "text", "text": "Token-based"}
))r7   r$   r%   re   r"   join)rates_pairsrS   prompt_per_1kcompletion_per_1krates_blocks        r2   _price_badge_jsonatar      s    K _ _, NT1]]]]]]EV]]]]^^^^**[))K
	
	 
	 
	r1   clsrU   c                 ^   K   t          | ||j        ddd           d {V }d |D             S )Ni  @ z	image/pngzUploading reference images)r&   total_pixels	mime_type
wait_labelc                 J    g | ] }t          t          |                     !S )url)	image_url)r   r   )r9   r   s     r2   r[   z'_build_image_blocks.<locals>.<listcomp>   s0    ZZZc"-?C-H-H-HIIIZZZr1   )r   r&   )r   rS   rU   urlss       r2   _build_image_blocksr      sf       +? /        D [ZUYZZZZr1   ra   c                   K   g }t          |          }t          |          D ]d\  }}d}|dk    r| d|dz    d| d}t          | ||           d {V }|                    t	          t          |                               e|S )	NzUploading reference videor]   z (/))r   r   )	video_url)len	enumerater   re   r   r   )r   ra   blockstotalidxrb   labelr   s           r2   _build_video_blocksr      s      +-FKKE'' U U
U+19922a22%222E,S%EJJJJJJJJJ,7Ic7R7R7RSSSTTTTMr1   promptmedia_blocksc                     |st          d|           S t          |          }|                    t          |                      t          d|          S )Nuserrolecontent)text)r   listre   r   )r   r   r   s      r2   _user_messager      s[     > ff====+/+=+=F
MM'V444555&&9999r1   system_promptc                     g }| r$|                     t          d|                      |                     t          ||                     |S )Nsystemr   )re   r   r   )r   r   r   messagess       r2   _build_messagesr      sS     )+H Q)xOOOPPPOOM&,77888Or1   r"   seedrC   rM   c                    d }|r|dk    rt          |d          }d }|rt          |          }t          | t          |||          |dk    r|nd ||          S )Nr=   T)effortexclude)rM   r   )modelr   r   r   web_search_options)r   r   r	   r   )	r"   r   r   r   r   rC   rM   reasoning_cfgweb_search_cfgs	            r2   _build_requestr      s     7;M Y,5519ISWXXX8<N ]3H[\\\  EEAXXTT4)   r1   c                 F   | j         r;| j         j        | j         j        nd}t          d| d| j         j        pd           | j        st          d          | j        d         j        }|st          d          |j        rt          d|j                   |j        pd	S )
NunknownzOpenRouter error (z): z
no messagez,Empty response from OpenRouter (no choices).r   z,Empty response from OpenRouter (no message).zModel refused to respond:  )errorcoderQ   messagechoicesrefusalr   )ru   r   r   s      r2   _extract_textr      s    ~ a&.n&9&Ex~""9_d__x~7M7]Q]__``` IGHHHq!)G IGHHH IGgoGGHHH? b r1   c                   ^    e Zd Zed             Ze	 d
dededededej	        f
d            Z
d	S )OpenRouterLLMNodec                 n   t          j        dddddt           j                            dddd	
          t           j                            dt                      d          t           j                            dddddd          t           j                            dddddd          gt           j                                        gt           j        j	        t           j        j
        t           j        j        gdt          j        t          j        dg          t                                
  
        S )Nr   zOpenRouter LLMzpartner/text/OpenRouterzText GenerationzGenerate text responses through OpenRouter. Routes to a curated set of popular models from xAI, DeepSeek, Qwen, Mistral, Z.AI (GLM), Moonshot (Kimi), and Perplexity Sonar.r   Tr   zText input to the model.)	multilinerF   rG   r   z3The OpenRouter model used to generate the response.)rE   rG   r   r   izKSeed for sampling. Set to 0 to omit. Most models treat this as a hint only.)rF   r_   maxcontrol_after_generaterG   r   z<Foundational instructions that dictate the model's behavior.)r   rF   optionalrH   rG   )widgets)
depends_onexpr)
node_iddisplay_namecategoryessentials_categorydescriptioninputsoutputshiddenis_api_nodeprice_badge)r   SchemaStringr   rr   rt   IntOutputHiddenauth_token_comfy_orgapi_key_comfy_org	unique_id
PriceBadgePriceBadgeDependsr   )r   s    r2   define_schemazOpenRouterLLMNode.define_schema  sC   y'). 1$
 	"6	     %%022Q &  
 "+/i    	#"!!Z    ): Y%%''(	.	+	#
 /	BBB)++  ]2
 2
 2
 2	
r1   r   r   r   r   r   rA   c           	      .  K   t          |dd           |d         }t                              |          }|t          d|           |                    d          }|                    d          }d |                    d	          pi                                 D             }	|	r=t          d
 |	D                       |j        k    rt          d|j         d| d          d |                    d          pi                                 D             }
|
r3t          |
          |j        k    rt          d|j         d| d          g }|	r*|	                    t          | ||	           d {V            |
r)|	                    t          | |
           d {V            t          |||||||          }t          | t          t          d          t           |t"                     d {V }t%          j        t)          |                    S )NTr]   )strip_whitespace
min_lengthr   zUnknown OpenRouter model: rC   rM   c                     g | ]}||S rn   r0   r9   ts     r2   r[   z-OpenRouterLLMNode.execute.<locals>.<listcomp>N  s    +m+m+m!_`_lA_l_l_lr1   rU   c              3   4   K   | ]}t          |          V  d S rn   )r   r   s     r2   	<genexpr>z,OpenRouterLLMNode.execute.<locals>.<genexpr>O  s+       P PQ!5a!8!8 P P P P P Pr1   zUp to z images are supported for .c                     g | ]}||S rn   r0   )r9   vs     r2   r[   z-OpenRouterLLMNode.execute.<locals>.<listcomp>Q  s    *l*l*l^_^k1^k^k^kr1   ra   z videos are supported for )r   rC   rM   POST)pathmethod)response_modeldataprice_extractor)r   r<   getrQ   valuessumr&   r   r'   extendr   r   r   r   r   OPENROUTER_CHAT_ENDPOINTr
   ry   r   
NodeOutputr   )r   r   r   r   r   r"   rS   rC   rM   image_tensorsvideo_inputsr   requestru   s                 r2   executezOpenRouterLLMNode.execute=  s      	!DDDD'N""4((<@$@@AAA',yy1C'D'D*/))4I*J*J+m+m		(8K8K8Qr7Y7Y7[7[+m+m+m 	ZS P P- P P PPPSWSbbbXdoXXQUXXXYYY*l*luyy7J7J7Pb6X6X6Z6Z*l*l*l 	ZC--??XdoXXQUXXXYYY57 	U&9#t]&S&S S S S S S STTT 	N&9#|&L&L L L L L L LMMM - 3
 
 
 !5fEEE1,
 
 
 
 
 
 
 
 
 }]844555r1   N)r   )r(   r)   r*   classmethodr   r+   dictr/   r   r   r   r0   r1   r2   r   r     s        3
 3
 [3
j   .6 .6.6 .6 	.6
 .6 
.6 .6 .6 [.6 .6 .6r1   r   c                   L    e Zd Zedeeej                          fd            ZdS )OpenRouterExtensionrA   c                    K   t           gS rn   )r   )selfs    r2   get_node_listz!OpenRouterExtension.get_node_listp  s      !""r1   N)	r(   r)   r*   r   r   typer   	ComfyNoder   r0   r1   r2   r   r   o  sE        #T$r|*<%= # # # X# # #r1   r   c                  "   K   t                      S rn   )r   r0   r1   r2   comfy_entrypointr   u  s         r1   )B__doc__dataclassesr   typingr   typing_extensionsr   comfy_api.latestr   r   r   comfy_api_nodes.apis.openrouterr	   r
   r   r   r   r   r   r   r   r   r   comfy_api_nodes.utilr   r   r   r   r   r   r   r-   r!   r7   r   r,   r<   r   r+   rJ   rN   rK   rO   rR   rl   ro   rr   rs   rt   r.   ry   r   r   r   rh   r   rj   r   r   r   r/   r   r   r   r   r   r0   r1   r2   <module>r     s   4 4 4 ! ! ! ! ! !       & & & & & & 6 6 6 6 6 6 6 6 6 6                                         G  e
f $        J*,@(HacdddJ#%97GXZ[[[J!5xUWXXXJ(+y(WYfghhhJj)PRSSSJZrRRRJ);ZPPJ+[+{SSJ'k;OOJ);
JOOJ"KjUWdefffJ#[,XZghiiiJ-z9i\]^^^J-{Iy]^___J~{J
CCJ|[)Z@@J%{J
WYZZZJ,k9iPPJ%|XxHHJ/1GS[\\J/1GS[\\+Z   0 *E)DV)D)D)Dc:o& D D D555 111 	 	 	 	 		$ 	 	 	 		4W 	4 	4 	4 	4 	4
 t    :?J ?4 ? ? ? ?[d2?#9: [ [ [ [5 %$,    c    ,[	bl	[#-[7;EK7H[	
 ![ [ [ [	4#5 	tEK?P 	UYZpUq 	 	 	 	:# :T2H-I :N_ : : : : #378N3O	
   
  -.	  Dj t    4!2 !s ! ! ! !g6 g6 g6 g6 g6 g6 g6 g6T# # # # #. # # #! 3 ! ! ! ! ! !r1   