4.4 KiB
name, description
| name | description |
|---|---|
| lab-imagegen | Generate images using the user's home-lab ComfyUI server (SDXL-turbo on 2x AMD Radeon Pro VII). Use when the user asks to generate, render, or create an image or picture. |
Lab Image Generation (ComfyUI)
The user's home lab runs a ComfyUI server at http://192.168.31.240:8188 (server1, 2x AMD Radeon Pro VII 16GB, SDXL-turbo). Use it to generate images on demand. No authentication is required — the API is open on the LAN.
Workflow
Use run_code to generate: submit a JSON workflow, poll /history/{prompt_id}, then return the served image URL.
1. Submit the prompt
POST /prompt with Content-Type: application/json and body:
{ "prompt": <workflow>, "client_id": "dsh" }
The workflow for SDXL-turbo (4 steps):
{
"3": { "class_type": "KSampler", "inputs": { "seed": 123, "steps": 4, "cfg": 1.0, "sampler_name": "euler", "scheduler": "normal", "denoise": 1.0, "model": ["4", 0], "positive": ["6", 0], "negative": ["7", 0], "latent_image": ["5", 0] } },
"4": { "class_type": "CheckpointLoaderSimple", "inputs": { "ckpt_name": "sdxl_turbo.safetensors" } },
"5": { "class_type": "EmptyLatentImage", "inputs": { "width": 512, "height": 512, "batch_size": 1 } },
"6": { "class_type": "CLIPTextEncode", "inputs": { "text": "<POSITIVE>", "clip": ["4", 1] } },
"7": { "class_type": "CLIPTextEncode", "inputs": { "text": "<NEGATIVE>", "clip": ["4", 1] } },
"8": { "class_type": "VAEDecode", "inputs": { "samples": ["3", 0], "vae": ["4", 2] } },
"9": { "class_type": "SaveImage", "inputs": { "filename_prefix": "dsh", "images": ["8", 0] } }
}
Defaults: 512x512, steps 4, cfg 1.0, euler/normal, seed random. The response contains prompt_id and node_errors. If node_errors is non-empty, the submission failed — report them.
2. Poll for completion
Poll GET http://192.168.31.240:8188/history/{prompt_id} every ~1.5s. The result entry under outputs["9"].images contains filename. Stop polling when the image array appears.
3. Return the image
The served image URL is:
http://192.168.31.240:8188/view?filename=<filename>&subfolder=&type=output
Return this URL (a well-formed markdown image link if the user wants to view it inline).
Policy
- Timeout: poll no longer than ~120s. SDXL-turbo at 4 steps typically finishes in well under 60s. If it exceeds the budget, report a timeout rather than looping forever.
- Prompt quality: SDXL-turbo responds best to a descriptive positive prompt and a modest negative prompt (e.g.
blurry, low quality, distorted). Highcfg(>1) and highstepsare unnecessary for turbo. - Keep the model
sdxl_turbo.safetensorsunless the user asks otherwise — it is the verified installed checkpoint. - Do not write the PNG bytes to a local file unless asked; return the server URL.
Example (run_code)
const base = 'http://192.168.31.240:8188'
const wf = { "3": { "class_type": "KSampler", "inputs": { "seed": 42, "steps": 4, "cfg": 1.0, "sampler_name": "euler", "scheduler": "normal", "denoise": 1.0, "model": ["4", 0], "positive": ["6", 0], "negative": ["7", 0], "latent_image": ["5", 0] } }, "4": { "class_type": "CheckpointLoaderSimple", "inputs": { "ckpt_name": "sdxl_turbo.safetensors" } }, "5": { "class_type": "EmptyLatentImage", "inputs": { "width": 512, "height": 512, "batch_size": 1 } }, "6": { "class_type": "CLIPTextEncode", "inputs": { "text": "a red fox in a snowy forest, photorealistic", "clip": ["4", 1] } }, "7": { "class_type": "CLIPTextEncode", "inputs": { "text": "blurry, low quality, distorted", "clip": ["4", 1] } }, "8": { "class_type": "VAEDecode", "inputs": { "samples": ["3", 0], "vae": ["4", 2] } }, "9": { "class_type": "SaveImage", "inputs": { "filename_prefix": "dsh", "images": ["8", 0] } } }
const r = await (await fetch(base + '/prompt', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: wf, client_id: 'dsh' }) })).json()
if (r.node_errors && Object.keys(r.node_errors).length) throw new Error('node_errors: ' + JSON.stringify(r.node_errors))
let url = null
for (let i = 0; i < 160 && !url; i++) {
const h = await (await fetch(base + '/history/' + r.prompt_id)).json()
const imgs = h[r.prompt_id]?.outputs?.['9']?.images
if (imgs?.length) url = base + '/view?filename=' + encodeURIComponent(imgs[0].filename) + '&subfolder=&type=output'
else await new Promise(s => setTimeout(s, 1500))
}
return url