Programmatically schedule ComfyUI workflows.
Project description
README
Warning: Very raw and unmaintained code. Use at your own risk. Mainly intended as a starting point.
Branch | Build Status | |
---|---|---|
Master | ||
Develop |
ComfyUI API Endpoint <| <= Comfy Catapult <=> HTTP Server <| <= Public users
<| <|
<| Your python program <| Your Webui/JS frontend
<| <|
<| Your workflows <|
<| Your HTTP server <|
What is it?
Comfy Catapult is a library for scheduling and running ComfyUI workflows from a Python program, via the existing API endpoint. ComfyUI typically works by hosting this API endpoint for its user interface.
This makes it easier for you to make workflows via the UI, and then use it from a program.
Scheduling a job
From
comfy_catapult/catapult_base.py
:
async def Catapult(
self,
*,
job_id: str,
prepared_workflow: dict,
important: Sequence[APINodeID],
job_debug_path: Path | None = None,
) -> dict:
"""Schedule a ComfyUI workflow job.
Args:
job_id (str): A unique identifier for the job. Note: This string must
be unique, and must be slugified! Use python-slugify to slugify the
string.
prepared_workflow (dict): Workflow to submit.
important (List[APINodeID]): List of important nodes (e.g output nodes we
are interested in).
job_debug_path (Path, optional): Path to save debug information. If
None, will use sensible defaults.
Raises:
WorkflowSubmissionError: Failed to submit workflow.
Returns:
dict: The history of the job returned from the ComfyUI API.
"""
From
examples/sdxlturbo_example_catapulter.py
:
class ExampleWorkflowInfo:
# Direct wrapper around the ComfyUI API.
client: ComfyAPIClientBase
# Job scheduler (the main point of this library).
catapult: ComfyCatapultBase
# Something to help with retrieving files from the ComfyUI storage.
remote: RemoteFileAPIBase
comfy_api_url: str
# This should be the workflow json as a dict.
workflow_template_dict: dict
# This should begin as a deep copy of the template.
workflow_dict: dict
# This will hold the node ids that we must have results for.
important: List[APINodeID]
# Make this any string unique to this job.
job_id: str
# When the job is complete, this will be the `/history` json/dictionary for
# this job.
job_history_dict: dict | None
# These are inputs that modify this particular workflow.
ckpt_name: str | None
positive_prompt: str
negative_prompt: str
# For this particular workflow, this will define the path to the output image.
output_path: Path
async def RunExampleWorkflow(*, job_info: ExampleWorkflowInfo):
# You have to write this function, to change the workflow_dict as you like.
await PrepareWorkflow(job_info=job_info)
job_id: str = job_info.job_id
workflow_dict: dict = job_info.workflow_dict
important: List[APINodeID] = job_info.important
# Here the magic happens, the job is submitted to the ComfyUI server.
job_info.job_history_dict = await job_info.catapult.Catapult(
job_id=job_id, prepared_workflow=workflow_dict, important=important)
# Now that the job is done, you have to write something that will go and get
# the results you care about, if necessary.
await DownloadResults(job_info=job_info)
Related Projects
Project | ComfyUI API Wrapper | Outsource Backend | Distribute Execution | Wrap Workflow | Studio |
---|---|---|---|---|---|
CushyStudio | ? | ? | ? | ? | Yes |
ComfyUI-Serving-Toolkit | X | ? | ? | Yes | ? |
ComfyUI_NetDist | X | ? | Yes | ? | ? |
ComfyUI script_examples | Yes | No | No | No | No |
comfyui-python-api | ? | ? | ? | Yes | ? |
comfyui-deploy | ? | ? | ? | Yes | ? |
ComfyUI-to-Python-Extension | ? | ? | ? | Yes | ? |
ComfyScript | ? | ? | ? | Yes | ? |
hordelib | ? | Yes | ? | ? | ? |
comfyui-cloud | ? | Yes | ? | ? | ? |
Outsource compute:
Getting Started
Exporting workflows in the API json format
In ComfyUI web interface:
- Open settings (gear box in the corner).
- Enable the ability to export in the API format,
Enable Dev mode Options
. - Click new menu item
Save (API format)
.
Example workflow: Prepare ComfyUI
If you don't want to try the example workflow, you can skip this section.
You need to get sd_xl_turbo_1.0_fp16.safetensors
into the ComfyUI model
directory.
Hugging Face page: huggingface.co/stabilityai/sdxl-turbo/blob/main/sd_xl_turbo_1.0_fp16.safetensors.
Direct download link: huggingface.co/stabilityai/sdxl-turbo/resolve/main/sd_xl_turbo_1.0_fp16.safetensors.
Download the example workflow, and export it in the API format
This is optional, you can use the example workflow in test_data/
instead and
skip this step.
# Download the workflow:
wget https://github.com/comfyanonymous/ComfyUI_examples/raw/master/sdturbo/sdxlturbo_example.png
# 1. Open the Workflow in ComfyUI and export it. AFAIK there isn't a nice way
# to automated this right now.
#
# 2, Save to `./sdxlturbo_example_api.json`.
#
# Or just use `test_data/sdxlturbo_example_api.json`.
Install as a library and run the examples
# Inside your environment:
pip install comfy-catapult
# If you set this environment variable, you don't have to specify it as an
# argument.
export COMFY_API_URL=http://127.0.0.1:8188
# Note, in WSL2 you may have to use the IP of the host to connect to ComfyUI.
python -m comfy_catapult.examples.sdxlturbo_example_catapulter \
--api_workflow_json_path "$PWD/sdxlturbo_example_api.json" \
--tmp_path "$PWD/.deleteme/tmp/" \
--output_path "$PWD/.deleteme/output.png" \
--positive_prompt "amazing cloudscape, towering clouds, thunderstorm, awe" \
--negative_prompt "dull, blurry, nsfw"
# Optional if you don't want to set the environment variable:
# --comfy_api_url "..."
# Done! Now $PWD/.deleteme/output.png should contain the output image.
# Some other examples:
python -m comfy_catapult.examples.add_a_node
python -m comfy_catapult.examples.using_pydantic
- Examine
examples/sdxlturbo_example_catapulter.py
to see how to use the mainComfyCatapult
library. - Examine
test_data/sdxlturbo_example_api.json
to see the API format. This will be necessary in order to programmatically set the proper inputs for the workflow.- (Optional) See
examples/using_pydantic.py
for how to parse the API format into the Pydantic models schema for easier navigation. - (Optional) See
examples/add_a_node.py
for how to add a new node to a workflow. This is useful when you need to add nodes at runtime (such as adding a bunch of LoadImage nodes).
- (Optional) See
- See
comfy_catapult/catapult_base.py
for the main library interface. - (Optional) See
comfy_catapult/catapult.py
for the main library implementation. - (Optional) See
comfy_catapult/api_client_base.py
for the direct ComfyUI API endpoint client library interface; you don't need to use this usually. - (Optional) For those who want to do use the raw API themselves and learn how
it works: Examine
comfy_catapult/api_client.py
to see the API client implementation if you want to directly interface with ComfyUI endpoints yourself.- (Optional) Also see
ComfyUI/server.py
(pinned to a specific commit) for the server
@routes
endpoint implementations.
- (Optional) Also see
ComfyUI/server.py
(pinned to a specific commit) for the server
Development; install dependencies and run the examples
This is if you are intending on contributing or altering the library itself.
git clone https://github.com/realazthat/comfy-catapult.git
cd comfy-catapult
pip install -r requirements.txt
# Run the example workflow:
PYTHONPATH=$PYTHONPATH:$PWD python examples/sdxlturbo_example_catapulter.py \
--api_workflow_json_path "$PWD/sdxlturbo_example_api.json"
--tmp_path "$PWD/.deleteme/tmp/" \
--output_path "$PWD/.deleteme/output.png" \
--positive_prompt "amazing cloudscape, towering clouds, thunderstorm, awe" \
--negative_prompt "dull, blurry, nsfw"
Parsing the API format into the Pydantic models schema for easier navigation
From examples/using_pydantic.py
:
from comfy_catapult.comfy_schema import APIWorkflow
api_workflow_json_str: str = """
{
"1": {
"inputs": {
"image": "{remote_image_path} [input]",
"upload": "image"
},
"class_type": "LoadImage",
"_meta": {
"title": "My Loader Title"
}
},
"25": {
"inputs": {
"images": [
"8",
0
]
},
"class_type": "PreviewImage",
"_meta": {
"title": "Preview Image"
}
}
}
"""
api_workflow: APIWorkflow = APIWorkflow.model_validate_json(
api_workflow_json_str)
# Or, if you have a APIWorkflow, and you want to deal with a dict instead:
api_workflow_dict = api_workflow.model_dump()
# Or, back to json:
api_workflow_json = api_workflow.model_dump_json()
# See comfy_catapult/comfyui_schema.py for the schema definition.
print(api_workflow_json)
#
Adding a new node to a workflow
From examples/add_a_node.py
:
from pathlib import Path
from comfy_catapult.comfy_schema import (APIWorkflow, APIWorkflowNodeInfo,
APIWorkflowNodeMeta)
from comfy_catapult.comfy_utils import GenerateNewNodeID
api_workflow_json_str: str = """
{
"1": {
"inputs": {
"image": "{remote_image_path} [input]",
"upload": "image"
},
"class_type": "LoadImage",
"_meta": {
"title": "My Loader Title"
}
},
"25": {
"inputs": {
"images": [
"8",
0
]
},
"class_type": "PreviewImage",
"_meta": {
"title": "Preview Image"
}
}
}
"""
api_workflow: APIWorkflow = APIWorkflow.model_validate_json(
api_workflow_json_str)
path_to_comfy_input = Path('/path/to/ComfyUI/input')
path_to_image = path_to_comfy_input / 'image.jpg'
rel_path_to_image = path_to_image.relative_to(path_to_comfy_input)
# Add a new LoadImage node to the workflow.
new_node_id = GenerateNewNodeID(workflow=api_workflow)
api_workflow.root[new_node_id] = APIWorkflowNodeInfo(
inputs={
'image': f'{rel_path_to_image} [input]',
'upload': 'image',
},
class_type='LoadImage',
_meta=APIWorkflowNodeMeta(title='My Loader Title'))
print(api_workflow.model_dump_json())
Requirements
- Python 3.10+
- ComfyUI server with API endpoint enabled.
Known to work on
- Python 3.10.0, WSL2/Windows11, Ubuntu 22.04.2 LTS
- Python 3.10.0, Ubuntu 22.04
Limitations
- ETA estimator isn't working
TODO
- Helpers should support remote/cloud storage for ComfyUI input/output/model directories (Currently only supports local paths).
- ETA Estimator.
- Make sure the schema can parse the formats even if the format adds new fields.
Contributions
Development environment: Linux-like
- For running
pre.sh
(Linux-like environment).- Requires
pyenv
, or an exact matching version of python as in.python-version
. jq
, (installation) required for yq, which is itself required for ourREADME.md
generation, which usestomlq
(from the yq package) to include version strings frompyproject.toml
.bash
,grep
,awk
,sed
xxd
,git
,xxhash
(for tests/workflows).- Requires nodejs (for act).
- Requires Go (to run act).
- docker (for act).
- Requires
Commit Process
- (Optionally) Fork the
develop
branch. - Stage your files:
git add path/to/file.py
. bash scripts/pre.sh
, this will format, lint, and test the code.git status
check if anything changed (generatedREADME.md
for example), if so,git add
the changes, and go back to the previous step.git commit -m "..."
.- Make a PR to
develop
(or push to develop if you have the rights).
Release Process
These instructions are for maintainers of the project.
develop
branch: Runbash scripts/pre.sh
to ensure everything is in order.develop
branch: Bump the version inpyproject.toml
, following semantic versioning principles. Also modify thelast_unstable_release
andlast_stable_release
in the[tool.comfy-catapult-project-metadata]
table as appropriate.develop
branch: Commit these changes with a message like "Prepare release X.Y.Z". (See the contributions section above).master
branch: Merge thedevelop
branch into themaster
branch:git checkout master && git merge develop --no-ff
.master
branch: Tag the release: Create a git tag for the release withgit tag -a vX.Y.Z -m "Version X.Y.Z"
.- Publish to PyPI: Publish the release to PyPI with
bash scripts/deploy-to-pypi.sh
. - Push to GitHub: Push the commit and tags to GitHub with
git push
andgit push --tags
. git checkout develop && git merge master
The--no-ff
option adds a commit to the master branch for the merge, so refork the develop branch from the master branch.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for comfy_catapult-2.0.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0275340cf557a3067c442d5633981bab268263782c5fc37d9b355540f5f651d8 |
|
MD5 | 1435236430922d6fe673986ed660e0b4 |
|
BLAKE2b-256 | ecf4076d8262077df526bfedf88667c05281755d15dc851a8dff9aef4a2b0732 |