comfykit


Namecomfykit JSON
Version 0.1.2 PyPI version JSON
download
home_pageNone
SummaryComfyUI Python SDK for developers - Build AI generation into your app
upload_time2025-10-18 09:21:33
maintainerNone
docs_urlNone
authorNone
requires_python>=3.10
licenseMIT
keywords ai api comfyui flux image generation runninghub sdk stable diffusion
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # ComfyKit

> **ComfyUI - UI + Kit = ComfyKit**
>
> Python SDK for ComfyUI - Support Local or Cloud - Generate images, videos, audio in 3 lines

<div align="center">

**English** | [δΈ­ζ–‡](README_CN.md)

[![PyPI version](https://badge.fury.io/py/comfykit.svg)](https://pypi.org/project/comfykit/)
[![Python](https://img.shields.io/pypi/pyversions/comfykit.svg)](https://pypi.org/project/comfykit/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![GitHub stars](https://img.shields.io/github/stars/puke3615/ComfyKit?style=social)](https://github.com/puke3615/ComfyKit)
[![GitHub last commit](https://img.shields.io/github/last-commit/puke3615/ComfyKit)](https://github.com/puke3615/ComfyKit)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/puke3615/ComfyKit/pulls)

[**πŸ“– Documentation**](https://puke3615.github.io/ComfyKit/) | 
[**πŸš€ Quick Start**](#-quick-start) | 
[**🎯 DSL Reference**](#️-workflow-dsl-quick-reference) | 
[**πŸ’‘ Examples**](examples/) | 
[**❓ Issues**](https://github.com/puke3615/ComfyKit/issues)

</div>

---

## ✨ What is ComfyKit?

**ComfyKit is a pure Python SDK** that provides a clean API for executing ComfyUI workflows and returns structured Python objects.

### Execute a workflow in 3 lines of code

```python
from comfykit import ComfyKit

kit = ComfyKit()
result = await kit.execute("workflow.json", {"prompt": "a cute cat"})

print(result.images)  # ['http://127.0.0.1:8188/view?filename=cat_001.png']
```

### Get structured data back

```python
# ExecuteResult object, not strings!
result.status          # "completed"
result.images          # All generated image URLs
result.images_by_var   # Images grouped by variable name
result.videos          # Video URLs (if any)
result.audios          # Audio URLs (if any)
result.duration        # Execution time
```

---

## 🎯 Key Features

- ⚑ **Zero Configuration**: Works out of the box, connects to local ComfyUI by default (`http://127.0.0.1:8188`)
- ☁️ **Cloud Execution**: Seamless RunningHub cloud support - **No GPU or local ComfyUI needed**
- 🎨 **Simple API**: 3 lines of code to execute workflows, no need to understand internals
- πŸ“Š **Structured Output**: Returns `ExecuteResult` objects, not strings
- πŸ”„ **Smart Detection**: Auto-detects local files, URLs, and RunningHub workflow IDs
- πŸ”Œ **Lightweight**: Less than 10 core dependencies
- 🎭 **Multimodal Support**: Images, videos, audio - all in one place

---

## πŸ“¦ Installation

### Using pip

```bash
pip install comfykit
```

### Using uv (recommended)

```bash
uv add comfykit
```

---

## πŸš€ Quick Start

### Option 1: RunningHub Cloud (No GPU needed) ⭐

If you don't have a local GPU or ComfyUI environment, use RunningHub cloud:

```python
import asyncio
from comfykit import ComfyKit

async def main():
    # Initialize with RunningHub (only API key needed)
    kit = ComfyKit(
        runninghub_api_key="your-runninghub-key"
    )
    
    # Execute with workflow ID
    result = await kit.execute("12345", {
        "prompt": "a beautiful sunset over the ocean"
    })
    
    print(f"πŸ–ΌοΈ  Generated images: {result.images}")

asyncio.run(main())
```

> πŸ’‘ **Tip**: Get your free API key at [RunningHub](https://www.runninghub.ai)

### Option 2: Local ComfyUI

If you have ComfyUI running locally:

#### 1. Start ComfyUI

```bash
# Start ComfyUI (default port 8188)
python main.py
```

#### 2. Execute workflow

```python
import asyncio
from comfykit import ComfyKit

async def main():
    # Initialize (uses default config)
    kit = ComfyKit()
    
    # Execute workflow
    result = await kit.execute(
        "workflow.json",
        params={"prompt": "a cute cat playing with yarn"}
    )
    
    # Check results
    if result.status == "completed":
        print(f"βœ… Success! Duration: {result.duration:.2f}s")
        print(f"πŸ–ΌοΈ  Images: {result.images}")
    else:
        print(f"❌ Failed: {result.msg}")

asyncio.run(main())
```

---

## πŸ“š Usage Examples

### Execute local ComfyUI workflow

```python
from comfykit import ComfyKit

# Connect to local ComfyUI (default)
kit = ComfyKit()

# Execute local workflow file
result = await kit.execute("workflow.json", {
    "prompt": "a cat",
    "seed": 42,
    "steps": 20
})
```

### Custom ComfyUI server

```python
# Connect to remote ComfyUI server
kit = ComfyKit(
    comfyui_url="http://my-server:8188",
    api_key="your-api-key"  # If authentication is required
)
```

### RunningHub cloud execution

```python
# Use RunningHub cloud (no local ComfyUI needed)
kit = ComfyKit(
    runninghub_api_key="your-runninghub-key"
)

# Execute with workflow ID
result = await kit.execute("12345", {
    "prompt": "a beautiful landscape"
})
```

### Execute remote workflow URL

```python
# Automatically download and execute
result = await kit.execute(
    "https://example.com/workflow.json",
    {"prompt": "a cat"}
)
```

### Execute workflow from dict

```python
workflow_dict = {
    "nodes": [...],
    "edges": [...]
}

result = await kit.execute_json(workflow_dict, {
    "prompt": "a cat"
})
```

### Process results

```python
result = await kit.execute("workflow.json", {"prompt": "a cat"})

# Basic info
print(f"Status: {result.status}")           # completed / failed
print(f"Duration: {result.duration}s")      # 3.45
print(f"Prompt ID: {result.prompt_id}")     # uuid

# Generated media files
print(f"Images: {result.images}")           # ['http://...']
print(f"Videos: {result.videos}")           # ['http://...']
print(f"Audios: {result.audios}")           # ['http://...']

# Grouped by variable name (if workflow defines output variables)
print(f"Cover: {result.images_by_var['cover']}")
print(f"Thumbnail: {result.images_by_var['thumbnail']}")
```

---

## 🏷️ Workflow DSL Quick Reference

ComfyKit provides a concise DSL (Domain Specific Language) for marking workflow nodes, allowing you to:
- Define dynamic parameters
- Mark output variables
- Specify required/optional parameters
- Automatically handle media file uploads

### DSL Syntax Quick Reference

| Syntax | Description | Example | Effect |
|--------|-------------|---------|--------|
| `$param` | Basic parameter (shorthand) | `$prompt` | Parameter `prompt`, maps to field `prompt` |
| `$param.field` | Specify field mapping | `$prompt.text` | Parameter `prompt`, maps to field `text` |
| `$param!` | Required parameter | `$prompt!` | Parameter `prompt` is required, no default |
| `$~param` | Media parameter (upload) | `$~image` | Parameter `image` requires file upload |
| `$~param!` | Required media parameter | `$~image!` | Parameter `image` is required and needs upload |
| `$param.~field!` | Combined markers | `$img.~image!` | Parameter `img` maps to `image`, required and upload |
| `$output.name` | Output variable marker | `$output.cover` | Mark output variable name as `cover` |
| `Text, $p1, $p2` | Multiple parameters | `Size, $width!, $height!` | Define multiple parameters in one node |

### Parameter Marking Examples

#### 1. Text Prompt Parameter

In a ComfyUI workflow CLIPTextEncode node:

```json
{
  "6": {
    "class_type": "CLIPTextEncode",
    "_meta": {
      "title": "$prompt.text!"
    },
    "inputs": {
      "text": "a beautiful landscape",
      "clip": ["4", 1]
    }
  }
}
```

**Marker explanation**:
- `$prompt` - Parameter name is `prompt`
- `.text` - Maps to node's `text` field
- `!` - Required parameter, must be provided

**Usage**:
```python
result = await kit.execute("workflow.json", {
    "prompt": "a cute cat"  # Replaces inputs.text value
})
```

#### 2. Image Upload Parameter

In a LoadImage node:

```json
{
  "10": {
    "class_type": "LoadImage",
    "_meta": {
      "title": "$~input_image!"
    },
    "inputs": {
      "image": "default.png"
    }
  }
}
```

**Marker explanation**:
- `$~input_image!` - Parameter `input_image`, needs upload (`~`), required (`!`)
- ComfyKit handles file upload automatically

**Usage**:
```python
result = await kit.execute("workflow.json", {
    "input_image": "/path/to/cat.jpg"  # Automatically uploads to ComfyUI
})
```

#### 3. Multiple Parameters in One Node

```json
{
  "5": {
    "class_type": "EmptyLatentImage",
    "_meta": {
      "title": "Size, $width!, $height!"
    },
    "inputs": {
      "width": 512,
      "height": 512,
      "batch_size": 1
    }
  }
}
```

**Marker explanation**:
- `Size` - Display text, not a parameter
- `$width!` - Required parameter `width` (shorthand, maps to same field)
- `$height!` - Required parameter `height`

**Usage**:
```python
result = await kit.execute("workflow.json", {
    "width": 1024,
    "height": 768
})
```

#### 4. Optional Parameters (with defaults)

```json
{
  "3": {
    "class_type": "KSampler",
    "_meta": {
      "title": "Sampler, $seed, $steps"
    },
    "inputs": {
      "seed": 0,          # Default value 0
      "steps": 20,        # Default value 20
      "cfg": 8.0,
      "model": ["4", 0]
    }
  }
}
```

**Marker explanation**:
- `$seed` and `$steps` have no `!`, they are optional
- If not provided, uses default values from workflow

**Usage**:
```python
# Use defaults
result = await kit.execute("workflow.json", {})

# Override some parameters
result = await kit.execute("workflow.json", {
    "seed": 42  # Only override seed, steps uses default 20
})
```

### Output Marking Examples

#### 1. Using Output Variable Marker

```json
{
  "9": {
    "class_type": "SaveImage",
    "_meta": {
      "title": "$output.cover"
    },
    "inputs": {
      "filename_prefix": "book_cover",
      "images": ["8", 0]
    }
  }
}
```

**Marker explanation**:
- `$output.cover` - Mark this node's output as `cover` variable

**Usage**:
```python
result = await kit.execute("workflow.json", params)

# Access output by variable name
cover_images = result.images_by_var["cover"]
print(f"Cover image: {cover_images[0]}")
```

#### 2. Multiple Output Variables

```json
{
  "9": {
    "class_type": "SaveImage",
    "_meta": {
      "title": "$output.cover"
    }
  },
  "15": {
    "class_type": "SaveImage",
    "_meta": {
      "title": "$output.thumbnail"
    }
  }
}
```

**Usage**:
```python
result = await kit.execute("workflow.json", params)

# Get different outputs separately
cover = result.images_by_var["cover"][0]
thumbnail = result.images_by_var["thumbnail"][0]
```

#### 3. Automatic Output Recognition (no marker needed)

If you don't use `$output.xxx` markers, ComfyKit auto-detects output nodes:

```json
{
  "9": {
    "class_type": "SaveImage",
    "_meta": {
      "title": "Final Output"
    }
  }
}
```

**Usage**:
```python
result = await kit.execute("workflow.json", params)

# All images are in the images list
all_images = result.images

# Access by node ID
images_from_node_9 = result.images_by_var["9"]
```

### DSL Best Practices

1. **Parameter Naming**: Use descriptive names like `$positive_prompt` instead of `$p`
2. **Required Markers**: Use `!` for parameters with no reasonable default
3. **Upload Markers**: Use `~` for image, video, audio parameters
4. **Output Variables**: Use `$output.xxx` for important outputs to make them easy to reference
5. **Display Text**: Add descriptive text in multi-param markers, e.g. `"Size, $width!, $height!"`

### Complete Example

A complete Text-to-Image workflow with DSL markers:

```json
{
  "4": {
    "class_type": "CheckpointLoaderSimple",
    "_meta": {
      "title": "$model.ckpt_name"
    },
    "inputs": {
      "ckpt_name": "sd_xl_base_1.0.safetensors"
    }
  },
  "5": {
    "class_type": "EmptyLatentImage",
    "_meta": {
      "title": "Canvas, $width!, $height!"
    },
    "inputs": {
      "width": 1024,
      "height": 1024,
      "batch_size": 1
    }
  },
  "6": {
    "class_type": "CLIPTextEncode",
    "_meta": {
      "title": "$prompt.text!"
    },
    "inputs": {
      "text": "a beautiful landscape",
      "clip": ["4", 1]
    }
  },
  "9": {
    "class_type": "SaveImage",
    "_meta": {
      "title": "$output.result"
    },
    "inputs": {
      "filename_prefix": "output",
      "images": ["8", 0]
    }
  }
}
```

**Execution**:
```python
result = await kit.execute("t2i_workflow.json", {
    "prompt": "a cute cat playing with yarn",
    "width": 1024,
    "height": 768,
    "model": "dreamshaper_8.safetensors"  # Optional, has default
})

# Get result
output_image = result.images_by_var["result"][0]
```

---

## βš™οΈ Configuration

### Configuration Priority

ComfyKit uses the following priority for configuration:

1. **Constructor parameters** (highest priority)
2. **Environment variables**
3. **Default values**

### Local ComfyUI configuration

```python
kit = ComfyKit(
    # ComfyUI server URL
    comfyui_url="http://127.0.0.1:8188",  # Default
    
    # Execution mode: http (recommended) or websocket
    executor_type="http",  # Default
    
    # API Key (if ComfyUI requires authentication)
    api_key="your-api-key",
    
    # Cookies (if needed)
    cookies="session=abc123"
)
```

### RunningHub cloud configuration

```python
kit = ComfyKit(
    # RunningHub API URL
    runninghub_url="https://www.runninghub.ai",  # Default
    
    # RunningHub API Key (required)
    runninghub_api_key="rh-key-xxx",
    
    # Timeout (seconds)
    runninghub_timeout=300,  # Default: 5 minutes
    
    # Retry count
    runninghub_retry_count=3  # Default: 3 retries
)
```

### Environment variables

```bash
# ComfyUI configuration
export COMFYUI_BASE_URL="http://127.0.0.1:8188"
export COMFYUI_EXECUTOR_TYPE="http"
export COMFYUI_API_KEY="your-api-key"
export COMFYUI_COOKIES="session=abc123"

# RunningHub configuration
export RUNNINGHUB_BASE_URL="https://www.runninghub.ai"
export RUNNINGHUB_API_KEY="rh-key-xxx"
export RUNNINGHUB_TIMEOUT="300"
export RUNNINGHUB_RETRY_COUNT="3"
```

---

## πŸ” ComfyKit vs ComfyUI Native API

| Aspect | ComfyUI Native API | ComfyKit |
|--------|-------------------|----------|
| **Complexity** | Manual WebSocket/HTTP handling | 3 lines of code |
| **Return Value** | Raw JSON, need to parse yourself | Structured `ExecuteResult` object |
| **Media Handling** | Need to construct URLs manually | Automatically generates complete media URLs |
| **Error Handling** | Need to implement yourself | Built-in comprehensive error handling |
| **Best For** | Familiar with ComfyUI internals | Just want quick integration |

---

## πŸ“– API Reference

### ComfyKit Class

```python
class ComfyKit:
    def __init__(
        self,
        # Local ComfyUI configuration
        comfyui_url: Optional[str] = None,
        executor_type: Literal["http", "websocket"] = "http",
        api_key: Optional[str] = None,
        cookies: Optional[str] = None,
        
        # RunningHub cloud configuration
        runninghub_url: Optional[str] = None,
        runninghub_api_key: Optional[str] = None,
        runninghub_timeout: int = 300,
        runninghub_retry_count: int = 3,
    ):
        """Initialize ComfyKit
        
        All parameters are optional and can be configured via environment variables
        """
        
    async def execute(
        self,
        workflow: Union[str, Path],
        params: Optional[Dict[str, Any]] = None,
    ) -> ExecuteResult:
        """Execute workflow
        
        Args:
            workflow: Workflow source, can be:
                     - Local file path: "workflow.json"
                     - RunningHub ID: "12345" (numeric)
                     - Remote URL: "https://example.com/workflow.json"
            params: Workflow parameters, e.g. {"prompt": "a cat", "seed": 42}
        
        Returns:
            ExecuteResult: Structured execution result
        """
        
    async def execute_json(
        self,
        workflow_json: Dict[str, Any],
        params: Optional[Dict[str, Any]] = None,
    ) -> ExecuteResult:
        """Execute workflow from JSON dict
        
        Args:
            workflow_json: Workflow JSON dict
            params: Workflow parameters
        
        Returns:
            ExecuteResult: Structured execution result
        """
```

### ExecuteResult Class

```python
class ExecuteResult:
    """Workflow execution result"""
    
    status: str                           # Execution status: "completed" / "failed"
    prompt_id: Optional[str]              # Prompt ID
    duration: Optional[float]             # Execution duration (seconds)
    
    # Media outputs
    images: List[str]                     # All image URLs
    videos: List[str]                     # All video URLs
    audios: List[str]                     # All audio URLs
    texts: List[str]                      # All text outputs
    
    # Grouped by variable name
    images_by_var: Dict[str, List[str]]   # Images grouped by variable name
    videos_by_var: Dict[str, List[str]]   # Videos grouped by variable name
    audios_by_var: Dict[str, List[str]]   # Audios grouped by variable name
    texts_by_var: Dict[str, List[str]]    # Texts grouped by variable name
    
    # Raw outputs
    outputs: Optional[Dict[str, Any]]     # Raw output data
    msg: Optional[str]                    # Error message (if failed)
```

---

## πŸ“‚ More Examples

The project includes complete example code in the `examples/` directory:

- [`01_quick_start.py`](examples/01_quick_start.py) - Quick start guide
- [`02_configuration.py`](examples/02_configuration.py) - Configuration options
- [`03_local_workflows.py`](examples/03_local_workflows.py) - Local workflow execution
- [`04_runninghub_cloud.py`](examples/04_runninghub_cloud.py) - RunningHub cloud execution
- [`05_advanced_features.py`](examples/05_advanced_features.py) - Advanced features

Run all examples:

```bash
cd examples
python run_all.py
```

---

## πŸ› οΈ Development

### Install development dependencies

```bash
uv sync --extra dev
```

### Run tests

```bash
pytest
```

### Code formatting

```bash
ruff check --fix
ruff format
```

---

## 🀝 Contributing

Contributions are welcome! Please check [Issues](https://github.com/puke3615/ComfyKit/issues) for areas that need help.

### Contribution Process

1. Fork this repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

---

## πŸ“„ License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

---

## πŸ™ Acknowledgments

- [ComfyUI](https://github.com/comfyanonymous/ComfyUI) - Powerful AI image generation framework
- [RunningHub](https://www.runninghub.ai) - ComfyUI cloud platform

---

## πŸ“ž Contact

- Author: Fan Wu
- Email: 1129090915@qq.com
- GitHub: [@puke3615](https://github.com/puke3615)

---

<div align="center">

**If ComfyKit helps you, please give it a ⭐ Star!**

[GitHub](https://github.com/puke3615/ComfyKit) Β· [PyPI](https://pypi.org/project/comfykit/) Β· [Issues](https://github.com/puke3615/ComfyKit/issues)

</div>

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "comfykit",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "AI, API, ComfyUI, FLUX, Image Generation, RunningHub, SDK, Stable Diffusion",
    "author": null,
    "author_email": "Fan Wu <1129090915@qq.com>",
    "download_url": "https://files.pythonhosted.org/packages/d0/2b/805903126e34769e870cfe857e5235c7799ae0f36f25cf9ddc41ef35d25f/comfykit-0.1.2.tar.gz",
    "platform": null,
    "description": "# ComfyKit\n\n> **ComfyUI - UI + Kit = ComfyKit**\n>\n> Python SDK for ComfyUI - Support Local or Cloud - Generate images, videos, audio in 3 lines\n\n<div align=\"center\">\n\n**English** | [\u4e2d\u6587](README_CN.md)\n\n[![PyPI version](https://badge.fury.io/py/comfykit.svg)](https://pypi.org/project/comfykit/)\n[![Python](https://img.shields.io/pypi/pyversions/comfykit.svg)](https://pypi.org/project/comfykit/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![GitHub stars](https://img.shields.io/github/stars/puke3615/ComfyKit?style=social)](https://github.com/puke3615/ComfyKit)\n[![GitHub last commit](https://img.shields.io/github/last-commit/puke3615/ComfyKit)](https://github.com/puke3615/ComfyKit)\n[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/puke3615/ComfyKit/pulls)\n\n[**\ud83d\udcd6 Documentation**](https://puke3615.github.io/ComfyKit/) | \n[**\ud83d\ude80 Quick Start**](#-quick-start) | \n[**\ud83c\udfaf DSL Reference**](#\ufe0f-workflow-dsl-quick-reference) | \n[**\ud83d\udca1 Examples**](examples/) | \n[**\u2753 Issues**](https://github.com/puke3615/ComfyKit/issues)\n\n</div>\n\n---\n\n## \u2728 What is ComfyKit?\n\n**ComfyKit is a pure Python SDK** that provides a clean API for executing ComfyUI workflows and returns structured Python objects.\n\n### Execute a workflow in 3 lines of code\n\n```python\nfrom comfykit import ComfyKit\n\nkit = ComfyKit()\nresult = await kit.execute(\"workflow.json\", {\"prompt\": \"a cute cat\"})\n\nprint(result.images)  # ['http://127.0.0.1:8188/view?filename=cat_001.png']\n```\n\n### Get structured data back\n\n```python\n# ExecuteResult object, not strings!\nresult.status          # \"completed\"\nresult.images          # All generated image URLs\nresult.images_by_var   # Images grouped by variable name\nresult.videos          # Video URLs (if any)\nresult.audios          # Audio URLs (if any)\nresult.duration        # Execution time\n```\n\n---\n\n## \ud83c\udfaf Key Features\n\n- \u26a1 **Zero Configuration**: Works out of the box, connects to local ComfyUI by default (`http://127.0.0.1:8188`)\n- \u2601\ufe0f **Cloud Execution**: Seamless RunningHub cloud support - **No GPU or local ComfyUI needed**\n- \ud83c\udfa8 **Simple API**: 3 lines of code to execute workflows, no need to understand internals\n- \ud83d\udcca **Structured Output**: Returns `ExecuteResult` objects, not strings\n- \ud83d\udd04 **Smart Detection**: Auto-detects local files, URLs, and RunningHub workflow IDs\n- \ud83d\udd0c **Lightweight**: Less than 10 core dependencies\n- \ud83c\udfad **Multimodal Support**: Images, videos, audio - all in one place\n\n---\n\n## \ud83d\udce6 Installation\n\n### Using pip\n\n```bash\npip install comfykit\n```\n\n### Using uv (recommended)\n\n```bash\nuv add comfykit\n```\n\n---\n\n## \ud83d\ude80 Quick Start\n\n### Option 1: RunningHub Cloud (No GPU needed) \u2b50\n\nIf you don't have a local GPU or ComfyUI environment, use RunningHub cloud:\n\n```python\nimport asyncio\nfrom comfykit import ComfyKit\n\nasync def main():\n    # Initialize with RunningHub (only API key needed)\n    kit = ComfyKit(\n        runninghub_api_key=\"your-runninghub-key\"\n    )\n    \n    # Execute with workflow ID\n    result = await kit.execute(\"12345\", {\n        \"prompt\": \"a beautiful sunset over the ocean\"\n    })\n    \n    print(f\"\ud83d\uddbc\ufe0f  Generated images: {result.images}\")\n\nasyncio.run(main())\n```\n\n> \ud83d\udca1 **Tip**: Get your free API key at [RunningHub](https://www.runninghub.ai)\n\n### Option 2: Local ComfyUI\n\nIf you have ComfyUI running locally:\n\n#### 1. Start ComfyUI\n\n```bash\n# Start ComfyUI (default port 8188)\npython main.py\n```\n\n#### 2. Execute workflow\n\n```python\nimport asyncio\nfrom comfykit import ComfyKit\n\nasync def main():\n    # Initialize (uses default config)\n    kit = ComfyKit()\n    \n    # Execute workflow\n    result = await kit.execute(\n        \"workflow.json\",\n        params={\"prompt\": \"a cute cat playing with yarn\"}\n    )\n    \n    # Check results\n    if result.status == \"completed\":\n        print(f\"\u2705 Success! Duration: {result.duration:.2f}s\")\n        print(f\"\ud83d\uddbc\ufe0f  Images: {result.images}\")\n    else:\n        print(f\"\u274c Failed: {result.msg}\")\n\nasyncio.run(main())\n```\n\n---\n\n## \ud83d\udcda Usage Examples\n\n### Execute local ComfyUI workflow\n\n```python\nfrom comfykit import ComfyKit\n\n# Connect to local ComfyUI (default)\nkit = ComfyKit()\n\n# Execute local workflow file\nresult = await kit.execute(\"workflow.json\", {\n    \"prompt\": \"a cat\",\n    \"seed\": 42,\n    \"steps\": 20\n})\n```\n\n### Custom ComfyUI server\n\n```python\n# Connect to remote ComfyUI server\nkit = ComfyKit(\n    comfyui_url=\"http://my-server:8188\",\n    api_key=\"your-api-key\"  # If authentication is required\n)\n```\n\n### RunningHub cloud execution\n\n```python\n# Use RunningHub cloud (no local ComfyUI needed)\nkit = ComfyKit(\n    runninghub_api_key=\"your-runninghub-key\"\n)\n\n# Execute with workflow ID\nresult = await kit.execute(\"12345\", {\n    \"prompt\": \"a beautiful landscape\"\n})\n```\n\n### Execute remote workflow URL\n\n```python\n# Automatically download and execute\nresult = await kit.execute(\n    \"https://example.com/workflow.json\",\n    {\"prompt\": \"a cat\"}\n)\n```\n\n### Execute workflow from dict\n\n```python\nworkflow_dict = {\n    \"nodes\": [...],\n    \"edges\": [...]\n}\n\nresult = await kit.execute_json(workflow_dict, {\n    \"prompt\": \"a cat\"\n})\n```\n\n### Process results\n\n```python\nresult = await kit.execute(\"workflow.json\", {\"prompt\": \"a cat\"})\n\n# Basic info\nprint(f\"Status: {result.status}\")           # completed / failed\nprint(f\"Duration: {result.duration}s\")      # 3.45\nprint(f\"Prompt ID: {result.prompt_id}\")     # uuid\n\n# Generated media files\nprint(f\"Images: {result.images}\")           # ['http://...']\nprint(f\"Videos: {result.videos}\")           # ['http://...']\nprint(f\"Audios: {result.audios}\")           # ['http://...']\n\n# Grouped by variable name (if workflow defines output variables)\nprint(f\"Cover: {result.images_by_var['cover']}\")\nprint(f\"Thumbnail: {result.images_by_var['thumbnail']}\")\n```\n\n---\n\n## \ud83c\udff7\ufe0f Workflow DSL Quick Reference\n\nComfyKit provides a concise DSL (Domain Specific Language) for marking workflow nodes, allowing you to:\n- Define dynamic parameters\n- Mark output variables\n- Specify required/optional parameters\n- Automatically handle media file uploads\n\n### DSL Syntax Quick Reference\n\n| Syntax | Description | Example | Effect |\n|--------|-------------|---------|--------|\n| `$param` | Basic parameter (shorthand) | `$prompt` | Parameter `prompt`, maps to field `prompt` |\n| `$param.field` | Specify field mapping | `$prompt.text` | Parameter `prompt`, maps to field `text` |\n| `$param!` | Required parameter | `$prompt!` | Parameter `prompt` is required, no default |\n| `$~param` | Media parameter (upload) | `$~image` | Parameter `image` requires file upload |\n| `$~param!` | Required media parameter | `$~image!` | Parameter `image` is required and needs upload |\n| `$param.~field!` | Combined markers | `$img.~image!` | Parameter `img` maps to `image`, required and upload |\n| `$output.name` | Output variable marker | `$output.cover` | Mark output variable name as `cover` |\n| `Text, $p1, $p2` | Multiple parameters | `Size, $width!, $height!` | Define multiple parameters in one node |\n\n### Parameter Marking Examples\n\n#### 1. Text Prompt Parameter\n\nIn a ComfyUI workflow CLIPTextEncode node:\n\n```json\n{\n  \"6\": {\n    \"class_type\": \"CLIPTextEncode\",\n    \"_meta\": {\n      \"title\": \"$prompt.text!\"\n    },\n    \"inputs\": {\n      \"text\": \"a beautiful landscape\",\n      \"clip\": [\"4\", 1]\n    }\n  }\n}\n```\n\n**Marker explanation**:\n- `$prompt` - Parameter name is `prompt`\n- `.text` - Maps to node's `text` field\n- `!` - Required parameter, must be provided\n\n**Usage**:\n```python\nresult = await kit.execute(\"workflow.json\", {\n    \"prompt\": \"a cute cat\"  # Replaces inputs.text value\n})\n```\n\n#### 2. Image Upload Parameter\n\nIn a LoadImage node:\n\n```json\n{\n  \"10\": {\n    \"class_type\": \"LoadImage\",\n    \"_meta\": {\n      \"title\": \"$~input_image!\"\n    },\n    \"inputs\": {\n      \"image\": \"default.png\"\n    }\n  }\n}\n```\n\n**Marker explanation**:\n- `$~input_image!` - Parameter `input_image`, needs upload (`~`), required (`!`)\n- ComfyKit handles file upload automatically\n\n**Usage**:\n```python\nresult = await kit.execute(\"workflow.json\", {\n    \"input_image\": \"/path/to/cat.jpg\"  # Automatically uploads to ComfyUI\n})\n```\n\n#### 3. Multiple Parameters in One Node\n\n```json\n{\n  \"5\": {\n    \"class_type\": \"EmptyLatentImage\",\n    \"_meta\": {\n      \"title\": \"Size, $width!, $height!\"\n    },\n    \"inputs\": {\n      \"width\": 512,\n      \"height\": 512,\n      \"batch_size\": 1\n    }\n  }\n}\n```\n\n**Marker explanation**:\n- `Size` - Display text, not a parameter\n- `$width!` - Required parameter `width` (shorthand, maps to same field)\n- `$height!` - Required parameter `height`\n\n**Usage**:\n```python\nresult = await kit.execute(\"workflow.json\", {\n    \"width\": 1024,\n    \"height\": 768\n})\n```\n\n#### 4. Optional Parameters (with defaults)\n\n```json\n{\n  \"3\": {\n    \"class_type\": \"KSampler\",\n    \"_meta\": {\n      \"title\": \"Sampler, $seed, $steps\"\n    },\n    \"inputs\": {\n      \"seed\": 0,          # Default value 0\n      \"steps\": 20,        # Default value 20\n      \"cfg\": 8.0,\n      \"model\": [\"4\", 0]\n    }\n  }\n}\n```\n\n**Marker explanation**:\n- `$seed` and `$steps` have no `!`, they are optional\n- If not provided, uses default values from workflow\n\n**Usage**:\n```python\n# Use defaults\nresult = await kit.execute(\"workflow.json\", {})\n\n# Override some parameters\nresult = await kit.execute(\"workflow.json\", {\n    \"seed\": 42  # Only override seed, steps uses default 20\n})\n```\n\n### Output Marking Examples\n\n#### 1. Using Output Variable Marker\n\n```json\n{\n  \"9\": {\n    \"class_type\": \"SaveImage\",\n    \"_meta\": {\n      \"title\": \"$output.cover\"\n    },\n    \"inputs\": {\n      \"filename_prefix\": \"book_cover\",\n      \"images\": [\"8\", 0]\n    }\n  }\n}\n```\n\n**Marker explanation**:\n- `$output.cover` - Mark this node's output as `cover` variable\n\n**Usage**:\n```python\nresult = await kit.execute(\"workflow.json\", params)\n\n# Access output by variable name\ncover_images = result.images_by_var[\"cover\"]\nprint(f\"Cover image: {cover_images[0]}\")\n```\n\n#### 2. Multiple Output Variables\n\n```json\n{\n  \"9\": {\n    \"class_type\": \"SaveImage\",\n    \"_meta\": {\n      \"title\": \"$output.cover\"\n    }\n  },\n  \"15\": {\n    \"class_type\": \"SaveImage\",\n    \"_meta\": {\n      \"title\": \"$output.thumbnail\"\n    }\n  }\n}\n```\n\n**Usage**:\n```python\nresult = await kit.execute(\"workflow.json\", params)\n\n# Get different outputs separately\ncover = result.images_by_var[\"cover\"][0]\nthumbnail = result.images_by_var[\"thumbnail\"][0]\n```\n\n#### 3. Automatic Output Recognition (no marker needed)\n\nIf you don't use `$output.xxx` markers, ComfyKit auto-detects output nodes:\n\n```json\n{\n  \"9\": {\n    \"class_type\": \"SaveImage\",\n    \"_meta\": {\n      \"title\": \"Final Output\"\n    }\n  }\n}\n```\n\n**Usage**:\n```python\nresult = await kit.execute(\"workflow.json\", params)\n\n# All images are in the images list\nall_images = result.images\n\n# Access by node ID\nimages_from_node_9 = result.images_by_var[\"9\"]\n```\n\n### DSL Best Practices\n\n1. **Parameter Naming**: Use descriptive names like `$positive_prompt` instead of `$p`\n2. **Required Markers**: Use `!` for parameters with no reasonable default\n3. **Upload Markers**: Use `~` for image, video, audio parameters\n4. **Output Variables**: Use `$output.xxx` for important outputs to make them easy to reference\n5. **Display Text**: Add descriptive text in multi-param markers, e.g. `\"Size, $width!, $height!\"`\n\n### Complete Example\n\nA complete Text-to-Image workflow with DSL markers:\n\n```json\n{\n  \"4\": {\n    \"class_type\": \"CheckpointLoaderSimple\",\n    \"_meta\": {\n      \"title\": \"$model.ckpt_name\"\n    },\n    \"inputs\": {\n      \"ckpt_name\": \"sd_xl_base_1.0.safetensors\"\n    }\n  },\n  \"5\": {\n    \"class_type\": \"EmptyLatentImage\",\n    \"_meta\": {\n      \"title\": \"Canvas, $width!, $height!\"\n    },\n    \"inputs\": {\n      \"width\": 1024,\n      \"height\": 1024,\n      \"batch_size\": 1\n    }\n  },\n  \"6\": {\n    \"class_type\": \"CLIPTextEncode\",\n    \"_meta\": {\n      \"title\": \"$prompt.text!\"\n    },\n    \"inputs\": {\n      \"text\": \"a beautiful landscape\",\n      \"clip\": [\"4\", 1]\n    }\n  },\n  \"9\": {\n    \"class_type\": \"SaveImage\",\n    \"_meta\": {\n      \"title\": \"$output.result\"\n    },\n    \"inputs\": {\n      \"filename_prefix\": \"output\",\n      \"images\": [\"8\", 0]\n    }\n  }\n}\n```\n\n**Execution**:\n```python\nresult = await kit.execute(\"t2i_workflow.json\", {\n    \"prompt\": \"a cute cat playing with yarn\",\n    \"width\": 1024,\n    \"height\": 768,\n    \"model\": \"dreamshaper_8.safetensors\"  # Optional, has default\n})\n\n# Get result\noutput_image = result.images_by_var[\"result\"][0]\n```\n\n---\n\n## \u2699\ufe0f Configuration\n\n### Configuration Priority\n\nComfyKit uses the following priority for configuration:\n\n1. **Constructor parameters** (highest priority)\n2. **Environment variables**\n3. **Default values**\n\n### Local ComfyUI configuration\n\n```python\nkit = ComfyKit(\n    # ComfyUI server URL\n    comfyui_url=\"http://127.0.0.1:8188\",  # Default\n    \n    # Execution mode: http (recommended) or websocket\n    executor_type=\"http\",  # Default\n    \n    # API Key (if ComfyUI requires authentication)\n    api_key=\"your-api-key\",\n    \n    # Cookies (if needed)\n    cookies=\"session=abc123\"\n)\n```\n\n### RunningHub cloud configuration\n\n```python\nkit = ComfyKit(\n    # RunningHub API URL\n    runninghub_url=\"https://www.runninghub.ai\",  # Default\n    \n    # RunningHub API Key (required)\n    runninghub_api_key=\"rh-key-xxx\",\n    \n    # Timeout (seconds)\n    runninghub_timeout=300,  # Default: 5 minutes\n    \n    # Retry count\n    runninghub_retry_count=3  # Default: 3 retries\n)\n```\n\n### Environment variables\n\n```bash\n# ComfyUI configuration\nexport COMFYUI_BASE_URL=\"http://127.0.0.1:8188\"\nexport COMFYUI_EXECUTOR_TYPE=\"http\"\nexport COMFYUI_API_KEY=\"your-api-key\"\nexport COMFYUI_COOKIES=\"session=abc123\"\n\n# RunningHub configuration\nexport RUNNINGHUB_BASE_URL=\"https://www.runninghub.ai\"\nexport RUNNINGHUB_API_KEY=\"rh-key-xxx\"\nexport RUNNINGHUB_TIMEOUT=\"300\"\nexport RUNNINGHUB_RETRY_COUNT=\"3\"\n```\n\n---\n\n## \ud83d\udd0d ComfyKit vs ComfyUI Native API\n\n| Aspect | ComfyUI Native API | ComfyKit |\n|--------|-------------------|----------|\n| **Complexity** | Manual WebSocket/HTTP handling | 3 lines of code |\n| **Return Value** | Raw JSON, need to parse yourself | Structured `ExecuteResult` object |\n| **Media Handling** | Need to construct URLs manually | Automatically generates complete media URLs |\n| **Error Handling** | Need to implement yourself | Built-in comprehensive error handling |\n| **Best For** | Familiar with ComfyUI internals | Just want quick integration |\n\n---\n\n## \ud83d\udcd6 API Reference\n\n### ComfyKit Class\n\n```python\nclass ComfyKit:\n    def __init__(\n        self,\n        # Local ComfyUI configuration\n        comfyui_url: Optional[str] = None,\n        executor_type: Literal[\"http\", \"websocket\"] = \"http\",\n        api_key: Optional[str] = None,\n        cookies: Optional[str] = None,\n        \n        # RunningHub cloud configuration\n        runninghub_url: Optional[str] = None,\n        runninghub_api_key: Optional[str] = None,\n        runninghub_timeout: int = 300,\n        runninghub_retry_count: int = 3,\n    ):\n        \"\"\"Initialize ComfyKit\n        \n        All parameters are optional and can be configured via environment variables\n        \"\"\"\n        \n    async def execute(\n        self,\n        workflow: Union[str, Path],\n        params: Optional[Dict[str, Any]] = None,\n    ) -> ExecuteResult:\n        \"\"\"Execute workflow\n        \n        Args:\n            workflow: Workflow source, can be:\n                     - Local file path: \"workflow.json\"\n                     - RunningHub ID: \"12345\" (numeric)\n                     - Remote URL: \"https://example.com/workflow.json\"\n            params: Workflow parameters, e.g. {\"prompt\": \"a cat\", \"seed\": 42}\n        \n        Returns:\n            ExecuteResult: Structured execution result\n        \"\"\"\n        \n    async def execute_json(\n        self,\n        workflow_json: Dict[str, Any],\n        params: Optional[Dict[str, Any]] = None,\n    ) -> ExecuteResult:\n        \"\"\"Execute workflow from JSON dict\n        \n        Args:\n            workflow_json: Workflow JSON dict\n            params: Workflow parameters\n        \n        Returns:\n            ExecuteResult: Structured execution result\n        \"\"\"\n```\n\n### ExecuteResult Class\n\n```python\nclass ExecuteResult:\n    \"\"\"Workflow execution result\"\"\"\n    \n    status: str                           # Execution status: \"completed\" / \"failed\"\n    prompt_id: Optional[str]              # Prompt ID\n    duration: Optional[float]             # Execution duration (seconds)\n    \n    # Media outputs\n    images: List[str]                     # All image URLs\n    videos: List[str]                     # All video URLs\n    audios: List[str]                     # All audio URLs\n    texts: List[str]                      # All text outputs\n    \n    # Grouped by variable name\n    images_by_var: Dict[str, List[str]]   # Images grouped by variable name\n    videos_by_var: Dict[str, List[str]]   # Videos grouped by variable name\n    audios_by_var: Dict[str, List[str]]   # Audios grouped by variable name\n    texts_by_var: Dict[str, List[str]]    # Texts grouped by variable name\n    \n    # Raw outputs\n    outputs: Optional[Dict[str, Any]]     # Raw output data\n    msg: Optional[str]                    # Error message (if failed)\n```\n\n---\n\n## \ud83d\udcc2 More Examples\n\nThe project includes complete example code in the `examples/` directory:\n\n- [`01_quick_start.py`](examples/01_quick_start.py) - Quick start guide\n- [`02_configuration.py`](examples/02_configuration.py) - Configuration options\n- [`03_local_workflows.py`](examples/03_local_workflows.py) - Local workflow execution\n- [`04_runninghub_cloud.py`](examples/04_runninghub_cloud.py) - RunningHub cloud execution\n- [`05_advanced_features.py`](examples/05_advanced_features.py) - Advanced features\n\nRun all examples:\n\n```bash\ncd examples\npython run_all.py\n```\n\n---\n\n## \ud83d\udee0\ufe0f Development\n\n### Install development dependencies\n\n```bash\nuv sync --extra dev\n```\n\n### Run tests\n\n```bash\npytest\n```\n\n### Code formatting\n\n```bash\nruff check --fix\nruff format\n```\n\n---\n\n## \ud83e\udd1d Contributing\n\nContributions are welcome! Please check [Issues](https://github.com/puke3615/ComfyKit/issues) for areas that need help.\n\n### Contribution Process\n\n1. Fork this repository\n2. Create a feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n---\n\n## \ud83d\udcc4 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n---\n\n## \ud83d\ude4f Acknowledgments\n\n- [ComfyUI](https://github.com/comfyanonymous/ComfyUI) - Powerful AI image generation framework\n- [RunningHub](https://www.runninghub.ai) - ComfyUI cloud platform\n\n---\n\n## \ud83d\udcde Contact\n\n- Author: Fan Wu\n- Email: 1129090915@qq.com\n- GitHub: [@puke3615](https://github.com/puke3615)\n\n---\n\n<div align=\"center\">\n\n**If ComfyKit helps you, please give it a \u2b50 Star!**\n\n[GitHub](https://github.com/puke3615/ComfyKit) \u00b7 [PyPI](https://pypi.org/project/comfykit/) \u00b7 [Issues](https://github.com/puke3615/ComfyKit/issues)\n\n</div>\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "ComfyUI Python SDK for developers - Build AI generation into your app",
    "version": "0.1.2",
    "project_urls": {
        "Homepage": "https://github.com/puke3615/ComfyKit",
        "Issues": "https://github.com/puke3615/ComfyKit/issues",
        "Repository": "https://github.com/puke3615/ComfyKit"
    },
    "split_keywords": [
        "ai",
        " api",
        " comfyui",
        " flux",
        " image generation",
        " runninghub",
        " sdk",
        " stable diffusion"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "9ae255586bedecf5c3e5faca73d1a3a3a53e10743f319838002909eaa1345677",
                "md5": "310e85f54dd047e18e894ba5cc67f930",
                "sha256": "fa18acf8a2e5d75287fee936e9d1ca15e1e4a64675c3ee31d722f124b0e52db2"
            },
            "downloads": -1,
            "filename": "comfykit-0.1.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "310e85f54dd047e18e894ba5cc67f930",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 51620,
            "upload_time": "2025-10-18T09:21:31",
            "upload_time_iso_8601": "2025-10-18T09:21:31.005852Z",
            "url": "https://files.pythonhosted.org/packages/9a/e2/55586bedecf5c3e5faca73d1a3a3a53e10743f319838002909eaa1345677/comfykit-0.1.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "d02b805903126e34769e870cfe857e5235c7799ae0f36f25cf9ddc41ef35d25f",
                "md5": "4280611336098418fb5d2ac85a68c528",
                "sha256": "27e6ab6871792ca3a733f41f3ba09850fad119ef121582c19d6d732a40467f84"
            },
            "downloads": -1,
            "filename": "comfykit-0.1.2.tar.gz",
            "has_sig": false,
            "md5_digest": "4280611336098418fb5d2ac85a68c528",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 48223,
            "upload_time": "2025-10-18T09:21:33",
            "upload_time_iso_8601": "2025-10-18T09:21:33.018168Z",
            "url": "https://files.pythonhosted.org/packages/d0/2b/805903126e34769e870cfe857e5235c7799ae0f36f25cf9ddc41ef35d25f/comfykit-0.1.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-18 09:21:33",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "puke3615",
    "github_project": "ComfyKit",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "comfykit"
}
        
Elapsed time: 1.42173s