Improved API support

- Run comfyui workflows directly inside other python applications using
   EmbeddedComfyClient.
 - Optional telemetry in prompts and models using anonymity preserving
   Plausible self-hosted or hosted.
 - Better OpenAPI schema
 - Basic support for distributed ComfyUI backends. Limitations: no
   progress reporting, no easy way to start your own distributed
   backend, requires RabbitMQ as a message broker.
This commit is contained in:
doctorpangloss
2024-02-07 14:20:21 -08:00
parent 32d83e52ff
commit 1b2ea61345
271 changed files with 16925 additions and 413 deletions
+5
View File
@@ -0,0 +1,5 @@
# do not import all endpoints into this module because that uses a lot of memory and stack frames
# if you need the ability to import all endpoints from this module, import them with
# from comfy.api.apis.paths.object_info import ObjectInfo
path = "/object_info"
@@ -0,0 +1,114 @@
# coding: utf-8
"""
Generated by: https://github.com/openapi-json-schema-tools/openapi-json-schema-generator
"""
from comfy.api import api_client
from comfy.api.shared_imports.operation_imports import * # pyright: ignore [reportWildcardImportFromLibrary]
from .. import path
from .responses import response_200
__StatusCodeToResponse = typing.TypedDict(
'__StatusCodeToResponse',
{
'200': typing.Type[response_200.ResponseFor200],
}
)
_status_code_to_response: __StatusCodeToResponse = {
'200': response_200.ResponseFor200,
}
_non_error_status_codes = frozenset({
'200',
})
_all_accept_content_types = (
"application/json",
)
class BaseApi(api_client.Api):
@typing.overload
def _get_object_info(
self,
*,
skip_deserialization: typing.Literal[False] = False,
accept_content_types: typing.Tuple[str, ...] = _all_accept_content_types,
server_index: typing.Optional[int] = None,
stream: bool = False,
timeout: typing.Optional[typing.Union[int, float, typing.Tuple]] = None,
) -> response_200.ApiResponse: ...
@typing.overload
def _get_object_info(
self,
*,
skip_deserialization: typing.Literal[True],
accept_content_types: typing.Tuple[str, ...] = _all_accept_content_types,
server_index: typing.Optional[int] = None,
stream: bool = False,
timeout: typing.Optional[typing.Union[int, float, typing.Tuple]] = None,
) -> api_response.ApiResponseWithoutDeserialization: ...
def _get_object_info(
self,
*,
skip_deserialization: bool = False,
accept_content_types: typing.Tuple[str, ...] = _all_accept_content_types,
server_index: typing.Optional[int] = None,
stream: bool = False,
timeout: typing.Optional[typing.Union[int, float, typing.Tuple]] = None,
):
"""
(UI) Get object info
:param skip_deserialization: If true then api_response.response will be set but
api_response.body and api_response.headers will not be deserialized into schema
class instances
"""
used_path = path
headers = self._get_headers(accept_content_types=accept_content_types)
# TODO add cookie handling
host = self.api_client.configuration.get_server_url(
"servers", server_index
)
raw_response = self.api_client.call_api(
resource_path=used_path,
method='get',
host=host,
headers=headers,
stream=stream,
timeout=timeout,
)
if skip_deserialization:
skip_deser_response = api_response.ApiResponseWithoutDeserialization(response=raw_response)
self._verify_response_status(skip_deser_response)
return skip_deser_response
status = str(raw_response.status)
if status in _non_error_status_codes:
status_code = typing.cast(
typing.Literal[
'200',
],
status
)
return _status_code_to_response[status_code].deserialize(
raw_response, self.api_client.schema_configuration)
response = api_response.ApiResponseWithoutDeserialization(response=raw_response)
self._verify_response_status(response)
return response
class GetObjectInfo(BaseApi):
# this class is used by api classes that refer to endpoints with operationId.snakeCase fn names
get_object_info = BaseApi._get_object_info
class ApiForGet(BaseApi):
# this class is used by api classes that refer to endpoints by path and http method names
get = BaseApi._get_object_info
@@ -0,0 +1,28 @@
# coding: utf-8
"""
Generated by: https://github.com/openapi-json-schema-tools/openapi-json-schema-generator
"""
from comfy.api.shared_imports.response_imports import * # pyright: ignore [reportWildcardImportFromLibrary]
from .content.application_json import schema as application_json_schema
@dataclasses.dataclass(frozen=True)
class ApiResponse(api_response.ApiResponse):
body: application_json_schema.SchemaDict
headers: schemas.Unset
class ResponseFor200(api_client.OpenApiResponse[ApiResponse]):
@classmethod
def get_response(cls, response, headers, body) -> ApiResponse:
return ApiResponse(response=response, body=body, headers=headers)
class ApplicationJsonMediaType(api_client.MediaType):
schema: typing_extensions.TypeAlias = application_json_schema.Schema
content = {
'application/json': ApplicationJsonMediaType,
}
@@ -0,0 +1,90 @@
# coding: utf-8
"""
comfyui
No description provided (generated by Openapi JSON Schema Generator https://github.com/openapi-json-schema-tools/openapi-json-schema-generator) # noqa: E501
The version of the OpenAPI document: 0.0.1
Generated by: https://github.com/openapi-json-schema-tools/openapi-json-schema-generator
"""
from __future__ import annotations
from comfy.api.shared_imports.schema_imports import * # pyright: ignore [reportWildcardImportFromLibrary]
from comfy.api.components.schema import node
class SchemaDict(schemas.immutabledict[str, node.NodeDict]):
__required_keys__: typing.FrozenSet[str] = frozenset({
})
__optional_keys__: typing.FrozenSet[str] = frozenset({
})
def __new__(
cls,
configuration_: typing.Optional[schema_configuration.SchemaConfiguration] = None,
**kwargs: typing.Union[
node.NodeDictInput,
node.NodeDict,
],
):
used_kwargs = typing.cast(SchemaDictInput, kwargs)
return Schema.validate(used_kwargs, configuration=configuration_)
@staticmethod
def from_dict_(
arg: typing.Union[
SchemaDictInput,
SchemaDict
],
configuration: typing.Optional[schema_configuration.SchemaConfiguration] = None
) -> SchemaDict:
return Schema.validate(arg, configuration=configuration)
def get_additional_property_(self, name: str) -> typing.Union[node.NodeDict, schemas.Unset]:
schemas.raise_if_key_known(name, self.__required_keys__, self.__optional_keys__)
val = self.get(name, schemas.unset)
if isinstance(val, schemas.Unset):
return val
return typing.cast(
node.NodeDict,
val
)
SchemaDictInput = typing.Mapping[
str,
typing.Union[
node.NodeDictInput,
node.NodeDict,
],
]
@dataclasses.dataclass(frozen=True)
class Schema(
schemas.Schema[SchemaDict, tuple]
):
types: typing.FrozenSet[typing.Type] = frozenset({schemas.immutabledict})
additional_properties: typing.Type[node.Node] = dataclasses.field(default_factory=lambda: node.Node) # type: ignore
type_to_output_cls: typing.Mapping[
typing.Type,
typing.Type
] = dataclasses.field(
default_factory=lambda: {
schemas.immutabledict: SchemaDict
}
)
@classmethod
def validate(
cls,
arg: typing.Union[
SchemaDictInput,
SchemaDict,
],
configuration: typing.Optional[schema_configuration.SchemaConfiguration] = None
) -> SchemaDict:
return super().validate_base(
arg,
configuration=configuration,
)