Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 64c0a62b49 | |||
| f24aafec69 | |||
| 4165018ab2 | |||
| 2f01ff0639 | |||
| 7a21b02081 |
@@ -27,11 +27,11 @@ The MCP server connects to ComfyUI's REST API at `http://localhost:8188`. If Com
|
|||||||
|
|
||||||
### Install ComfyUI
|
### Install ComfyUI
|
||||||
|
|
||||||
```bash
|
> ⚠️ **ComfyUI is NOT on PyPI** — `pip install comfyui` will fail with "No matching distribution found".
|
||||||
# Option A — pip install (simplest)
|
> It must be installed from source via `git clone`.
|
||||||
pip install comfyui
|
|
||||||
|
|
||||||
# Option B — git clone (more control)
|
```bash
|
||||||
|
# Clone from source (the only correct installation method)
|
||||||
git clone https://github.com/comfyanonymous/ComfyUI.git
|
git clone https://github.com/comfyanonymous/ComfyUI.git
|
||||||
cd ComfyUI
|
cd ComfyUI
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
@@ -53,17 +53,48 @@ pip install torch torchvision --index-url https://download.pytorch.org/whl/rocm6
|
|||||||
|
|
||||||
FLUX.1-schnell is the recommended model — fast (4 steps), Apache 2.0 licensed, excellent quality.
|
FLUX.1-schnell is the recommended model — fast (4 steps), Apache 2.0 licensed, excellent quality.
|
||||||
|
|
||||||
```bash
|
> ⚠️ **FLUX.1-schnell is a gated model on HuggingFace.**
|
||||||
# Download (~8GB) — place in ComfyUI/models/checkpoints/
|
> A bare `wget` on the URL returns HTTP 401. You must:
|
||||||
wget https://huggingface.co/black-forest-labs/FLUX.1-schnell/resolve/main/flux1-schnell.safetensors \
|
> 1. Accept the license at https://huggingface.co/black-forest-labs/FLUX.1-schnell (click **"Agree and access repository"** — one-time)
|
||||||
-O ~/ComfyUI/models/checkpoints/flux1-schnell.safetensors
|
> 2. Create a HuggingFace access token with **Read** permissions at https://huggingface.co/settings/tokens
|
||||||
|
|
||||||
# Or use huggingface_hub:
|
#### Option A — `huggingface-cli` (recommended)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install the HuggingFace Hub CLI
|
||||||
|
pip install huggingface_hub
|
||||||
|
|
||||||
|
# Log in — paste your Read token when prompted
|
||||||
|
huggingface-cli login
|
||||||
|
|
||||||
|
# Download (~8GB) directly into ComfyUI checkpoints
|
||||||
huggingface-cli download black-forest-labs/FLUX.1-schnell \
|
huggingface-cli download black-forest-labs/FLUX.1-schnell \
|
||||||
flux1-schnell.safetensors \
|
flux1-schnell.safetensors \
|
||||||
--local-dir ~/ComfyUI/models/checkpoints/
|
--local-dir ~/ComfyUI/models/checkpoints/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Option B — `wget` with Authorization header
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget --header="Authorization: Bearer hf_YOUR_TOKEN_HERE" \
|
||||||
|
https://huggingface.co/black-forest-labs/FLUX.1-schnell/resolve/main/flux1-schnell.safetensors \
|
||||||
|
-O ~/ComfyUI/models/checkpoints/flux1-schnell.safetensors
|
||||||
|
```
|
||||||
|
|
||||||
|
> Replace `hf_YOUR_TOKEN_HERE` with your actual HuggingFace token from https://huggingface.co/settings/tokens
|
||||||
|
|
||||||
|
#### Alternative: fp8 quantized variant (~8.1GB, faster inference)
|
||||||
|
|
||||||
|
If you want slightly faster inference with near-identical quality, the fp8 quantized version is also available:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
huggingface-cli download black-forest-labs/FLUX.1-schnell-fp8 \
|
||||||
|
flux1-schnell-fp8.safetensors \
|
||||||
|
--local-dir ~/ComfyUI/models/checkpoints/
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Download note:** Both variants are ~8GB — expect 10–30 minutes depending on connection speed.
|
||||||
|
|
||||||
You'll also need the CLIP and VAE models — see the [ComfyUI FLUX guide](https://github.com/comfyanonymous/ComfyUI/blob/master/README.md) for full model list.
|
You'll also need the CLIP and VAE models — see the [ComfyUI FLUX guide](https://github.com/comfyanonymous/ComfyUI/blob/master/README.md) for full model list.
|
||||||
|
|
||||||
### Start ComfyUI (AMD ROCm)
|
### Start ComfyUI (AMD ROCm)
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 992 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 860 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
@@ -40,7 +40,9 @@ class ComfyUIClient:
|
|||||||
|
|
||||||
async def queue_prompt(self, workflow: dict) -> str:
|
async def queue_prompt(self, workflow: dict) -> str:
|
||||||
"""Submit a workflow to ComfyUI and return the prompt_id."""
|
"""Submit a workflow to ComfyUI and return the prompt_id."""
|
||||||
payload = {"prompt": workflow}
|
# Strip internal metadata keys (e.g. "_meta") — they are not ComfyUI nodes
|
||||||
|
clean_workflow = {k: v for k, v in workflow.items() if not k.startswith("_")}
|
||||||
|
payload = {"prompt": clean_workflow}
|
||||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||||
resp = await client.post(f"{self.base_url}/api/prompt", json=payload)
|
resp = await client.post(f"{self.base_url}/api/prompt", json=payload)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
@@ -115,7 +117,8 @@ def build_flux_workflow(
|
|||||||
wf["27"]["inputs"]["height"] = height
|
wf["27"]["inputs"]["height"] = height
|
||||||
wf["13"]["inputs"]["steps"] = steps
|
wf["13"]["inputs"]["steps"] = steps
|
||||||
wf["13"]["inputs"]["seed"] = actual_seed
|
wf["13"]["inputs"]["seed"] = actual_seed
|
||||||
wf["30"]["inputs"]["ckpt_name"] = model
|
# Node 32 = UNETLoader (flux1-schnell.safetensors is UNet-only, not all-in-one checkpoint)
|
||||||
|
wf["32"]["inputs"]["unet_name"] = model
|
||||||
|
|
||||||
# Attach the actual seed as metadata so callers can retrieve it
|
# Attach the actual seed as metadata so callers can retrieve it
|
||||||
wf["_meta"] = {"actual_seed": actual_seed}
|
wf["_meta"] = {"actual_seed": actual_seed}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"6": {
|
"6": {
|
||||||
"class_type": "CLIPTextEncode",
|
"class_type": "CLIPTextEncode",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"clip": ["30", 1],
|
"clip": ["30", 0],
|
||||||
"text": "PROMPT_PLACEHOLDER"
|
"text": "PROMPT_PLACEHOLDER"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
"class_type": "VAEDecode",
|
"class_type": "VAEDecode",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"samples": ["13", 0],
|
"samples": ["13", 0],
|
||||||
"vae": ["30", 2]
|
"vae": ["31", 0]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"9": {
|
"9": {
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
"cfg": 1.0,
|
"cfg": 1.0,
|
||||||
"denoise": 1.0,
|
"denoise": 1.0,
|
||||||
"latent_image": ["27", 0],
|
"latent_image": ["27", 0],
|
||||||
"model": ["30", 0],
|
"model": ["32", 0],
|
||||||
"negative": ["33", 0],
|
"negative": ["33", 0],
|
||||||
"positive": ["6", 0],
|
"positive": ["6", 0],
|
||||||
"sampler_name": "euler",
|
"sampler_name": "euler",
|
||||||
@@ -44,15 +44,31 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"30": {
|
"30": {
|
||||||
"class_type": "CheckpointLoaderSimple",
|
"class_type": "DualCLIPLoader",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"ckpt_name": "flux1-schnell.safetensors"
|
"clip_name1": "t5xxl_fp8_e4m3fn.safetensors",
|
||||||
|
"clip_name2": "clip_l.safetensors",
|
||||||
|
"type": "flux",
|
||||||
|
"device": "default"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"31": {
|
||||||
|
"class_type": "VAELoader",
|
||||||
|
"inputs": {
|
||||||
|
"vae_name": "ae.safetensors"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"32": {
|
||||||
|
"class_type": "UNETLoader",
|
||||||
|
"inputs": {
|
||||||
|
"unet_name": "flux1-schnell.safetensors",
|
||||||
|
"weight_dtype": "fp8_e4m3fn"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"33": {
|
"33": {
|
||||||
"class_type": "CLIPTextEncode",
|
"class_type": "CLIPTextEncode",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"clip": ["30", 1],
|
"clip": ["30", 0],
|
||||||
"text": "NEGATIVE_PLACEHOLDER"
|
"text": "NEGATIVE_PLACEHOLDER"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,9 @@ def test_build_flux_workflow_structure():
|
|||||||
assert wf["9"]["class_type"] == "SaveImage"
|
assert wf["9"]["class_type"] == "SaveImage"
|
||||||
assert wf["13"]["class_type"] == "KSampler"
|
assert wf["13"]["class_type"] == "KSampler"
|
||||||
assert wf["27"]["class_type"] == "EmptySD3LatentImage"
|
assert wf["27"]["class_type"] == "EmptySD3LatentImage"
|
||||||
assert wf["30"]["class_type"] == "CheckpointLoaderSimple"
|
assert wf["30"]["class_type"] == "DualCLIPLoader"
|
||||||
|
assert wf["31"]["class_type"] == "VAELoader"
|
||||||
|
assert wf["32"]["class_type"] == "UNETLoader"
|
||||||
assert wf["33"]["class_type"] == "CLIPTextEncode"
|
assert wf["33"]["class_type"] == "CLIPTextEncode"
|
||||||
|
|
||||||
|
|
||||||
@@ -65,7 +67,7 @@ def test_build_flux_workflow_params_injected():
|
|||||||
assert wf["27"]["inputs"]["height"] == 768
|
assert wf["27"]["inputs"]["height"] == 768
|
||||||
assert wf["13"]["inputs"]["steps"] == 8
|
assert wf["13"]["inputs"]["steps"] == 8
|
||||||
assert wf["13"]["inputs"]["seed"] == 12345
|
assert wf["13"]["inputs"]["seed"] == 12345
|
||||||
assert wf["30"]["inputs"]["ckpt_name"] == "sdxl.safetensors"
|
assert wf["32"]["inputs"]["unet_name"] == "sdxl.safetensors"
|
||||||
|
|
||||||
|
|
||||||
def test_negative_prompt_included():
|
def test_negative_prompt_included():
|
||||||
|
|||||||
Reference in New Issue
Block a user