import asyncio import contextlib import json import logging import time import uuid from collections.abc import Callable, Iterable from dataclasses import dataclass from enum import Enum from io import BytesIO from typing import Any, Literal, TypeVar from urllib.parse import urljoin, urlparse import aiohttp from aiohttp.client_exceptions import ClientError, ContentTypeError from pydantic import BaseModel from comfy import utils from comfy_api.latest import IO from server import PromptServer from comfy.deploy_environment import get_deploy_environment from . import request_logger from ._helpers import ( default_base_url, get_auth_header, get_node_id, is_processing_interrupted, sleep_with_interrupt, ) from .common_exceptions import ApiServerError, LocalNetworkError, ProcessingInterrupted M = TypeVar("M", bound=BaseModel) class ApiEndpoint: def __init__( self, path: str, method: Literal["GET", "POST", "PUT", "DELETE", "PATCH"] = "GET", *, query_params: dict[str, Any] | None = None, headers: dict[str, str] | None = None, ): self.path = path self.method = method self.query_params = query_params or {} self.headers = headers or {} @dataclass class _RequestConfig: node_cls: type[IO.ComfyNode] | None endpoint: ApiEndpoint timeout: float content_type: str data: dict[str, Any] | None files: dict[str, Any] | list[tuple[str, Any]] | None multipart_parser: Callable | None max_retries: int max_retries_on_rate_limit: int retry_delay: float retry_backoff: float wait_label: str = "Waiting" monitor_progress: bool = True estimated_total: int | None = None final_label_on_success: str | None = "Completed" progress_origin_ts: float | None = None price_extractor: Callable[[dict[str, Any]], float | None] | None = None is_rate_limited: Callable[[int, Any], bool] | None = None response_header_validator: Callable[[dict[str, str]], None] | None = None base_url: str | None = None auth_headers: dict[str, str] | None = None allow_304: bool = False error_parser: Callable[[int, Any], Exception | None] | None = None # Optional callback to render a per-second progress label while # waiting out a rate-limit / SERVER_BUSY / MAINTENANCE retry. Called # with ``(status, body, retry_after_s)`` and should return the label # string used by ``_display_time_progress`` (which renders it as # ``Status: