# 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)
[](https://pypi.org/project/comfykit/)
[](https://pypi.org/project/comfykit/)
[](https://opensource.org/licenses/MIT)
[](https://github.com/puke3615/ComfyKit)
[](https://github.com/puke3615/ComfyKit)
[](https://github.com/astral-sh/ruff)
[](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[](https://pypi.org/project/comfykit/)\n[](https://pypi.org/project/comfykit/)\n[](https://opensource.org/licenses/MIT)\n[](https://github.com/puke3615/ComfyKit)\n[](https://github.com/puke3615/ComfyKit)\n[](https://github.com/astral-sh/ruff)\n[](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"
}