serverless: client scripts (sync/async) + README; timings from live endpoint tests
Browse files- serverless/README.md +7 -3
- serverless/client/README.md +81 -0
- serverless/client/qwen-edit-turbo-v4.json +758 -0
- serverless/client/qwen_edit_async.py +168 -0
- serverless/client/qwen_edit_sync.py +145 -0
serverless/README.md
CHANGED
|
@@ -27,9 +27,13 @@ serverless/
|
|
| 27 |
│ ├── endpoint.json # models + custom nodes + default workflow
|
| 28 |
│ ├── workflows/<name>.json # API-format workflow(s) baked into the image
|
| 29 |
│ └── params/<name>.json # friendly-param -> node.input mapping
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
```
|
| 34 |
|
| 35 |
## Calling the endpoint
|
|
|
|
| 27 |
│ ├── endpoint.json # models + custom nodes + default workflow
|
| 28 |
│ ├── workflows/<name>.json # API-format workflow(s) baked into the image
|
| 29 |
│ └── params/<name>.json # friendly-param -> node.input mapping
|
| 30 |
+
├── tests/
|
| 31 |
+
│ ├── local_test.py # drive handler against a locally running ComfyUI
|
| 32 |
+
│ └── endpoint_test.py # drive a deployed RunPod endpoint
|
| 33 |
+
└── client/ # standalone caller scripts (stdlib-only) + usage README
|
| 34 |
+
├── qwen_edit_sync.py # /runsync: block until done
|
| 35 |
+
├── qwen_edit_async.py # /run: submit, poll, fire-and-forget, collect, cancel
|
| 36 |
+
└── qwen-edit-turbo-v4.json
|
| 37 |
```
|
| 38 |
|
| 39 |
## Calling the endpoint
|
serverless/client/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# qwen-edit-turbo — client scripts
|
| 2 |
+
|
| 3 |
+
Call the deployed RunPod serverless endpoint (`dom5lwr0o5wq6u`, image
|
| 4 |
+
`plx1029/comfyui-serverless:qwen-edit-turbo-v1`) from anywhere. Both scripts
|
| 5 |
+
are standalone (Python 3.8+, stdlib only — no pip installs).
|
| 6 |
+
|
| 7 |
+
| file | what it does |
|
| 8 |
+
|---|---|
|
| 9 |
+
| `qwen_edit_sync.py` | `/runsync` — blocks until done, saves image(s) |
|
| 10 |
+
| `qwen_edit_async.py` | `/run` — submit, poll, or fire-and-forget + collect later |
|
| 11 |
+
| `qwen-edit-turbo-v4.json` | the API-format workflow baked into the endpoint (reference; also usable with `--workflow-json` after editing) |
|
| 12 |
+
|
| 13 |
+
## Quick start
|
| 14 |
+
|
| 15 |
+
```bash
|
| 16 |
+
export RUNPOD_API_KEY=... # or pass --api-key
|
| 17 |
+
|
| 18 |
+
# synchronous: person + outfit -> edited image in ./results
|
| 19 |
+
python qwen_edit_sync.py \
|
| 20 |
+
--image person.jpg --ref-image outfit.png \
|
| 21 |
+
--prompt "replace the outfit of the person in the first image with the outfit in the second image" \
|
| 22 |
+
--mode turbo-8 --out ./results
|
| 23 |
+
|
| 24 |
+
# async fire-and-forget
|
| 25 |
+
job=$(python qwen_edit_async.py --image person.jpg --prompt "remove the background people" --no-wait)
|
| 26 |
+
python qwen_edit_async.py --job-id "$job" --out ./results # collect later
|
| 27 |
+
```
|
| 28 |
+
|
| 29 |
+
Single-image edits: just omit `--ref-image` (the workflow's reference branch
|
| 30 |
+
switches off automatically).
|
| 31 |
+
|
| 32 |
+
## Arguments (both scripts)
|
| 33 |
+
|
| 34 |
+
| arg | default | meaning |
|
| 35 |
+
|---|---|---|
|
| 36 |
+
| `--api-key` | `$RUNPOD_API_KEY` | RunPod API key |
|
| 37 |
+
| `--endpoint-id` | `dom5lwr0o5wq6u` | endpoint to call |
|
| 38 |
+
| `--image` | required | main input image |
|
| 39 |
+
| `--ref-image` | none | reference image (outfit/style/person source) |
|
| 40 |
+
| `--prompt` | required | edit instruction |
|
| 41 |
+
| `--mode` | `turbo-8` | `turbo-4` (fastest) / `turbo-8` / `quality` (12-step, cfg 3) |
|
| 42 |
+
| `--seed` | random | reproducibility; the used seed is always printed |
|
| 43 |
+
| `--input-max-dim` | 2048 | main image is resized to fit this before editing |
|
| 44 |
+
| `--ref-max-dim` | 1024 | same for the reference image |
|
| 45 |
+
| `--output-max-dim` | 2560 | result upscale target |
|
| 46 |
+
| `--lora-skin-fix [--lora-skin-fix-strength]` | off / 1.0 | optional Skin-Fix lora |
|
| 47 |
+
| `--lora-amateur [--lora-amateur-strength]` | off / 1.0 | optional Amateur-Photo lora |
|
| 48 |
+
| `--workflow` | `qwen-edit-turbo-v4` | which baked workflow to run |
|
| 49 |
+
| `--set NODE.INPUT=VALUE` | — | raw graph override, repeatable (e.g. `--set 43.cfg=1.5`) |
|
| 50 |
+
| `--workflow-json FILE` | — | run a full custom API-format graph instead |
|
| 51 |
+
| `--out` | `.` | where to save result images |
|
| 52 |
+
| `--timeout` | 600 / 1800 | max seconds to wait |
|
| 53 |
+
|
| 54 |
+
`qwen_edit_async.py` extras: `--no-wait` (print job id and exit), `--job-id ID`
|
| 55 |
+
(poll/collect an existing job), `--poll SECONDS`, `--cancel JOB_ID`.
|
| 56 |
+
|
| 57 |
+
## What to expect (measured 2026-07-23)
|
| 58 |
+
|
| 59 |
+
| situation | delay | execution |
|
| 60 |
+
|---|---|---|
|
| 61 |
+
| warm worker | ~0.1–0.5 s | turbo-4 ≈ 42 s, turbo-8 ≈ 60 s (2048px, 2 input images, 2560px output) |
|
| 62 |
+
| FlashBoot resume (idle worker) | ~0.5 s | same as warm — models stay in VRAM |
|
| 63 |
+
| brand-new host (first ever pull) | ~15 min once | ~78 s (model load from NVMe + inference) |
|
| 64 |
+
|
| 65 |
+
Payload limits: ~10 MB on `/run`, ~20 MB on `/runsync`; base64 adds ~33 % to
|
| 66 |
+
image bytes. Keep the two inputs under ~7 MB combined for sync calls, or use
|
| 67 |
+
async. (URL inputs / S3 outputs are the planned upgrade if this becomes a
|
| 68 |
+
bottleneck.)
|
| 69 |
+
|
| 70 |
+
## Raw HTTP (no script)
|
| 71 |
+
|
| 72 |
+
```bash
|
| 73 |
+
curl -s -X POST "https://api.runpod.ai/v2/dom5lwr0o5wq6u/runsync" \
|
| 74 |
+
-H "Authorization: Bearer $RUNPOD_API_KEY" -H "Content-Type: application/json" \
|
| 75 |
+
-d '{"input": {"images": [{"name": "a.jpg", "image": "'"$(base64 -w0 a.jpg)"'"}],
|
| 76 |
+
"params": {"prompt": "make it night time", "mode": "turbo-8"}}}'
|
| 77 |
+
```
|
| 78 |
+
|
| 79 |
+
Response: `output.images[].data` is a base64 PNG; `output.seed`,
|
| 80 |
+
`output.timings` included. See `../README.md` for the full API schema and how
|
| 81 |
+
the endpoint image is built.
|
serverless/client/qwen-edit-turbo-v4.json
ADDED
|
@@ -0,0 +1,758 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"1": {
|
| 3 |
+
"class_type": "UNETLoader",
|
| 4 |
+
"inputs": {
|
| 5 |
+
"unet_name": "qwen_image_edit_2511_bf16.safetensors",
|
| 6 |
+
"weight_dtype": "default"
|
| 7 |
+
}
|
| 8 |
+
},
|
| 9 |
+
"13": {
|
| 10 |
+
"class_type": "ModelSamplingAuraFlow",
|
| 11 |
+
"inputs": {
|
| 12 |
+
"model": [
|
| 13 |
+
"53:6",
|
| 14 |
+
0
|
| 15 |
+
],
|
| 16 |
+
"shift": 3.1
|
| 17 |
+
}
|
| 18 |
+
},
|
| 19 |
+
"14": {
|
| 20 |
+
"class_type": "CFGNorm",
|
| 21 |
+
"inputs": {
|
| 22 |
+
"model": [
|
| 23 |
+
"13",
|
| 24 |
+
0
|
| 25 |
+
],
|
| 26 |
+
"pre_cfg": false,
|
| 27 |
+
"strength": 1
|
| 28 |
+
}
|
| 29 |
+
},
|
| 30 |
+
"15": {
|
| 31 |
+
"_meta": {
|
| 32 |
+
"title": "upscale_input_to_max_size"
|
| 33 |
+
},
|
| 34 |
+
"class_type": "PrimitiveBoolean",
|
| 35 |
+
"inputs": {
|
| 36 |
+
"value": true
|
| 37 |
+
}
|
| 38 |
+
},
|
| 39 |
+
"16": {
|
| 40 |
+
"_meta": {
|
| 41 |
+
"title": "downscale_input_to_max_size"
|
| 42 |
+
},
|
| 43 |
+
"class_type": "PrimitiveBoolean",
|
| 44 |
+
"inputs": {
|
| 45 |
+
"value": false
|
| 46 |
+
}
|
| 47 |
+
},
|
| 48 |
+
"20": {
|
| 49 |
+
"_meta": {
|
| 50 |
+
"title": "Main Image"
|
| 51 |
+
},
|
| 52 |
+
"class_type": "LoadImage",
|
| 53 |
+
"inputs": {
|
| 54 |
+
"image": "733427737_18059858666742129_6215894921112236726_n.jpg"
|
| 55 |
+
}
|
| 56 |
+
},
|
| 57 |
+
"21": {
|
| 58 |
+
"_meta": {
|
| 59 |
+
"title": "Input max size (main)"
|
| 60 |
+
},
|
| 61 |
+
"class_type": "PrimitiveInt",
|
| 62 |
+
"inputs": {
|
| 63 |
+
"value": 2048
|
| 64 |
+
}
|
| 65 |
+
},
|
| 66 |
+
"27": {
|
| 67 |
+
"_meta": {
|
| 68 |
+
"title": "Main image config"
|
| 69 |
+
},
|
| 70 |
+
"class_type": "QwenEditConfigPreparer",
|
| 71 |
+
"inputs": {
|
| 72 |
+
"image": [
|
| 73 |
+
"20",
|
| 74 |
+
0
|
| 75 |
+
],
|
| 76 |
+
"ref_crop": "pad",
|
| 77 |
+
"ref_longest_edge": [
|
| 78 |
+
"54:5",
|
| 79 |
+
0
|
| 80 |
+
],
|
| 81 |
+
"ref_main_image": true,
|
| 82 |
+
"ref_upscale": "lanczos",
|
| 83 |
+
"to_ref": true,
|
| 84 |
+
"to_vl": true,
|
| 85 |
+
"vl_crop": "center",
|
| 86 |
+
"vl_resize": true,
|
| 87 |
+
"vl_target_size": 384,
|
| 88 |
+
"vl_upscale": "bicubic"
|
| 89 |
+
}
|
| 90 |
+
},
|
| 91 |
+
"3": {
|
| 92 |
+
"class_type": "CLIPLoader",
|
| 93 |
+
"inputs": {
|
| 94 |
+
"clip_name": "qwen_2.5_vl_7b.safetensors",
|
| 95 |
+
"device": "default",
|
| 96 |
+
"type": "qwen_image"
|
| 97 |
+
}
|
| 98 |
+
},
|
| 99 |
+
"30": {
|
| 100 |
+
"_meta": {
|
| 101 |
+
"title": "Reference Image 2 (optional)"
|
| 102 |
+
},
|
| 103 |
+
"class_type": "LoadImage",
|
| 104 |
+
"inputs": {
|
| 105 |
+
"image": "b.png"
|
| 106 |
+
}
|
| 107 |
+
},
|
| 108 |
+
"31": {
|
| 109 |
+
"_meta": {
|
| 110 |
+
"title": "Input max size (ref 2)"
|
| 111 |
+
},
|
| 112 |
+
"class_type": "PrimitiveInt",
|
| 113 |
+
"inputs": {
|
| 114 |
+
"value": 1024
|
| 115 |
+
}
|
| 116 |
+
},
|
| 117 |
+
"37": {
|
| 118 |
+
"_meta": {
|
| 119 |
+
"title": "Reference image 2 config"
|
| 120 |
+
},
|
| 121 |
+
"class_type": "QwenEditConfigPreparer",
|
| 122 |
+
"inputs": {
|
| 123 |
+
"configs": [
|
| 124 |
+
"27",
|
| 125 |
+
0
|
| 126 |
+
],
|
| 127 |
+
"image": [
|
| 128 |
+
"30",
|
| 129 |
+
0
|
| 130 |
+
],
|
| 131 |
+
"ref_crop": "center",
|
| 132 |
+
"ref_longest_edge": [
|
| 133 |
+
"55:5",
|
| 134 |
+
0
|
| 135 |
+
],
|
| 136 |
+
"ref_main_image": false,
|
| 137 |
+
"ref_upscale": "lanczos",
|
| 138 |
+
"to_ref": true,
|
| 139 |
+
"to_vl": true,
|
| 140 |
+
"vl_crop": "center",
|
| 141 |
+
"vl_resize": true,
|
| 142 |
+
"vl_target_size": 384,
|
| 143 |
+
"vl_upscale": "bicubic"
|
| 144 |
+
}
|
| 145 |
+
},
|
| 146 |
+
"4": {
|
| 147 |
+
"class_type": "VAELoader",
|
| 148 |
+
"inputs": {
|
| 149 |
+
"vae_name": "qwen_image_vae.safetensors"
|
| 150 |
+
}
|
| 151 |
+
},
|
| 152 |
+
"40": {
|
| 153 |
+
"_meta": {
|
| 154 |
+
"title": "Qwen Edit Encode (prompt here)"
|
| 155 |
+
},
|
| 156 |
+
"class_type": "TextEncodeQwenImageEditPlusCustom_lrzjason",
|
| 157 |
+
"inputs": {
|
| 158 |
+
"clip": [
|
| 159 |
+
"3",
|
| 160 |
+
0
|
| 161 |
+
],
|
| 162 |
+
"configs": [
|
| 163 |
+
"57",
|
| 164 |
+
0
|
| 165 |
+
],
|
| 166 |
+
"instruction": "",
|
| 167 |
+
"prompt": "replace the outfit of the subject in the first image with the outfit in the second image, don't change anything else. do not change the body's anatomy or shape or skin tone. only put the outfit onto the person in the first image. ",
|
| 168 |
+
"return_full_refs_cond": true,
|
| 169 |
+
"vae": [
|
| 170 |
+
"4",
|
| 171 |
+
0
|
| 172 |
+
]
|
| 173 |
+
}
|
| 174 |
+
},
|
| 175 |
+
"41": {
|
| 176 |
+
"class_type": "QwenEditOutputExtractor",
|
| 177 |
+
"inputs": {
|
| 178 |
+
"custom_output": [
|
| 179 |
+
"40",
|
| 180 |
+
2
|
| 181 |
+
]
|
| 182 |
+
}
|
| 183 |
+
},
|
| 184 |
+
"42": {
|
| 185 |
+
"_meta": {
|
| 186 |
+
"title": "Negative (zeroed)"
|
| 187 |
+
},
|
| 188 |
+
"class_type": "ConditioningZeroOut",
|
| 189 |
+
"inputs": {
|
| 190 |
+
"conditioning": [
|
| 191 |
+
"40",
|
| 192 |
+
0
|
| 193 |
+
]
|
| 194 |
+
}
|
| 195 |
+
},
|
| 196 |
+
"43": {
|
| 197 |
+
"class_type": "KSampler",
|
| 198 |
+
"inputs": {
|
| 199 |
+
"cfg": [
|
| 200 |
+
"53:14",
|
| 201 |
+
0
|
| 202 |
+
],
|
| 203 |
+
"denoise": 1,
|
| 204 |
+
"latent_image": [
|
| 205 |
+
"40",
|
| 206 |
+
1
|
| 207 |
+
],
|
| 208 |
+
"model": [
|
| 209 |
+
"14",
|
| 210 |
+
0
|
| 211 |
+
],
|
| 212 |
+
"negative": [
|
| 213 |
+
"42",
|
| 214 |
+
0
|
| 215 |
+
],
|
| 216 |
+
"positive": [
|
| 217 |
+
"40",
|
| 218 |
+
0
|
| 219 |
+
],
|
| 220 |
+
"sampler_name": "euler",
|
| 221 |
+
"scheduler": "simple",
|
| 222 |
+
"seed": 440984114065874,
|
| 223 |
+
"steps": [
|
| 224 |
+
"53:11",
|
| 225 |
+
0
|
| 226 |
+
]
|
| 227 |
+
}
|
| 228 |
+
},
|
| 229 |
+
"44": {
|
| 230 |
+
"class_type": "VAEDecode",
|
| 231 |
+
"inputs": {
|
| 232 |
+
"samples": [
|
| 233 |
+
"43",
|
| 234 |
+
0
|
| 235 |
+
],
|
| 236 |
+
"vae": [
|
| 237 |
+
"4",
|
| 238 |
+
0
|
| 239 |
+
]
|
| 240 |
+
}
|
| 241 |
+
},
|
| 242 |
+
"45": {
|
| 243 |
+
"_meta": {
|
| 244 |
+
"title": "Remove padding"
|
| 245 |
+
},
|
| 246 |
+
"class_type": "CropWithPadInfo",
|
| 247 |
+
"inputs": {
|
| 248 |
+
"image": [
|
| 249 |
+
"44",
|
| 250 |
+
0
|
| 251 |
+
],
|
| 252 |
+
"pad_info": [
|
| 253 |
+
"41",
|
| 254 |
+
0
|
| 255 |
+
]
|
| 256 |
+
}
|
| 257 |
+
},
|
| 258 |
+
"46": {
|
| 259 |
+
"_meta": {
|
| 260 |
+
"title": "upscale_output_to_selected_size"
|
| 261 |
+
},
|
| 262 |
+
"class_type": "PrimitiveBoolean",
|
| 263 |
+
"inputs": {
|
| 264 |
+
"value": true
|
| 265 |
+
}
|
| 266 |
+
},
|
| 267 |
+
"47": {
|
| 268 |
+
"_meta": {
|
| 269 |
+
"title": "Selected output size"
|
| 270 |
+
},
|
| 271 |
+
"class_type": "PrimitiveInt",
|
| 272 |
+
"inputs": {
|
| 273 |
+
"value": 2560
|
| 274 |
+
}
|
| 275 |
+
},
|
| 276 |
+
"48": {
|
| 277 |
+
"_meta": {
|
| 278 |
+
"title": "Scale output"
|
| 279 |
+
},
|
| 280 |
+
"class_type": "ImageScaleToMaxDimension",
|
| 281 |
+
"inputs": {
|
| 282 |
+
"image": [
|
| 283 |
+
"45",
|
| 284 |
+
0
|
| 285 |
+
],
|
| 286 |
+
"largest_size": [
|
| 287 |
+
"47",
|
| 288 |
+
0
|
| 289 |
+
],
|
| 290 |
+
"upscale_method": "lanczos"
|
| 291 |
+
}
|
| 292 |
+
},
|
| 293 |
+
"49": {
|
| 294 |
+
"_meta": {
|
| 295 |
+
"title": "Output size switch"
|
| 296 |
+
},
|
| 297 |
+
"class_type": "ComfySwitchNode",
|
| 298 |
+
"inputs": {
|
| 299 |
+
"on_false": [
|
| 300 |
+
"45",
|
| 301 |
+
0
|
| 302 |
+
],
|
| 303 |
+
"on_true": [
|
| 304 |
+
"48",
|
| 305 |
+
0
|
| 306 |
+
],
|
| 307 |
+
"switch": [
|
| 308 |
+
"46",
|
| 309 |
+
0
|
| 310 |
+
]
|
| 311 |
+
}
|
| 312 |
+
},
|
| 313 |
+
"5": {
|
| 314 |
+
"_meta": {
|
| 315 |
+
"title": "Sampling mode: 1=4-step / 2=8-step / 3=quality"
|
| 316 |
+
},
|
| 317 |
+
"class_type": "PrimitiveInt",
|
| 318 |
+
"inputs": {
|
| 319 |
+
"value": 1
|
| 320 |
+
}
|
| 321 |
+
},
|
| 322 |
+
"50": {
|
| 323 |
+
"class_type": "SaveImage",
|
| 324 |
+
"inputs": {
|
| 325 |
+
"filename_prefix": "qwen-edit-turbo-v4",
|
| 326 |
+
"images": [
|
| 327 |
+
"49",
|
| 328 |
+
0
|
| 329 |
+
]
|
| 330 |
+
}
|
| 331 |
+
},
|
| 332 |
+
"52": {
|
| 333 |
+
"_meta": {
|
| 334 |
+
"title": "User LoRAs"
|
| 335 |
+
},
|
| 336 |
+
"class_type": "Power Lora Loader (rgthree)",
|
| 337 |
+
"inputs": {
|
| 338 |
+
"lora_1": {
|
| 339 |
+
"lora": "Qwen_LoRA_Skin_Fix_v2.safetensors",
|
| 340 |
+
"on": false,
|
| 341 |
+
"strength": 1,
|
| 342 |
+
"strengthTwo": null
|
| 343 |
+
},
|
| 344 |
+
"lora_2": {
|
| 345 |
+
"lora": "Qwen_LoRA_Amateur_Photo_v1.safetensors",
|
| 346 |
+
"on": false,
|
| 347 |
+
"strength": 1,
|
| 348 |
+
"strengthTwo": null
|
| 349 |
+
},
|
| 350 |
+
"model": [
|
| 351 |
+
"1",
|
| 352 |
+
0
|
| 353 |
+
]
|
| 354 |
+
}
|
| 355 |
+
},
|
| 356 |
+
"53:1": {
|
| 357 |
+
"_meta": {
|
| 358 |
+
"title": "LoRA: Lightning 4 steps"
|
| 359 |
+
},
|
| 360 |
+
"class_type": "LoraLoaderModelOnly",
|
| 361 |
+
"inputs": {
|
| 362 |
+
"lora_name": "Qwen-Image-Edit-2511-Lightning-4steps-V1.0-bf16.safetensors",
|
| 363 |
+
"model": [
|
| 364 |
+
"52",
|
| 365 |
+
0
|
| 366 |
+
],
|
| 367 |
+
"strength_model": 1
|
| 368 |
+
}
|
| 369 |
+
},
|
| 370 |
+
"53:10": {
|
| 371 |
+
"_meta": {
|
| 372 |
+
"title": "Steps switch 4/8"
|
| 373 |
+
},
|
| 374 |
+
"class_type": "ComfySwitchNode",
|
| 375 |
+
"inputs": {
|
| 376 |
+
"on_false": [
|
| 377 |
+
"53:7",
|
| 378 |
+
0
|
| 379 |
+
],
|
| 380 |
+
"on_true": [
|
| 381 |
+
"53:8",
|
| 382 |
+
0
|
| 383 |
+
],
|
| 384 |
+
"switch": [
|
| 385 |
+
"53:3",
|
| 386 |
+
2
|
| 387 |
+
]
|
| 388 |
+
}
|
| 389 |
+
},
|
| 390 |
+
"53:11": {
|
| 391 |
+
"_meta": {
|
| 392 |
+
"title": "Steps switch quality"
|
| 393 |
+
},
|
| 394 |
+
"class_type": "ComfySwitchNode",
|
| 395 |
+
"inputs": {
|
| 396 |
+
"on_false": [
|
| 397 |
+
"53:10",
|
| 398 |
+
0
|
| 399 |
+
],
|
| 400 |
+
"on_true": [
|
| 401 |
+
"53:9",
|
| 402 |
+
0
|
| 403 |
+
],
|
| 404 |
+
"switch": [
|
| 405 |
+
"53:4",
|
| 406 |
+
2
|
| 407 |
+
]
|
| 408 |
+
}
|
| 409 |
+
},
|
| 410 |
+
"53:12": {
|
| 411 |
+
"_meta": {
|
| 412 |
+
"title": "CFG (lightning)"
|
| 413 |
+
},
|
| 414 |
+
"class_type": "PrimitiveFloat",
|
| 415 |
+
"inputs": {
|
| 416 |
+
"value": 1.0
|
| 417 |
+
}
|
| 418 |
+
},
|
| 419 |
+
"53:13": {
|
| 420 |
+
"_meta": {
|
| 421 |
+
"title": "CFG (quality)"
|
| 422 |
+
},
|
| 423 |
+
"class_type": "PrimitiveFloat",
|
| 424 |
+
"inputs": {
|
| 425 |
+
"value": 3.0
|
| 426 |
+
}
|
| 427 |
+
},
|
| 428 |
+
"53:14": {
|
| 429 |
+
"_meta": {
|
| 430 |
+
"title": "CFG switch"
|
| 431 |
+
},
|
| 432 |
+
"class_type": "ComfySwitchNode",
|
| 433 |
+
"inputs": {
|
| 434 |
+
"on_false": [
|
| 435 |
+
"53:12",
|
| 436 |
+
0
|
| 437 |
+
],
|
| 438 |
+
"on_true": [
|
| 439 |
+
"53:13",
|
| 440 |
+
0
|
| 441 |
+
],
|
| 442 |
+
"switch": [
|
| 443 |
+
"53:4",
|
| 444 |
+
2
|
| 445 |
+
]
|
| 446 |
+
}
|
| 447 |
+
},
|
| 448 |
+
"53:2": {
|
| 449 |
+
"_meta": {
|
| 450 |
+
"title": "LoRA: Lightning 8 steps"
|
| 451 |
+
},
|
| 452 |
+
"class_type": "LoraLoaderModelOnly",
|
| 453 |
+
"inputs": {
|
| 454 |
+
"lora_name": "Qwen-Image-Edit-2511-Lightning-8steps-V1.0-bf16.safetensors",
|
| 455 |
+
"model": [
|
| 456 |
+
"52",
|
| 457 |
+
0
|
| 458 |
+
],
|
| 459 |
+
"strength_model": 1
|
| 460 |
+
}
|
| 461 |
+
},
|
| 462 |
+
"53:3": {
|
| 463 |
+
"_meta": {
|
| 464 |
+
"title": "is 8-step (mode == 2)"
|
| 465 |
+
},
|
| 466 |
+
"class_type": "ComfyMathExpression",
|
| 467 |
+
"inputs": {
|
| 468 |
+
"expression": "a == 2",
|
| 469 |
+
"values.a": [
|
| 470 |
+
"5",
|
| 471 |
+
0
|
| 472 |
+
]
|
| 473 |
+
}
|
| 474 |
+
},
|
| 475 |
+
"53:4": {
|
| 476 |
+
"_meta": {
|
| 477 |
+
"title": "is quality (mode == 3)"
|
| 478 |
+
},
|
| 479 |
+
"class_type": "ComfyMathExpression",
|
| 480 |
+
"inputs": {
|
| 481 |
+
"expression": "a == 3",
|
| 482 |
+
"values.a": [
|
| 483 |
+
"5",
|
| 484 |
+
0
|
| 485 |
+
]
|
| 486 |
+
}
|
| 487 |
+
},
|
| 488 |
+
"53:5": {
|
| 489 |
+
"_meta": {
|
| 490 |
+
"title": "Model switch 4/8"
|
| 491 |
+
},
|
| 492 |
+
"class_type": "ComfySwitchNode",
|
| 493 |
+
"inputs": {
|
| 494 |
+
"on_false": [
|
| 495 |
+
"53:1",
|
| 496 |
+
0
|
| 497 |
+
],
|
| 498 |
+
"on_true": [
|
| 499 |
+
"53:2",
|
| 500 |
+
0
|
| 501 |
+
],
|
| 502 |
+
"switch": [
|
| 503 |
+
"53:3",
|
| 504 |
+
2
|
| 505 |
+
]
|
| 506 |
+
}
|
| 507 |
+
},
|
| 508 |
+
"53:6": {
|
| 509 |
+
"_meta": {
|
| 510 |
+
"title": "Model switch quality"
|
| 511 |
+
},
|
| 512 |
+
"class_type": "ComfySwitchNode",
|
| 513 |
+
"inputs": {
|
| 514 |
+
"on_false": [
|
| 515 |
+
"53:5",
|
| 516 |
+
0
|
| 517 |
+
],
|
| 518 |
+
"on_true": [
|
| 519 |
+
"52",
|
| 520 |
+
0
|
| 521 |
+
],
|
| 522 |
+
"switch": [
|
| 523 |
+
"53:4",
|
| 524 |
+
2
|
| 525 |
+
]
|
| 526 |
+
}
|
| 527 |
+
},
|
| 528 |
+
"53:7": {
|
| 529 |
+
"_meta": {
|
| 530 |
+
"title": "Steps (4-step)"
|
| 531 |
+
},
|
| 532 |
+
"class_type": "PrimitiveInt",
|
| 533 |
+
"inputs": {
|
| 534 |
+
"value": 4
|
| 535 |
+
}
|
| 536 |
+
},
|
| 537 |
+
"53:8": {
|
| 538 |
+
"_meta": {
|
| 539 |
+
"title": "Steps (8-step)"
|
| 540 |
+
},
|
| 541 |
+
"class_type": "PrimitiveInt",
|
| 542 |
+
"inputs": {
|
| 543 |
+
"value": 8
|
| 544 |
+
}
|
| 545 |
+
},
|
| 546 |
+
"53:9": {
|
| 547 |
+
"_meta": {
|
| 548 |
+
"title": "Steps (quality)"
|
| 549 |
+
},
|
| 550 |
+
"class_type": "PrimitiveInt",
|
| 551 |
+
"inputs": {
|
| 552 |
+
"value": 12
|
| 553 |
+
}
|
| 554 |
+
},
|
| 555 |
+
"54:1": {
|
| 556 |
+
"_meta": {
|
| 557 |
+
"title": "native edge"
|
| 558 |
+
},
|
| 559 |
+
"class_type": "MathExpression|pysssss",
|
| 560 |
+
"inputs": {
|
| 561 |
+
"a": [
|
| 562 |
+
"20",
|
| 563 |
+
0
|
| 564 |
+
],
|
| 565 |
+
"expression": "max(a.width, a.height)"
|
| 566 |
+
}
|
| 567 |
+
},
|
| 568 |
+
"54:2": {
|
| 569 |
+
"_meta": {
|
| 570 |
+
"title": "upscaled edge"
|
| 571 |
+
},
|
| 572 |
+
"class_type": "MathExpression|pysssss",
|
| 573 |
+
"inputs": {
|
| 574 |
+
"a": [
|
| 575 |
+
"20",
|
| 576 |
+
0
|
| 577 |
+
],
|
| 578 |
+
"c": [
|
| 579 |
+
"21",
|
| 580 |
+
0
|
| 581 |
+
],
|
| 582 |
+
"expression": "max(a.width, a.height, c)"
|
| 583 |
+
}
|
| 584 |
+
},
|
| 585 |
+
"54:3": {
|
| 586 |
+
"_meta": {
|
| 587 |
+
"title": "apply upscale"
|
| 588 |
+
},
|
| 589 |
+
"class_type": "ComfySwitchNode",
|
| 590 |
+
"inputs": {
|
| 591 |
+
"on_false": [
|
| 592 |
+
"54:1",
|
| 593 |
+
0
|
| 594 |
+
],
|
| 595 |
+
"on_true": [
|
| 596 |
+
"54:2",
|
| 597 |
+
0
|
| 598 |
+
],
|
| 599 |
+
"switch": [
|
| 600 |
+
"15",
|
| 601 |
+
0
|
| 602 |
+
]
|
| 603 |
+
}
|
| 604 |
+
},
|
| 605 |
+
"54:4": {
|
| 606 |
+
"_meta": {
|
| 607 |
+
"title": "downscaled edge"
|
| 608 |
+
},
|
| 609 |
+
"class_type": "MathExpression|pysssss",
|
| 610 |
+
"inputs": {
|
| 611 |
+
"a": [
|
| 612 |
+
"54:3",
|
| 613 |
+
0
|
| 614 |
+
],
|
| 615 |
+
"c": [
|
| 616 |
+
"21",
|
| 617 |
+
0
|
| 618 |
+
],
|
| 619 |
+
"expression": "min(a, c)"
|
| 620 |
+
}
|
| 621 |
+
},
|
| 622 |
+
"54:5": {
|
| 623 |
+
"_meta": {
|
| 624 |
+
"title": "apply downscale"
|
| 625 |
+
},
|
| 626 |
+
"class_type": "ComfySwitchNode",
|
| 627 |
+
"inputs": {
|
| 628 |
+
"on_false": [
|
| 629 |
+
"54:3",
|
| 630 |
+
0
|
| 631 |
+
],
|
| 632 |
+
"on_true": [
|
| 633 |
+
"54:4",
|
| 634 |
+
0
|
| 635 |
+
],
|
| 636 |
+
"switch": [
|
| 637 |
+
"16",
|
| 638 |
+
0
|
| 639 |
+
]
|
| 640 |
+
}
|
| 641 |
+
},
|
| 642 |
+
"55:1": {
|
| 643 |
+
"_meta": {
|
| 644 |
+
"title": "native edge"
|
| 645 |
+
},
|
| 646 |
+
"class_type": "MathExpression|pysssss",
|
| 647 |
+
"inputs": {
|
| 648 |
+
"a": [
|
| 649 |
+
"30",
|
| 650 |
+
0
|
| 651 |
+
],
|
| 652 |
+
"expression": "max(a.width, a.height)"
|
| 653 |
+
}
|
| 654 |
+
},
|
| 655 |
+
"55:2": {
|
| 656 |
+
"_meta": {
|
| 657 |
+
"title": "upscaled edge"
|
| 658 |
+
},
|
| 659 |
+
"class_type": "MathExpression|pysssss",
|
| 660 |
+
"inputs": {
|
| 661 |
+
"a": [
|
| 662 |
+
"30",
|
| 663 |
+
0
|
| 664 |
+
],
|
| 665 |
+
"c": [
|
| 666 |
+
"31",
|
| 667 |
+
0
|
| 668 |
+
],
|
| 669 |
+
"expression": "max(a.width, a.height, c)"
|
| 670 |
+
}
|
| 671 |
+
},
|
| 672 |
+
"55:3": {
|
| 673 |
+
"_meta": {
|
| 674 |
+
"title": "apply upscale"
|
| 675 |
+
},
|
| 676 |
+
"class_type": "ComfySwitchNode",
|
| 677 |
+
"inputs": {
|
| 678 |
+
"on_false": [
|
| 679 |
+
"55:1",
|
| 680 |
+
0
|
| 681 |
+
],
|
| 682 |
+
"on_true": [
|
| 683 |
+
"55:2",
|
| 684 |
+
0
|
| 685 |
+
],
|
| 686 |
+
"switch": [
|
| 687 |
+
"15",
|
| 688 |
+
0
|
| 689 |
+
]
|
| 690 |
+
}
|
| 691 |
+
},
|
| 692 |
+
"55:4": {
|
| 693 |
+
"_meta": {
|
| 694 |
+
"title": "downscaled edge"
|
| 695 |
+
},
|
| 696 |
+
"class_type": "MathExpression|pysssss",
|
| 697 |
+
"inputs": {
|
| 698 |
+
"a": [
|
| 699 |
+
"55:3",
|
| 700 |
+
0
|
| 701 |
+
],
|
| 702 |
+
"c": [
|
| 703 |
+
"31",
|
| 704 |
+
0
|
| 705 |
+
],
|
| 706 |
+
"expression": "min(a, c)"
|
| 707 |
+
}
|
| 708 |
+
},
|
| 709 |
+
"55:5": {
|
| 710 |
+
"_meta": {
|
| 711 |
+
"title": "apply downscale"
|
| 712 |
+
},
|
| 713 |
+
"class_type": "ComfySwitchNode",
|
| 714 |
+
"inputs": {
|
| 715 |
+
"on_false": [
|
| 716 |
+
"55:3",
|
| 717 |
+
0
|
| 718 |
+
],
|
| 719 |
+
"on_true": [
|
| 720 |
+
"55:4",
|
| 721 |
+
0
|
| 722 |
+
],
|
| 723 |
+
"switch": [
|
| 724 |
+
"16",
|
| 725 |
+
0
|
| 726 |
+
]
|
| 727 |
+
}
|
| 728 |
+
},
|
| 729 |
+
"56": {
|
| 730 |
+
"_meta": {
|
| 731 |
+
"title": "Use Reference Image 2"
|
| 732 |
+
},
|
| 733 |
+
"class_type": "PrimitiveBoolean",
|
| 734 |
+
"inputs": {
|
| 735 |
+
"value": true
|
| 736 |
+
}
|
| 737 |
+
},
|
| 738 |
+
"57": {
|
| 739 |
+
"_meta": {
|
| 740 |
+
"title": "Ref 2 on/off switch"
|
| 741 |
+
},
|
| 742 |
+
"class_type": "ComfySwitchNode",
|
| 743 |
+
"inputs": {
|
| 744 |
+
"on_false": [
|
| 745 |
+
"27",
|
| 746 |
+
0
|
| 747 |
+
],
|
| 748 |
+
"on_true": [
|
| 749 |
+
"37",
|
| 750 |
+
0
|
| 751 |
+
],
|
| 752 |
+
"switch": [
|
| 753 |
+
"56",
|
| 754 |
+
0
|
| 755 |
+
]
|
| 756 |
+
}
|
| 757 |
+
}
|
| 758 |
+
}
|
serverless/client/qwen_edit_async.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Call the qwen-edit-turbo RunPod serverless endpoint ASYNCHRONOUSLY (/run).
|
| 3 |
+
|
| 4 |
+
Standalone: python 3.8+, stdlib only. Submits the job, then either polls until
|
| 5 |
+
done (default) or exits immediately with the job id (--no-wait) so you can
|
| 6 |
+
collect later with --job-id.
|
| 7 |
+
|
| 8 |
+
# submit and wait
|
| 9 |
+
python qwen_edit_async.py --api-key $RUNPOD_API_KEY \
|
| 10 |
+
--image person.jpg --ref-image outfit.png \
|
| 11 |
+
--prompt "put the outfit from the second image on the person"
|
| 12 |
+
|
| 13 |
+
# fire-and-forget, collect later
|
| 14 |
+
python qwen_edit_async.py ... --no-wait # prints JOB_ID
|
| 15 |
+
python qwen_edit_async.py --api-key $RUNPOD_API_KEY --job-id JOB_ID
|
| 16 |
+
|
| 17 |
+
Arguments are identical to qwen_edit_sync.py, plus:
|
| 18 |
+
--no-wait submit only; print the job id and exit
|
| 19 |
+
--job-id ID skip submission; poll/collect an existing job
|
| 20 |
+
--poll SECONDS poll interval (default 3)
|
| 21 |
+
--cancel ID cancel a queued/running job and exit
|
| 22 |
+
"""
|
| 23 |
+
import argparse
|
| 24 |
+
import base64
|
| 25 |
+
import json
|
| 26 |
+
import os
|
| 27 |
+
import pathlib
|
| 28 |
+
import sys
|
| 29 |
+
import time
|
| 30 |
+
import urllib.error
|
| 31 |
+
import urllib.request
|
| 32 |
+
|
| 33 |
+
DEFAULT_ENDPOINT = "dom5lwr0o5wq6u"
|
| 34 |
+
TERMINAL = ("COMPLETED", "FAILED", "CANCELLED", "TIMED_OUT")
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def api(args, method, path, payload=None, timeout=90):
|
| 38 |
+
req = urllib.request.Request(
|
| 39 |
+
f"https://api.runpod.ai/v2/{args.endpoint_id}/{path}", method=method,
|
| 40 |
+
data=json.dumps(payload).encode() if payload is not None else None,
|
| 41 |
+
headers={"Content-Type": "application/json",
|
| 42 |
+
"Authorization": f"Bearer {args.api_key}"})
|
| 43 |
+
try:
|
| 44 |
+
with urllib.request.urlopen(req, timeout=timeout) as r:
|
| 45 |
+
return json.load(r)
|
| 46 |
+
except urllib.error.HTTPError as e:
|
| 47 |
+
sys.exit(f"ERROR: HTTP {e.code} on /{path}: {e.read().decode(errors='replace')[:2000]}")
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def build_input(args):
|
| 51 |
+
images, params = [], {"prompt": args.prompt, "mode": args.mode}
|
| 52 |
+
images.append({"name": os.path.basename(args.image),
|
| 53 |
+
"image": base64.b64encode(open(args.image, "rb").read()).decode()})
|
| 54 |
+
if args.ref_image:
|
| 55 |
+
images.append({"name": os.path.basename(args.ref_image),
|
| 56 |
+
"image": base64.b64encode(open(args.ref_image, "rb").read()).decode()})
|
| 57 |
+
if args.seed is not None:
|
| 58 |
+
params["seed"] = args.seed
|
| 59 |
+
for name in ("input_max_dim", "ref_max_dim", "output_max_dim"):
|
| 60 |
+
v = getattr(args, name)
|
| 61 |
+
if v is not None:
|
| 62 |
+
params[name] = v
|
| 63 |
+
if args.lora_skin_fix:
|
| 64 |
+
params["lora_skin_fix"] = True
|
| 65 |
+
params["lora_skin_fix_strength"] = args.lora_skin_fix_strength
|
| 66 |
+
if args.lora_amateur:
|
| 67 |
+
params["lora_amateur"] = True
|
| 68 |
+
params["lora_amateur_strength"] = args.lora_amateur_strength
|
| 69 |
+
|
| 70 |
+
payload = {"images": images, "params": params}
|
| 71 |
+
if args.workflow:
|
| 72 |
+
payload["workflow"] = args.workflow
|
| 73 |
+
if args.workflow_json:
|
| 74 |
+
payload["workflow_json"] = json.load(open(args.workflow_json))
|
| 75 |
+
overrides = {}
|
| 76 |
+
for s in args.set or []:
|
| 77 |
+
k, v = s.split("=", 1)
|
| 78 |
+
try:
|
| 79 |
+
overrides[k] = json.loads(v)
|
| 80 |
+
except json.JSONDecodeError:
|
| 81 |
+
overrides[k] = v
|
| 82 |
+
if overrides:
|
| 83 |
+
payload["set"] = overrides
|
| 84 |
+
return payload
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def save_outputs(output, out_dir):
|
| 88 |
+
out_dir = pathlib.Path(out_dir)
|
| 89 |
+
out_dir.mkdir(parents=True, exist_ok=True)
|
| 90 |
+
saved = []
|
| 91 |
+
for i, img in enumerate(output.get("images", [])):
|
| 92 |
+
p = out_dir / f"{int(time.time())}-{i}-{img['filename']}"
|
| 93 |
+
p.write_bytes(base64.b64decode(img["data"]))
|
| 94 |
+
saved.append(str(p))
|
| 95 |
+
return saved
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def main():
|
| 99 |
+
ap = argparse.ArgumentParser(description=__doc__,
|
| 100 |
+
formatter_class=argparse.RawDescriptionHelpFormatter)
|
| 101 |
+
ap.add_argument("--api-key", default=os.environ.get("RUNPOD_API_KEY"))
|
| 102 |
+
ap.add_argument("--endpoint-id", default=DEFAULT_ENDPOINT)
|
| 103 |
+
ap.add_argument("--image")
|
| 104 |
+
ap.add_argument("--ref-image")
|
| 105 |
+
ap.add_argument("--prompt")
|
| 106 |
+
ap.add_argument("--mode", default="turbo-8", choices=["turbo-4", "turbo-8", "quality"])
|
| 107 |
+
ap.add_argument("--seed", type=int)
|
| 108 |
+
ap.add_argument("--input-max-dim", type=int)
|
| 109 |
+
ap.add_argument("--ref-max-dim", type=int)
|
| 110 |
+
ap.add_argument("--output-max-dim", type=int)
|
| 111 |
+
ap.add_argument("--lora-skin-fix", action="store_true")
|
| 112 |
+
ap.add_argument("--lora-skin-fix-strength", type=float, default=1.0)
|
| 113 |
+
ap.add_argument("--lora-amateur", action="store_true")
|
| 114 |
+
ap.add_argument("--lora-amateur-strength", type=float, default=1.0)
|
| 115 |
+
ap.add_argument("--workflow")
|
| 116 |
+
ap.add_argument("--set", action="append", metavar="NODE.INPUT=VALUE")
|
| 117 |
+
ap.add_argument("--workflow-json")
|
| 118 |
+
ap.add_argument("--out", default=".")
|
| 119 |
+
ap.add_argument("--timeout", type=float, default=1800)
|
| 120 |
+
ap.add_argument("--poll", type=float, default=3.0)
|
| 121 |
+
ap.add_argument("--no-wait", action="store_true")
|
| 122 |
+
ap.add_argument("--job-id")
|
| 123 |
+
ap.add_argument("--cancel", metavar="JOB_ID")
|
| 124 |
+
args = ap.parse_args()
|
| 125 |
+
if not args.api_key:
|
| 126 |
+
sys.exit("ERROR: pass --api-key or set RUNPOD_API_KEY")
|
| 127 |
+
|
| 128 |
+
if args.cancel:
|
| 129 |
+
print(json.dumps(api(args, "POST", f"cancel/{args.cancel}"), indent=2))
|
| 130 |
+
return
|
| 131 |
+
|
| 132 |
+
if args.job_id:
|
| 133 |
+
job_id = args.job_id
|
| 134 |
+
else:
|
| 135 |
+
if not args.image or not args.prompt:
|
| 136 |
+
sys.exit("ERROR: --image and --prompt are required to submit a job")
|
| 137 |
+
job = api(args, "POST", "run", {"input": build_input(args)})
|
| 138 |
+
job_id = job["id"]
|
| 139 |
+
print(f"# submitted job {job_id}", file=sys.stderr)
|
| 140 |
+
if args.no_wait:
|
| 141 |
+
print(job_id)
|
| 142 |
+
return
|
| 143 |
+
|
| 144 |
+
t0 = time.monotonic()
|
| 145 |
+
while True:
|
| 146 |
+
result = api(args, "GET", f"status/{job_id}")
|
| 147 |
+
status = result.get("status")
|
| 148 |
+
if status in TERMINAL:
|
| 149 |
+
break
|
| 150 |
+
if time.monotonic() - t0 > args.timeout:
|
| 151 |
+
sys.exit(f"ERROR: timed out after {args.timeout}s (last status {status}); "
|
| 152 |
+
f"job {job_id} is still yours to collect with --job-id")
|
| 153 |
+
print(f"# {status} ... {time.monotonic()-t0:.0f}s", file=sys.stderr)
|
| 154 |
+
time.sleep(args.poll)
|
| 155 |
+
|
| 156 |
+
if status != "COMPLETED":
|
| 157 |
+
sys.exit(f"ERROR: {json.dumps(result, indent=2)[:3000]}")
|
| 158 |
+
output = result["output"]
|
| 159 |
+
if "error" in output:
|
| 160 |
+
sys.exit(f"ERROR from handler: {output['error']}")
|
| 161 |
+
for p in save_outputs(output, args.out):
|
| 162 |
+
print(p)
|
| 163 |
+
print(f"# seed={output.get('seed')} delay={result.get('delayTime')}ms "
|
| 164 |
+
f"exec={result.get('executionTime')}ms", file=sys.stderr)
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
if __name__ == "__main__":
|
| 168 |
+
main()
|
serverless/client/qwen_edit_sync.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Call the qwen-edit-turbo RunPod serverless endpoint SYNCHRONOUSLY (/runsync).
|
| 3 |
+
|
| 4 |
+
Standalone: python 3.8+, stdlib only. Blocks until the edit is done (typically
|
| 5 |
+
40-65 s warm), saves the returned image(s), prints their paths.
|
| 6 |
+
|
| 7 |
+
python qwen_edit_sync.py \
|
| 8 |
+
--api-key $RUNPOD_API_KEY \
|
| 9 |
+
--image person.jpg --ref-image outfit.png \
|
| 10 |
+
--prompt "put the outfit from the second image on the person" \
|
| 11 |
+
--mode turbo-8 --out ./results
|
| 12 |
+
|
| 13 |
+
All arguments:
|
| 14 |
+
--api-key RunPod API key (or set RUNPOD_API_KEY env var)
|
| 15 |
+
--endpoint-id RunPod endpoint id (default: dom5lwr0o5wq6u)
|
| 16 |
+
--image main input image (required)
|
| 17 |
+
--ref-image optional reference image (outfit/style source)
|
| 18 |
+
--prompt edit instruction (required)
|
| 19 |
+
--mode turbo-4 | turbo-8 | quality (default turbo-8)
|
| 20 |
+
--seed integer seed (default: random; used seed is printed)
|
| 21 |
+
--input-max-dim max size of the main image fed to the model (default 2048)
|
| 22 |
+
--ref-max-dim max size of the reference image (default 1024)
|
| 23 |
+
--output-max-dim upscale target for the result (default 2560)
|
| 24 |
+
--lora-skin-fix / --lora-amateur enable the optional loras
|
| 25 |
+
--lora-skin-fix-strength / --lora-amateur-strength (default 1.0)
|
| 26 |
+
--workflow which baked workflow to run (default qwen-edit-turbo-v4)
|
| 27 |
+
--set NODE.INPUT=VALUE raw graph override, repeatable (advanced)
|
| 28 |
+
--workflow-json FILE full API-format graph passthrough (advanced)
|
| 29 |
+
--out output directory (default .)
|
| 30 |
+
--timeout max seconds to wait (default 600)
|
| 31 |
+
"""
|
| 32 |
+
import argparse
|
| 33 |
+
import base64
|
| 34 |
+
import json
|
| 35 |
+
import os
|
| 36 |
+
import pathlib
|
| 37 |
+
import sys
|
| 38 |
+
import time
|
| 39 |
+
import urllib.error
|
| 40 |
+
import urllib.request
|
| 41 |
+
|
| 42 |
+
DEFAULT_ENDPOINT = "dom5lwr0o5wq6u"
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def build_input(args):
|
| 46 |
+
images, params = [], {"prompt": args.prompt, "mode": args.mode}
|
| 47 |
+
images.append({"name": os.path.basename(args.image),
|
| 48 |
+
"image": base64.b64encode(open(args.image, "rb").read()).decode()})
|
| 49 |
+
if args.ref_image:
|
| 50 |
+
images.append({"name": os.path.basename(args.ref_image),
|
| 51 |
+
"image": base64.b64encode(open(args.ref_image, "rb").read()).decode()})
|
| 52 |
+
if args.seed is not None:
|
| 53 |
+
params["seed"] = args.seed
|
| 54 |
+
for cli, param in [("input_max_dim", "input_max_dim"), ("ref_max_dim", "ref_max_dim"),
|
| 55 |
+
("output_max_dim", "output_max_dim")]:
|
| 56 |
+
v = getattr(args, cli)
|
| 57 |
+
if v is not None:
|
| 58 |
+
params[param] = v
|
| 59 |
+
if args.lora_skin_fix:
|
| 60 |
+
params["lora_skin_fix"] = True
|
| 61 |
+
params["lora_skin_fix_strength"] = args.lora_skin_fix_strength
|
| 62 |
+
if args.lora_amateur:
|
| 63 |
+
params["lora_amateur"] = True
|
| 64 |
+
params["lora_amateur_strength"] = args.lora_amateur_strength
|
| 65 |
+
|
| 66 |
+
payload = {"images": images, "params": params}
|
| 67 |
+
if args.workflow:
|
| 68 |
+
payload["workflow"] = args.workflow
|
| 69 |
+
if args.workflow_json:
|
| 70 |
+
payload["workflow_json"] = json.load(open(args.workflow_json))
|
| 71 |
+
overrides = {}
|
| 72 |
+
for s in args.set or []:
|
| 73 |
+
k, v = s.split("=", 1)
|
| 74 |
+
try:
|
| 75 |
+
overrides[k] = json.loads(v)
|
| 76 |
+
except json.JSONDecodeError:
|
| 77 |
+
overrides[k] = v
|
| 78 |
+
if overrides:
|
| 79 |
+
payload["set"] = overrides
|
| 80 |
+
return payload
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def save_outputs(output, out_dir):
|
| 84 |
+
out_dir = pathlib.Path(out_dir)
|
| 85 |
+
out_dir.mkdir(parents=True, exist_ok=True)
|
| 86 |
+
saved = []
|
| 87 |
+
for i, img in enumerate(output.get("images", [])):
|
| 88 |
+
p = out_dir / f"{int(time.time())}-{i}-{img['filename']}"
|
| 89 |
+
p.write_bytes(base64.b64decode(img["data"]))
|
| 90 |
+
saved.append(str(p))
|
| 91 |
+
return saved
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def main():
|
| 95 |
+
ap = argparse.ArgumentParser(description=__doc__,
|
| 96 |
+
formatter_class=argparse.RawDescriptionHelpFormatter)
|
| 97 |
+
ap.add_argument("--api-key", default=os.environ.get("RUNPOD_API_KEY"))
|
| 98 |
+
ap.add_argument("--endpoint-id", default=DEFAULT_ENDPOINT)
|
| 99 |
+
ap.add_argument("--image", required=True)
|
| 100 |
+
ap.add_argument("--ref-image")
|
| 101 |
+
ap.add_argument("--prompt", required=True)
|
| 102 |
+
ap.add_argument("--mode", default="turbo-8", choices=["turbo-4", "turbo-8", "quality"])
|
| 103 |
+
ap.add_argument("--seed", type=int)
|
| 104 |
+
ap.add_argument("--input-max-dim", type=int)
|
| 105 |
+
ap.add_argument("--ref-max-dim", type=int)
|
| 106 |
+
ap.add_argument("--output-max-dim", type=int)
|
| 107 |
+
ap.add_argument("--lora-skin-fix", action="store_true")
|
| 108 |
+
ap.add_argument("--lora-skin-fix-strength", type=float, default=1.0)
|
| 109 |
+
ap.add_argument("--lora-amateur", action="store_true")
|
| 110 |
+
ap.add_argument("--lora-amateur-strength", type=float, default=1.0)
|
| 111 |
+
ap.add_argument("--workflow")
|
| 112 |
+
ap.add_argument("--set", action="append", metavar="NODE.INPUT=VALUE")
|
| 113 |
+
ap.add_argument("--workflow-json")
|
| 114 |
+
ap.add_argument("--out", default=".")
|
| 115 |
+
ap.add_argument("--timeout", type=float, default=600)
|
| 116 |
+
args = ap.parse_args()
|
| 117 |
+
if not args.api_key:
|
| 118 |
+
sys.exit("ERROR: pass --api-key or set RUNPOD_API_KEY")
|
| 119 |
+
|
| 120 |
+
req = urllib.request.Request(
|
| 121 |
+
f"https://api.runpod.ai/v2/{args.endpoint_id}/runsync",
|
| 122 |
+
data=json.dumps({"input": build_input(args)}).encode(),
|
| 123 |
+
headers={"Content-Type": "application/json",
|
| 124 |
+
"Authorization": f"Bearer {args.api_key}"})
|
| 125 |
+
t0 = time.monotonic()
|
| 126 |
+
try:
|
| 127 |
+
with urllib.request.urlopen(req, timeout=args.timeout) as r:
|
| 128 |
+
result = json.load(r)
|
| 129 |
+
except urllib.error.HTTPError as e:
|
| 130 |
+
sys.exit(f"ERROR: HTTP {e.code}: {e.read().decode(errors='replace')[:2000]}")
|
| 131 |
+
|
| 132 |
+
if result.get("status") != "COMPLETED":
|
| 133 |
+
sys.exit(f"ERROR: {json.dumps(result, indent=2)[:3000]}")
|
| 134 |
+
output = result["output"]
|
| 135 |
+
if "error" in output:
|
| 136 |
+
sys.exit(f"ERROR from handler: {output['error']}")
|
| 137 |
+
for p in save_outputs(output, args.out):
|
| 138 |
+
print(p)
|
| 139 |
+
print(f"# seed={output.get('seed')} wall={time.monotonic()-t0:.1f}s "
|
| 140 |
+
f"delay={result.get('delayTime')}ms exec={result.get('executionTime')}ms",
|
| 141 |
+
file=sys.stderr)
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
if __name__ == "__main__":
|
| 145 |
+
main()
|