)2hi8dZddlmZddlZddlZddlZddlZddlmZm Z m Z m Z m Z m Z mZmZddlmZddlZddlZddlmZmZddlmZmZddlZddlmZdd lmZdd lm Z d d l!m"Z"e d eZ#e deZ$e deZ%dZ&Gdde'Z(Gdde(Z)Gdde(Z*GddeZ+GddeZ,GddeZ-Gdde.eZ/Gd d!Z0Gd"d#e e#e$fZ1Gd$d%e e#e$fZ2Gd&d'e.eZ3Gd(d)e e#e$fZ4y)*aw API Client Framework for api.comfy.org. This module provides a flexible framework for making API requests from ComfyUI nodes. It supports both synchronous and asynchronous API operations with proper type validation. Key Components: -------------- 1. ApiClient - Handles HTTP requests with authentication and error handling 2. ApiEndpoint - Defines a single HTTP endpoint with its request/response models 3. ApiOperation - Executes a single synchronous API operation Usage Examples: -------------- # Example 1: Synchronous API Operation # ------------------------------------ # For a simple API call that returns the result immediately: # 1. Create the API client api_client = ApiClient( base_url="https://api.example.com", auth_token="your_auth_token_here", comfy_api_key="your_comfy_api_key_here", timeout=30.0, verify_ssl=True ) # 2. Define the endpoint user_info_endpoint = ApiEndpoint( path="/v1/users/me", method=HttpMethod.GET, request_model=EmptyRequest, # No request body needed response_model=UserProfile, # Pydantic model for the response query_params=None ) # 3. Create the request object request = EmptyRequest() # 4. Create and execute the operation operation = ApiOperation( endpoint=user_info_endpoint, request=request ) user_profile = operation.execute(client=api_client) # Returns immediately with the result # Example 2: Asynchronous API Operation with Polling # ------------------------------------------------- # For an API that starts a task and requires polling for completion: # 1. Define the endpoints (initial request and polling) generate_image_endpoint = ApiEndpoint( path="/v1/images/generate", method=HttpMethod.POST, request_model=ImageGenerationRequest, response_model=TaskCreatedResponse, query_params=None ) check_task_endpoint = ApiEndpoint( path="/v1/tasks/{task_id}", method=HttpMethod.GET, request_model=EmptyRequest, response_model=ImageGenerationResult, query_params=None ) # 2. Create the request object request = ImageGenerationRequest( prompt="a beautiful sunset over mountains", width=1024, height=1024, num_images=1 ) # 3. Create and execute the polling operation operation = PollingOperation( initial_endpoint=generate_image_endpoint, initial_request=request, poll_endpoint=check_task_endpoint, task_id_field="task_id", status_field="status", completed_statuses=["completed"], failed_statuses=["failed", "error"] ) # This will make the initial request and then poll until completion result = operation.execute(client=api_client) # Returns the final ImageGenerationResult when done ) annotationsN)DictTypeOptionalAnyTypeVarGenericCallableTuple)Enum)urljoinurlparse) BaseModelField) PromptServer)args)utils)request_loggerT)boundRPdceZdZdZy) NetworkErrorzFBase exception for network-related errors with diagnostic information.N__name__ __module__ __qualname____doc__BD:\ComfyUI_windows_portable\ComfyUI\comfy_api_nodes\apis\client.pyrrvsPr#rceZdZdZy)LocalNetworkErrorzEException raised when local network connectivity issues are detected.Nrr"r#r$r&r&{sOr#r&ceZdZdZy)ApiServerErrorzLException raised when the API server is unreachable but internet is working.Nrr"r#r$r(r(sVr#r(ceZdZdZy) EmptyRequestzcBase class for empty request bodies. For GET requests, fields will be sent as query parameters.Nrr"r#r$r*r*s B r#r*cJeZdZUeddZded<eddZded <y) UploadRequest.zFilename to upload descriptionstr file_nameNzJMime type of the file. For example: image/png, image/jpeg, video/mp4, etc. str | None content_type)rrr rr0__annotations__r2r"r#r$r,r,s+3,@AIsA$ ` L*r#r,cJeZdZUeddZded<eddZded<y) UploadResponse.zURL to GET uploaded filer-r/ download_urlzURL to PUT file to upload upload_urlN)rrr rr6r3r7r"r#r$r5r5s&c/IJL#JC-HIJIr#r5c eZdZdZdZdZdZdZy) HttpMethodGETPOSTPUTDELETEPATCHN)rrr r:r;r<r=r>r"r#r$r9r9s C D C F Er#r9c$eZdZdZ d ddZddZ d ddZ d ddZ d ddZddZ dd Z d dd Z d Z e d dd Zy) ApiClientzi Client for making HTTP requests to an API with authentication, error handling, and retry logic. Nc ||_||_||_||_||_||_||_||_| xsd|_y)N)iiii) base_url auth_token comfy_api_keytimeout verify_ssl max_retries retry_delayretry_backoff_factorretry_status_codes) selfrDrErFrGrHrIrJrKrLs r$__init__zApiClient.__init__sP! $* $&&$8!#5"V8Vr#c|jdjdddtjjddS)z,Generates a unique operation ID for logging./_N)stripreplaceuuiduuid4hex)rMpaths r$_generate_operation_idz ApiClient._generate_operation_ids=**S/))#s34Adjjl6F6Fr6J5KLLr#c ||dS)N)jsonheadersr"rMdatar\s r$_create_json_payload_argsz#ApiClient._create_json_payload_argss   r#c4|rd|vr|d=|r||}|||dS)N Content-Type)r^filesr\r")rMr^rbr\multipart_parsers r$_create_form_data_argsz ApiClient._create_form_data_argss8 ~0' #D)D  r#c"|xsi}d|d<||dS)N!application/x-www-form-urlencodedrar^r\r"r]s r$!_create_urlencoded_form_data_argsz+ApiClient._create_urlencoded_form_data_argss) -R"E  r#cddd}|jrd|j|d<|S|jr|j|d<|S)zCGet headers for API requests, including authentication if availableapplication/json)raAcceptzBearer Authorizationz X-API-KEY)rErF)rMr\s r$ get_headerszApiClient.get_headerssQ#5AST ??)00A'BGO $  #'#5#5GK r#cddddd} tjdd|j}|jdkrd|d< t|}|jd |j}tj|d d|j}|jdkrd|d <|Sd|d <d|d < |S#tjt j f$rd|d<d|d <|cYSwxYw#tj$rd|d <d|d <Y|SwxYw)z Check connectivity to determine if network issues are local or server-related. Args: target_url: URL to check connectivity to Returns: Dictionary with connectivity status details F)internet_accessibleapi_accessibleis_local_issue is_api_issuehttps://www.google.com@rGverifyrCTrorqz://z/healthrprr) requestsgetrH status_codeRequestExceptionsocketerrorrschemenetloc)rM target_urlresultscheck_response parsed_urlapi_base api_responses r$_check_connectivityzApiClient._check_connectivitysK$)##!   %\\*B1404AN))C/15-. +!*-J$++,C 0A0A/BCH$<<8*G(|| ||j"|j$d | }|j&|j(vr| |j*kr|j,|j.| zz}tj0d |j&d |dd| dzd|j*d t3j4||j!||||||||| dz S|j7|j8} |j;}tj| || |j&t?|j@||j8r|j;SiS#t:j<$rYkwxYw#tjB$rV}dtE|}tj| || || |j*k\rK|jG|j}|dr tId||drtKd|jd|| |j*kr|j,|j.| zz}tj0dtE|d |dd| dzd|j*d t3j4||j!||||||||| dz cYd}~Sd|j*d}tj| || |tM||d}~wtjN$r}d tE|}tj| || || |j*kr|j,|j.| zz}tj0d!|dd| dzd|j*dt3j4||j!||||||||| dz cYd}~Sd"|j"d#|j*d$}tj| || |tM||d}~wtjP$r}tS|d%r|jTj&nd}d&tE|}d}tS|d%rW|jTK|jTj8} |jTj;}n#t:j<$rYnwxYw|} tS|d%r|jT|jTj8r~|jTj;}d'|vr)d(|d'vr"d)|d'd(}d*|d'vrG|d+|d'd*dz }n7tW|t>rd)t;jX|}nd)tE|}n#t:j<$rqtS|d%rb|jTV|jTj8r@|jTj8j[d,-}t]|d.krd/|}nd0|d}YnwxYwtj| || |tS|d%r+|jTt?|jTj@nd||1tjd2|d3|dtS|d%rN|jTB|jTj8r,tjd4|jTj8||j(vr| |j*kr|j,|j.| zz}tj0d5|d |dd| dzd|j*d t3j4||j!||||||||| dz cYd}~S|d6k(r d7}tM||d8k(r d9}tM||d:k(r d;}tM||d >AA$XL 2 266e_.>L 99$PL2248 ++%!+!!-1C!CI_  g 8''    H$$(?(??d...((D,E,E,TU1(2F2F1GH##(+Sq0A4CSCSBTTUW  5!||!!#!-%5 +a$    % % ''/&6&6 # *2--/'  / /)%%-%9%9!%h&6&6!7!8  x   ==? " C''  ''7 8/Ax8M  / /)%+  d...#77 F  01+O".1(,T]]OQ1Q7B:gCVW`CaBb5c2!Z%886HZPWEXY_E`Daab:cc6#J57B4::jCYBZ5[27B3z?BS5T2'' ^1j)ajj.DI[I["#**"4"4";";8";"LK;'#-7H 5V27N{m[\5]2 ^  / /)%3%0=DQ =SXYXbXbXnajj&8&8!9tx!64   MM/0J/K:VaUbbcd eq*%!***@QZZEWEW :1::;M;M:NOPt666d...((D,E,E,TU!+/##(+Sq0A4CSCSBTTUW 5!||!!#!-%5 +a$  c!-a*67 7#-u*67 7 #-v* 67 7 #-[*67 7eR 8s=CJ7J78J8J7J41J73J44J77c DP!c'5Pc2B0T*"c(AT**cA"c$V?>c?WcWcB,ZcB\  c \  E&c2c8Accc,| | td|xs|S)z@Verify that an auth token is present or comfy_api_key is presentr)r)rMrErFs r$rzApiClient.check_auth[s$  -"7PQ Q*]*r#c i}|r||d<t|tjr"|jd|j }nAt|t r&t |d5}|j }dddn tdd} d|jddd tjjdd } tj| d ||d |xsd dtdt|dzD]f} t!j"|||} | j%tj| d || j&t)| j*d| cSd|dzdt | } t!jHdd d!"}|j&d#k\rd$|j&d%t | }tj| d ||(tS|| #1swYfxYw#t j,t j.t j0f$rC} | } t3| j4dt | }d}d}d}t7| dr| j8| j8j&}t)| j8j*} | j8j;}n/#t:j<$r| j8j>}YnwxYwtj| d |||||| |krM||| zz}tAjBdt | d|dd| dzd|d tEjF|nYd} ~ Yd} ~ cd} ~ wwxYw#t jJtLjNf$rD}d&t |d't | }tj| d ||(tQ|| d}~wwxYw))aUpload a file to the API with retry logic. Args: upload_url: The URL to upload to file: Either a file path string, BytesIO object, or tuple of (file_path, filename) content_type: Optional mime type to set for the upload max_retries: Maximum number of retry attempts retry_delay: Initial delay between retries in seconds retry_backoff_factor: Multiplier for the delay after each retry rarrbNz:File must be either a BytesIO object or a file path stringupload_rPrQrRr<z[File data of type unknownz, size z bytes])rrrrrrrgzFile uploaded successfully.rz: rrzFile upload failed: rrrrzFailed to upload file after z attempts. Error: rsrtTrurCzLFailed to upload file. Internet connectivity check to Google failed (status z). Original error: zOFailed to upload file due to network connectivity issues (cannot reach Google: z). Original upload error: r)*rioBytesIOseekreadr/open ValueErrorsplitrUrVrWrrrrangerwputrryrr\rrrrrrrr[rrrrrrrxrzr{r|r&r)r7filer2rIrJrKr\r^flast_exceptionr retry_attemptrrerror_message_for_logresponse_content_for_logstatus_code_for_logheaders_for_logrrrconn_check_excs r$ upload_filezApiClient.upload_fileas& &2GN # dBJJ ' IIaL99;D c "dD!Qvvx"!YZ Z !1!1#!6r!: ;1TZZ\=M=Mbq=Q,,h.>.>@R@RS! !"+/7+;+;*OF34::??3D0//F34::3E3E0F33!-#(j)<%4%="7 !;.'+?=+PQEOO.s1vh7'',Sk]Q5F4Gq UVXJJu%&?! `))6<<8 M&<$r'}t!j:dt=|d}~wt@$r:}t!j:dt=|tAt=|d}~wwxYw)zTExecute the API operation using the provided client or create one with retry supportN)rDrErFrGrHrIrJrKT exclude_nonez[DEBUG] API Request:  z[DEBUG] Request Data: indentz[DEBUG] Query Params: )rrXr^rrbr2rcz2==================================================z[DEBUG] RESPONSE DETAILS:z"[DEBUG] Status Code: 200 (Success)z[DEBUG] Response Body: z[ERROR] Local network error: z[ERROR] API server error: z[ERROR] API Exception: )!r@rrErFrGrHrIrJrKrrr* model_dumpitemsr valuerrrrrXr[rrrbr2rc_parse_responser&r|r/r(r)rMclient request_dictkeyrresprs r$executezSynchronousOperation.execute$szA $~"!]]#"&"4"4 LL# $ 0 0 $ 0 0)-)B)B dllL9\\,,$,?  "."4"4"6JC!%.,1KK S)#7 MM' (<(<(B(B'C1T]]EWEWDXY  MM24::lST3U2VW X MM24==3M3M2NO P>>}}++11]]''!}}11jj!..!%!6!6"D MM( # MM5 6 MM> ? MM3DJJtA4N3OP Q MM( #''- -   MM9#a&B C   MM6s1vh? @  $ MM3CF8< =CF# # $s7CJ GJ L,"J88 L,"K&& L,25L''L,c|jjj||_t j d|j|jS)z5Parse response data - can be overridden by subclassesz[DEBUG] Parsed Response: )rrmodel_validaterrr)rMrs r$rz$SynchronousOperation._parse_responseisB  44CCDI  1$--AB}}r#) NNNNNgu"ATrjNrrr)rzApiEndpoint[T, R]rrrbrrr1rErrFrrrrGrrHrr2r/rcr rIrrJrrKrrrzOptional[ApiClient]rr)rrr r!rNrrr"r#r$rrs+/#$('+/3!.%) &)"9#"9"9( "9  "9 " "9%"9-"9"9"9"9#"9"9"9$"9HC$Jr#rceZdZdZdZdZdZy) TaskStatuszEnum for task status values completedfailedpendingN)rrr r! COMPLETEDFAILEDPENDINGr"r#r$r r ts%I FGr#r ceZdZdZ d d dZd d dZd dZddZddZddZ y)PollingOperationzX Represents an asynchronous API operation that requires polling for completion. Nc||_||_|xstj|_| |_| |_| B| jd|j |_| jd|j |_| |_| |_ ||_ ||_ ||_ ||_ |xsd|_||_||_||_||_||_d|_d|_y)NrErFct|ddS)Nstatus)getattr)xs r$z+PollingOperation.__init__..s ga40r#) poll_endpointrrrrrErFrx poll_intervalmax_poll_attemptsrIrJrKestimated_durationstatus_extractorprogress_extractorresult_url_extractornode_idcompleted_statusesfailed_statusesfinal_responser|)rMrr!r"rrrrrrErFrrrrIrJrKrr s r$rNzPollingOperation.__init__s*+ %<)<)< $*  ")oolDOOLDO!,$BTBT!UD *!2&&$8!"4!1! 0 #5$8! "4.# r#c |Mt|j|j|j|j|j |j }|j|S#t$r}tdt||d}~wt$r}tdt||d}~wt$r}tdt|d}~wwxYw)zWExecute the polling operation using the provided client. If failed, raise an exception.N)rDrErFrIrJrKz\Polling failed due to local network issues. Please check your internet connection. Details: ztPolling failed due to API server issues. The service may be experiencing problems. Please try again later. Details: zError during polling: ) r@rrErFrIrJrK_poll_until_completer&rr/r()rMrrs r$rzPollingOperation.executes ?~"!]]#"&"4"4 $ 0 0 $ 0 0)-)B)B ,,V4 4  F8%  447F8=  ?4SVH=> > ?s0AA"" C+B CB'' C3C  Ccr|jsytjj||jy)zFSends text to the client which will be displayed on the node in the UIN)r rinstancesend_progress_text)rMtexts r$_display_text_on_nodez&PollingOperation._display_text_on_nodes&|| 00t||Dr#c|jsy|j7tdt|jt|z }d|dd|dd}nd|dd}|j |y)NrzTask in progress: z.0fzs (~z s remaining)s)r rmaxrr*)rMtime_completedestimated_time_remainingrs r$_display_time_progress_on_nodez/PollingOperation._display_time_progress_on_nodes{||   " " .'*3t../#n2EE( $+>#*>dC[\_B``lmG*>#*>a@G ""7+r#c: |j|}||jvrtjS||jvrtj Stj S#t$r2}tjd|tj cYd}~Sd}~wwxYw)z5Check task status using the status extractor functionzError extracting status: N) rr!r rr"rrrrr|)rMrrrs r$_check_task_statusz#PollingOperation._check_task_statuss &**84F000!+++4///!(((%% % & MM5aS9 :%% % &s(.AAA B('BBBc  d}d}td|jdz}|jrtjt }||j kr4 |dz }tjd||j|jjdnd}|dk(r~tjd |jjjd |jjtjd |rtj |d nd |j|jjj|jj|jj"|}d}|jj$j'|}|j)|} tjd| |jr*|j|} | j+| t | t,j.k(rd} |j0r|j1|} | rd| } nd} tjd| |j3| ||_|jrj7d|j4S| t,j8k(r;dtj |} tj:d| t=| tjdtjd|j>dtAtC|j>D]:} ||j>z| z}|jE|tGjHd< ||j kr4t=d$|d%||j>zd&#tJtLf$r}|dz }||k\rt=d|dtO||tjPd|d|j dtO|d|j>d tGjH|j>Yd}~d}~wt<$r}|dz }||k\s t,j8k(rt=d|d!tO||tj:d"tO|tjPd#|d|j dtO|d|j>d tGjH|j>Yd}~d}~wwxYw)'zPoll until the task is completerrrz[DEBUG] Polling attempt #NTrz[DEBUG] Poll Request: rzEBUG] Poll Request Data: rNone)rrXrr^z[DEBUG] Task Status: )totalzTask completed successfullyz Result URL: zTask completed successfully!z[DEBUG] rz Task failed: z1[DEBUG] Task still pending, continuing to poll...z[DEBUG] Waiting z seconds before next pollzPolling aborted after z consecutive network errors: z&Network error during polling (attempt rPz): z. Will retry in z seconds.z consecutive errors: z[DEBUG] Polling error: zError during polling (attempt zPolling timed out after z attempts (z_ seconds). The operation may still be running on the server but is taking longer than expected.))minrIrr ProgressBarPROGRESS_BAR_MAXrrrrrrrrrXr[rrrrr2update_absoluter rrr*r#rrr|rrrrr0rrr&r(r/r)rMr poll_countconsecutive_errorsmax_consecutive_errorsprogressrr response_objr new_progressr result_urlir.rs r$r%z%PollingOperation._poll_until_completes !$Q(8(81( ?MM01C1C1J1J1P1P0QQRSWSeSeSjSjRklMM5\hdjjVW6Xnt5uv ~~--44::++00--::% &&'" $11@@OOPTU 00> 5fX>?**#'#:#:<#HL#/ 00EU0VZ111;G00%)%>%>|%L %(4ZL&AG"@MMHWI"67..w7*6D'.. ,...z000 -djj.>-?@GMMHWI"67#G,,MM"UV &t'9'9&::STs4#5#567A&043E3E&E%JN77GJJqM8I4111P&zl+j4K]K]>]=^_c d  ?&~6 /"a'"%)??#01C0DDabefgbhaij rOs$Zx# OOO *%  Cy! Cy! Cy! 9    \  9 IJYJ d|A|A~/'!Q$-/6v71a4=vrdh wq!t}h r#