confyui 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- confyui-0.1.0/PKG-INFO +152 -0
- confyui-0.1.0/README.md +117 -0
- confyui-0.1.0/pyproject.toml +126 -0
- confyui-0.1.0/src/confyui/__init__.py +10 -0
- confyui-0.1.0/src/confyui/cli.py +203 -0
- confyui-0.1.0/src/confyui/client.py +248 -0
- confyui-0.1.0/src/confyui/config.py +103 -0
- confyui-0.1.0/src/confyui/server.py +264 -0
- confyui-0.1.0/src/confyui/workflows.py +212 -0
confyui-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: confyui
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: KpihX sovereign local image generation and editing daemon over ComfyUI (txt2img, img2img, inpaint; XPU-first)
|
|
5
|
+
Author: KpihX
|
|
6
|
+
Author-email: KpihX <kapoivha@gmail.com>
|
|
7
|
+
Requires-Dist: fastapi>=0.159.1
|
|
8
|
+
Requires-Dist: httpx>=0.28.1
|
|
9
|
+
Requires-Dist: huggingface-hub>=2.2.0
|
|
10
|
+
Requires-Dist: pillow>=13.0.0
|
|
11
|
+
Requires-Dist: psutil>=7.2.2
|
|
12
|
+
Requires-Dist: python-dotenv>=1.2.4
|
|
13
|
+
Requires-Dist: rich>=15.0.0
|
|
14
|
+
Requires-Dist: typer>=0.27.3
|
|
15
|
+
Requires-Dist: uvicorn>=0.54.0
|
|
16
|
+
Requires-Dist: torch>=2.8 ; extra == 'cpu'
|
|
17
|
+
Requires-Dist: torchvision>=0.23 ; extra == 'cpu'
|
|
18
|
+
Requires-Dist: torch>=2.8 ; extra == 'cuda'
|
|
19
|
+
Requires-Dist: torchvision>=0.23 ; extra == 'cuda'
|
|
20
|
+
Requires-Dist: transformers>=4.56 ; extra == 'cuda'
|
|
21
|
+
Requires-Dist: accelerate>=1.10.1 ; extra == 'cuda'
|
|
22
|
+
Requires-Dist: huggingface-hub>=0.34.4 ; extra == 'cuda'
|
|
23
|
+
Requires-Dist: safetensors>=0.6.2 ; extra == 'cuda'
|
|
24
|
+
Requires-Dist: torch>=2.8 ; extra == 'xpu'
|
|
25
|
+
Requires-Dist: torchvision>=0.23 ; extra == 'xpu'
|
|
26
|
+
Requires-Dist: transformers>=4.56 ; extra == 'xpu'
|
|
27
|
+
Requires-Dist: accelerate>=1.10.1 ; extra == 'xpu'
|
|
28
|
+
Requires-Dist: huggingface-hub>=0.34.4 ; extra == 'xpu'
|
|
29
|
+
Requires-Dist: safetensors>=0.6.2 ; extra == 'xpu'
|
|
30
|
+
Requires-Python: >=3.12
|
|
31
|
+
Provides-Extra: cpu
|
|
32
|
+
Provides-Extra: cuda
|
|
33
|
+
Provides-Extra: xpu
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# confyui
|
|
37
|
+
|
|
38
|
+
KπX sovereign local image generation and editing daemon over ComfyUI.
|
|
39
|
+
|
|
40
|
+
Hardware-accelerated on Intel Arc (XPU) via PyTorch XPU wheels, with portable CPU and CUDA extras.
|
|
41
|
+
|
|
42
|
+
## Philosophy
|
|
43
|
+
|
|
44
|
+
- **0 Magic:** The CLI talks to ComfyUI's standard REST API via structured JSON graphs.
|
|
45
|
+
- **0 Manual Start:** The first call (`generate`, `edit`, `inpaint`) automatically starts ComfyUI in the background.
|
|
46
|
+
- **0 Wasted RAM:** An idle watchdog shuts ComfyUI down after `CONFYUI_IDLE_TIMEOUT` seconds of inactivity (default: 30 minutes).
|
|
47
|
+
- **0 Hardcoding:** Paths expand `$HOME` at runtime and are 100% overridable via environment variables.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Clone or navigate to the repository
|
|
55
|
+
cd ~/Labs/KpihX-Labs/AI/confyui
|
|
56
|
+
|
|
57
|
+
# Intel Arc workstation (XPU)
|
|
58
|
+
make sync-xpu
|
|
59
|
+
# or: uv sync --extra xpu
|
|
60
|
+
|
|
61
|
+
# Generic CPU fallback
|
|
62
|
+
make sync-cpu
|
|
63
|
+
|
|
64
|
+
# NVIDIA CUDA host
|
|
65
|
+
make sync-cuda
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Environment Variables
|
|
71
|
+
|
|
72
|
+
| Variable | Default | Description |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| `CONFYUI_DIR` | `~/RAW/comfyui-local/ComfyUI` | Path to the ComfyUI checkout |
|
|
75
|
+
| `CONFYUI_PYTHON` | `$CONFYUI_DIR/.venv/bin/python` | Python interpreter used to run ComfyUI |
|
|
76
|
+
| `CONFYUI_HOST` | `127.0.0.1` | Bind address |
|
|
77
|
+
| `CONFYUI_PORT` | `8188` | Port |
|
|
78
|
+
| `CONFYUI_IDLE_TIMEOUT` | `1800` | Seconds of inactivity before auto-shutdown |
|
|
79
|
+
| `CONFYUI_SPAWN_TIMEOUT` | `90` | Max seconds to wait for daemon readiness |
|
|
80
|
+
| `CONFYUI_STATE_DIR` | `~/.cache/confyui` | Directory for PID, logs, and heartbeat |
|
|
81
|
+
| `CONFYUI_DEFAULT_CKPT` | `Realistic_Vision_V5.1_noVAE.safetensors` | Default checkpoint for txt2img & img2img |
|
|
82
|
+
| `CONFYUI_INPAINT_CKPT` | `Realistic_Vision_V5.1_fp16-no-ema-inpainting.safetensors` | Default inpainting checkpoint |
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Usage
|
|
87
|
+
|
|
88
|
+
### 1. Text-to-Image (`generate`)
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Minimal
|
|
92
|
+
uv run confyui generate "a red panda wearing a top hat on the moon, cinematic lighting"
|
|
93
|
+
|
|
94
|
+
# Full options
|
|
95
|
+
uv run confyui generate "a red panda wearing a top hat on the moon" \
|
|
96
|
+
--negative "blurry, low quality, artifacts" \
|
|
97
|
+
--width 512 \
|
|
98
|
+
--height 512 \
|
|
99
|
+
--steps 20 \
|
|
100
|
+
--cfg 7.0 \
|
|
101
|
+
--seed 42 \
|
|
102
|
+
--sampler euler \
|
|
103
|
+
--scheduler normal \
|
|
104
|
+
--model "Realistic_Vision_V5.1_noVAE.safetensors" \
|
|
105
|
+
--out panda_moon.png
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 2. Image-to-Image (`edit`)
|
|
109
|
+
|
|
110
|
+
Modifies an existing image based on a prompt. The source image is kept and only altered proportionally to `--denoise` (0.0 = unchanged, 0.45-0.60 = balanced modification, 1.0 = total repaint):
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
uv run confyui edit panda_moon.png "make the red panda wear a golden astronaut suit" \
|
|
114
|
+
--denoise 0.50 \
|
|
115
|
+
--steps 20 \
|
|
116
|
+
--out panda_astronaut.png
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 3. Inpainting (`inpaint`)
|
|
120
|
+
|
|
121
|
+
Repaints only the masked region (white pixels in the mask are regenerated, black pixels preserved):
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
uv run confyui inpaint portrait.png torso_mask.png "she wears a long red silk dress" \
|
|
125
|
+
--denoise 1.0 \
|
|
126
|
+
--steps 25 \
|
|
127
|
+
--out portrait_red_dress.png
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 4. Daemon Management
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# Inspect status, uptime, PID, and idle time remaining
|
|
134
|
+
uv run confyui status
|
|
135
|
+
|
|
136
|
+
# List installed checkpoints
|
|
137
|
+
uv run confyui models
|
|
138
|
+
|
|
139
|
+
# Stop the daemon immediately to free RAM and VRAM
|
|
140
|
+
uv run confyui stop
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Development
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
make help # Show available targets
|
|
149
|
+
make check # Lint (ruff), format check, compile, pyright, pytest
|
|
150
|
+
make smoke # Fast sanity check of CLI and imports
|
|
151
|
+
make demo # Run a real 12-step test generation
|
|
152
|
+
```
|
confyui-0.1.0/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# confyui
|
|
2
|
+
|
|
3
|
+
KπX sovereign local image generation and editing daemon over ComfyUI.
|
|
4
|
+
|
|
5
|
+
Hardware-accelerated on Intel Arc (XPU) via PyTorch XPU wheels, with portable CPU and CUDA extras.
|
|
6
|
+
|
|
7
|
+
## Philosophy
|
|
8
|
+
|
|
9
|
+
- **0 Magic:** The CLI talks to ComfyUI's standard REST API via structured JSON graphs.
|
|
10
|
+
- **0 Manual Start:** The first call (`generate`, `edit`, `inpaint`) automatically starts ComfyUI in the background.
|
|
11
|
+
- **0 Wasted RAM:** An idle watchdog shuts ComfyUI down after `CONFYUI_IDLE_TIMEOUT` seconds of inactivity (default: 30 minutes).
|
|
12
|
+
- **0 Hardcoding:** Paths expand `$HOME` at runtime and are 100% overridable via environment variables.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Clone or navigate to the repository
|
|
20
|
+
cd ~/Labs/KpihX-Labs/AI/confyui
|
|
21
|
+
|
|
22
|
+
# Intel Arc workstation (XPU)
|
|
23
|
+
make sync-xpu
|
|
24
|
+
# or: uv sync --extra xpu
|
|
25
|
+
|
|
26
|
+
# Generic CPU fallback
|
|
27
|
+
make sync-cpu
|
|
28
|
+
|
|
29
|
+
# NVIDIA CUDA host
|
|
30
|
+
make sync-cuda
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Environment Variables
|
|
36
|
+
|
|
37
|
+
| Variable | Default | Description |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `CONFYUI_DIR` | `~/RAW/comfyui-local/ComfyUI` | Path to the ComfyUI checkout |
|
|
40
|
+
| `CONFYUI_PYTHON` | `$CONFYUI_DIR/.venv/bin/python` | Python interpreter used to run ComfyUI |
|
|
41
|
+
| `CONFYUI_HOST` | `127.0.0.1` | Bind address |
|
|
42
|
+
| `CONFYUI_PORT` | `8188` | Port |
|
|
43
|
+
| `CONFYUI_IDLE_TIMEOUT` | `1800` | Seconds of inactivity before auto-shutdown |
|
|
44
|
+
| `CONFYUI_SPAWN_TIMEOUT` | `90` | Max seconds to wait for daemon readiness |
|
|
45
|
+
| `CONFYUI_STATE_DIR` | `~/.cache/confyui` | Directory for PID, logs, and heartbeat |
|
|
46
|
+
| `CONFYUI_DEFAULT_CKPT` | `Realistic_Vision_V5.1_noVAE.safetensors` | Default checkpoint for txt2img & img2img |
|
|
47
|
+
| `CONFYUI_INPAINT_CKPT` | `Realistic_Vision_V5.1_fp16-no-ema-inpainting.safetensors` | Default inpainting checkpoint |
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
### 1. Text-to-Image (`generate`)
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Minimal
|
|
57
|
+
uv run confyui generate "a red panda wearing a top hat on the moon, cinematic lighting"
|
|
58
|
+
|
|
59
|
+
# Full options
|
|
60
|
+
uv run confyui generate "a red panda wearing a top hat on the moon" \
|
|
61
|
+
--negative "blurry, low quality, artifacts" \
|
|
62
|
+
--width 512 \
|
|
63
|
+
--height 512 \
|
|
64
|
+
--steps 20 \
|
|
65
|
+
--cfg 7.0 \
|
|
66
|
+
--seed 42 \
|
|
67
|
+
--sampler euler \
|
|
68
|
+
--scheduler normal \
|
|
69
|
+
--model "Realistic_Vision_V5.1_noVAE.safetensors" \
|
|
70
|
+
--out panda_moon.png
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2. Image-to-Image (`edit`)
|
|
74
|
+
|
|
75
|
+
Modifies an existing image based on a prompt. The source image is kept and only altered proportionally to `--denoise` (0.0 = unchanged, 0.45-0.60 = balanced modification, 1.0 = total repaint):
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
uv run confyui edit panda_moon.png "make the red panda wear a golden astronaut suit" \
|
|
79
|
+
--denoise 0.50 \
|
|
80
|
+
--steps 20 \
|
|
81
|
+
--out panda_astronaut.png
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3. Inpainting (`inpaint`)
|
|
85
|
+
|
|
86
|
+
Repaints only the masked region (white pixels in the mask are regenerated, black pixels preserved):
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
uv run confyui inpaint portrait.png torso_mask.png "she wears a long red silk dress" \
|
|
90
|
+
--denoise 1.0 \
|
|
91
|
+
--steps 25 \
|
|
92
|
+
--out portrait_red_dress.png
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 4. Daemon Management
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Inspect status, uptime, PID, and idle time remaining
|
|
99
|
+
uv run confyui status
|
|
100
|
+
|
|
101
|
+
# List installed checkpoints
|
|
102
|
+
uv run confyui models
|
|
103
|
+
|
|
104
|
+
# Stop the daemon immediately to free RAM and VRAM
|
|
105
|
+
uv run confyui stop
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Development
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
make help # Show available targets
|
|
114
|
+
make check # Lint (ruff), format check, compile, pyright, pytest
|
|
115
|
+
make smoke # Fast sanity check of CLI and imports
|
|
116
|
+
make demo # Run a real 12-step test generation
|
|
117
|
+
```
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "confyui"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "KpihX sovereign local image generation and editing daemon over ComfyUI (txt2img, img2img, inpaint; XPU-first)"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "KpihX", email = "kapoivha@gmail.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.12"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"fastapi>=0.159.1",
|
|
12
|
+
"httpx>=0.28.1",
|
|
13
|
+
"huggingface-hub>=2.2.0",
|
|
14
|
+
"pillow>=13.0.0",
|
|
15
|
+
"psutil>=7.2.2",
|
|
16
|
+
"python-dotenv>=1.2.4",
|
|
17
|
+
"rich>=15.0.0",
|
|
18
|
+
"typer>=0.27.3",
|
|
19
|
+
"uvicorn>=0.54.0",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
# ── Hardware extras (mutually exclusive) ─────────────────────────────────────
|
|
23
|
+
# Usage: bare sync = client only, plus exactly one accelerator backend.
|
|
24
|
+
# uv sync client only (remote-style, no torch)
|
|
25
|
+
# uv sync --extra cpu + CPU torch
|
|
26
|
+
# uv sync --extra xpu + Intel Arc (XPU) torch
|
|
27
|
+
# uv sync --extra cuda + NVIDIA CUDA torch
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
cpu = [
|
|
30
|
+
"torch>=2.8",
|
|
31
|
+
"torchvision>=0.23",
|
|
32
|
+
]
|
|
33
|
+
xpu = [
|
|
34
|
+
"torch>=2.8",
|
|
35
|
+
"torchvision>=0.23",
|
|
36
|
+
"transformers>=4.56",
|
|
37
|
+
"accelerate>=1.10.1",
|
|
38
|
+
"huggingface_hub>=0.34.4",
|
|
39
|
+
"safetensors>=0.6.2",
|
|
40
|
+
]
|
|
41
|
+
cuda = [
|
|
42
|
+
"torch>=2.8",
|
|
43
|
+
"torchvision>=0.23",
|
|
44
|
+
"transformers>=4.56",
|
|
45
|
+
"accelerate>=1.10.1",
|
|
46
|
+
"huggingface_hub>=0.34.4",
|
|
47
|
+
"safetensors>=0.6.2",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[project.scripts]
|
|
51
|
+
confyui = "confyui.cli:app"
|
|
52
|
+
|
|
53
|
+
[build-system]
|
|
54
|
+
requires = ["uv_build>=0.10.3,<0.11.0"]
|
|
55
|
+
build-backend = "uv_build"
|
|
56
|
+
|
|
57
|
+
[tool.uv.build-backend]
|
|
58
|
+
module-name = "confyui"
|
|
59
|
+
|
|
60
|
+
# ── Resolution ───────────────────────────────────────────────────────────────
|
|
61
|
+
# On Intel Arc, unsafe-best-match makes the XPU wheels rank correctly against
|
|
62
|
+
# PyPI during resolution. Removing it is how PyTorch silently resolves to a
|
|
63
|
+
# CUDA build and the model ends up on the CPU. Rationale and full doctrine:
|
|
64
|
+
# k-ai/references/pyproject-hardware.md, template: AI/nobg/pyproject.toml.
|
|
65
|
+
[tool.uv]
|
|
66
|
+
package = true
|
|
67
|
+
index-strategy = "unsafe-best-match"
|
|
68
|
+
# One accelerator backend per environment, never a mixture.
|
|
69
|
+
conflicts = [
|
|
70
|
+
[{ extra = "cpu" }, { extra = "xpu" }, { extra = "cuda" }],
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
# ── PyTorch indexes ──────────────────────────────────────────────────────────
|
|
74
|
+
# `explicit = true` keeps an index out of general resolution so the base
|
|
75
|
+
# client-only sync is unaffected; the XPU index is left non-explicit on
|
|
76
|
+
# purpose — that combination is the one measured working on this machine.
|
|
77
|
+
[[tool.uv.index]]
|
|
78
|
+
name = "pytorch-cpu"
|
|
79
|
+
url = "https://download.pytorch.org/whl/cpu"
|
|
80
|
+
explicit = true
|
|
81
|
+
|
|
82
|
+
[[tool.uv.index]]
|
|
83
|
+
name = "pytorch-xpu"
|
|
84
|
+
url = "https://download.pytorch.org/whl/xpu"
|
|
85
|
+
|
|
86
|
+
[[tool.uv.index]]
|
|
87
|
+
name = "pytorch-cuda"
|
|
88
|
+
url = "https://download.pytorch.org/whl/cu130"
|
|
89
|
+
explicit = true
|
|
90
|
+
|
|
91
|
+
[tool.uv.sources]
|
|
92
|
+
# This binding is what actually selects the accelerator.
|
|
93
|
+
torch = [
|
|
94
|
+
{ index = "pytorch-cpu", extra = "cpu" },
|
|
95
|
+
{ index = "pytorch-xpu", extra = "xpu" },
|
|
96
|
+
{ index = "pytorch-cuda", extra = "cuda" },
|
|
97
|
+
]
|
|
98
|
+
torchvision = [
|
|
99
|
+
{ index = "pytorch-cpu", extra = "cpu" },
|
|
100
|
+
{ index = "pytorch-xpu", extra = "xpu" },
|
|
101
|
+
{ index = "pytorch-cuda", extra = "cuda" },
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
# ── Development ──────────────────────────────────────────────────────────────
|
|
105
|
+
[dependency-groups]
|
|
106
|
+
dev = [
|
|
107
|
+
"pytest",
|
|
108
|
+
"pytest-cov",
|
|
109
|
+
"ruff",
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
[tool.ruff]
|
|
113
|
+
line-length = 120
|
|
114
|
+
target-version = "py312"
|
|
115
|
+
|
|
116
|
+
[tool.ruff.lint.flake8-bugbear]
|
|
117
|
+
# `typer.Argument(...)` and `typer.Option(...)` are immutable factories, not mutable
|
|
118
|
+
# default values, so B008's concern does not apply to them. Declared once here instead of
|
|
119
|
+
# a `# noqa: B008` on every parameter that typer requires inline in the signature.
|
|
120
|
+
extend-immutable-calls = ["typer.Argument", "typer.Option"]
|
|
121
|
+
|
|
122
|
+
[tool.pyright]
|
|
123
|
+
typeCheckingMode = "basic"
|
|
124
|
+
|
|
125
|
+
[tool.pytest.ini_options]
|
|
126
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""KπX sovereign local image generation & editing daemon over ComfyUI."""
|
|
2
|
+
|
|
3
|
+
# Note: per sovereign standard, package version is defined exclusively in
|
|
4
|
+
# pyproject.toml to eliminate desynchronization risks. Do NOT add __version__ here.
|
|
5
|
+
|
|
6
|
+
from confyui.client import ComfyClient
|
|
7
|
+
from confyui.config import Config
|
|
8
|
+
from confyui.server import ServerManager
|
|
9
|
+
|
|
10
|
+
__all__ = ["ComfyClient", "Config", "ServerManager"]
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""confyui CLI — Typer-based interface to the sovereign ComfyUI daemon.
|
|
2
|
+
|
|
3
|
+
Provides `generate` (txt2img), `edit` (img2img), `inpaint`, `status`, `stop`, and `models`.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
|
|
14
|
+
from confyui.client import ComfyClient
|
|
15
|
+
from confyui.config import Config
|
|
16
|
+
from confyui.server import ServerManager
|
|
17
|
+
|
|
18
|
+
app = typer.Typer(
|
|
19
|
+
name="confyui",
|
|
20
|
+
help="KπX sovereign local image generation & editing daemon over ComfyUI (XPU-first).",
|
|
21
|
+
no_args_is_help=True,
|
|
22
|
+
)
|
|
23
|
+
console = Console()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@app.command()
|
|
27
|
+
def generate(
|
|
28
|
+
prompt: str = typer.Argument(..., help="Text prompt to generate"),
|
|
29
|
+
negative: str = typer.Option("", "--negative", "-n", help="Negative prompt"),
|
|
30
|
+
width: int = typer.Option(512, "--width", "-w", help="Image width in pixels"),
|
|
31
|
+
height: int = typer.Option(512, "--height", "-h", help="Image height in pixels"),
|
|
32
|
+
steps: int = typer.Option(20, "--steps", "-s", help="Sampling steps"),
|
|
33
|
+
cfg: float = typer.Option(7.0, "--cfg", help="Classifier-free guidance scale"),
|
|
34
|
+
seed: int | None = typer.Option(None, "--seed", help="Random seed for reproducibility"),
|
|
35
|
+
sampler: str = typer.Option("euler", "--sampler", help="Sampler name (euler, dpmpp_2m, etc.)"),
|
|
36
|
+
scheduler: str = typer.Option("normal", "--scheduler", help="Scheduler name (normal, karras, etc.)"),
|
|
37
|
+
model: str | None = typer.Option(None, "--model", "-m", help="Checkpoint filename"),
|
|
38
|
+
out: Path | None = typer.Option(None, "--out", "-o", help="Destination path for output image"),
|
|
39
|
+
timeout: float = typer.Option(300.0, "--timeout", help="Max seconds to wait"),
|
|
40
|
+
) -> None:
|
|
41
|
+
"""Generate an image from text (txt2img)."""
|
|
42
|
+
client = ComfyClient()
|
|
43
|
+
with console.status("[bold green]Generating image..."):
|
|
44
|
+
dest = client.generate(
|
|
45
|
+
prompt=prompt,
|
|
46
|
+
negative_prompt=negative,
|
|
47
|
+
width=width,
|
|
48
|
+
height=height,
|
|
49
|
+
steps=steps,
|
|
50
|
+
cfg=cfg,
|
|
51
|
+
seed=seed,
|
|
52
|
+
sampler_name=sampler,
|
|
53
|
+
scheduler=scheduler,
|
|
54
|
+
model=model,
|
|
55
|
+
out=out,
|
|
56
|
+
timeout=timeout,
|
|
57
|
+
)
|
|
58
|
+
console.print(f"[bold green]✓[/bold green] Saved: [cyan]{dest}[/cyan] ({dest.stat().st_size:,} bytes)")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@app.command()
|
|
62
|
+
def edit(
|
|
63
|
+
source: Path = typer.Argument(..., help="Path to source image to modify"),
|
|
64
|
+
prompt: str = typer.Argument(..., help="Text prompt describing the desired modification"),
|
|
65
|
+
negative: str = typer.Option("", "--negative", "-n", help="Negative prompt"),
|
|
66
|
+
denoise: float = typer.Option(
|
|
67
|
+
0.55, "--denoise", "-d", help="Denoising strength (0.0 = untouched, 1.0 = full rewrite)"
|
|
68
|
+
),
|
|
69
|
+
steps: int = typer.Option(20, "--steps", "-s", help="Sampling steps"),
|
|
70
|
+
cfg: float = typer.Option(7.0, "--cfg", help="Classifier-free guidance scale"),
|
|
71
|
+
seed: int | None = typer.Option(None, "--seed", help="Random seed for reproducibility"),
|
|
72
|
+
sampler: str = typer.Option("euler", "--sampler", help="Sampler name"),
|
|
73
|
+
scheduler: str = typer.Option("normal", "--scheduler", help="Scheduler name"),
|
|
74
|
+
model: str | None = typer.Option(None, "--model", "-m", help="Checkpoint filename"),
|
|
75
|
+
out: Path | None = typer.Option(None, "--out", "-o", help="Destination path"),
|
|
76
|
+
timeout: float = typer.Option(300.0, "--timeout", help="Max seconds to wait"),
|
|
77
|
+
) -> None:
|
|
78
|
+
"""Edit an existing image with a text prompt (img2img)."""
|
|
79
|
+
client = ComfyClient()
|
|
80
|
+
with console.status(f"[bold green]Editing {source.name} (denoise={denoise})..."):
|
|
81
|
+
dest = client.edit(
|
|
82
|
+
source=source,
|
|
83
|
+
prompt=prompt,
|
|
84
|
+
negative_prompt=negative,
|
|
85
|
+
denoise=denoise,
|
|
86
|
+
steps=steps,
|
|
87
|
+
cfg=cfg,
|
|
88
|
+
seed=seed,
|
|
89
|
+
sampler_name=sampler,
|
|
90
|
+
scheduler=scheduler,
|
|
91
|
+
model=model,
|
|
92
|
+
out=out,
|
|
93
|
+
timeout=timeout,
|
|
94
|
+
)
|
|
95
|
+
console.print(f"[bold green]✓[/bold green] Saved: [cyan]{dest}[/cyan] ({dest.stat().st_size:,} bytes)")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@app.command()
|
|
99
|
+
def inpaint(
|
|
100
|
+
source: Path = typer.Argument(..., help="Path to source image"),
|
|
101
|
+
mask: Path = typer.Argument(..., help="Path to binary mask (white = regenerate, black = preserve)"),
|
|
102
|
+
prompt: str = typer.Argument(..., help="Text prompt for the masked region"),
|
|
103
|
+
negative: str = typer.Option("", "--negative", "-n", help="Negative prompt"),
|
|
104
|
+
denoise: float = typer.Option(1.0, "--denoise", "-d", help="Denoising strength for masked region"),
|
|
105
|
+
steps: int = typer.Option(25, "--steps", "-s", help="Sampling steps"),
|
|
106
|
+
cfg: float = typer.Option(7.0, "--cfg", help="Classifier-free guidance scale"),
|
|
107
|
+
seed: int | None = typer.Option(None, "--seed", help="Random seed"),
|
|
108
|
+
sampler: str = typer.Option("euler", "--sampler", help="Sampler name"),
|
|
109
|
+
scheduler: str = typer.Option("normal", "--scheduler", help="Scheduler name"),
|
|
110
|
+
model: str | None = typer.Option(None, "--model", "-m", help="Inpainting checkpoint filename"),
|
|
111
|
+
out: Path | None = typer.Option(None, "--out", "-o", help="Destination path"),
|
|
112
|
+
timeout: float = typer.Option(300.0, "--timeout", help="Max seconds to wait"),
|
|
113
|
+
) -> None:
|
|
114
|
+
"""Inpaint a region defined by a mask."""
|
|
115
|
+
client = ComfyClient()
|
|
116
|
+
with console.status(f"[bold green]Inpainting {source.name}..."):
|
|
117
|
+
dest = client.inpaint(
|
|
118
|
+
source=source,
|
|
119
|
+
mask=mask,
|
|
120
|
+
prompt=prompt,
|
|
121
|
+
negative_prompt=negative,
|
|
122
|
+
denoise=denoise,
|
|
123
|
+
steps=steps,
|
|
124
|
+
cfg=cfg,
|
|
125
|
+
seed=seed,
|
|
126
|
+
sampler_name=sampler,
|
|
127
|
+
scheduler=scheduler,
|
|
128
|
+
model=model,
|
|
129
|
+
out=out,
|
|
130
|
+
timeout=timeout,
|
|
131
|
+
)
|
|
132
|
+
console.print(f"[bold green]✓[/bold green] Saved: [cyan]{dest}[/cyan] ({dest.stat().st_size:,} bytes)")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@app.command()
|
|
136
|
+
def status() -> None:
|
|
137
|
+
"""Show the status of the ComfyUI background daemon."""
|
|
138
|
+
mgr = ServerManager()
|
|
139
|
+
st = mgr.status()
|
|
140
|
+
|
|
141
|
+
table = Table(title="confyui Daemon Status")
|
|
142
|
+
table.add_column("Property", style="bold cyan")
|
|
143
|
+
table.add_column("Value")
|
|
144
|
+
|
|
145
|
+
table.add_row("HTTP Server", "[green]UP[/green]" if st["http_up"] else "[red]DOWN[/red]")
|
|
146
|
+
table.add_row("Supervisor Process", "[green]ALIVE[/green]" if st["running"] else "[dim]STOPPED[/dim]")
|
|
147
|
+
table.add_row("PID", str(st["pid"]) if st["pid"] else "—")
|
|
148
|
+
table.add_row("Base URL", st["base_url"])
|
|
149
|
+
table.add_row("Idle Timeout", f"{st['idle_timeout_sec']}s")
|
|
150
|
+
|
|
151
|
+
rem = st["idle_remaining_sec"]
|
|
152
|
+
if rem is not None:
|
|
153
|
+
table.add_row("Idle Remaining", f"{rem}s before auto-shutdown")
|
|
154
|
+
else:
|
|
155
|
+
table.add_row("Idle Remaining", "—")
|
|
156
|
+
|
|
157
|
+
table.add_row("ComfyUI Dir", st["comfyui_dir"])
|
|
158
|
+
table.add_row("Log File", st["log_file"])
|
|
159
|
+
|
|
160
|
+
console.print(table)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@app.command()
|
|
164
|
+
def stop() -> None:
|
|
165
|
+
"""Stop the running ComfyUI daemon and free RAM/VRAM immediately."""
|
|
166
|
+
mgr = ServerManager()
|
|
167
|
+
if mgr.stop():
|
|
168
|
+
console.print("[bold yellow]✓[/bold yellow] ComfyUI daemon stopped.")
|
|
169
|
+
else:
|
|
170
|
+
console.print("[dim]ComfyUI daemon is not running.[/dim]")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@app.command()
|
|
174
|
+
def models() -> None:
|
|
175
|
+
"""List available checkpoint models in ComfyUI."""
|
|
176
|
+
client = ComfyClient()
|
|
177
|
+
ckpts = client.list_checkpoints()
|
|
178
|
+
if not ckpts:
|
|
179
|
+
console.print("[yellow]No checkpoints found in ComfyUI checkpoints directory.[/yellow]")
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
table = Table(title="Available Checkpoints")
|
|
183
|
+
table.add_column("#", style="dim")
|
|
184
|
+
table.add_column("Model Checkpoint", style="cyan")
|
|
185
|
+
|
|
186
|
+
cfg = Config.from_env()
|
|
187
|
+
for idx, c in enumerate(ckpts, 1):
|
|
188
|
+
suffix = ""
|
|
189
|
+
if c == cfg.default_ckpt:
|
|
190
|
+
suffix = " [bold green](default)[/bold green]"
|
|
191
|
+
elif c == cfg.default_inpaint_ckpt:
|
|
192
|
+
suffix = " [bold blue](default inpaint)[/bold blue]"
|
|
193
|
+
table.add_row(str(idx), c + suffix)
|
|
194
|
+
|
|
195
|
+
console.print(table)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def main() -> None:
|
|
199
|
+
app()
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
if __name__ == "__main__":
|
|
203
|
+
main()
|