# generated by datamodel-codegen:
#   filename:  openapi.yaml

from __future__ import annotations

from enum import Enum
from typing import Any
from uuid import UUID

from pydantic import (
    AnyUrl,
    AwareDatetime,
    BaseModel,
    ConfigDict,
    EmailStr,
    Field,
    RootModel,
    confloat,
    conint,
    constr,
)


class PromptRequest(BaseModel):
    prompt: dict[str, Any] = Field(
        ...,
        description='The workflow graph to execute. Keys are node IDs (strings);\nvalues are objects with class_type and inputs.\n',
    )
    number: float | None = Field(
        None,
        description='Priority number for the queue (lower numbers have higher priority)',
    )
    front: bool | None = Field(
        None, description='If true, adds the prompt to the front of the queue'
    )
    extra_data: dict[str, Any] | None = Field(
        None, description='Extra data associated with the prompt (e.g. extra_pnginfo)'
    )
    client_id: str | None = Field(
        None, description='WebSocket client ID to receive progress updates'
    )
    prompt_id: UUID | None = Field(
        None,
        description='Client-supplied prompt ID. Server generates a UUID if omitted.',
    )
    partial_execution_targets: list[str] | None = Field(
        None, description='List of node IDs to execute (partial graph execution)'
    )
    workflow_id: UUID | None = Field(
        None,
        description='[cloud-only] Cloud workflow entity ID for tracking and gallery association. Ignored by local ComfyUI.',
    )
    workflow_version_id: UUID | None = Field(
        None,
        description='[cloud-only] Cloud workflow version ID for pinning execution to a specific version. Ignored by local ComfyUI.',
    )


class PromptErrorResponse(BaseModel):
    model_config = ConfigDict(
        extra='allow',
    )


class PromptError(BaseModel):
    type: str | None = None
    message: str | None = None
    details: str | None = None


class ExtraInfo(BaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    input_name: str | None = None


class Error(BaseModel):
    type: str | None = None
    message: str | None = None
    details: str | None = None
    extra_info: ExtraInfo | None = None


class NodeError(BaseModel):
    errors: list[Error] | None = None
    class_type: str | None = Field(None, description="The node's class type")
    dependent_outputs: list[Any] | None = None


class ExecInfo(BaseModel):
    queue_remaining: int | None = Field(
        None, description='Number of items remaining in the queue'
    )


class PromptInfo(BaseModel):
    exec_info: ExecInfo | None = None


class QueueInfo(BaseModel):
    queue_running: (
        list[tuple[float, UUID, dict[str, Any], dict[str, Any], list[str]]] | None
    ) = Field(None, description='Currently running queue items')
    queue_pending: (
        list[tuple[float, UUID, dict[str, Any], dict[str, Any], list[str]]] | None
    ) = Field(None, description='Pending queue items (oldest first)')


class QueueManageRequest(BaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    clear: bool | None = Field(None, description='If true, clear all pending items')
    delete: list[str] | None = Field(
        None, description='Array of prompt IDs to delete from queue'
    )


class QueueManageResponse(BaseModel):
    deleted: list[str] | None = Field(
        None, description='Prompt IDs that were deleted from the queue.'
    )
    cleared: bool | None = Field(None, description='Whether the queue was cleared.')


class Prompt(BaseModel):
    extra_data: dict[str, Any] | None = Field(
        None,
        description='Additional execution data (workflow removed from extra_pnginfo)',
    )
    priority: float | None = Field(None, description='Execution priority')
    prompt_id: str | None = Field(None, description='The prompt ID')


class HistoryEntry(BaseModel):
    prompt: list[Any] | Prompt | None = Field(
        None,
        description='Prompt tuple: [number, prompt_id, prompt_graph, extra_data, output_node_ids]\n',
    )
    outputs: dict[str, Any] | None = Field(
        None, description='Output data from execution keyed by node ID'
    )
    status: dict[str, Any] | None = Field(
        None, description='Execution status (status_str, completed, messages, etc.)'
    )
    meta: dict[str, Any] | None = Field(
        None, description='Metadata about the execution and nodes'
    )
    create_time: int | None = Field(
        None, description='Job creation timestamp (Unix timestamp in milliseconds)'
    )
    prompt_id: str | None = Field(
        None, description='Unique identifier for this prompt execution'
    )
    workflow_id: str | None = Field(
        None, description='UUID identifying the workflow graph definition'
    )


class HistoryManageRequest(BaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    clear: bool | None = Field(None, description='If true, clear all history')
    delete: list[str] | None = Field(
        None, description='Array of prompt IDs to delete from history'
    )


class Status(Enum):
    pending = 'pending'
    in_progress = 'in_progress'
    completed = 'completed'
    failed = 'failed'
    cancelled = 'cancelled'


class ExecutionError(BaseModel):
    node_id: str = Field(..., description='ID of the node that failed')
    node_type: str = Field(..., description='Type name of the node')
    exception_message: str = Field(..., description='Human-readable error message')
    exception_type: str = Field(..., description='Python exception type')
    traceback: list[str] = Field(..., description='Traceback lines')
    current_inputs: dict[str, Any] = Field(
        ...,
        description='Input values at time of failure (empty object if not available)',
    )
    current_outputs: dict[str, Any] = Field(
        ...,
        description='Output values at time of failure (empty object if not available)',
    )


class PaginationInfo(BaseModel):
    offset: conint(ge=0) = Field(
        ...,
        deprecated=True,
        description='Current offset (0-based). Deprecated: use cursor-based pagination.',
    )
    limit: conint(ge=1) = Field(..., description='Items per page')
    total: conint(ge=0) = Field(
        ...,
        description='Total number of items matching filters (may be 0 when using cursor pagination)',
    )
    has_more: bool = Field(
        ..., description='Whether more items are available beyond this page'
    )
    next_cursor: str | None = Field(
        None,
        description='Opaque cursor for the next page. Pass this value as the `after`\nquery parameter on the next request. Empty or absent when there\nare no more results.\n',
    )


class UploadResult(BaseModel):
    name: str | None = Field(
        None, description='Saved filename (may be renamed to avoid collisions)'
    )
    subfolder: str | None = Field(None, description='Subfolder the file was saved to')
    type: str | None = Field(None, description='Directory type (input, temp)')


class DeviceStats(BaseModel):
    name: str = Field(..., description='Device name')
    type: str = Field(..., description='Device type (cuda, mps, cpu, etc.)')
    index: float = Field(
        ...,
        description="Device index within its type (e.g. CUDA ordinal for `cuda:0`,\n`cuda:1`). `null` for devices with no index, including the CPU\ndevice returned in `--cpu` mode (PyTorch's `torch.device('cpu').index`\nis `None`).\n",
    )
    vram_total: float | None = Field(None, description='Total VRAM in bytes')
    vram_free: float | None = Field(None, description='Free VRAM in bytes')
    torch_vram_total: float | None = Field(
        None, description='Total PyTorch-managed VRAM in bytes'
    )
    torch_vram_free: float | None = Field(
        None, description='Free PyTorch-managed VRAM in bytes'
    )


class ComfyPackageVersion(BaseModel):
    name: str
    installed: str
    required: str


class System(BaseModel):
    os: str = Field(..., description='Operating system')
    python_version: str = Field(..., description='Python version')
    embedded_python: bool = Field(..., description='Whether using embedded Python')
    comfyui_version: str = Field(..., description='ComfyUI version string')
    pytorch_version: str = Field(..., description='PyTorch version')
    required_frontend_version: str | None = Field(
        None, description='Required frontend version'
    )
    argv: list[str] = Field(..., description='Command line arguments')
    ram_total: float = Field(..., description='Total RAM in bytes')
    ram_free: float = Field(..., description='Free RAM in bytes')
    installed_templates_version: str | None = Field(
        None, description='Version of the currently installed workflow templates'
    )
    required_templates_version: str | None = Field(
        None,
        description='Minimum required workflow templates version for this ComfyUI build',
    )
    comfy_package_versions: list[ComfyPackageVersion] | None = Field(
        None,
        description='Installed and required versions for every comfy* package pinned in requirements.txt',
    )
    cloud_version: str | None = Field(
        None, description='Cloud ingest service version (commit hash)'
    )
    comfyui_frontend_version: str | None = Field(
        None, description='ComfyUI frontend version (commit hash or tag)'
    )
    deploy_environment: str | None = Field(
        None,
        description='How this ComfyUI instance is deployed (e.g. cloud, local-git, local-portable, local-desktop)',
    )
    workflow_templates_version: str | None = Field(
        None, description='Workflow templates version'
    )


class Device(BaseModel):
    name: str = Field(..., description='Device name')
    type: str = Field(..., description='Device type')
    index: float = Field(
        ...,
        description="Device index within its type (e.g. CUDA ordinal for `cuda:0`,\n`cuda:1`). `null` for devices with no index, including the CPU\ndevice returned in `--cpu` mode (PyTorch's `torch.device('cpu').index`\nis `None`).\n",
    )
    vram_total: float | None = Field(None, description='Total VRAM in bytes')
    vram_free: float | None = Field(None, description='Free VRAM in bytes')
    torch_vram_total: float | None = Field(
        None, description='Total PyTorch-managed VRAM in bytes'
    )
    torch_vram_free: float | None = Field(
        None, description='Free PyTorch-managed VRAM in bytes'
    )


class SystemStatsResponse(BaseModel):
    system: System
    devices: list[Device]


class NodeInfo(BaseModel):
    input: dict[str, Any] | None = Field(
        None, description='Input specifications (required and optional groups)'
    )
    input_order: dict[str, list[str]] | None = Field(
        None, description='Ordered input names per group'
    )
    output: list[str] | None = Field(None, description='Output type names')
    output_is_list: list[bool] | None = Field(
        None, description='Whether each output is a list'
    )
    output_name: list[str] | None = Field(None, description='Display names of outputs')
    name: str | None = Field(None, description='Internal class name')
    display_name: str | None = Field(None, description='Human-readable display name')
    description: str | None = Field(None, description='Node description')
    python_module: str | None = Field(
        None, description='Python module implementing the node'
    )
    category: str | None = Field(None, description='Node category path')
    output_node: bool | None = Field(None, description='Whether this is an output node')
    output_tooltips: list[str] | None = Field(
        None, description='Tooltips for each output'
    )
    deprecated: bool | None = Field(None, description='Whether the node is deprecated')
    experimental: bool | None = Field(
        None, description='Whether the node is experimental'
    )
    api_node: bool | None = Field(None, description='Whether this is an API node')
    is_input_list: bool | None = Field(
        None, description='Whether the node accepts list inputs'
    )
    dev_only: bool | None = Field(
        None, description='Whether the node is developer-only (hidden in production UI)'
    )
    has_intermediate_output: bool | None = Field(
        None, description='Whether the node emits intermediate output during execution'
    )
    search_aliases: list[str] | None = Field(
        None, description='Alternative search terms for finding this node'
    )
    essentials_category: str | None = Field(
        None,
        description="Category override used by the essentials pack. The\n`essentials_category` key may be present with a string value,\npresent and `null`, or absent entirely:\n\n- V1 nodes: `essentials_category` is **omitted** when the node\n  class doesn't define an `ESSENTIALS_CATEGORY` attribute, and\n  **`null`** if the attribute is explicitly set to `None`.\n- V3 nodes (`comfy_api.latest.io`): `essentials_category` is\n  **always present**, and **`null`** for nodes whose `Schema`\n  doesn't populate it.\n",
    )


class ModelFolder(BaseModel):
    name: str = Field(
        ...,
        description='Model folder type name (e.g. "checkpoints")',
        examples=['checkpoints'],
    )
    folders: list[str] = Field(
        ...,
        description='Filesystem paths for this model type',
        examples=[['checkpoints']],
    )
    extensions: list[str] = Field(
        ...,
        description='Registered file-extension allowlist. An empty array accepts any extension.',
    )


class ModelFile(BaseModel):
    name: str = Field(..., description='Model filename', examples=['model.safetensors'])
    pathIndex: int = Field(
        ..., description="Index into the folder's paths array", examples=[0]
    )
    modified: float | None = Field(None, description='File modification timestamp')
    created: float | None = Field(None, description='File creation timestamp')
    size: int | None = Field(None, description='File size in bytes')


class Info(BaseModel):
    node_pack: str = Field(
        ..., description='The node pack/module providing this subgraph'
    )


class GlobalSubgraphInfo(BaseModel):
    source: str = Field(..., description='Source type ("templates" or "custom_node")')
    name: str = Field(..., description='Display name of the subgraph blueprint')
    info: Info = Field(..., description='Additional information about the subgraph')
    data: str | None = Field(
        None, description='The full subgraph JSON data (may be empty in list view)'
    )


class GlobalSubgraphData(BaseModel):
    source: str = Field(..., description='Source type ("templates" or "custom_node")')
    name: str = Field(..., description='Display name of the subgraph blueprint')
    info: Info = Field(..., description='Additional information about the subgraph')
    data: str = Field(..., description='The full subgraph JSON data as a string')


class GetUserDataResponseFullFile(BaseModel):
    path: str | None = Field(
        None, description='File name or path relative to the user directory'
    )
    created: float | None = Field(None, description='Unix timestamp of file creation')
    size: int | None = Field(None, description='File size in bytes')
    modified: int | None = Field(
        None, description='Unix timestamp of last modification in milliseconds'
    )


class Asset(BaseModel):
    id: UUID = Field(..., description='Unique identifier for the asset')
    name: str = Field(..., description='Name of the asset file')
    loader_path: str | None = Field(
        None,
        description='The value a loader consumes to load this asset. Null when no loader can resolve the file.',
    )
    display_name: str | None = Field(
        None, description='Human-facing label for the asset. Not unique.'
    )
    hash: constr(pattern=r'^blake3:[a-f0-9]{64}$') | None = Field(
        None, description='Blake3 content hash of the asset (preferred over asset_hash)'
    )
    asset_hash: constr(pattern=r'^blake3:[a-f0-9]{64}$') | None = Field(
        None,
        deprecated=True,
        description='Deprecated: use `hash` instead. Blake3 hash of the asset content.',
    )
    size: int = Field(..., description='Size of the asset in bytes')
    mime_type: str | None = Field(None, description='MIME type of the asset')
    tags: list[str] | None = Field(None, description='Tags associated with the asset')
    user_metadata: dict[str, Any] | None = Field(
        None, description='Custom user metadata'
    )
    metadata: dict[str, Any] | None = Field(
        None, description='System-managed metadata (read-only)'
    )
    preview_url: AnyUrl | None = Field(
        None, description='URL for asset preview/thumbnail'
    )
    preview_id: UUID | None = Field(
        None, description='ID of the preview asset if available'
    )
    prompt_id: UUID | None = Field(
        None,
        deprecated=True,
        description='Deprecated: use job_id instead. ID of the prompt that created this asset.',
    )
    job_id: UUID | None = Field(
        None, description='ID of the job that created this asset'
    )
    created_at: AwareDatetime = Field(
        ..., description='Timestamp when the asset was created'
    )
    updated_at: AwareDatetime = Field(
        ..., description='Timestamp when the asset was last updated'
    )
    last_access_time: AwareDatetime | None = Field(
        None, description='Timestamp when the asset was last accessed'
    )
    is_immutable: bool | None = Field(
        None, description='Whether this asset is immutable'
    )
    file_path: str | None = Field(
        None,
        description='Relative path in global-namespace-root form (e.g. "models/checkpoints/flux.safetensors")',
    )
    short_url: str | None = Field(
        None,
        description="Durable, owner-gated short link to this asset's content (relative `/api/s/{id}` path). Stable across the underlying signed URL's expiry — resolving it re-mints a fresh signed URL on every request — so it is safe to persist or share into chat, unlike `preview_url`. Only the minting user can resolve it. Omitted when the short-link surface is disabled or the asset has no resolvable content hash.",
    )


class AssetCreated(Asset):
    created_new: bool = Field(
        ...,
        description='Whether this was a new creation (true) or returned existing (false)',
    )


class AssetUpdated(BaseModel):
    id: UUID = Field(..., description='Asset ID')
    name: str | None = Field(None, description='Updated name of the asset')
    hash: constr(pattern=r'^blake3:[a-f0-9]{64}$') | None = Field(
        None, description='Blake3 content hash of the asset (preferred over asset_hash)'
    )
    asset_hash: constr(pattern=r'^blake3:[a-f0-9]{64}$') | None = Field(
        None,
        deprecated=True,
        description='Deprecated: use `hash` instead. Blake3 hash of the asset content.',
    )
    tags: list[str] | None = Field(None, description='Tags associated with the asset')
    mime_type: str | None = Field(None, description='Updated MIME type of the asset')
    user_metadata: dict[str, Any] | None = Field(
        None, description='Updated custom metadata'
    )
    prompt_id: UUID | None = Field(
        None,
        deprecated=True,
        description='Deprecated: use job_id instead. ID of the prompt that created this asset.',
    )
    job_id: UUID | None = Field(
        None, description='ID of the job that created this asset'
    )
    updated_at: AwareDatetime = Field(..., description='Timestamp of the update')
    display_name: str | None = Field(
        None,
        description='Display name of the asset. Mirrors name for backwards compatibility.',
    )
    file_path: str | None = Field(
        None,
        description='Relative path in global-namespace-root form (e.g. "models/checkpoints/flux.safetensors")',
    )


class ListAssetsResponse(BaseModel):
    assets: list[Asset] = Field(..., description='List of assets matching the query')
    total: int = Field(..., description='Total number of assets matching the filters')
    has_more: bool = Field(
        ..., description='Whether more assets are available beyond this page'
    )
    next_cursor: str | None = Field(
        None,
        description='Opaque cursor to pass as the `after` query parameter to fetch the\nnext page. Omitted from the response when there are no more results.\n',
    )


class TagInfo(BaseModel):
    name: str = Field(..., description='Tag name')
    count: int = Field(..., description='Number of assets using this tag')


class ListTagsResponse(BaseModel):
    tags: list[TagInfo] = Field(..., description='List of tags')
    total: int = Field(..., description='Total number of tags')
    has_more: bool = Field(..., description='Whether more tags are available')


class AssetTagHistogramResponse(BaseModel):
    tag_counts: dict[str, int] = Field(
        ...,
        description='Map of tag names to occurrence counts',
        examples=[{'checkpoint': 32, 'lora': 193, 'vae': 6}],
    )


class TagsModificationResponse(BaseModel):
    added: list[str] | None = Field(None, description='Tags successfully added')
    removed: list[str] | None = Field(None, description='Tags successfully removed')
    already_present: list[str] | None = Field(
        None, description='Tags already present (for add)'
    )
    not_present: list[str] | None = Field(
        None, description='Tags not present (for remove)'
    )
    total_tags: list[str] = Field(
        ..., description='All tags on the asset after the operation'
    )


class Type(Enum):
    input = 'input'
    output = 'output'
    temp = 'temp'


class ResultItem(BaseModel):
    filename: str | None = None
    subfolder: str | None = None
    type: Type | None = None
    display_name: str | None = None


class NodeOutputs(BaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    images: list[ResultItem] | None = None
    audio: list[ResultItem] | None = None
    video: list[ResultItem] | None = None
    animated: list[bool] | None = None
    text: str | list[str] | None = None


class TerminalSize(BaseModel):
    cols: float | None = None
    row: float | None = None


class LogEntry(BaseModel):
    t: str | None = Field(None, description='Timestamp')
    m: str | None = Field(None, description='Log message')


class ExecInfo1(BaseModel):
    queue_remaining: int


class StatusWsMessageStatus(BaseModel):
    exec_info: ExecInfo1 | None = None


class StatusWsMessage(BaseModel):
    status: StatusWsMessageStatus | None = None
    sid: str | None = Field(None, description='Session ID assigned by the server')


class ProgressWsMessage(BaseModel):
    value: int = Field(..., description='Current step')
    max: int = Field(..., description='Total steps')
    prompt_id: str
    node: str = Field(..., description='Node ID currently executing')


class ProgressTextWsMessage(BaseModel):
    nodeId: str | None = None
    text: str | None = None
    prompt_id: str | None = None


class State(Enum):
    pending = 'pending'
    running = 'running'
    finished = 'finished'
    error = 'error'


class NodeProgressState(BaseModel):
    value: float | None = None
    max: float | None = None
    state: State | None = None
    node_id: str | None = None
    prompt_id: str | None = None
    display_node_id: str | None = None
    parent_node_id: str | None = None
    real_node_id: str | None = None


class ProgressStateWsMessage(BaseModel):
    prompt_id: str
    nodes: dict[str, NodeProgressState] = Field(
        ..., description='Map of node ID to progress state'
    )


class ExecutingWsMessage(BaseModel):
    node: str = Field(..., description='Node ID')
    display_node: str = Field(
        ..., description='Display node ID (may differ for subgraphs)'
    )
    prompt_id: str


class ExecutedWsMessage(BaseModel):
    node: str
    display_node: str
    prompt_id: str
    output: NodeOutputs
    merge: bool | None = Field(
        None, description='Whether to merge with existing output'
    )


class ExecutionWsMessageBase(BaseModel):
    prompt_id: str
    timestamp: int = Field(..., description='Unix timestamp in milliseconds')


class ExecutionStartWsMessage(ExecutionWsMessageBase):
    pass


class ExecutionSuccessWsMessage(ExecutionWsMessageBase):
    pass


class ExecutionCachedWsMessage(ExecutionWsMessageBase):
    nodes: list[str] | None = Field(
        None, description='List of node IDs that were cached'
    )


class ExecutionInterruptedWsMessage(ExecutionWsMessageBase):
    node_id: str | None = None
    node_type: str | None = None
    executed: list[str] | None = Field(
        None, description='Node IDs that completed before interruption'
    )


class ExecutionErrorWsMessage(ExecutionWsMessageBase):
    node_id: str | None = None
    node_type: str | None = None
    executed: list[str] | None = None
    exception_message: str | None = None
    exception_type: str | None = None
    traceback: list[str] | None = None
    current_inputs: Any | None = None
    current_outputs: Any | None = None


class LogsWsMessage(BaseModel):
    size: TerminalSize | None = None
    entries: list[LogEntry] | None = None


class NotificationWsMessage(BaseModel):
    value: str | None = None
    id: str | None = None


class FeatureFlagsWsMessage(BaseModel):
    model_config = ConfigDict(
        extra='allow',
    )


class Status2(Enum):
    created = 'created'
    running = 'running'
    completed = 'completed'
    failed = 'failed'


class AssetDownloadWsMessage(BaseModel):
    task_id: str
    asset_name: str
    bytes_total: float
    bytes_downloaded: float
    progress: float = Field(..., description='0.0 to 1.0')
    status: Status2
    asset_id: str | None = None
    error: str | None = None


class AssetExportWsMessage(BaseModel):
    task_id: str
    export_name: str | None = None
    assets_total: float
    assets_attempted: float
    assets_failed: float
    bytes_total: float
    bytes_processed: float
    progress: float = Field(..., description='0.0 to 1.0')
    status: Status2
    error: str | None = None


class CloudError(BaseModel):
    error: str = Field(..., description='Error message')
    code: str | None = Field(None, description='Machine-readable error code')
    details: dict[str, Any] | None = Field(None, description='Additional error context')


class Status4(Enum):
    pending = 'pending'
    running = 'running'
    completed = 'completed'
    failed = 'failed'
    cancelled = 'cancelled'


class CloudJobStatus(BaseModel):
    id: UUID
    status: Status4
    progress: confloat(ge=0.0, le=1.0) | None = Field(
        None, description='Execution progress (0.0 to 1.0)'
    )
    started_at: AwareDatetime | None = None
    completed_at: AwareDatetime | None = None


class CloudPrompt(BaseModel):
    id: UUID
    status: str
    workflow: dict[str, Any] | None = None
    outputs: dict[str, Any] | None = None
    created_at: AwareDatetime | None = None
    completed_at: AwareDatetime | None = None


class HistoryV2Entry(BaseModel):
    id: UUID
    status: str
    workflow: dict[str, Any] | None = None
    outputs: dict[str, Any] | None = None
    created_at: AwareDatetime | None = None
    started_at: AwareDatetime | None = None
    completed_at: AwareDatetime | None = None
    preview_output: dict[str, Any] | None = None


class Level(Enum):
    debug = 'debug'
    info = 'info'
    warn = 'warn'
    error = 'error'


class Entry(BaseModel):
    timestamp: AwareDatetime | None = None
    level: Level | None = None
    message: str | None = None
    job_id: UUID | None = None


class CloudLogsResponse(BaseModel):
    entries: list[Entry]
    total: int | None = None
    has_more: bool | None = None


class AssetDownloadRequest(BaseModel):
    asset_id: UUID = Field(..., description='ID of the asset to download')
    target_path: str | None = Field(
        None, description='Target path on the runtime filesystem'
    )


class ImportPublishedAssetsRequest(BaseModel):
    published_asset_ids: list[str] = Field(
        ..., description='IDs of published assets (inputs and models) to import.'
    )
    share_id: str | None = Field(
        None,
        description="Optional. Share ID of the published workflow these assets belong to. When provided (non-null, non-empty): all `published_asset_ids` must belong to this share's workflow version; returns 400 if the share is not found or any asset does not belong to it. When omitted, null, or empty string: no share-scoped validation is performed and the assets are validated only against global rules (preserved for clients that have not yet adopted `share_id`).\n",
    )


class RemoteAssetMetadata(BaseModel):
    content_type: str | None = Field(None, description='MIME type of the remote file')
    content_length: int | None = Field(None, description='Size in bytes')
    filename: str | None = Field(
        None, description='Suggested filename from Content-Disposition or URL'
    )


class CloudNode(BaseModel):
    id: str
    name: str
    version: str | None = None
    description: str | None = None
    author: str | None = None
    repository: AnyUrl | None = None
    installed_at: AwareDatetime | None = None
    enabled: bool | None = None


class HubLabel(BaseModel):
    id: str
    name: str
    description: str | None = None
    color: str | None = Field(None, description='Hex color code for the label')


class HubProfile(BaseModel):
    username: str
    display_name: str | None = None
    bio: str | None = None
    avatar_url: AnyUrl | None = None
    links: list[AnyUrl] | None = None
    workflow_count: int | None = None
    created_at: AwareDatetime | None = None


class HubWorkflow(BaseModel):
    share_id: str
    name: str
    description: str | None = None
    author: HubProfile | None = None
    labels: list[HubLabel] | None = None
    thumbnail_url: AnyUrl | None = None
    content: dict[str, Any] | None = Field(None, description='Workflow graph JSON')
    likes: int | None = None
    views: int | None = None
    forks: int | None = None
    created_at: AwareDatetime | None = None
    updated_at: AwareDatetime | None = None


class HubWorkflowList(BaseModel):
    workflows: list[HubWorkflow]
    total: int
    has_more: bool


class HubWorkflowIndexEntry(BaseModel):
    share_id: str
    name: str
    author_username: str | None = None
    labels: list[str] | None = None
    likes: int | None = None
    updated_at: AwareDatetime | None = None


class CloudWorkflow(BaseModel):
    id: UUID
    name: str
    description: str | None = None
    share_id: str | None = Field(
        None, description='Public share identifier if published'
    )
    latest_version_id: UUID | None = None
    thumbnail_url: AnyUrl | None = None
    created_at: AwareDatetime | None = None
    updated_at: AwareDatetime | None = None


class CloudWorkflowList(BaseModel):
    workflows: list[CloudWorkflow]
    total: int
    has_more: bool


class CloudWorkflowVersion(BaseModel):
    id: UUID
    workflow_id: UUID
    version_number: int | None = None
    created_at: AwareDatetime | None = None


class AuthTokenResponse(BaseModel):
    access_token: str
    token_type: str = Field(..., description='Always "Bearer"')
    expires_in: int | None = Field(None, description='Token lifetime in seconds')
    refresh_token: str | None = None
    scope: str | None = None


class Key(BaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    kty: str = Field(..., description='Key type (e.g. RSA)')
    kid: str = Field(..., description='Key ID')
    use: str = Field(..., description='Key use (e.g. sig)')
    alg: str | None = Field(None, description='Algorithm (e.g. RS256)')
    n: str | None = Field(None, description='RSA modulus (base64url)')
    e: str | None = Field(None, description='RSA exponent (base64url)')


class JwksResponse(BaseModel):
    keys: list[Key]


class OAuthAuthorizationServerMetadata(BaseModel):
    issuer: AnyUrl
    authorization_endpoint: AnyUrl
    token_endpoint: AnyUrl
    jwks_uri: AnyUrl
    registration_endpoint: AnyUrl | None = Field(
        None,
        description='[cloud-only] RFC 7591 §3.1 Dynamic Client Registration endpoint. Advertised so MCP-spec-compliant clients can auto-discover and self-register without operator involvement. Present only when DCR is enabled.',
    )
    response_types_supported: list[str]
    grant_types_supported: list[str]
    code_challenge_methods_supported: list[str]
    token_endpoint_auth_methods_supported: list[str]
    scopes_supported: list[str] | None = None


class OAuthProtectedResourceMetadata(BaseModel):
    resource: AnyUrl
    authorization_servers: list[AnyUrl]
    scopes_supported: list[str]
    bearer_methods_supported: list[str] | None = None


class Type1(Enum):
    personal = 'personal'
    team = 'team'


class Role(Enum):
    owner = 'owner'
    member = 'member'


class OAuthConsentChallengeWorkspace(BaseModel):
    id: str
    name: str
    type: Type1
    role: Role


class OAuthAuthorizeRedirectResponse(BaseModel):
    redirect_url: AnyUrl = Field(
        ...,
        description='OAuth client redirect URI with either code+state for allow, or error+state for deny.',
    )


class TokenType(Enum):
    Bearer = 'Bearer'


class OAuthTokenResponse(BaseModel):
    access_token: str = Field(
        ...,
        description='Resource-bound access token (audience matches the protected resource).',
    )
    token_type: TokenType
    expires_in: int = Field(..., description='Access token lifetime in seconds.')
    refresh_token: str = Field(
        ...,
        description='Opaque refresh token. Rotates on every successful refresh; presenting an already-rotated token revokes the entire family.',
    )
    scope: str = Field(
        ..., description='Space-delimited scopes granted with this token.'
    )


class OAuthTokenError(BaseModel):
    error: str = Field(
        ...,
        description='RFC 6749 §5.2 error code: invalid_request, invalid_client, invalid_grant, unauthorized_client, unsupported_grant_type, invalid_scope.',
    )
    error_description: str | None = Field(
        None, description='Human-readable, no leak of internal storage state.'
    )


class ApplicationType(Enum):
    native = 'native'
    web = 'web'


class TokenEndpointAuthMethod(Enum):
    none = 'none'


class GrantType(Enum):
    authorization_code = 'authorization_code'
    refresh_token = 'refresh_token'


class ResponseType(Enum):
    code = 'code'


class OAuthRegisterRequest(BaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    redirect_uris: list[str] = Field(
        ...,
        description='1–5 redirect URIs. Validated against `application_type` policy.',
        max_length=5,
        min_length=1,
    )
    client_name: constr(max_length=100) | None = Field(
        None,
        description='Human-readable name shown in the consent UI. Reserved-name list rejects impersonation of major clients.',
    )
    application_type: ApplicationType = Field(
        ...,
        description='RFC 7591 §2 application_type. **REQUIRED** — clients MUST declare intent; the server does not default this field. `native` for desktop / CLI / MCP-spec-strict clients (loopback redirects); `web` for hosted clients (HTTPS only, host must be allowlisted). A missing or explicitly empty `application_type` rejects with `invalid_client_metadata`.\n',
    )
    token_endpoint_auth_method: TokenEndpointAuthMethod | None = Field(
        None,
        description='Public clients only this phase — must be `none` if present. The server forces `none` regardless.',
    )
    grant_types: list[GrantType] | None = Field(
        None,
        description='Optional. Defaults to `["authorization_code","refresh_token"]`.',
    )
    response_types: list[ResponseType] | None = Field(
        None, description='Optional. Defaults to `["code"]`.'
    )
    scope: str | None = Field(
        None,
        description="**REJECTED IF PRESENT.** Dynamic clients do not pick scopes — the server assigns scopes from the active resource's published list. Sending `scope` in the registration body is treated as a privilege-escalation attempt and returns `invalid_client_metadata`.",
    )
    resource_grants: dict[str, list[str]] | None = Field(
        None,
        description='**REJECTED IF PRESENT.** Same reason as `scope`. The set of resources and scopes a dynamic client may request is server-policy, not request-driven.',
    )
    client_uri: str | None = Field(
        None,
        description='**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase.',
    )
    logo_uri: str | None = Field(
        None,
        description='**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase.',
    )
    tos_uri: str | None = Field(
        None,
        description='**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase.',
    )
    policy_uri: str | None = Field(
        None,
        description='**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase.',
    )
    software_id: str | None = Field(
        None,
        description='**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase.',
    )
    software_version: str | None = Field(
        None,
        description='**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase.',
    )
    contacts: list[str] | None = Field(
        None,
        description='**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase.',
    )
    jwks: dict[str, Any] | None = Field(
        None,
        description='**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase.',
    )
    jwks_uri: str | None = Field(
        None,
        description='**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase.',
    )


class OAuthRegisterResponse(BaseModel):
    client_id: str = Field(..., description='Server-generated client_id.')
    client_id_issued_at: int = Field(
        ..., description='Unix timestamp (seconds) when the client was registered.'
    )
    client_name: str | None = None
    redirect_uris: list[str]
    grant_types: list[str]
    response_types: list[str]
    token_endpoint_auth_method: TokenEndpointAuthMethod
    application_type: ApplicationType


class Error1(Enum):
    invalid_redirect_uri = 'invalid_redirect_uri'
    invalid_client_metadata = 'invalid_client_metadata'


class OAuthRegisterError(BaseModel):
    error: Error1
    error_description: str | None = None


class BillingBalance(BaseModel):
    credits_remaining: int = Field(..., description='Available credits')
    credits_used: int | None = Field(
        None, description='Credits used in current billing period'
    )
    credits_total: int | None = Field(
        None, description='Total credits allocated in current period'
    )


class Type2(Enum):
    charge = 'charge'
    credit = 'credit'
    refund = 'refund'
    topup = 'topup'
    subscription = 'subscription'


class BillingEvent(BaseModel):
    id: str
    type: Type2
    amount: int = Field(..., description='Amount in credits')
    description: str | None = None
    job_id: UUID | None = None
    created_at: AwareDatetime


class BillingEventList(BaseModel):
    events: list[BillingEvent]
    total: int
    has_more: bool


class Status5(Enum):
    pending = 'pending'
    completed = 'completed'
    failed = 'failed'


class BillingOp(BaseModel):
    id: str
    status: Status5
    type: str | None = None
    amount: int | None = None
    created_at: AwareDatetime | None = None
    completed_at: AwareDatetime | None = None


class BillingPlan(BaseModel):
    id: str
    name: str
    description: str | None = None
    credits_per_month: int | None = None
    price_cents: int | None = Field(None, description='Monthly price in cents (USD)')
    currency: str | None = 'usd'
    features: list[str] | None = Field(None, description='List of plan features')


class BillingStatus(Enum):
    awaiting_payment_method = 'awaiting_payment_method'
    pending_payment = 'pending_payment'
    paid = 'paid'
    payment_failed = 'payment_failed'
    inactive = 'inactive'


class Status6(Enum):
    active = 'active'
    cancelled = 'cancelled'
    past_due = 'past_due'
    trialing = 'trialing'


class BillingSubscription(BaseModel):
    id: str
    status: Status6
    plan_id: str
    plan_name: str | None = None
    current_period_start: AwareDatetime | None = None
    current_period_end: AwareDatetime | None = None
    cancel_at_period_end: bool | None = None


class SubscriptionPreview(BaseModel):
    plan_id: str | None = None
    plan_name: str | None = None
    amount_due: int | None = Field(None, description='Amount due in cents')
    proration_amount: int | None = Field(
        None, description='Proration adjustment in cents'
    )
    currency: str | None = None
    next_billing_date: AwareDatetime | None = None


class Type3(Enum):
    personal = 'personal'
    team = 'team'


class Workspace(BaseModel):
    id: str
    name: str
    type: Type3 | None = Field(None, description='Workspace type (personal vs. team).')
    owner_id: str | None = None
    member_count: int | None = None
    created_at: AwareDatetime | None = None
    updated_at: AwareDatetime | None = None


class Role1(Enum):
    owner = 'owner'
    admin = 'admin'
    member = 'member'


class WorkspaceMember(BaseModel):
    user_id: str
    email: EmailStr | None = None
    display_name: str | None = None
    avatar_url: AnyUrl | None = None
    role: Role1
    joined_at: AwareDatetime | None = None


class Role2(Enum):
    admin = 'admin'
    member = 'member'


class WorkspaceInvite(BaseModel):
    id: str
    email: EmailStr
    role: Role2
    invited_by: str | None = None
    created_at: AwareDatetime | None = None
    expires_at: AwareDatetime | None = None


class WorkspaceApiKey(BaseModel):
    id: str
    name: str
    description: constr(max_length=5000) = Field(
        ...,
        description="User-provided description of the key's purpose. Always present in responses; empty string when no description was supplied on create.",
    )
    prefix: str | None = Field(
        None, description='First few characters of the key for identification'
    )
    created_at: AwareDatetime | None = None
    last_used_at: AwareDatetime | None = None
    created_by: str | None = None


class WorkspaceApiKeyCreated(BaseModel):
    id: str
    name: str
    description: constr(max_length=5000) = Field(
        ...,
        description="User-provided description of the key's purpose. Always present in responses; empty string when no description was supplied on create.",
    )
    key: str = Field(..., description='Full API key value (only returned on creation)')
    prefix: str | None = None
    created_at: AwareDatetime | None = None


class CloudUser(BaseModel):
    id: str
    email: EmailStr
    display_name: str | None = None
    avatar_url: AnyUrl | None = None
    created_at: AwareDatetime | None = None


class SecretMeta(BaseModel):
    id: str
    name: str
    provider: str | None = Field(
        None,
        description='[cloud-only] Provider identifier (e.g., huggingface, civitai).',
    )
    last_used_at: AwareDatetime | None = Field(
        None, description='[cloud-only] When the secret was last used for decryption.'
    )
    created_at: AwareDatetime | None = None
    updated_at: AwareDatetime | None = None


class UpdateSecretRequest(BaseModel):
    name: str | None = Field(None, description='New name for the secret')
    secret_value: str | None = Field(
        None, description='New secret value (API key, token, etc.)'
    )


class CreateSessionResponse(BaseModel):
    success: bool
    expiresIn: int | None = Field(
        None, description='Session expiration time in seconds.'
    )


class DeleteSessionResponse(BaseModel):
    success: bool


class CreateHubProfileRequest(BaseModel):
    workspace_id: str
    username: str = Field(
        ..., description='Unique URL-safe slug. Immutable after creation.'
    )
    display_name: str | None = None
    description: str | None = None
    avatar_token: str | None = None
    website_urls: list[str] | None = None


class ThumbnailType(Enum):
    image = 'image'
    video = 'video'
    image_comparison = 'image_comparison'


class PublishHubWorkflowRequest(BaseModel):
    username: str
    name: str
    workflow_filename: str
    asset_ids: list[str]
    description: str | None = None
    tags: list[str] | None = None
    models: list[str] | None = None
    custom_nodes: list[str] | None = None
    tutorial_url: str | None = None
    metadata: dict[str, Any] | None = None
    thumbnail_type: ThumbnailType | None = None
    thumbnail_token_or_url: str | None = None
    thumbnail_comparison_token_or_url: str | None = None
    sample_image_tokens_or_urls: list[str] | None = None


class Status7(Enum):
    pending = 'pending'
    approved = 'approved'
    rejected = 'rejected'
    deprecated = 'deprecated'


class AssetInfo(BaseModel):
    id: str = Field(..., description='Asset identifier.')
    filename: str
    mime_type: str | None = None
    size_bytes: int | None = None
    in_library: bool | None = Field(
        None, description='Whether the caller already owns this asset.'
    )
    model: bool | None = Field(None, description='Whether this asset is a model.')
    name: str | None = None
    preview_url: str | None = Field(
        None, description='Signed URL for previewing the asset.'
    )
    public: bool | None = Field(
        None, description='Whether this is a public (platform-provided) asset.'
    )
    storage_url: str | None = None


class BulkRevokeAPIKeysResponse(BaseModel):
    revoked_count: conint(ge=0)


class CreateWorkflowVersionRequest(BaseModel):
    base_version: int = Field(
        ...,
        description='Version number this change is based on (for optimistic concurrency).',
    )
    workflow_json: dict[str, Any] = Field(
        ..., description='The updated ComfyUI workflow JSON'
    )


class WorkflowVersionResponse(BaseModel):
    id: str
    version: int
    latest_version: int
    created_by: str
    created_at: AwareDatetime


class WorkflowPublishInfo(BaseModel):
    workflow_id: str
    share_id: str
    publish_time: AwareDatetime | None = None
    listed: bool
    assets: list[AssetInfo] = Field(
        ..., description='Published assets (inputs and models).'
    )


class Status8(Enum):
    created = 'created'
    running = 'running'
    completed = 'completed'
    failed = 'failed'


class TaskEntry(BaseModel):
    id: UUID = Field(..., description='Unique task identifier')
    task_name: str = Field(..., description='Task type name (e.g., model_upload)')
    status: Status8 = Field(..., description='Current task status')
    create_time: AwareDatetime = Field(..., description='Task creation timestamp')
    started_at: AwareDatetime | None = Field(
        None, description='When task execution started (null if not started)'
    )
    completed_at: AwareDatetime | None = Field(
        None, description='When task completed or failed (null if not finished)'
    )


class TaskResponse(BaseModel):
    id: UUID = Field(..., description='Unique task identifier')
    idempotency_key: str = Field(
        ..., description='Caller-provided key for idempotent task creation'
    )
    task_name: str = Field(..., description='Task type name (e.g., model_upload)')
    payload: dict[str, Any] = Field(..., description='Task input data')
    status: Status8 = Field(..., description='Current task status')
    result: dict[str, Any] | None = Field(
        None, description='Task output data (null if not completed)'
    )
    create_time: AwareDatetime = Field(..., description='Task creation timestamp')
    update_time: AwareDatetime = Field(..., description='Task last update timestamp')
    started_at: AwareDatetime | None = Field(
        None, description='When task execution started (null if not started)'
    )
    completed_at: AwareDatetime | None = Field(
        None, description='When task completed or failed (null if not finished)'
    )
    error: str | None = None
    error_message: str | None = Field(
        None, description='Error message on failure (null if not failed)'
    )


class TasksListResponse(BaseModel):
    tasks: list[TaskEntry] = Field(
        ..., description='Array of tasks ordered by create_time'
    )
    pagination: PaginationInfo


class AssetDownloadResponse(BaseModel):
    task_id: UUID = Field(
        ...,
        description='Task ID for tracking download progress via GET /api/tasks/{task_id}',
    )
    status: Status8 = Field(..., description='Current task status')
    message: str | None = Field(
        None,
        description='Human-readable message',
        examples=['Download task created. Use task_id to track progress.'],
    )


class BillingBalanceResponse(BaseModel):
    amount_micros: float = Field(
        ...,
        description='The total remaining balance in microamount (1/1,000,000 of the currency unit)',
    )
    prepaid_balance_micros: float | None = Field(
        None, description='The remaining balance from prepaid commits in microamount'
    )
    cloud_credit_balance_micros: float | None = Field(
        None, description='The remaining balance from cloud credits in microamount'
    )
    pending_charges_micros: float | None = Field(
        None,
        description='The total amount of pending/unbilled charges from draft invoices in microamount',
    )
    effective_balance_micros: float | None = Field(
        None,
        description='The effective balance (total balance minus pending charges). Can be negative if pending charges exceed the balance.',
    )
    currency: str = Field(..., description='Currency code', examples=['usd'])


class SubscriptionStatus(Enum):
    active = 'active'
    ended = 'ended'
    canceled = 'canceled'


class GetUserDataResponseFull(RootModel[list[GetUserDataResponseFullFile]]):
    root: list[GetUserDataResponseFullFile] = Field(
        ...,
        description='[cloud-only] List of user data file entries (each with path, size, and modification time) returned when full_info=true.',
    )


class Prompt1(BaseModel):
    priority: float | None = Field(None, description='Execution priority')
    prompt_id: str | None = Field(None, description='The prompt ID')
    prompt: dict[str, Any] | None = Field(None, description='The workflow nodes')
    extra_data: dict[str, Any] | None = Field(
        None, description='Additional execution data'
    )
    outputs_to_execute: list[str] | None = Field(
        None, description='Output nodes to execute'
    )


class HistoryDetailEntry(BaseModel):
    prompt: Prompt1 | None = Field(None, description='Full prompt execution data')
    outputs: dict[str, Any] | None = Field(
        None, description='Output data from execution (generated images, files, etc.)'
    )
    status: dict[str, Any] | None = Field(
        None, description='Execution status and timeline information'
    )
    meta: dict[str, Any] | None = Field(
        None, description='Metadata about the execution and nodes'
    )


class HistoryDetailResponse(RootModel[dict[str, HistoryDetailEntry]]):
    root: dict[str, HistoryDetailEntry]


class HistoryResponse(BaseModel):
    history: list[HistoryEntry] = Field(
        ...,
        description='Array of history entries ordered by creation time (newest first)',
    )


class Type4(Enum):
    tag = 'tag'
    model = 'model'
    custom_node = 'custom_node'


class HubLabelInfo(BaseModel):
    name: str = Field(..., description='Slug identifier.')
    display_name: str = Field(..., description='Human-readable display name.')
    description: str | None = Field(
        None, description='Optional description of the label.'
    )
    type: Type4 = Field(..., description='Label category.')


class HubLabelListResponse(BaseModel):
    labels: list[HubLabelInfo] = Field(
        ..., description='Available labels, optionally filtered by type.'
    )


class HubProfileSummary(BaseModel):
    username: str
    display_name: str | None = None
    avatar_url: str | None = Field(
        None, description='Public URL of the profile avatar image.'
    )


class HubWorkflowStatus(Enum):
    pending = 'pending'
    approved = 'approved'
    rejected = 'rejected'
    deprecated = 'deprecated'


class Io(BaseModel):
    inputs: list[dict[str, Any]] | None = None
    outputs: list[dict[str, Any]] | None = None


class FaqItem(BaseModel):
    question: str
    answer: str


class HubWorkflowTemplateEntry(BaseModel):
    name: str = Field(..., description='Slug identifier for the template')
    title: str
    status: HubWorkflowStatus
    description: str | None = None
    tags: list[str] | None = None
    models: list[str] | None = None
    requiresCustomNodes: list[str] | None = None
    thumbnailVariant: str | None = None
    mediaType: str | None = None
    mediaSubtype: str | None = None
    size: int | None = Field(None, description='Workflow asset size in bytes.')
    vram: int | None = Field(None, description='Approximate VRAM requirement in bytes.')
    usage: int | None = Field(None, description='Usage count reported upstream.')
    searchRank: int | None = Field(
        None, description='Search ranking score reported upstream.'
    )
    isEssential: bool | None = Field(
        None,
        description='Whether the template belongs to a module marked as essential.',
    )
    openSource: bool | None = None
    profile: HubProfileSummary | None = None
    tutorialUrl: str | None = None
    logos: list[dict[str, Any]] | None = None
    date: str | None = Field(None, description='Publication date in YYYY-MM-DD format')
    io: Io | None = None
    includeOnDistributions: list[str] | None = None
    thumbnailUrl: str | None = Field(
        None, description='Public URL of the primary thumbnail'
    )
    thumbnailComparisonUrl: str | None = Field(
        None, description='Public URL of the comparison thumbnail'
    )
    shareId: str | None = Field(
        None, description='Share ID for linking to the hub workflow detail'
    )
    extendedDescription: str | None = Field(
        None, description='AI-generated extended description of the workflow'
    )
    metaDescription: str | None = Field(
        None, description='AI-generated SEO meta description (under 160 chars)'
    )
    howToUse: list[str] | None = Field(
        None, description='AI-generated step-by-step usage instructions'
    )
    suggestedUseCases: list[str] | None = Field(
        None, description='AI-generated suggested use cases'
    )
    faqItems: list[FaqItem] | None = Field(None, description='AI-generated FAQ items')
    contentTemplate: str | None = Field(
        None,
        description='Content template used for generation (tutorial, showcase, comparison, breakthrough)',
    )


class Status11(Enum):
    waiting_to_dispatch = 'waiting_to_dispatch'
    pending = 'pending'
    in_progress = 'in_progress'
    completed = 'completed'
    error = 'error'
    cancelled = 'cancelled'


class JobStatusResponse(BaseModel):
    id: UUID = Field(..., description='The job ID')
    status: Status11 = Field(..., description='Current job status')
    created_at: AwareDatetime = Field(..., description='When the job was created')
    updated_at: AwareDatetime = Field(..., description='When the job was last updated')
    last_state_update: AwareDatetime | None = Field(
        None, description='When the job status was last changed'
    )
    assigned_inference: str | None = Field(
        None, description='The inference instance assigned to this job (if any)'
    )
    error_message: str | None = Field(
        None, description='Error message if the job failed'
    )


class LabelRef(BaseModel):
    name: str = Field(
        ..., description='Slug identifier (e.g. "video-generation", "flux").'
    )
    display_name: str = Field(
        ...,
        description='Human-readable display name (e.g. "Video Generation", "Flux").',
    )


class LogsResponseItem(BaseModel):
    timestamp: AwareDatetime | None = Field(
        None, description='When the log entry was created'
    )
    level: Level | None = Field(None, description='Log level')
    message: str | None = Field(None, description='Log message')
    source: str | None = Field(None, description='Source of the log entry')
    metadata: dict[str, Any] | None = Field(None, description='Additional log metadata')


class LogsResponse(RootModel[list[LogsResponseItem]]):
    root: list[LogsResponseItem] = Field(
        ..., description='[cloud-only] System logs response'
    )


class Role3(Enum):
    owner = 'owner'
    member = 'member'


class Member(BaseModel):
    id: str = Field(..., description='User ID')
    name: str = Field(..., description="User's display name")
    email: EmailStr = Field(..., description="User's email address")
    role: Role3 = Field(..., description="User's role in the workspace")
    joined_at: AwareDatetime = Field(
        ..., description='When the user joined the workspace'
    )


class PendingInvite(BaseModel):
    id: str = Field(..., description='Invite ID')
    email: EmailStr = Field(..., description='Email address of the invited user')
    token: str | None = Field(
        None,
        description='Invite token for constructing invite links. Empty for expired invites.',
    )
    invited_at: AwareDatetime = Field(..., description='When the invite was created')
    expires_at: AwareDatetime = Field(..., description='When the invite expires')


class PlanAvailabilityReason(Enum):
    same_plan = 'same_plan'
    incompatible_transition = 'incompatible_transition'
    requires_team = 'requires_team'
    requires_personal = 'requires_personal'
    exceeds_max_seats = 'exceeds_max_seats'


class PlanSeatSummary(BaseModel):
    seat_count: int = Field(
        ...,
        description='Total number of seats (owner + members) that would be charged',
        examples=[5],
    )
    total_cost_cents: int = Field(
        ..., description='Total cost for all seats in cents', examples=[50000]
    )
    total_credits_cents: int = Field(
        ...,
        description='Total credits granted for all seats in cents',
        examples=[50000],
    )


class TransitionType(Enum):
    new_subscription = 'new_subscription'
    upgrade = 'upgrade'
    downgrade = 'downgrade'
    duration_change = 'duration_change'


class PublishedWorkflowDetail(BaseModel):
    share_id: str
    workflow_id: str
    name: str = Field(..., description='Human-readable workflow name.')
    listed: bool
    publish_time: AwareDatetime | None = None
    workflow_json: dict[str, Any] = Field(
        ..., description='The workflow JSON content at publish time.'
    )
    assets: list[AssetInfo] = Field(
        ..., description='Published assets with their library status for the caller.'
    )


class SecretResponse(BaseModel):
    id: UUID = Field(..., description='Unique identifier for the secret')
    name: str = Field(..., description='User-provided label for the secret')
    provider: str | None = Field(
        None, description='Provider identifier (e.g., huggingface, civitai)'
    )
    last_used_at: AwareDatetime | None = Field(
        None, description='When the secret was last used for decryption'
    )
    created_at: AwareDatetime = Field(..., description='When the secret was created')
    updated_at: AwareDatetime = Field(
        ..., description='When the secret was last updated'
    )


class SubscriptionDuration(Enum):
    MONTHLY = 'MONTHLY'
    ANNUAL = 'ANNUAL'


class SubscriptionTier(Enum):
    FREE = 'FREE'
    STANDARD = 'STANDARD'
    CREATOR = 'CREATOR'
    PRO = 'PRO'
    FOUNDERS_EDITION = 'FOUNDERS_EDITION'


class UserDataResponseFull(BaseModel):
    path: str | None = None
    size: int | None = None
    modified: int | None = Field(
        None, description='UNIX timestamp of the last modification in milliseconds.'
    )


class ValidationError(BaseModel):
    code: str = Field(
        ..., description='Machine-readable error code', examples=['FORMAT_NOT_ALLOWED']
    )
    message: str = Field(
        ...,
        description='Human-readable error message',
        examples=[
            'File format "PickleTensor" is not allowed. Allowed formats: [SafeTensor]'
        ],
    )
    field: str = Field(
        ..., description='Field that failed validation', examples=['format']
    )


class ValidationResult(BaseModel):
    is_valid: bool = Field(
        ...,
        description='Overall validation status (true if all checks passed)',
        examples=[True],
    )
    errors: list[ValidationError] | None = Field(
        None, description='Blocking validation errors that prevent download'
    )
    warnings: list[ValidationError] | None = Field(
        None, description='Non-blocking validation warnings (informational only)'
    )


class WorkflowForkedFrom(BaseModel):
    workflow_id: str | None = None
    workflow_version_id: str | None = None


class DefaultView(Enum):
    workflow = 'workflow'
    app = 'app'


class WorkflowResponse(BaseModel):
    id: str
    name: str | None = None
    description: str | None = None
    default_view: DefaultView | None = None
    latest_version: int
    forked_from: WorkflowForkedFrom | None = None
    created_by: str
    created_at: AwareDatetime
    updated_at: AwareDatetime


class WorkflowVersionContentResponse(BaseModel):
    id: str
    version: int
    workflow_json: dict[str, Any]
    created_by: str
    created_at: AwareDatetime
    dependency_asset_ids: list[str] | None = None


class WorkspaceAPIKeyInfo(BaseModel):
    id: UUID = Field(..., description='API key ID')
    workspace_id: str = Field(..., description='Workspace this key belongs to')
    user_id: str = Field(..., description='User who created this key')
    name: str = Field(..., description='User-provided label')
    description: constr(max_length=5000) = Field(
        ...,
        description="User-provided description of the key's purpose. Limit is byte-based (UTF-8 encoding); 5000 bytes equals 5000 ASCII characters or fewer multi-byte characters.",
    )
    key_prefix: str = Field(..., description='First 8 chars after prefix for display')
    expires_at: AwareDatetime | None = Field(
        None, description='When the key expires (if set)'
    )
    last_used_at: AwareDatetime | None = Field(
        None, description='Last time the key was used'
    )
    revoked_at: AwareDatetime | None = Field(
        None, description='When the key was revoked (if revoked)'
    )
    created_at: AwareDatetime = Field(..., description='When the key was created')


class Type5(Enum):
    personal = 'personal'
    team = 'team'


class WorkspaceSummary(BaseModel):
    id: str = Field(..., examples=['w-a1b2c3d4-5678-90ab-cdef-1234567890ab'])
    name: str = Field(..., examples=['My Team'])
    type: Type5


class WorkspaceWithRole(BaseModel):
    id: str = Field(..., examples=['w-a1b2c3d4-5678-90ab-cdef-1234567890ab'])
    name: str = Field(..., examples=['My Team'])
    type: Type5
    role: Role3
    created_at: AwareDatetime = Field(..., description='When the workspace was created')
    joined_at: AwareDatetime = Field(
        ...,
        description='When the user joined the workspace (same as created_at for the workspace creator)',
    )
    subscription_tier: SubscriptionTier | None = None


class BindingErrorResponse(BaseModel):
    message: str


class ErrorResponse(BaseModel):
    code: str = Field(..., description='Machine-readable error code')
    message: str = Field(..., description='Human-readable error message')
    details: dict[str, Any] | None = Field(
        None,
        description='Optional open object carrying structured, machine-readable context about the error (e.g. offending field names, validation specifics). Absent for most errors; consumers must not assume any particular shape.',
    )


class AcceptInviteResponse(BaseModel):
    workspace_id: str = Field(..., description='ID of the workspace joined')
    workspace_name: str = Field(..., description='Name of the workspace joined')


class BillingEventsResponse(BaseModel):
    total: int = Field(..., description='Total number of events')
    events: list[BillingEvent]
    page: int = Field(..., description='Current page number (1-indexed)')
    limit: int = Field(..., description='Items per page')
    totalPages: int = Field(..., description='Total number of pages')


class Status12(Enum):
    pending = 'pending'
    succeeded = 'succeeded'
    failed = 'failed'


class BillingOpStatusResponse(BaseModel):
    id: str = Field(..., description='Unique identifier for the billing operation')
    status: Status12 = Field(..., description='Current status of the operation')
    error_message: str | None = Field(
        None, description='Error message if status is failed'
    )
    started_at: AwareDatetime = Field(
        ..., description='When the operation was initiated'
    )
    completed_at: AwareDatetime | None = Field(
        None, description='When the operation completed (success or failure)'
    )


class CancelSubscriptionResponse(BaseModel):
    billing_op_id: str = Field(
        ...,
        description='Billing operation ID to poll for status via GET /api/billing/ops/{id}',
    )
    cancel_at: AwareDatetime = Field(
        ...,
        description='The date when the subscription will end (end of current billing period)',
    )


class Status13(Enum):
    pending = 'pending'
    completed = 'completed'
    failed = 'failed'


class CreateTopupResponse(BaseModel):
    billing_op_id: str = Field(
        ...,
        description='Billing operation ID to poll for status via GET /api/billing/ops/{id}',
    )
    topup_id: str = Field(
        ...,
        description='Unique identifier for the top-up request (same as billing_op_id, deprecated)',
    )
    status: Status13 = Field(..., description='Current status of the top-up')
    amount_cents: int = Field(..., description='Amount being charged in cents')


class CreateWorkspaceAPIKeyResponse(BaseModel):
    id: UUID = Field(..., description='API key ID')
    name: str = Field(..., description='User-provided label')
    description: constr(max_length=5000) = Field(
        ...,
        description="User-provided description of the key's purpose. Limit is byte-based (UTF-8 encoding); 5000 bytes equals 5000 ASCII characters or fewer multi-byte characters.",
    )
    key: str = Field(..., description='The full plaintext API key (only shown once)')
    key_prefix: str = Field(..., description='First 8 chars after prefix for display')
    expires_at: AwareDatetime | None = Field(
        None, description='When the key expires (if set)'
    )
    created_at: AwareDatetime = Field(..., description='When the key was created')


class ExchangeTokenResponse(BaseModel):
    token: str = Field(..., description='Cloud JWT token')
    expires_at: AwareDatetime = Field(
        ..., description='Token expiration time (RFC 3339)'
    )
    workspace: WorkspaceSummary
    role: Role3 = Field(..., description="User's role in the workspace")
    permissions: list[str] = Field(
        ..., description='Permission strings for the role', examples=[['owner:*']]
    )


class JobCancelResponse(BaseModel):
    cancelled: bool = Field(
        ...,
        description='True when a cancel event was successfully dispatched by this call.\nFalse when the job was already in a terminal or cancelling state,\nin which case the call is a no-op (still 200 — idempotent).\n',
    )


class Status14(Enum):
    active = 'active'


class ResubscribeResponse(BaseModel):
    billing_op_id: str = Field(
        ...,
        description='Billing operation ID to poll for status via GET /api/billing/ops/{id}',
    )
    status: Status14 = Field(
        ..., description='The subscription status after resubscribing'
    )
    message: str | None = Field(None, description='Human-readable confirmation message')


class Status15(Enum):
    subscribed = 'subscribed'
    needs_payment_method = 'needs_payment_method'
    pending_payment = 'pending_payment'


class SubscribeResponse(BaseModel):
    billing_op_id: str = Field(
        ...,
        description='Billing operation ID to poll for status via GET /api/billing/ops/{id}',
    )
    status: Status15 = Field(
        ...,
        description='Status of the subscription operation:\n- subscribed: Subscription is active immediately\n- needs_payment_method: User must add payment method via payment_method_url\n- pending_payment: Upgrade initiated, waiting for payment to complete\n',
    )
    effective_at: AwareDatetime | None = Field(
        None,
        description='When the subscription became/becomes active (present when status=subscribed or pending_payment)',
    )
    payment_method_url: str | None = Field(
        None,
        description='URL to redirect user to add payment method (present when status=needs_payment_method)',
    )


class UserResponse(BaseModel):
    id: str = Field(..., description='Firebase UID of the authenticated user')
    status: str = Field(
        ..., description='User status (always "active" for authenticated users)'
    )


class WorkflowListResponse(BaseModel):
    data: list[WorkflowResponse]
    pagination: PaginationInfo


class Type7(Enum):
    missing_nodes = 'missing_nodes'
    general = 'general'
    missing_models = 'missing_models'


class FeedbackRequest(BaseModel):
    type: Type7 | None = Field(None, description='Feedback category')
    category: str | None = Field(None, description='Additional category metadata')
    message: str = Field(..., description='User-provided feedback message')
    content: str | None = Field(None, description='The feedback content or message')
    metadata: dict[str, Any] | None = Field(
        None, description='Additional metadata about the feedback'
    )
    rating: conint(ge=1, le=5) | None = Field(
        None, description="User's rating of ComfyUI Cloud experience (1-5 stars)"
    )


class InputSpec(RootModel[list[Any]]):
    root: list[Any]


class Input(BaseModel):
    required: dict[str, InputSpec]
    optional: dict[str, InputSpec] | None = None
    hidden: dict[str, InputSpec] | None = None


class Node(BaseModel):
    input: Input | None = None
    output: list[str] | None = None
    output_is_list: list[bool] | None = Field(
        None,
        description='Indicates if the output of the corresponding index as the item in this array is a list output.',
    )
    output_name: list[str] | None = None
    name: str | None = None
    display_name: str | None = None
    description: str | None = None
    category: str | None = None
    output_node: bool | None = None


class FieldMeta(BaseModel):
    title: str | None = Field(
        None,
        description='The title of the node when authored in the workflow. Set only when the end user changed it using the\npanel properties in the UI.\n',
    )


class PromptNode(BaseModel):
    field_meta: FieldMeta | None = Field(None, alias='_meta')
    class_type: str = Field(
        ...,
        description="The node's class type, which maps to a class in NODE_CLASS_MAPPINGS.",
    )
    inputs: dict[
        str, float | str | bool | dict[str, Any] | list[Any] | tuple[str, int] | None
    ] = Field(
        ...,
        description="The inputs for the node, which can be scalar values or references to other nodes' outputs.",
    )
    is_changed: list[str] | str | None = None


class Size(BaseModel):
    field_0: float | None = Field(None, alias='0')
    field_1: float | None = Field(None, alias='1')


class Input1(BaseModel):
    name: str | None = None
    type: str | None = None
    link: int | None = None


class Output(BaseModel):
    name: str | None = None
    type: str | None = None
    links: list[int] | None = None
    slot_index: int | None = None


class Node1(BaseModel):
    id: int | None = None
    type: str | None = None
    pos: list[float] | None = Field(None, max_length=2, min_length=2)
    size: Size | None = None
    flags: dict[str, dict[str, Any]] | None = None
    order: int | None = None
    mode: int | None = None
    inputs: list[Input1] | None = None
    outputs: list[Output] | None = None
    properties: dict[str, Any] | None = None
    widgets_values: list[str] | None = None


class Workflow(BaseModel):
    last_node_id: int | None = None
    last_link_id: int | None = None
    nodes: list[Node1] | None = None
    links: list[list[int | str]] | None = None
    groups: list[dict[str, Any]] | None = None
    config: dict[str, Any] | None = None
    extra: dict[str, Any] | None = None
    version: float | None = None


class QueueTuple(RootModel[list[Any]]):
    root: list[Any] = Field(
        ...,
        description='An item that was added to the queue.\n',
        max_length=5,
        min_length=3,
    )


class FileOutput(BaseModel):
    filename: str
    subfolder: str
    type: str
    abs_path: str


class ArbitraryObject(BaseModel):
    model_config = ConfigDict(
        extra='allow',
    )


class UploadImageResponse(BaseModel):
    name: str | None = None
    subfolder: str | None = None
    type: str | None = None


class ModelFolderInfo(BaseModel):
    name: str
    folders: list[str]


class ModelFileInfo(BaseModel):
    name: str
    pathIndex: int
    modified: float | None = None
    created: float | None = None
    size: int | None = None


class System1(BaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    os: str
    python_version: str
    embedded_python: bool
    comfyui_version: str
    pytorch_version: str
    required_frontend_version: str | None = None
    comfyui_frontend_version: str | None = None
    workflow_templates_version: str | None = None
    installed_templates_version: str | None = None
    required_templates_version: str | None = None
    cloud_version: str | None = None
    argv: list[str]
    ram_total: float
    ram_free: float


class SystemStats(BaseModel):
    system: System1
    devices: list[DeviceStats]


class Storage(Enum):
    server = 'server'


class UserConfig(BaseModel):
    storage: Storage
    migrated: bool | None = None
    users: dict[str, str] | None = None


class Settings(BaseModel):
    model_config = ConfigDict(
        extra='allow',
    )


class UserDataFileInfo(BaseModel):
    path: str
    size: int
    modified: float
    created: float | None = None


class LogsRawResponse(BaseModel):
    entries: list[LogEntry]
    size: TerminalSize


class FolderPaths(RootModel[dict[str, list[str]]]):
    root: dict[str, list[str]]


class GlobalSubgraphSummary(BaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    source: str | None = None
    name: str | None = None
    info: GlobalSubgraphInfo | None = None


class GlobalSubgraphDetail(GlobalSubgraphSummary):
    data: str | None = None


class WorkflowTemplatesMap(RootModel[dict[str, list[str]]]):
    root: dict[str, list[str]]


class CustomNodesI18n(BaseModel):
    model_config = ConfigDict(
        extra='allow',
    )


class Type8(Enum):
    output = 'output'
    input = 'input'
    temp = 'temp'


class PreviewOutput(BaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    filename: str | None = None
    subfolder: str | None = None
    type: Type8 | None = None
    nodeId: str
    mediaType: str
    display_name: str | None = None


class JobExecutionError(BaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    prompt_id: str | None = None
    timestamp: float | None = None
    node_id: str
    node_type: str
    executed: list[str] | None = None
    exception_message: str
    exception_type: str
    traceback: list[str]
    current_inputs: Any
    current_outputs: Any


class Status16(Enum):
    pending = 'pending'
    in_progress = 'in_progress'
    completed = 'completed'
    failed = 'failed'
    cancelled = 'cancelled'


class JobListItem(BaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    id: str
    status: Status16
    create_time: float
    execution_start_time: float | None = None
    execution_end_time: float | None = None
    preview_output: PreviewOutput | None = None
    outputs_count: int | None = None
    execution_error: JobExecutionError | None = None
    workflow_id: str | None = None
    priority: float | None = None


class StatusStr(Enum):
    success = 'success'
    error = 'error'


class ExecutionStatusAsDict(BaseModel):
    status_str: StatusStr
    completed: bool
    messages: list[str]


class CreateWorkflowRequest(BaseModel):
    default_view: DefaultView | None = Field(None, description='Default view mode')
    description: str | None = Field(None, description='Description of the workflow')
    forked_from_workflow_id: str | None = Field(
        None, description='ID of the source workflow if forked'
    )
    forked_from_workflow_version_id: str | None = Field(
        None, description='ID of the source workflow version if forked'
    )
    name: str | None = Field(None, description='Display name for the workflow')
    workflow_json: dict[str, Any] = Field(..., description='The ComfyUI workflow JSON')


class FeedbackResponse(BaseModel):
    pass


class ForkWorkflowRequest(BaseModel):
    name: str | None = Field(None, description='Name for the forked workflow')
    source_version: int = Field(..., description='Version number to fork from')


class JobsCancelRequest(BaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    job_ids: list[UUID] = Field(
        ...,
        description='Job identifiers (UUIDs) to cancel.',
        max_length=100,
        min_length=1,
    )


class JobsCancelResponse(BaseModel):
    cancelled: list[str] = Field(
        ...,
        description='Job IDs for which a cancel event was successfully dispatched by this\ncall. Jobs already in a terminal or cancelling state are idempotently\nskipped and will not appear here.\n',
    )


class PublishWorkflowAssetsRequest(BaseModel):
    asset_ids: list[str] = Field(
        ..., description='IDs of assets (inputs and models) to snapshot.'
    )


class UpdateWorkflowRequest(BaseModel):
    default_view: DefaultView | None = Field(None, description='New default view mode')
    description: str | None = Field(None, description='New description')
    name: str | None = Field(None, description='New display name')


class PromptResponse(BaseModel):
    prompt_id: UUID | None = Field(
        None, description='Unique identifier for the prompt execution'
    )
    number: float | None = Field(None, description='Priority number in the queue')
    node_errors: dict[str, NodeError] | None = Field(
        None, description='Validation errors keyed by node ID'
    )
    error: str | PromptError | None = Field(
        None, description='Top-level prompt error (string message or structured error)'
    )


class JobEntry(BaseModel):
    id: UUID = Field(..., description='Unique job identifier (same as prompt_id)')
    status: Status = Field(..., description='Current job status')
    create_time: int | None = Field(
        None, description='Job creation timestamp (Unix milliseconds).'
    )
    execution_start_time: int | None = Field(
        None,
        description='Workflow execution start timestamp (Unix milliseconds, terminal states only).',
    )
    execution_end_time: int | None = Field(
        None,
        description='Workflow execution end timestamp (Unix milliseconds, terminal states only).',
    )
    preview_output: dict[str, Any] | None = Field(
        None, description='Primary preview output'
    )
    outputs_count: int | None = Field(None, description='Total number of output files')
    workflow_id: str | None = Field(
        None,
        description='[cloud-only] UUID of the Cloud workflow entity this job is associated with. Local ComfyUI returns null.',
    )
    execution_error: ExecutionError | None = Field(
        None,
        description='[cloud-only] Detailed execution error from ComfyUI for failed jobs. Absent on local ComfyUI.',
    )


class ExecutionError1(ExecutionError):
    node_id: str = Field(..., description='ID of the node that failed')
    node_type: str = Field(..., description='Type name of the node')
    exception_message: str = Field(..., description='Human-readable error message')
    exception_type: str = Field(..., description='Python exception type')
    traceback: list[str] = Field(..., description='Traceback lines')
    current_inputs: dict[str, Any] = Field(
        ...,
        description='Input values at time of failure (empty object if not available)',
    )
    current_outputs: dict[str, Any] = Field(
        ...,
        description='Output values at time of failure (empty object if not available)',
    )


class JobDetailResponse(BaseModel):
    id: UUID = Field(..., description='Unique job identifier')
    status: Status = Field(..., description='User-friendly job status')
    workflow: dict[str, Any] | None = Field(None, description='Full ComfyUI workflow')
    outputs: dict[str, Any] | None = Field(
        None, description='Full outputs object from execution'
    )
    execution_error: ExecutionError1 | None = Field(
        None,
        description='Detailed execution error from ComfyUI (only for failed jobs with structured error data)',
    )
    create_time: int | None = Field(
        None, description='Job creation timestamp (Unix milliseconds).'
    )
    update_time: int | None = Field(
        None, description='Last state-change timestamp (Unix milliseconds).'
    )
    execution_start_time: int | None = Field(
        None,
        description='Workflow execution start timestamp (Unix milliseconds, terminal states only).',
    )
    execution_end_time: int | None = Field(
        None,
        description='Workflow execution end timestamp (Unix milliseconds, terminal states only).',
    )
    preview_output: dict[str, Any] | None = Field(
        None, description='Primary preview output (only for terminal states)'
    )
    outputs_count: int | None = Field(
        None,
        description='Total number of output files (omitted for non-terminal states)',
    )
    execution_status: dict[str, Any] | None = Field(
        None,
        description='ComfyUI execution status and timeline (only for terminal states)',
    )
    execution_meta: dict[str, Any] | None = Field(
        None, description='Node-level execution metadata (only for terminal states)'
    )
    user_id: str | None = Field(
        None,
        description="ID of the user that owns this job (see the `workspace_id`\ndescription above for why this is always the caller's own id\non a successful response).\n",
    )
    workflow_id: str | None = Field(
        None, description='UUID identifying the workflow graph definition'
    )
    workspace_id: str | None = Field(
        None,
        description="ID of the workspace that owns this job. A successful (200)\nresponse from this operation is only ever returned for the\ncaller's own job (see this operation's ownership-scoped\nquery), so this is always the caller's own workspace —\nconsumers that also need to correlate this job to its\nlive-progress broadcast channel (workspace+user scoped; see\nthe internal common/gateways/broadcast package) can use this\nvalue directly rather than resolving their own identity a\nsecond way.\n",
    )


class UserDataResponse(RootModel[GetUserDataResponseFullFile | str]):
    root: GetUserDataResponseFullFile | str = Field(
        ...,
        description='Response body for the POST endpoints `/api/userdata/{file}` and\n`/api/userdata/{file}/move/{dest}`. Returns a single item whose\nshape depends on the `full_info` query parameter.\n',
    )


class ListUserdataResponse(
    RootModel[list[GetUserDataResponseFullFile] | list[list[str]] | list[str]]
):
    root: list[GetUserDataResponseFullFile] | list[list[str]] | list[str] = Field(
        ...,
        description='Response body for `GET /api/userdata`. The array item shape is\ndetermined by the `full_info` and `split` query parameters.\n',
    )


class HistoryV2Response(BaseModel):
    items: list[HistoryV2Entry]
    total: int
    has_more: bool


class ImportPublishedAssetsResponse(BaseModel):
    assets: list[AssetInfo]


class AuthSession(BaseModel):
    user: CloudUser
    workspace: Workspace | None = None
    expires_at: AwareDatetime | None = None


class OAuthConsentChallenge(BaseModel):
    oauth_request_id: UUID = Field(
        ...,
        description='Opaque server-side identifier for the authorization-request row. Carried back unchanged in the consent submission.',
    )
    csrf_token: str = Field(
        ...,
        description='Per-row CSRF token bound to this authorization request (not to the session). Must be echoed back on POST.',
    )
    client_display_name: str = Field(
        ...,
        description='Human-readable name of the OAuth client requesting authorization.',
    )
    resource_display_name: str = Field(
        ..., description='Human-readable name of the protected resource.'
    )
    scopes: list[str] = Field(
        ...,
        description='Scopes the client is requesting for this resource. The frontend should present these for the user to approve.',
    )
    workspaces: list[OAuthConsentChallengeWorkspace] = Field(
        ...,
        description='Workspaces the user can select from. Membership is re-checked on POST.',
    )


class HubWorkflowDetail(BaseModel):
    share_id: str
    workflow_id: str
    name: str
    status: Status7
    description: str | None = None
    thumbnail_type: ThumbnailType | None = None
    thumbnail_url: str | None = None
    thumbnail_comparison_url: str | None = None
    tutorial_url: str | None = None
    metadata: dict[str, Any] | None = None
    sample_image_urls: list[str] | None = None
    publish_time: AwareDatetime | None = None
    workflow_json: dict[str, Any]
    assets: list[AssetInfo]
    profile: HubProfile


class AssetMetadataResponse(BaseModel):
    content_length: int = Field(
        ...,
        description='Size of the asset in bytes (-1 if unknown)',
        examples=[4294967296],
    )
    content_type: str | None = Field(
        None,
        description='MIME type of the asset',
        examples=['application/octet-stream'],
    )
    filename: str | None = Field(
        None,
        description='Suggested filename for the asset from source',
        examples=['realistic-vision-v5.safetensors'],
    )
    name: str | None = Field(
        None,
        description='Display name or title for the asset from source',
        examples=['Realistic Vision v5.0'],
    )
    tags: list[str] | None = Field(
        None,
        description='Tags for categorization from source',
        examples=[['models', 'checkpoint']],
    )
    preview_image: str | None = Field(
        None,
        description='Preview image as base64-encoded data URL',
        examples=['data:image/jpeg;base64,/9j/4AAQSkZJRg...'],
    )
    validation: ValidationResult | None = Field(
        None, description='Validation results for the file'
    )


class BillingStatusResponse(BaseModel):
    is_active: bool = Field(
        ..., description='Whether the workspace has an active subscription'
    )
    subscription_status: SubscriptionStatus | None = Field(
        None,
        description='Subscription activity status (scheduled subscriptions are not returned)',
    )
    subscription_tier: SubscriptionTier | None = None
    subscription_duration: SubscriptionDuration | None = None
    plan_slug: str | None = Field(
        None, description='Plan identifier (e.g., standard-monthly, team-pro-annual)'
    )
    billing_status: BillingStatus | None = None
    has_funds: bool = Field(
        ..., description='Whether the workspace has available credits'
    )
    cancel_at: AwareDatetime | None = Field(
        None, description='When the subscription will become inactive (if canceled)'
    )
    renewal_date: AwareDatetime | None = Field(
        None, description='When the current billing period ends and the next one begins'
    )


class HubWorkflowSummary(BaseModel):
    share_id: str
    name: str
    status: HubWorkflowStatus
    description: str | None = None
    tags: list[LabelRef] | None = None
    models: list[LabelRef] | None = None
    custom_nodes: list[LabelRef] | None = None
    thumbnail_type: ThumbnailType | None = None
    thumbnail_url: str | None = None
    thumbnail_comparison_url: str | None = None
    publish_time: AwareDatetime | None = None
    profile: HubProfileSummary
    metadata: dict[str, Any] | None = None
    tutorial_url: str | None = None
    sample_image_urls: list[str] | None = None


class JobsListResponse(BaseModel):
    jobs: list[JobEntry] = Field(
        ..., description='Array of jobs ordered by specified sort field'
    )
    pagination: PaginationInfo


class OAuthRegisterBadRequestResponse(
    RootModel[OAuthRegisterError | BindingErrorResponse]
):
    root: OAuthRegisterError | BindingErrorResponse = Field(
        ...,
        description='[cloud-only] Union of the two 400 shapes /oauth/register can emit. `OAuthRegisterError` is the handler-shaped RFC 7591 §3.2.2 error; `BindingErrorResponse` is the strict-server binding-layer error fired when the request body fails OpenAPI-schema validation before the handler runs.\n',
    )


class PlanAvailability(BaseModel):
    available: bool = Field(
        ..., description='Whether the workspace can subscribe to this plan'
    )
    reason: PlanAvailabilityReason | None = None


class PreviewPlanInfo(BaseModel):
    slug: str = Field(..., description='Plan slug', examples=['team-pro-monthly'])
    tier: SubscriptionTier
    duration: SubscriptionDuration
    price_cents: int = Field(
        ..., description='Per-seat price in cents', examples=[10000]
    )
    credits_cents: int = Field(
        ..., description='Per-seat credits in cents', examples=[10000]
    )
    seat_summary: PlanSeatSummary
    period_start: AwareDatetime | None = Field(
        None, description='Current billing period start (only for current_plan)'
    )
    period_end: AwareDatetime | None = Field(
        None, description='Current billing period end (only for current_plan)'
    )


class PreviewSubscribeResponse(BaseModel):
    allowed: bool = Field(
        ..., description='Whether this subscription change is allowed'
    )
    reason: str | None = Field(
        None,
        description='Reason why the change is not allowed (only present if allowed=false)',
    )
    transition_type: TransitionType = Field(
        ..., description='Type of subscription transition'
    )
    effective_at: AwareDatetime = Field(..., description='When the change takes effect')
    is_immediate: bool = Field(
        ...,
        description='Whether the change takes effect immediately (true) or at period end (false)',
    )
    cost_today_cents: int = Field(
        ...,
        description='Amount to charge today in cents (0 for downgrades)',
        examples=[5000],
    )
    cost_next_period_cents: int = Field(
        ...,
        description='Amount that will be charged at next billing period in cents',
        examples=[10000],
    )
    credits_today_cents: int = Field(
        ...,
        description='Credits granted today in cents (prorated for mid-period upgrades)',
        examples=[5000],
    )
    credits_next_period_cents: int = Field(
        ...,
        description='Credits that will be granted at next billing period in cents',
        examples=[10000],
    )
    current_plan: PreviewPlanInfo | None = None
    new_plan: PreviewPlanInfo


class ExtraPnginfo(BaseModel):
    workflow: Workflow | None = None


class ExtraData(BaseModel):
    extra_pnginfo: ExtraPnginfo | None = None


class Prompt2(RootModel[dict[str, PromptNode]]):
    root: dict[str, PromptNode]


class OutputModel(BaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    __pydantic_extra__: dict[str, FileOutput]
    images: list[FileOutput] | None = None
    latents: list[FileOutput] | None = None


class JobWorkflowContainer(BaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    prompt: Prompt2 | None = None
    extra_data: ExtraData | None = None


class HubWorkflowListResponse(BaseModel):
    workflows: list[HubWorkflowSummary | HubWorkflowDetail] = Field(
        ...,
        description='Array of HubWorkflowSummary (default) or HubWorkflowDetail (when detail=true).',
    )
    next_cursor: str | None = Field(
        None, description='Cursor for the next page, empty if no more results.'
    )


class Plan(BaseModel):
    slug: str = Field(
        ...,
        description='Plan identifier (e.g., "pro-monthly", "team-standard-annual")',
        examples=['pro-monthly'],
    )
    tier: SubscriptionTier
    duration: SubscriptionDuration
    price_cents: int = Field(
        ..., description='Per-member price in cents (base + one seat)', examples=[10000]
    )
    credits_cents: int = Field(
        ...,
        description='Per-member credits in cents (base + one seat)',
        examples=[10000],
    )
    max_seats: int = Field(
        ..., description='Maximum number of seats allowed for this plan', examples=[20]
    )
    availability: PlanAvailability
    seat_summary: PlanSeatSummary


class Outputs(RootModel[dict[str, OutputModel]]):
    root: dict[str, OutputModel]


class JobDetail(JobListItem):
    workflow: JobWorkflowContainer | None = None
    outputs: Outputs | None = None
    update_time: float | None = None
    execution_status: Any | None = None
    execution_meta: Any | None = None


class BillingPlansResponse(BaseModel):
    current_plan_slug: str | None = Field(
        None, description='Current plan slug if subscribed'
    )
    plans: list[Plan]


class NodeErrors(BaseModel):
    errors: list[ValidationErrorDict] | None = None
    dependent_outputs: list[str] | None = None
    class_type: str | None = None


class ExtraInfo1(BaseModel):
    exception_type: str
    traceback: list[str]
    node_errors: dict[str, NodeErrors] | None = Field(
        None, description='Detailed validation errors per node'
    )


class ValidationErrorDict(BaseModel):
    type: str
    message: str
    details: str
    extra_info: ExtraInfo1


NodeErrors.model_rebuild()
