comfy-script


Namecomfy-script JSON
Version 0.5.1 PyPI version JSON
download
home_pageNone
SummaryA Python frontend and library for ComfyUI
upload_time2024-09-07 20:45:22
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseNone
keywords comfyui
VCS
bugtrack_url
requirements aiohttp nest_asyncio networkx Pillow wrapt ipywidgets ComfyUI_Ib_CustomNodes comfyui-tooling-nodes
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # ComfyScript
[![PyPI - Version](https://img.shields.io/pypi/v/comfy-script)](https://pypi.org/project/comfy-script) ![Python Version from PEP 621 TOML](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2FChaoses-Ib%2FComfyScript%2Fmain%2Fpyproject.toml) [![License](https://img.shields.io/pypi/l/comfy-script)](LICENSE.txt)

A Python frontend and library for [ComfyUI](https://github.com/comfyanonymous/ComfyUI).

![](docs/imgs/README/plot.png)

It has the following use cases:
- Serving as a [human-readable format](https://github.com/comfyanonymous/ComfyUI/issues/612) for ComfyUI's workflows.

  This makes it easy to compare and reuse different parts of one's workflows.
  
  It is also possible to train LLMs to generate workflows, since many LLMs can handle Python code relatively well. This approach can be more powerful than just asking LLMs for some hardcoded parameters.

  Scripts can be automatically translated from ComfyUI's workflows. See [transpiler](#transpiler) for details.

- Directly running the script to generate images.

  The main advantage of doing this than using the web UI is being able to mix Python code with ComfyUI's nodes, such as doing loops, calling library functions, and easily encapsulating custom nodes. This also makes adding interaction easier since the UI and logic can be both written in Python. And, some people may feel more comfortable with simple Python code than a graph-based GUI.[^graph-gui]

  See [runtime](#runtime) for details. Scripts can be executed locally or remotely with a ComfyUI server.

- Using ComfyUI as a function library.

  With ComfyScript, ComfyUI's nodes can be used as functions to do ML research, reuse nodes in other projects, debug custom nodes, and optimize caching to run workflows faster.

  See runtime's [real mode](docs/Runtime.md#real-mode) for details.

- Generating ComfyUI's workflows with scripts.

  Scripts can also be used to generate ComfyUI's workflows and then used in the web UI or elsewhere. This way, one can use loops and generate huge workflows where it would be time-consuming or impractical to create them manually. See [workflow generation](docs/Runtime.md#workflow-generation) for details. It is also possible to load workflows from images generated by ComfyScript.

- Retrieving any wanted information by running the script with some stubs.

  See [workflow information retrieval](docs/README.md#workflow-information-retrieval) for details.

- Converting workflows from ComfyUI's web UI format to API format without the web UI.

## [Documentation](docs/README.md)
- [Runtime](docs/Runtime.md)
- [Images](docs/Images/README.md)
- [Models](docs/Models/README.md)
- [Additional Nodes](docs/Nodes/README.md)
- [Transpiler](docs/Transpiler.md)
- UI
  - [ipywidgets UI](docs/UI/ipywidgets.md)
  - [Solara UI](docs/UI/Solara.md)

## Installation
### With ComfyUI
Install ComfyUI first, see [Installing](https://github.com/comfyanonymous/ComfyUI#installing) or use [Comfy-Cli](https://github.com/Comfy-Org/comfy-cli) to install:
```sh
python -m pip install comfy-cli
comfy --here install
```
And then run the following commands to install ComfyScript:
```sh
cd ComfyUI/custom_nodes
git clone https://github.com/Chaoses-Ib/ComfyScript.git
cd ComfyScript
python -m pip install -e ".[default]"
```

Update:
```sh
cd ComfyUI/custom_nodes/ComfyScript
git pull
python -m pip install -e ".[default]"
```

`[default]` is necessary to install common dependencies. See [`pyproject.toml`](pyproject.toml) for other options. If no option is specified, ComfyScript will be installed without any dependencies.

### With ComfyUI package
Install [ComfyUI package](https://github.com/hiddenswitch/ComfyUI) first:
- If PyTorch is not installed:

  ```sh
  python -m pip install git+https://github.com/hiddenswitch/ComfyUI.git
  ```
- If PyTorch is already installed (e.g. Google Colab):

  ```sh
  python -m pip install wheel
  python -m pip install --no-build-isolation git+https://github.com/hiddenswitch/ComfyUI.git
  ```

Install/update ComfyScript:
```sh
python -m pip install -U "comfy-script[default]"
```

If there are problems with the latest ComfyUI package, one can use the last tested version:
```
python -m pip install --no-build-isolation git+https://github.com/hiddenswitch/ComfyUI.git@e8eab4dbc6487a14a0c548a806b0b772e274e3df
```

### Containers
- [Modal](examples/modal.py) by @the-dream-machine (ComfyUI + Comfy-Cli)

### Others
See [troubleshooting](docs/README.md#troubleshooting) if you encountered any problems. To use ComfyScript without installed ComfyUI, see [only ComfyScript package](docs/README.md#only-comfyscript-package) for details. To uninstall, see [uninstallation](docs/README.md#uninstallation).

## Transpiler
The transpiler can translate ComfyUI's workflows to ComfyScript.

When ComfyScript is installed as custom nodes, `SaveImage` and similar nodes will be hooked to automatically save the script as the image's metadata. The script will also be printed to the terminal.

For example, here is a workflow in ComfyUI:

![](docs/imgs/README/workflow.png)

ComfyScript translated from it:
```python
model, clip, vae = CheckpointLoaderSimple('v1-5-pruned-emaonly.ckpt')
conditioning = CLIPTextEncode('beautiful scenery nature glass bottle landscape, , purple galaxy bottle,', clip)
conditioning2 = CLIPTextEncode('text, watermark', clip)
latent = EmptyLatentImage(512, 512, 1)
latent = KSampler(model, 156680208700286, 20, 8, 'euler', 'normal', conditioning, conditioning2, latent, 1)
image = VAEDecode(latent, vae)
SaveImage(image, 'ComfyUI')
```

If there two or more `SaveImage` nodes in one workflow, only the necessary inputs of each node will be translated to scripts. For example, here is a 2 pass txt2img (hires fix) workflow:

![](docs/imgs/README/workflow2.png)

ComfyScript saved for each of the two saved image are respectively:
1. ```python
   model, clip, vae = CheckpointLoaderSimple('v2-1_768-ema-pruned.ckpt')
   conditioning = CLIPTextEncode('masterpiece HDR victorian portrait painting of woman, blonde hair, mountain nature, blue sky', clip)
   conditioning2 = CLIPTextEncode('bad hands, text, watermark', clip)
   latent = EmptyLatentImage(768, 768, 1)
   latent = KSampler(model, 89848141647836, 12, 8, 'dpmpp_sde', 'normal', conditioning, conditioning2, latent, 1)
   image = VAEDecode(latent, vae)
   SaveImage(image, 'ComfyUI')
   ```
2. ```python
   model, clip, vae = CheckpointLoaderSimple('v2-1_768-ema-pruned.ckpt')
   conditioning = CLIPTextEncode('masterpiece HDR victorian portrait painting of woman, blonde hair, mountain nature, blue sky', clip)
   conditioning2 = CLIPTextEncode('bad hands, text, watermark', clip)
   latent = EmptyLatentImage(768, 768, 1)
   latent = KSampler(model, 89848141647836, 12, 8, 'dpmpp_sde', 'normal', conditioning, conditioning2, latent, 1)
   latent2 = LatentUpscale(latent, 'nearest-exact', 1152, 1152, 'disabled')
   latent2 = KSampler(model, 469771404043268, 14, 8, 'dpmpp_2m', 'simple', conditioning, conditioning2, latent2, 0.5)
   image = VAEDecode(latent2, vae)
   SaveImage(image, 'ComfyUI')
   ```

Comparing scripts:

![](docs/imgs/README/diff.png)

To control these features, see [settings.example.toml](settings.example.toml).

You can also use the transpiler via the [CLI](docs/Transpiler.md#cli).

## Runtime
With the runtime, one can run ComfyScript like this:
```python
from comfy_script.runtime import *
load()
from comfy_script.runtime.nodes import *

with Workflow():
    model, clip, vae = CheckpointLoaderSimple('v1-5-pruned-emaonly.ckpt')
    conditioning = CLIPTextEncode('beautiful scenery nature glass bottle landscape, , purple galaxy bottle,', clip)
    conditioning2 = CLIPTextEncode('text, watermark', clip)
    latent = EmptyLatentImage(512, 512, 1)
    latent = KSampler(model, 156680208700286, 20, 8, 'euler', 'normal', conditioning, conditioning2, latent, 1)
    image = VAEDecode(latent, vae)
    SaveImage(image, 'ComfyUI')
```

A Jupyter Notebook example is available at [`examples/runtime.ipynb`](examples/runtime.ipynb).

- [Type stubs](https://typing.readthedocs.io/en/latest/source/stubs.html) will be generated at `comfy_script/runtime/nodes.pyi` after loading. Mainstream code editors (e.g. [VS Code](https://code.visualstudio.com/docs/languages/python)) can use them to help with coding:

  | | |
  | --- | --- |
  | ![](docs/imgs/README/type-stubs.png) | ![](docs/imgs/README/type-stubs2.png) |

  [Python enumerations](https://docs.python.org/3/howto/enum.html) are generated for all arguments provding the value list. So instead of copying and pasting strings like `'v1-5-pruned-emaonly.ckpt'`, you can use:
  ```python
  Checkpoints.v1_5_pruned_emaonly
  # or
  CheckpointLoaderSimple.ckpt_name.v1_5_pruned_emaonly
  ```
  
  Embeddings can also be referenced as `Embeddings.my_embedding`, which is equivalent to `'embedding:my-embedding'`. See [enumerations](docs/Runtime.md#enumerations) for details.

  If type stubs are not working for you (cannot get results similar to the screenshot), see [Type stubs not working](docs/Runtime.md#type-stubs-not-working).

- The runtime is asynchronous by default. You can queue multiple tasks without waiting for the first one to finish. A daemon thread will watch and report the remaining tasks in the queue and the current progress, for example:
  ```
  Queue remaining: 1
  Queue remaining: 2
  100%|██████████████████████████████████████████████████| 20/20
  Queue remaining: 1
  100%|██████████████████████████████████████████████████| 20/20
  Queue remaining: 0
  ```
  Some control functions are also available:
  ```python
  # Interrupt the current task
  queue.cancel_current()
  # Clear the queue
  queue.cancel_remaining()
  # Interrupt the current task and clear the queue
  queue.cancel_all()
  # Call the callback when the queue is empty
  queue.when_empty(callback)

  # With Workflow:
  Workflow(cancel_remaining=True)
  Workflow(cancel_all=True)
  ```

See [differences from ComfyUI's web UI](docs/Runtime.md#differences-from-comfyuis-web-ui) if you are a previous user of ComfyUI's web UI, and [runtime](docs/Runtime.md) for the details of runtime.

### Examples
#### Plotting
```python
with Workflow():
    seed = 0
    pos = 'sky, 1girl, smile'
    neg = 'embedding:easynegative'
    model, clip, vae = CheckpointLoaderSimple(Checkpoints.AOM3A1B_orangemixs)
    model2, clip2, vae2 = CheckpointLoaderSimple(Checkpoints.CounterfeitV25_25)
    model2 = TomePatchModel(model2, 0.5)
    for color in 'red', 'green', 'blue':
        latent = EmptyLatentImage(440, 640)
        latent = KSampler(model, seed, steps=15, cfg=6, sampler_name='uni_pc',
                          positive=CLIPTextEncode(f'{color}, {pos}', clip), negative=CLIPTextEncode(neg, clip),
                          latent_image=latent)
        SaveImage(VAEDecode(latent, vae2), f'{seed} {color}')
        latent = LatentUpscaleBy(latent, scale_by=2)
        latent = KSampler(model2, seed, steps=15, cfg=6, sampler_name='uni_pc',
                          positive=CLIPTextEncode(f'{color}, {pos}', clip2), negative=CLIPTextEncode(neg, clip2),
                          latent_image=latent, denoise=0.6)
        SaveImage(VAEDecode(latent, vae2), f'{seed} {color} hires')
```

![](docs/imgs/README/plot.png)

#### Auto queue
Automatically queue new workflows when the queue becomes empty.

For example, one can use [comfyui-photoshop](https://github.com/NimaNzrii/comfyui-photoshop) (currently a bit buggy) to automatically do img2img with the image in Photoshop when it changes:
```python
def f(wf):
    seed = 0
    pos = '1girl, angry, middle finger'
    neg = 'embedding:easynegative'
    model, clip, vae = CheckpointLoaderSimple(Checkpoints.CounterfeitV25_25)
    image, width, height = PhotoshopToComfyUI(wait_for_photoshop_changes=True)
    latent = VAEEncode(image, vae)
    latent = LatentUpscaleBy(latent, scale_by=1.5)
    latent = KSampler(model, seed, steps=15, cfg=6, sampler_name='uni_pc',
                        positive=CLIPTextEncode(pos, clip), negative=CLIPTextEncode(neg, clip),
                        latent_image=latent, denoise=0.8)
    PreviewImage(VAEDecode(latent, vae))
queue.when_empty(f)
```
Screenshot:

![](docs/imgs/README/auto-queue.png)

#### Select and process
For example, to generate 3 images at once, and then let the user decide which ones they want to hires fix:
```python
import ipywidgets as widgets

queue.watch_display(False)

latents = []
image_batches = []
with Workflow():
    seed = 0
    pos = 'sky, 1girl, smile'
    neg = 'embedding:easynegative'
    model, clip, vae = CheckpointLoaderSimple(Checkpoints.AOM3A1B_orangemixs)
    model2, clip2, vae2 = CheckpointLoaderSimple(Checkpoints.CounterfeitV25_25)
    for color in 'red', 'green', 'blue':
        latent = EmptyLatentImage(440, 640)
        latent = KSampler(model, seed, steps=15, cfg=6, sampler_name='uni_pc',
                          positive=CLIPTextEncode(f'{color}, {pos}', clip), negative=CLIPTextEncode(neg, clip),
                          latent_image=latent)
        latents.append(latent)
        image_batches.append(SaveImage(VAEDecode(latent, vae), f'{seed} {color}'))

grid = widgets.GridspecLayout(1, len(image_batches))
for i, image_batch in enumerate(image_batches):
    image_batch = image_batch.wait()
    image = widgets.Image(value=image_batch[0]._repr_png_())

    button = widgets.Button(description=f'Hires fix {i}')
    def hiresfix(button, i=i):
        print(f'Image {i} is chosen')
        with Workflow():
            latent = LatentUpscaleBy(latents[i], scale_by=2)
            latent = KSampler(model2, seed, steps=15, cfg=6, sampler_name='uni_pc',
                            positive=CLIPTextEncode(pos, clip2), negative=CLIPTextEncode(neg, clip2),
                            latent_image=latent, denoise=0.6)
            image_batch = SaveImage(VAEDecode(latent, vae2), f'{seed} hires')
        display(image_batch.wait())
    button.on_click(hiresfix)

    grid[0, i] = widgets.VBox(children=(image, button))
display(grid)
```
This example uses [ipywidgets](https://github.com/jupyter-widgets/ipywidgets) for the GUI, but other GUI frameworks can be used as well.

Screenshot:

![](docs/imgs/README/select.png)

## UI
### [ipywidgets UI](docs/UI/ipywidgets.md)
#### [ImageViewer](docs/UI/ipywidgets.md#imageviewer)
A simple image viewer that can display multiple images with optional titles.

![](docs/UI/images/ipywidgets/ImageViewer.png)

### [Solara UI](docs/UI/Solara.md)
These Solara widgets can be used in Jupyter Notebook and in web pages.

#### [MetadataViewer](docs/UI/Solara.md#metadataviewer)
A widget for viewing the metadata of an image generated by ComfyScript / ComfyUI / Stable Diffusion web UI. Workflow JSON files are supported too, including both the web UI format and the API format.

![](docs/UI/images/Solara/MetadataViewer.png)


[^graph-gui]: [I hate nodes. (No offense comfyui) : StableDiffusion](https://www.reddit.com/r/StableDiffusion/comments/15cr5xx/i_hate_nodes_no_offense_comfyui/)
            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "comfy-script",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "comfyui",
    "author": null,
    "author_email": "Chaoses-Ib <Chaos-es@outlook.com>",
    "download_url": "https://files.pythonhosted.org/packages/ee/90/3a063e01394b7fe5b4941a53f8a3e2bb381f943d28813272a3e05c4e3206/comfy_script-0.5.1.tar.gz",
    "platform": null,
    "description": "# ComfyScript\n[![PyPI - Version](https://img.shields.io/pypi/v/comfy-script)](https://pypi.org/project/comfy-script) ![Python Version from PEP 621 TOML](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2FChaoses-Ib%2FComfyScript%2Fmain%2Fpyproject.toml) [![License](https://img.shields.io/pypi/l/comfy-script)](LICENSE.txt)\n\nA Python frontend and library for [ComfyUI](https://github.com/comfyanonymous/ComfyUI).\n\n![](docs/imgs/README/plot.png)\n\nIt has the following use cases:\n- Serving as a [human-readable format](https://github.com/comfyanonymous/ComfyUI/issues/612) for ComfyUI's workflows.\n\n  This makes it easy to compare and reuse different parts of one's workflows.\n  \n  It is also possible to train LLMs to generate workflows, since many LLMs can handle Python code relatively well. This approach can be more powerful than just asking LLMs for some hardcoded parameters.\n\n  Scripts can be automatically translated from ComfyUI's workflows. See [transpiler](#transpiler) for details.\n\n- Directly running the script to generate images.\n\n  The main advantage of doing this than using the web UI is being able to mix Python code with ComfyUI's nodes, such as doing loops, calling library functions, and easily encapsulating custom nodes. This also makes adding interaction easier since the UI and logic can be both written in Python. And, some people may feel more comfortable with simple Python code than a graph-based GUI.[^graph-gui]\n\n  See [runtime](#runtime) for details. Scripts can be executed locally or remotely with a ComfyUI server.\n\n- Using ComfyUI as a function library.\n\n  With ComfyScript, ComfyUI's nodes can be used as functions to do ML research, reuse nodes in other projects, debug custom nodes, and optimize caching to run workflows faster.\n\n  See runtime's [real mode](docs/Runtime.md#real-mode) for details.\n\n- Generating ComfyUI's workflows with scripts.\n\n  Scripts can also be used to generate ComfyUI's workflows and then used in the web UI or elsewhere. This way, one can use loops and generate huge workflows where it would be time-consuming or impractical to create them manually. See [workflow generation](docs/Runtime.md#workflow-generation) for details. It is also possible to load workflows from images generated by ComfyScript.\n\n- Retrieving any wanted information by running the script with some stubs.\n\n  See [workflow information retrieval](docs/README.md#workflow-information-retrieval) for details.\n\n- Converting workflows from ComfyUI's web UI format to API format without the web UI.\n\n## [Documentation](docs/README.md)\n- [Runtime](docs/Runtime.md)\n- [Images](docs/Images/README.md)\n- [Models](docs/Models/README.md)\n- [Additional Nodes](docs/Nodes/README.md)\n- [Transpiler](docs/Transpiler.md)\n- UI\n  - [ipywidgets UI](docs/UI/ipywidgets.md)\n  - [Solara UI](docs/UI/Solara.md)\n\n## Installation\n### With ComfyUI\nInstall ComfyUI first, see [Installing](https://github.com/comfyanonymous/ComfyUI#installing) or use [Comfy-Cli](https://github.com/Comfy-Org/comfy-cli) to install:\n```sh\npython -m pip install comfy-cli\ncomfy --here install\n```\nAnd then run the following commands to install ComfyScript:\n```sh\ncd ComfyUI/custom_nodes\ngit clone https://github.com/Chaoses-Ib/ComfyScript.git\ncd ComfyScript\npython -m pip install -e \".[default]\"\n```\n\nUpdate:\n```sh\ncd ComfyUI/custom_nodes/ComfyScript\ngit pull\npython -m pip install -e \".[default]\"\n```\n\n`[default]` is necessary to install common dependencies. See [`pyproject.toml`](pyproject.toml) for other options. If no option is specified, ComfyScript will be installed without any dependencies.\n\n### With ComfyUI package\nInstall [ComfyUI package](https://github.com/hiddenswitch/ComfyUI) first:\n- If PyTorch is not installed:\n\n  ```sh\n  python -m pip install git+https://github.com/hiddenswitch/ComfyUI.git\n  ```\n- If PyTorch is already installed (e.g. Google Colab):\n\n  ```sh\n  python -m pip install wheel\n  python -m pip install --no-build-isolation git+https://github.com/hiddenswitch/ComfyUI.git\n  ```\n\nInstall/update ComfyScript:\n```sh\npython -m pip install -U \"comfy-script[default]\"\n```\n\nIf there are problems with the latest ComfyUI package, one can use the last tested version:\n```\npython -m pip install --no-build-isolation git+https://github.com/hiddenswitch/ComfyUI.git@e8eab4dbc6487a14a0c548a806b0b772e274e3df\n```\n\n### Containers\n- [Modal](examples/modal.py) by @the-dream-machine (ComfyUI + Comfy-Cli)\n\n### Others\nSee [troubleshooting](docs/README.md#troubleshooting) if you encountered any problems. To use ComfyScript without installed ComfyUI, see [only ComfyScript package](docs/README.md#only-comfyscript-package) for details. To uninstall, see [uninstallation](docs/README.md#uninstallation).\n\n## Transpiler\nThe transpiler can translate ComfyUI's workflows to ComfyScript.\n\nWhen ComfyScript is installed as custom nodes, `SaveImage` and similar nodes will be hooked to automatically save the script as the image's metadata. The script will also be printed to the terminal.\n\nFor example, here is a workflow in ComfyUI:\n\n![](docs/imgs/README/workflow.png)\n\nComfyScript translated from it:\n```python\nmodel, clip, vae = CheckpointLoaderSimple('v1-5-pruned-emaonly.ckpt')\nconditioning = CLIPTextEncode('beautiful scenery nature glass bottle landscape, , purple galaxy bottle,', clip)\nconditioning2 = CLIPTextEncode('text, watermark', clip)\nlatent = EmptyLatentImage(512, 512, 1)\nlatent = KSampler(model, 156680208700286, 20, 8, 'euler', 'normal', conditioning, conditioning2, latent, 1)\nimage = VAEDecode(latent, vae)\nSaveImage(image, 'ComfyUI')\n```\n\nIf there two or more `SaveImage` nodes in one workflow, only the necessary inputs of each node will be translated to scripts. For example, here is a 2 pass txt2img (hires fix) workflow:\n\n![](docs/imgs/README/workflow2.png)\n\nComfyScript saved for each of the two saved image are respectively:\n1. ```python\n   model, clip, vae = CheckpointLoaderSimple('v2-1_768-ema-pruned.ckpt')\n   conditioning = CLIPTextEncode('masterpiece HDR victorian portrait painting of woman, blonde hair, mountain nature, blue sky', clip)\n   conditioning2 = CLIPTextEncode('bad hands, text, watermark', clip)\n   latent = EmptyLatentImage(768, 768, 1)\n   latent = KSampler(model, 89848141647836, 12, 8, 'dpmpp_sde', 'normal', conditioning, conditioning2, latent, 1)\n   image = VAEDecode(latent, vae)\n   SaveImage(image, 'ComfyUI')\n   ```\n2. ```python\n   model, clip, vae = CheckpointLoaderSimple('v2-1_768-ema-pruned.ckpt')\n   conditioning = CLIPTextEncode('masterpiece HDR victorian portrait painting of woman, blonde hair, mountain nature, blue sky', clip)\n   conditioning2 = CLIPTextEncode('bad hands, text, watermark', clip)\n   latent = EmptyLatentImage(768, 768, 1)\n   latent = KSampler(model, 89848141647836, 12, 8, 'dpmpp_sde', 'normal', conditioning, conditioning2, latent, 1)\n   latent2 = LatentUpscale(latent, 'nearest-exact', 1152, 1152, 'disabled')\n   latent2 = KSampler(model, 469771404043268, 14, 8, 'dpmpp_2m', 'simple', conditioning, conditioning2, latent2, 0.5)\n   image = VAEDecode(latent2, vae)\n   SaveImage(image, 'ComfyUI')\n   ```\n\nComparing scripts:\n\n![](docs/imgs/README/diff.png)\n\nTo control these features, see [settings.example.toml](settings.example.toml).\n\nYou can also use the transpiler via the [CLI](docs/Transpiler.md#cli).\n\n## Runtime\nWith the runtime, one can run ComfyScript like this:\n```python\nfrom comfy_script.runtime import *\nload()\nfrom comfy_script.runtime.nodes import *\n\nwith Workflow():\n    model, clip, vae = CheckpointLoaderSimple('v1-5-pruned-emaonly.ckpt')\n    conditioning = CLIPTextEncode('beautiful scenery nature glass bottle landscape, , purple galaxy bottle,', clip)\n    conditioning2 = CLIPTextEncode('text, watermark', clip)\n    latent = EmptyLatentImage(512, 512, 1)\n    latent = KSampler(model, 156680208700286, 20, 8, 'euler', 'normal', conditioning, conditioning2, latent, 1)\n    image = VAEDecode(latent, vae)\n    SaveImage(image, 'ComfyUI')\n```\n\nA Jupyter Notebook example is available at [`examples/runtime.ipynb`](examples/runtime.ipynb).\n\n- [Type stubs](https://typing.readthedocs.io/en/latest/source/stubs.html) will be generated at `comfy_script/runtime/nodes.pyi` after loading. Mainstream code editors (e.g. [VS Code](https://code.visualstudio.com/docs/languages/python)) can use them to help with coding:\n\n  | | |\n  | --- | --- |\n  | ![](docs/imgs/README/type-stubs.png) | ![](docs/imgs/README/type-stubs2.png) |\n\n  [Python enumerations](https://docs.python.org/3/howto/enum.html) are generated for all arguments provding the value list. So instead of copying and pasting strings like `'v1-5-pruned-emaonly.ckpt'`, you can use:\n  ```python\n  Checkpoints.v1_5_pruned_emaonly\n  # or\n  CheckpointLoaderSimple.ckpt_name.v1_5_pruned_emaonly\n  ```\n  \n  Embeddings can also be referenced as `Embeddings.my_embedding`, which is equivalent to `'embedding:my-embedding'`. See [enumerations](docs/Runtime.md#enumerations) for details.\n\n  If type stubs are not working for you (cannot get results similar to the screenshot), see [Type stubs not working](docs/Runtime.md#type-stubs-not-working).\n\n- The runtime is asynchronous by default. You can queue multiple tasks without waiting for the first one to finish. A daemon thread will watch and report the remaining tasks in the queue and the current progress, for example:\n  ```\n  Queue remaining: 1\n  Queue remaining: 2\n  100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 20/20\n  Queue remaining: 1\n  100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 20/20\n  Queue remaining: 0\n  ```\n  Some control functions are also available:\n  ```python\n  # Interrupt the current task\n  queue.cancel_current()\n  # Clear the queue\n  queue.cancel_remaining()\n  # Interrupt the current task and clear the queue\n  queue.cancel_all()\n  # Call the callback when the queue is empty\n  queue.when_empty(callback)\n\n  # With Workflow:\n  Workflow(cancel_remaining=True)\n  Workflow(cancel_all=True)\n  ```\n\nSee [differences from ComfyUI's web UI](docs/Runtime.md#differences-from-comfyuis-web-ui) if you are a previous user of ComfyUI's web UI, and [runtime](docs/Runtime.md) for the details of runtime.\n\n### Examples\n#### Plotting\n```python\nwith Workflow():\n    seed = 0\n    pos = 'sky, 1girl, smile'\n    neg = 'embedding:easynegative'\n    model, clip, vae = CheckpointLoaderSimple(Checkpoints.AOM3A1B_orangemixs)\n    model2, clip2, vae2 = CheckpointLoaderSimple(Checkpoints.CounterfeitV25_25)\n    model2 = TomePatchModel(model2, 0.5)\n    for color in 'red', 'green', 'blue':\n        latent = EmptyLatentImage(440, 640)\n        latent = KSampler(model, seed, steps=15, cfg=6, sampler_name='uni_pc',\n                          positive=CLIPTextEncode(f'{color}, {pos}', clip), negative=CLIPTextEncode(neg, clip),\n                          latent_image=latent)\n        SaveImage(VAEDecode(latent, vae2), f'{seed} {color}')\n        latent = LatentUpscaleBy(latent, scale_by=2)\n        latent = KSampler(model2, seed, steps=15, cfg=6, sampler_name='uni_pc',\n                          positive=CLIPTextEncode(f'{color}, {pos}', clip2), negative=CLIPTextEncode(neg, clip2),\n                          latent_image=latent, denoise=0.6)\n        SaveImage(VAEDecode(latent, vae2), f'{seed} {color} hires')\n```\n\n![](docs/imgs/README/plot.png)\n\n#### Auto queue\nAutomatically queue new workflows when the queue becomes empty.\n\nFor example, one can use [comfyui-photoshop](https://github.com/NimaNzrii/comfyui-photoshop) (currently a bit buggy) to automatically do img2img with the image in Photoshop when it changes:\n```python\ndef f(wf):\n    seed = 0\n    pos = '1girl, angry, middle finger'\n    neg = 'embedding:easynegative'\n    model, clip, vae = CheckpointLoaderSimple(Checkpoints.CounterfeitV25_25)\n    image, width, height = PhotoshopToComfyUI(wait_for_photoshop_changes=True)\n    latent = VAEEncode(image, vae)\n    latent = LatentUpscaleBy(latent, scale_by=1.5)\n    latent = KSampler(model, seed, steps=15, cfg=6, sampler_name='uni_pc',\n                        positive=CLIPTextEncode(pos, clip), negative=CLIPTextEncode(neg, clip),\n                        latent_image=latent, denoise=0.8)\n    PreviewImage(VAEDecode(latent, vae))\nqueue.when_empty(f)\n```\nScreenshot:\n\n![](docs/imgs/README/auto-queue.png)\n\n#### Select and process\nFor example, to generate 3 images at once, and then let the user decide which ones they want to hires fix:\n```python\nimport ipywidgets as widgets\n\nqueue.watch_display(False)\n\nlatents = []\nimage_batches = []\nwith Workflow():\n    seed = 0\n    pos = 'sky, 1girl, smile'\n    neg = 'embedding:easynegative'\n    model, clip, vae = CheckpointLoaderSimple(Checkpoints.AOM3A1B_orangemixs)\n    model2, clip2, vae2 = CheckpointLoaderSimple(Checkpoints.CounterfeitV25_25)\n    for color in 'red', 'green', 'blue':\n        latent = EmptyLatentImage(440, 640)\n        latent = KSampler(model, seed, steps=15, cfg=6, sampler_name='uni_pc',\n                          positive=CLIPTextEncode(f'{color}, {pos}', clip), negative=CLIPTextEncode(neg, clip),\n                          latent_image=latent)\n        latents.append(latent)\n        image_batches.append(SaveImage(VAEDecode(latent, vae), f'{seed} {color}'))\n\ngrid = widgets.GridspecLayout(1, len(image_batches))\nfor i, image_batch in enumerate(image_batches):\n    image_batch = image_batch.wait()\n    image = widgets.Image(value=image_batch[0]._repr_png_())\n\n    button = widgets.Button(description=f'Hires fix {i}')\n    def hiresfix(button, i=i):\n        print(f'Image {i} is chosen')\n        with Workflow():\n            latent = LatentUpscaleBy(latents[i], scale_by=2)\n            latent = KSampler(model2, seed, steps=15, cfg=6, sampler_name='uni_pc',\n                            positive=CLIPTextEncode(pos, clip2), negative=CLIPTextEncode(neg, clip2),\n                            latent_image=latent, denoise=0.6)\n            image_batch = SaveImage(VAEDecode(latent, vae2), f'{seed} hires')\n        display(image_batch.wait())\n    button.on_click(hiresfix)\n\n    grid[0, i] = widgets.VBox(children=(image, button))\ndisplay(grid)\n```\nThis example uses [ipywidgets](https://github.com/jupyter-widgets/ipywidgets) for the GUI, but other GUI frameworks can be used as well.\n\nScreenshot:\n\n![](docs/imgs/README/select.png)\n\n## UI\n### [ipywidgets UI](docs/UI/ipywidgets.md)\n#### [ImageViewer](docs/UI/ipywidgets.md#imageviewer)\nA simple image viewer that can display multiple images with optional titles.\n\n![](docs/UI/images/ipywidgets/ImageViewer.png)\n\n### [Solara UI](docs/UI/Solara.md)\nThese Solara widgets can be used in Jupyter Notebook and in web pages.\n\n#### [MetadataViewer](docs/UI/Solara.md#metadataviewer)\nA widget for viewing the metadata of an image generated by ComfyScript / ComfyUI / Stable Diffusion web UI. Workflow JSON files are supported too, including both the web UI format and the API format.\n\n![](docs/UI/images/Solara/MetadataViewer.png)\n\n\n[^graph-gui]: [I hate nodes. (No offense comfyui) : StableDiffusion](https://www.reddit.com/r/StableDiffusion/comments/15cr5xx/i_hate_nodes_no_offense_comfyui/)",
    "bugtrack_url": null,
    "license": null,
    "summary": "A Python frontend and library for ComfyUI",
    "version": "0.5.1",
    "project_urls": {
        "Homepage": "https://github.com/Chaoses-Ib/ComfyScript",
        "Issues": "https://github.com/Chaoses-Ib/ComfyScript/issues"
    },
    "split_keywords": [
        "comfyui"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "0e38cacff92a9b3cd343f0cd3a3c9bdd8cc096e0c325945546dccd0aea9f2be3",
                "md5": "2fe91dd4974934086f1ce945291b0db0",
                "sha256": "f3c2d0c511297c8383b7bcbb9e52b02d0733ea0bda89640571fb0251071116dc"
            },
            "downloads": -1,
            "filename": "comfy_script-0.5.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "2fe91dd4974934086f1ce945291b0db0",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 52892,
            "upload_time": "2024-09-07T20:43:26",
            "upload_time_iso_8601": "2024-09-07T20:43:26.082942Z",
            "url": "https://files.pythonhosted.org/packages/0e/38/cacff92a9b3cd343f0cd3a3c9bdd8cc096e0c325945546dccd0aea9f2be3/comfy_script-0.5.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "ee903a063e01394b7fe5b4941a53f8a3e2bb381f943d28813272a3e05c4e3206",
                "md5": "df287a02fc21d1b310a2316ff363dc13",
                "sha256": "30f4fbe02e3aba15f6d72c2ce4fd0e5ba61e9edebd8e621bd5ea62c3c5fefc0a"
            },
            "downloads": -1,
            "filename": "comfy_script-0.5.1.tar.gz",
            "has_sig": false,
            "md5_digest": "df287a02fc21d1b310a2316ff363dc13",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 6206131,
            "upload_time": "2024-09-07T20:45:22",
            "upload_time_iso_8601": "2024-09-07T20:45:22.452628Z",
            "url": "https://files.pythonhosted.org/packages/ee/90/3a063e01394b7fe5b4941a53f8a3e2bb381f943d28813272a3e05c4e3206/comfy_script-0.5.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-09-07 20:45:22",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "Chaoses-Ib",
    "github_project": "ComfyScript",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "requirements": [
        {
            "name": "aiohttp",
            "specs": []
        },
        {
            "name": "nest_asyncio",
            "specs": [
                [
                    ">=",
                    "1.5.9"
                ],
                [
                    "~=",
                    "1.0"
                ]
            ]
        },
        {
            "name": "networkx",
            "specs": [
                [
                    "~=",
                    "3.0"
                ]
            ]
        },
        {
            "name": "Pillow",
            "specs": []
        },
        {
            "name": "wrapt",
            "specs": [
                [
                    "~=",
                    "1.0"
                ]
            ]
        },
        {
            "name": "ipywidgets",
            "specs": [
                [
                    "~=",
                    "8.1"
                ]
            ]
        },
        {
            "name": "ComfyUI_Ib_CustomNodes",
            "specs": [
                [
                    ">=",
                    "0.2.1"
                ]
            ]
        },
        {
            "name": "comfyui-tooling-nodes",
            "specs": []
        }
    ],
    "lcname": "comfy-script"
}
        
Elapsed time: 2.24140s