diff --git a/.gitignore b/.gitignore index 2700ad5c2..0ab4ba75e 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,5 @@ venv*/ *.log web_custom_versions/ .DS_Store -openapi.yaml filtered-openapi.yaml uv.lock diff --git a/blueprints/.glsl/Glow_30.frag b/blueprints/.glsl/Glow_30.frag index 0ee152628..f3c85a212 100644 --- a/blueprints/.glsl/Glow_30.frag +++ b/blueprints/.glsl/Glow_30.frag @@ -2,7 +2,6 @@ precision mediump float; uniform sampler2D u_image0; -uniform vec2 u_resolution; uniform int u_int0; // Blend mode uniform int u_int1; // Color tint uniform float u_float0; // Intensity @@ -75,7 +74,7 @@ void main() { float t0 = threshold - 0.15; float t1 = threshold + 0.15; - vec2 texelSize = 1.0 / u_resolution; + vec2 texelSize = 1.0 / vec2(textureSize(u_image0, 0)); float radius2 = radius * radius; float sampleScale = clamp(radius * 0.75, 0.35, 1.0); diff --git a/blueprints/.glsl/Image_Blur_1.frag b/blueprints/.glsl/Image_Blur_1.frag index 83238111d..1819e1695 100644 --- a/blueprints/.glsl/Image_Blur_1.frag +++ b/blueprints/.glsl/Image_Blur_1.frag @@ -12,7 +12,6 @@ const int RADIAL_SAMPLES = 12; const float RADIAL_STRENGTH = 0.0003; uniform sampler2D u_image0; -uniform vec2 u_resolution; uniform int u_int0; // Blur type (BLUR_GAUSSIAN, BLUR_BOX, BLUR_RADIAL) uniform float u_float0; // Blur radius/amount uniform int u_pass; // Pass index (0 = horizontal, 1 = vertical) @@ -25,7 +24,7 @@ float gaussian(float x, float sigma) { } void main() { - vec2 texelSize = 1.0 / u_resolution; + vec2 texelSize = 1.0 / vec2(textureSize(u_image0, 0)); float radius = max(u_float0, 0.0); // Radial (angular) blur - single pass, doesn't use separable diff --git a/blueprints/.glsl/Sharpen_23.frag b/blueprints/.glsl/Sharpen_23.frag index c03f94b66..e7463a329 100644 --- a/blueprints/.glsl/Sharpen_23.frag +++ b/blueprints/.glsl/Sharpen_23.frag @@ -2,14 +2,13 @@ precision highp float; uniform sampler2D u_image0; -uniform vec2 u_resolution; uniform float u_float0; // strength [0.0 – 2.0] typical: 0.3–1.0 in vec2 v_texCoord; layout(location = 0) out vec4 fragColor0; void main() { - vec2 texel = 1.0 / u_resolution; + vec2 texel = 1.0 / vec2(textureSize(u_image0, 0)); // Sample center and neighbors vec4 center = texture(u_image0, v_texCoord); diff --git a/blueprints/.glsl/Unsharp_Mask_26.frag b/blueprints/.glsl/Unsharp_Mask_26.frag index f5990cb4a..d968c9c03 100644 --- a/blueprints/.glsl/Unsharp_Mask_26.frag +++ b/blueprints/.glsl/Unsharp_Mask_26.frag @@ -2,7 +2,6 @@ precision highp float; uniform sampler2D u_image0; -uniform vec2 u_resolution; uniform float u_float0; // amount [0.0 - 3.0] typical: 0.5-1.5 uniform float u_float1; // radius [0.5 - 10.0] blur radius in pixels uniform float u_float2; // threshold [0.0 - 0.1] min difference to sharpen @@ -19,7 +18,7 @@ float getLuminance(vec3 color) { } void main() { - vec2 texel = 1.0 / u_resolution; + vec2 texel = 1.0 / vec2(textureSize(u_image0, 0)); float radius = max(u_float1, 0.5); float amount = u_float0; float threshold = u_float2; diff --git a/blueprints/Glow.json b/blueprints/Glow.json index 8c690fc68..1dafb2d35 100644 --- a/blueprints/Glow.json +++ b/blueprints/Glow.json @@ -268,7 +268,7 @@ "Node name for S&R": "GLSLShader" }, "widgets_values": [ - "#version 300 es\nprecision mediump float;\n\nuniform sampler2D u_image0;\nuniform vec2 u_resolution;\nuniform int u_int0; // Blend mode\nuniform int u_int1; // Color tint\nuniform float u_float0; // Intensity\nuniform float u_float1; // Radius\nuniform float u_float2; // Threshold\n\nin vec2 v_texCoord;\nout vec4 fragColor;\n\nconst int BLEND_ADD = 0;\nconst int BLEND_SCREEN = 1;\nconst int BLEND_SOFT = 2;\nconst int BLEND_OVERLAY = 3;\nconst int BLEND_LIGHTEN = 4;\n\nconst float GOLDEN_ANGLE = 2.39996323;\nconst int MAX_SAMPLES = 48;\nconst vec3 LUMA = vec3(0.299, 0.587, 0.114);\n\nfloat hash(vec2 p) {\n p = fract(p * vec2(123.34, 456.21));\n p += dot(p, p + 45.32);\n return fract(p.x * p.y);\n}\n\nvec3 hexToRgb(int h) {\n return vec3(\n float((h >> 16) & 255),\n float((h >> 8) & 255),\n float(h & 255)\n ) * (1.0 / 255.0);\n}\n\nvec3 blend(vec3 base, vec3 glow, int mode) {\n if (mode == BLEND_SCREEN) {\n return 1.0 - (1.0 - base) * (1.0 - glow);\n }\n if (mode == BLEND_SOFT) {\n return mix(\n base - (1.0 - 2.0 * glow) * base * (1.0 - base),\n base + (2.0 * glow - 1.0) * (sqrt(base) - base),\n step(0.5, glow)\n );\n }\n if (mode == BLEND_OVERLAY) {\n return mix(\n 2.0 * base * glow,\n 1.0 - 2.0 * (1.0 - base) * (1.0 - glow),\n step(0.5, base)\n );\n }\n if (mode == BLEND_LIGHTEN) {\n return max(base, glow);\n }\n return base + glow;\n}\n\nvoid main() {\n vec4 original = texture(u_image0, v_texCoord);\n \n float intensity = u_float0 * 0.05;\n float radius = u_float1 * u_float1 * 0.012;\n \n if (intensity < 0.001 || radius < 0.1) {\n fragColor = original;\n return;\n }\n \n float threshold = 1.0 - u_float2 * 0.01;\n float t0 = threshold - 0.15;\n float t1 = threshold + 0.15;\n \n vec2 texelSize = 1.0 / u_resolution;\n float radius2 = radius * radius;\n \n float sampleScale = clamp(radius * 0.75, 0.35, 1.0);\n int samples = int(float(MAX_SAMPLES) * sampleScale);\n \n float noise = hash(gl_FragCoord.xy);\n float angleOffset = noise * GOLDEN_ANGLE;\n float radiusJitter = 0.85 + noise * 0.3;\n \n float ca = cos(GOLDEN_ANGLE);\n float sa = sin(GOLDEN_ANGLE);\n vec2 dir = vec2(cos(angleOffset), sin(angleOffset));\n \n vec3 glow = vec3(0.0);\n float totalWeight = 0.0;\n \n // Center tap\n float centerMask = smoothstep(t0, t1, dot(original.rgb, LUMA));\n glow += original.rgb * centerMask * 2.0;\n totalWeight += 2.0;\n \n for (int i = 1; i < MAX_SAMPLES; i++) {\n if (i >= samples) break;\n \n float fi = float(i);\n float dist = sqrt(fi / float(samples)) * radius * radiusJitter;\n \n vec2 offset = dir * dist * texelSize;\n vec3 c = texture(u_image0, v_texCoord + offset).rgb;\n float mask = smoothstep(t0, t1, dot(c, LUMA));\n \n float w = 1.0 - (dist * dist) / (radius2 * 1.5);\n w = max(w, 0.0);\n w *= w;\n \n glow += c * mask * w;\n totalWeight += w;\n \n dir = vec2(\n dir.x * ca - dir.y * sa,\n dir.x * sa + dir.y * ca\n );\n }\n \n glow *= intensity / max(totalWeight, 0.001);\n \n if (u_int1 > 0) {\n glow *= hexToRgb(u_int1);\n }\n \n vec3 result = blend(original.rgb, glow, u_int0);\n result += (noise - 0.5) * (1.0 / 255.0);\n \n fragColor = vec4(clamp(result, 0.0, 1.0), original.a);\n}", + "#version 300 es\nprecision mediump float;\n\nuniform sampler2D u_image0;\nuniform int u_int0; // Blend mode\nuniform int u_int1; // Color tint\nuniform float u_float0; // Intensity\nuniform float u_float1; // Radius\nuniform float u_float2; // Threshold\n\nin vec2 v_texCoord;\nout vec4 fragColor;\n\nconst int BLEND_ADD = 0;\nconst int BLEND_SCREEN = 1;\nconst int BLEND_SOFT = 2;\nconst int BLEND_OVERLAY = 3;\nconst int BLEND_LIGHTEN = 4;\n\nconst float GOLDEN_ANGLE = 2.39996323;\nconst int MAX_SAMPLES = 48;\nconst vec3 LUMA = vec3(0.299, 0.587, 0.114);\n\nfloat hash(vec2 p) {\n p = fract(p * vec2(123.34, 456.21));\n p += dot(p, p + 45.32);\n return fract(p.x * p.y);\n}\n\nvec3 hexToRgb(int h) {\n return vec3(\n float((h >> 16) & 255),\n float((h >> 8) & 255),\n float(h & 255)\n ) * (1.0 / 255.0);\n}\n\nvec3 blend(vec3 base, vec3 glow, int mode) {\n if (mode == BLEND_SCREEN) {\n return 1.0 - (1.0 - base) * (1.0 - glow);\n }\n if (mode == BLEND_SOFT) {\n return mix(\n base - (1.0 - 2.0 * glow) * base * (1.0 - base),\n base + (2.0 * glow - 1.0) * (sqrt(base) - base),\n step(0.5, glow)\n );\n }\n if (mode == BLEND_OVERLAY) {\n return mix(\n 2.0 * base * glow,\n 1.0 - 2.0 * (1.0 - base) * (1.0 - glow),\n step(0.5, base)\n );\n }\n if (mode == BLEND_LIGHTEN) {\n return max(base, glow);\n }\n return base + glow;\n}\n\nvoid main() {\n vec4 original = texture(u_image0, v_texCoord);\n \n float intensity = u_float0 * 0.05;\n float radius = u_float1 * u_float1 * 0.012;\n \n if (intensity < 0.001 || radius < 0.1) {\n fragColor = original;\n return;\n }\n \n float threshold = 1.0 - u_float2 * 0.01;\n float t0 = threshold - 0.15;\n float t1 = threshold + 0.15;\n \n vec2 texelSize = 1.0 / vec2(textureSize(u_image0, 0));\n float radius2 = radius * radius;\n \n float sampleScale = clamp(radius * 0.75, 0.35, 1.0);\n int samples = int(float(MAX_SAMPLES) * sampleScale);\n \n float noise = hash(gl_FragCoord.xy);\n float angleOffset = noise * GOLDEN_ANGLE;\n float radiusJitter = 0.85 + noise * 0.3;\n \n float ca = cos(GOLDEN_ANGLE);\n float sa = sin(GOLDEN_ANGLE);\n vec2 dir = vec2(cos(angleOffset), sin(angleOffset));\n \n vec3 glow = vec3(0.0);\n float totalWeight = 0.0;\n \n // Center tap\n float centerMask = smoothstep(t0, t1, dot(original.rgb, LUMA));\n glow += original.rgb * centerMask * 2.0;\n totalWeight += 2.0;\n \n for (int i = 1; i < MAX_SAMPLES; i++) {\n if (i >= samples) break;\n \n float fi = float(i);\n float dist = sqrt(fi / float(samples)) * radius * radiusJitter;\n \n vec2 offset = dir * dist * texelSize;\n vec3 c = texture(u_image0, v_texCoord + offset).rgb;\n float mask = smoothstep(t0, t1, dot(c, LUMA));\n \n float w = 1.0 - (dist * dist) / (radius2 * 1.5);\n w = max(w, 0.0);\n w *= w;\n \n glow += c * mask * w;\n totalWeight += w;\n \n dir = vec2(\n dir.x * ca - dir.y * sa,\n dir.x * sa + dir.y * ca\n );\n }\n \n glow *= intensity / max(totalWeight, 0.001);\n \n if (u_int1 > 0) {\n glow *= hexToRgb(u_int1);\n }\n \n vec3 result = blend(original.rgb, glow, u_int0);\n result += (noise - 0.5) * (1.0 / 255.0);\n \n fragColor = vec4(clamp(result, 0.0, 1.0), original.a);\n}", "from_input" ] }, diff --git a/blueprints/Image Blur.json b/blueprints/Image Blur.json index b1d449e32..3c7a784b0 100644 --- a/blueprints/Image Blur.json +++ b/blueprints/Image Blur.json @@ -331,7 +331,7 @@ "Node name for S&R": "GLSLShader" }, "widgets_values": [ - "#version 300 es\n#pragma passes 2\nprecision highp float;\n\n// Blur type constants\nconst int BLUR_GAUSSIAN = 0;\nconst int BLUR_BOX = 1;\nconst int BLUR_RADIAL = 2;\n\n// Radial blur config\nconst int RADIAL_SAMPLES = 12;\nconst float RADIAL_STRENGTH = 0.0003;\n\nuniform sampler2D u_image0;\nuniform vec2 u_resolution;\nuniform int u_int0; // Blur type (BLUR_GAUSSIAN, BLUR_BOX, BLUR_RADIAL)\nuniform float u_float0; // Blur radius/amount\nuniform int u_pass; // Pass index (0 = horizontal, 1 = vertical)\n\nin vec2 v_texCoord;\nlayout(location = 0) out vec4 fragColor0;\n\nfloat gaussian(float x, float sigma) {\n return exp(-(x * x) / (2.0 * sigma * sigma));\n}\n\nvoid main() {\n vec2 texelSize = 1.0 / u_resolution;\n float radius = max(u_float0, 0.0);\n\n // Radial (angular) blur - single pass, doesn't use separable\n if (u_int0 == BLUR_RADIAL) {\n // Only execute on first pass\n if (u_pass > 0) {\n fragColor0 = texture(u_image0, v_texCoord);\n return;\n }\n\n vec2 center = vec2(0.5);\n vec2 dir = v_texCoord - center;\n float dist = length(dir);\n\n if (dist < 1e-4) {\n fragColor0 = texture(u_image0, v_texCoord);\n return;\n }\n\n vec4 sum = vec4(0.0);\n float totalWeight = 0.0;\n float angleStep = radius * RADIAL_STRENGTH;\n\n dir /= dist;\n\n float cosStep = cos(angleStep);\n float sinStep = sin(angleStep);\n\n float negAngle = -float(RADIAL_SAMPLES) * angleStep;\n vec2 rotDir = vec2(\n dir.x * cos(negAngle) - dir.y * sin(negAngle),\n dir.x * sin(negAngle) + dir.y * cos(negAngle)\n );\n\n for (int i = -RADIAL_SAMPLES; i <= RADIAL_SAMPLES; i++) {\n vec2 uv = center + rotDir * dist;\n float w = 1.0 - abs(float(i)) / float(RADIAL_SAMPLES);\n sum += texture(u_image0, uv) * w;\n totalWeight += w;\n\n rotDir = vec2(\n rotDir.x * cosStep - rotDir.y * sinStep,\n rotDir.x * sinStep + rotDir.y * cosStep\n );\n }\n\n fragColor0 = sum / max(totalWeight, 0.001);\n return;\n }\n\n // Separable Gaussian / Box blur\n int samples = int(ceil(radius));\n\n if (samples == 0) {\n fragColor0 = texture(u_image0, v_texCoord);\n return;\n }\n\n // Direction: pass 0 = horizontal, pass 1 = vertical\n vec2 dir = (u_pass == 0) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n\n vec4 color = vec4(0.0);\n float totalWeight = 0.0;\n float sigma = radius / 2.0;\n\n for (int i = -samples; i <= samples; i++) {\n vec2 offset = dir * float(i) * texelSize;\n vec4 sample_color = texture(u_image0, v_texCoord + offset);\n\n float weight;\n if (u_int0 == BLUR_GAUSSIAN) {\n weight = gaussian(float(i), sigma);\n } else {\n // BLUR_BOX\n weight = 1.0;\n }\n\n color += sample_color * weight;\n totalWeight += weight;\n }\n\n fragColor0 = color / totalWeight;\n}\n", + "#version 300 es\n#pragma passes 2\nprecision highp float;\n\n// Blur type constants\nconst int BLUR_GAUSSIAN = 0;\nconst int BLUR_BOX = 1;\nconst int BLUR_RADIAL = 2;\n\n// Radial blur config\nconst int RADIAL_SAMPLES = 12;\nconst float RADIAL_STRENGTH = 0.0003;\n\nuniform sampler2D u_image0;\nuniform int u_int0; // Blur type (BLUR_GAUSSIAN, BLUR_BOX, BLUR_RADIAL)\nuniform float u_float0; // Blur radius/amount\nuniform int u_pass; // Pass index (0 = horizontal, 1 = vertical)\n\nin vec2 v_texCoord;\nlayout(location = 0) out vec4 fragColor0;\n\nfloat gaussian(float x, float sigma) {\n return exp(-(x * x) / (2.0 * sigma * sigma));\n}\n\nvoid main() {\n vec2 texelSize = 1.0 / vec2(textureSize(u_image0, 0));\n float radius = max(u_float0, 0.0);\n\n // Radial (angular) blur - single pass, doesn't use separable\n if (u_int0 == BLUR_RADIAL) {\n // Only execute on first pass\n if (u_pass > 0) {\n fragColor0 = texture(u_image0, v_texCoord);\n return;\n }\n\n vec2 center = vec2(0.5);\n vec2 dir = v_texCoord - center;\n float dist = length(dir);\n\n if (dist < 1e-4) {\n fragColor0 = texture(u_image0, v_texCoord);\n return;\n }\n\n vec4 sum = vec4(0.0);\n float totalWeight = 0.0;\n float angleStep = radius * RADIAL_STRENGTH;\n\n dir /= dist;\n\n float cosStep = cos(angleStep);\n float sinStep = sin(angleStep);\n\n float negAngle = -float(RADIAL_SAMPLES) * angleStep;\n vec2 rotDir = vec2(\n dir.x * cos(negAngle) - dir.y * sin(negAngle),\n dir.x * sin(negAngle) + dir.y * cos(negAngle)\n );\n\n for (int i = -RADIAL_SAMPLES; i <= RADIAL_SAMPLES; i++) {\n vec2 uv = center + rotDir * dist;\n float w = 1.0 - abs(float(i)) / float(RADIAL_SAMPLES);\n sum += texture(u_image0, uv) * w;\n totalWeight += w;\n\n rotDir = vec2(\n rotDir.x * cosStep - rotDir.y * sinStep,\n rotDir.x * sinStep + rotDir.y * cosStep\n );\n }\n\n fragColor0 = sum / max(totalWeight, 0.001);\n return;\n }\n\n // Separable Gaussian / Box blur\n int samples = int(ceil(radius));\n\n if (samples == 0) {\n fragColor0 = texture(u_image0, v_texCoord);\n return;\n }\n\n // Direction: pass 0 = horizontal, pass 1 = vertical\n vec2 dir = (u_pass == 0) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n\n vec4 color = vec4(0.0);\n float totalWeight = 0.0;\n float sigma = radius / 2.0;\n\n for (int i = -samples; i <= samples; i++) {\n vec2 offset = dir * float(i) * texelSize;\n vec4 sample_color = texture(u_image0, v_texCoord + offset);\n\n float weight;\n if (u_int0 == BLUR_GAUSSIAN) {\n weight = gaussian(float(i), sigma);\n } else {\n // BLUR_BOX\n weight = 1.0;\n }\n\n color += sample_color * weight;\n totalWeight += weight;\n }\n\n fragColor0 = color / totalWeight;\n}\n", "from_input" ] } diff --git a/blueprints/Sharpen.json b/blueprints/Sharpen.json index bb79f61fc..f332400fd 100644 --- a/blueprints/Sharpen.json +++ b/blueprints/Sharpen.json @@ -267,7 +267,7 @@ "Node name for S&R": "GLSLShader" }, "widgets_values": [ - "#version 300 es\nprecision highp float;\n\nuniform sampler2D u_image0;\nuniform vec2 u_resolution;\nuniform float u_float0; // strength [0.0 – 2.0] typical: 0.3–1.0\n\nin vec2 v_texCoord;\nlayout(location = 0) out vec4 fragColor0;\n\nvoid main() {\n vec2 texel = 1.0 / u_resolution;\n \n // Sample center and neighbors\n vec4 center = texture(u_image0, v_texCoord);\n vec4 top = texture(u_image0, v_texCoord + vec2( 0.0, -texel.y));\n vec4 bottom = texture(u_image0, v_texCoord + vec2( 0.0, texel.y));\n vec4 left = texture(u_image0, v_texCoord + vec2(-texel.x, 0.0));\n vec4 right = texture(u_image0, v_texCoord + vec2( texel.x, 0.0));\n \n // Edge enhancement (Laplacian)\n vec4 edges = center * 4.0 - top - bottom - left - right;\n \n // Add edges back scaled by strength\n vec4 sharpened = center + edges * u_float0;\n \n fragColor0 = vec4(clamp(sharpened.rgb, 0.0, 1.0), center.a);\n}", + "#version 300 es\nprecision highp float;\n\nuniform sampler2D u_image0;\nuniform float u_float0; // strength [0.0 – 2.0] typical: 0.3–1.0\n\nin vec2 v_texCoord;\nlayout(location = 0) out vec4 fragColor0;\n\nvoid main() {\n vec2 texel = 1.0 / vec2(textureSize(u_image0, 0));\n \n // Sample center and neighbors\n vec4 center = texture(u_image0, v_texCoord);\n vec4 top = texture(u_image0, v_texCoord + vec2( 0.0, -texel.y));\n vec4 bottom = texture(u_image0, v_texCoord + vec2( 0.0, texel.y));\n vec4 left = texture(u_image0, v_texCoord + vec2(-texel.x, 0.0));\n vec4 right = texture(u_image0, v_texCoord + vec2( texel.x, 0.0));\n \n // Edge enhancement (Laplacian)\n vec4 edges = center * 4.0 - top - bottom - left - right;\n \n // Add edges back scaled by strength\n vec4 sharpened = center + edges * u_float0;\n \n fragColor0 = vec4(clamp(sharpened.rgb, 0.0, 1.0), center.a);\n}", "from_input" ] } diff --git a/blueprints/Unsharp Mask.json b/blueprints/Unsharp Mask.json index b673eb703..137acaa43 100644 --- a/blueprints/Unsharp Mask.json +++ b/blueprints/Unsharp Mask.json @@ -383,7 +383,7 @@ "Node name for S&R": "GLSLShader" }, "widgets_values": [ - "#version 300 es\nprecision highp float;\n\nuniform sampler2D u_image0;\nuniform vec2 u_resolution;\nuniform float u_float0; // amount [0.0 - 3.0] typical: 0.5-1.5\nuniform float u_float1; // radius [0.5 - 10.0] blur radius in pixels\nuniform float u_float2; // threshold [0.0 - 0.1] min difference to sharpen\n\nin vec2 v_texCoord;\nlayout(location = 0) out vec4 fragColor0;\n\nfloat gaussian(float x, float sigma) {\n return exp(-(x * x) / (2.0 * sigma * sigma));\n}\n\nfloat getLuminance(vec3 color) {\n return dot(color, vec3(0.2126, 0.7152, 0.0722));\n}\n\nvoid main() {\n vec2 texel = 1.0 / u_resolution;\n float radius = max(u_float1, 0.5);\n float amount = u_float0;\n float threshold = u_float2;\n\n vec4 original = texture(u_image0, v_texCoord);\n\n // Gaussian blur for the \"unsharp\" mask\n int samples = int(ceil(radius));\n float sigma = radius / 2.0;\n\n vec4 blurred = vec4(0.0);\n float totalWeight = 0.0;\n\n for (int x = -samples; x <= samples; x++) {\n for (int y = -samples; y <= samples; y++) {\n vec2 offset = vec2(float(x), float(y)) * texel;\n vec4 sample_color = texture(u_image0, v_texCoord + offset);\n\n float dist = length(vec2(float(x), float(y)));\n float weight = gaussian(dist, sigma);\n blurred += sample_color * weight;\n totalWeight += weight;\n }\n }\n blurred /= totalWeight;\n\n // Unsharp mask = original - blurred\n vec3 mask = original.rgb - blurred.rgb;\n\n // Luminance-based threshold with smooth falloff\n float lumaDelta = abs(getLuminance(original.rgb) - getLuminance(blurred.rgb));\n float thresholdScale = smoothstep(0.0, threshold, lumaDelta);\n mask *= thresholdScale;\n\n // Sharpen: original + mask * amount\n vec3 sharpened = original.rgb + mask * amount;\n\n fragColor0 = vec4(clamp(sharpened, 0.0, 1.0), original.a);\n}\n", + "#version 300 es\nprecision highp float;\n\nuniform sampler2D u_image0;\nuniform float u_float0; // amount [0.0 - 3.0] typical: 0.5-1.5\nuniform float u_float1; // radius [0.5 - 10.0] blur radius in pixels\nuniform float u_float2; // threshold [0.0 - 0.1] min difference to sharpen\n\nin vec2 v_texCoord;\nlayout(location = 0) out vec4 fragColor0;\n\nfloat gaussian(float x, float sigma) {\n return exp(-(x * x) / (2.0 * sigma * sigma));\n}\n\nfloat getLuminance(vec3 color) {\n return dot(color, vec3(0.2126, 0.7152, 0.0722));\n}\n\nvoid main() {\n vec2 texel = 1.0 / vec2(textureSize(u_image0, 0));\n float radius = max(u_float1, 0.5);\n float amount = u_float0;\n float threshold = u_float2;\n\n vec4 original = texture(u_image0, v_texCoord);\n\n // Gaussian blur for the \"unsharp\" mask\n int samples = int(ceil(radius));\n float sigma = radius / 2.0;\n\n vec4 blurred = vec4(0.0);\n float totalWeight = 0.0;\n\n for (int x = -samples; x <= samples; x++) {\n for (int y = -samples; y <= samples; y++) {\n vec2 offset = vec2(float(x), float(y)) * texel;\n vec4 sample_color = texture(u_image0, v_texCoord + offset);\n\n float dist = length(vec2(float(x), float(y)));\n float weight = gaussian(dist, sigma);\n blurred += sample_color * weight;\n totalWeight += weight;\n }\n }\n blurred /= totalWeight;\n\n // Unsharp mask = original - blurred\n vec3 mask = original.rgb - blurred.rgb;\n\n // Luminance-based threshold with smooth falloff\n float lumaDelta = abs(getLuminance(original.rgb) - getLuminance(blurred.rgb));\n float thresholdScale = smoothstep(0.0, threshold, lumaDelta);\n mask *= thresholdScale;\n\n // Sharpen: original + mask * amount\n vec3 sharpened = original.rgb + mask * amount;\n\n fragColor0 = vec4(clamp(sharpened, 0.0, 1.0), original.a);\n}\n", "from_input" ] } diff --git a/comfy_api/input/__init__.py b/comfy_api/input/__init__.py index 16d4acfd1..dc33533cc 100644 --- a/comfy_api/input/__init__.py +++ b/comfy_api/input/__init__.py @@ -9,6 +9,7 @@ from comfy_api.latest._input import ( CurveInput, MonotoneCubicCurve, LinearCurve, + RangeInput, ) __all__ = [ @@ -21,4 +22,5 @@ __all__ = [ "CurveInput", "MonotoneCubicCurve", "LinearCurve", + "RangeInput", ] diff --git a/comfy_api/latest/_input/__init__.py b/comfy_api/latest/_input/__init__.py index 05cd3d40a..f0229717e 100644 --- a/comfy_api/latest/_input/__init__.py +++ b/comfy_api/latest/_input/__init__.py @@ -1,5 +1,6 @@ from .basic_types import ImageInput, AudioInput, MaskInput, LatentInput from .curve_types import CurvePoint, CurveInput, MonotoneCubicCurve, LinearCurve +from .range_types import RangeInput from .video_types import VideoInput __all__ = [ @@ -12,4 +13,5 @@ __all__ = [ "CurveInput", "MonotoneCubicCurve", "LinearCurve", + "RangeInput", ] diff --git a/comfy_api/latest/_input/range_types.py b/comfy_api/latest/_input/range_types.py new file mode 100644 index 000000000..f4c5cb290 --- /dev/null +++ b/comfy_api/latest/_input/range_types.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import logging +import math +import numpy as np + +logger = logging.getLogger(__name__) + + +class RangeInput: + """Represents a levels/range adjustment: input range [min, max] with + optional midpoint (gamma control). + + Generates a 1D LUT identical to GIMP's levels mapping: + 1. Normalize input to [0, 1] using [min, max] + 2. Apply gamma correction: pow(value, 1/gamma) + 3. Clamp to [0, 1] + + The midpoint field is a position in [0, 1] representing where the + midtone falls within [min, max]. It maps to gamma via: + gamma = -log2(midpoint) + So midpoint=0.5 → gamma=1.0 (linear). + """ + + def __init__(self, min_val: float, max_val: float, midpoint: float | None = None): + self.min_val = min_val + self.max_val = max_val + self.midpoint = midpoint + + @staticmethod + def from_raw(data) -> RangeInput: + if isinstance(data, RangeInput): + return data + if isinstance(data, dict): + return RangeInput( + min_val=float(data.get("min", 0.0)), + max_val=float(data.get("max", 1.0)), + midpoint=float(data["midpoint"]) if data.get("midpoint") is not None else None, + ) + raise TypeError(f"Cannot convert {type(data)} to RangeInput") + + def to_lut(self, size: int = 256) -> np.ndarray: + """Generate a float64 lookup table mapping [0, 1] input through this + levels adjustment. + + The LUT maps normalized input values (0..1) to output values (0..1), + matching the GIMP levels formula. + """ + xs = np.linspace(0.0, 1.0, size, dtype=np.float64) + + in_range = self.max_val - self.min_val + if abs(in_range) < 1e-10: + return np.where(xs >= self.min_val, 1.0, 0.0).astype(np.float64) + + # Normalize: map [min, max] → [0, 1] + result = (xs - self.min_val) / in_range + result = np.clip(result, 0.0, 1.0) + + # Gamma correction from midpoint + if self.midpoint is not None and self.midpoint > 0 and self.midpoint != 0.5: + gamma = max(-math.log2(self.midpoint), 0.001) + inv_gamma = 1.0 / gamma + mask = result > 0 + result[mask] = np.power(result[mask], inv_gamma) + + return result + + def __repr__(self) -> str: + mid = f", midpoint={self.midpoint}" if self.midpoint is not None else "" + return f"RangeInput(min={self.min_val}, max={self.max_val}{mid})" diff --git a/comfy_api/latest/_input_impl/video_types.py b/comfy_api/latest/_input_impl/video_types.py index 1b4993aa7..bd8090635 100644 --- a/comfy_api/latest/_input_impl/video_types.py +++ b/comfy_api/latest/_input_impl/video_types.py @@ -248,8 +248,8 @@ class VideoFromFile(VideoInput): continue if self.__duration and frame.pts >= end_pts: break - img = frame.to_ndarray(format='rgb24') # shape: (H, W, 3) - img = torch.from_numpy(img) / 255.0 # shape: (H, W, 3) + img = frame.to_ndarray(format='gbrpf32le') # shape: (H, W, 3) + img = torch.from_numpy(img) frames.append(img) images = torch.stack(frames) if len(frames) > 0 else torch.zeros(0, 3, 0, 0) diff --git a/comfy_api/latest/_io.py b/comfy_api/latest/_io.py index fdeffea2d..4942ed46c 100644 --- a/comfy_api/latest/_io.py +++ b/comfy_api/latest/_io.py @@ -1266,6 +1266,43 @@ class Histogram(ComfyTypeIO): Type = list[int] +@comfytype(io_type="RANGE") +class Range(ComfyTypeIO): + from comfy_api.input import RangeInput + if TYPE_CHECKING: + Type = RangeInput + + class Input(WidgetInput): + def __init__(self, id: str, display_name: str=None, optional=False, tooltip: str=None, + socketless: bool=True, default: dict=None, + display: str=None, + gradient_stops: list=None, + show_midpoint: bool=None, + midpoint_scale: str=None, + value_min: float=None, + value_max: float=None, + advanced: bool=None): + super().__init__(id, display_name, optional, tooltip, None, default, socketless, None, None, None, None, advanced) + if default is None: + self.default = {"min": 0.0, "max": 1.0} + self.display = display + self.gradient_stops = gradient_stops + self.show_midpoint = show_midpoint + self.midpoint_scale = midpoint_scale + self.value_min = value_min + self.value_max = value_max + + def as_dict(self): + return super().as_dict() | prune_dict({ + "display": self.display, + "gradient_stops": self.gradient_stops, + "show_midpoint": self.show_midpoint, + "midpoint_scale": self.midpoint_scale, + "value_min": self.value_min, + "value_max": self.value_max, + }) + + DYNAMIC_INPUT_LOOKUP: dict[str, Callable[[dict[str, Any], dict[str, Any], tuple[str, dict[str, Any]], str, list[str] | None], None]] = {} def register_dynamic_input_func(io_type: str, func: Callable[[dict[str, Any], dict[str, Any], tuple[str, dict[str, Any]], str, list[str] | None], None]): DYNAMIC_INPUT_LOOKUP[io_type] = func @@ -2276,5 +2313,6 @@ __all__ = [ "BoundingBox", "Curve", "Histogram", + "Range", "NodeReplace", ] diff --git a/comfy_api_nodes/nodes_sora.py b/comfy_api_nodes/nodes_sora.py index afc18bb25..4d9075dcf 100644 --- a/comfy_api_nodes/nodes_sora.py +++ b/comfy_api_nodes/nodes_sora.py @@ -33,9 +33,13 @@ class OpenAIVideoSora2(IO.ComfyNode): def define_schema(cls): return IO.Schema( node_id="OpenAIVideoSora2", - display_name="OpenAI Sora - Video", + display_name="OpenAI Sora - Video (Deprecated)", category="api node/video/Sora", - description="OpenAI video and audio generation.", + description=( + "OpenAI video and audio generation.\n\n" + "DEPRECATION NOTICE: OpenAI will stop serving the Sora v2 API in September 2026. " + "This node will be removed from ComfyUI at that time." + ), inputs=[ IO.Combo.Input( "model", diff --git a/openapi.yaml b/openapi.yaml new file mode 100644 index 000000000..77d0e2318 --- /dev/null +++ b/openapi.yaml @@ -0,0 +1,3231 @@ +openapi: 3.1.0 +info: + title: ComfyUI API + description: | + API for ComfyUI - A powerful and modular stable diffusion GUI and backend. + + This API allows you to interact with ComfyUI programmatically, including: + - Submitting and managing workflow executions + - Querying node/object information + - Uploading and viewing files + - Managing user settings and data + - Asset management (feature-gated) + + ## Dual-path routing + Every route registered via `self.routes` in the ComfyUI server is available at + both its bare path (e.g. `/prompt`) and an `/api`-prefixed path (e.g. `/api/prompt`). + This spec uses the `/api`-prefixed versions as canonical. + + ## Multi-user mode + When ComfyUI is started with `--multi-user`, the `Comfy-User` header identifies + the active user for settings, userdata, and history isolation. This is **not** a + security mechanism — it is an organisational convenience with no authentication + or authorisation behind it. + version: 1.0.0 + license: + name: GNU General Public License v3.0 + url: https://github.com/comfyanonymous/ComfyUI/blob/master/LICENSE + +servers: + - url: / + description: Default ComfyUI server (typically http://127.0.0.1:8188) + +tags: + - name: prompt + description: Workflow submission and prompt info + - name: queue + description: Queue inspection and management + - name: history + description: Execution history + - name: upload + description: File upload endpoints + - name: view + description: File viewing / download + - name: system + description: System stats and feature flags + - name: node + description: Node / object_info definitions + - name: model + description: Model folder and file listing + - name: user + description: User management (multi-user mode) + - name: userdata + description: Per-user file storage + - name: settings + description: Per-user settings + - name: extensions + description: Frontend extension JS files + - name: subgraph + description: Global subgraph blueprints + - name: internal + description: Internal / debug endpoints + - name: assets + description: Asset management (feature-gated behind enable-assets) + +paths: + # --------------------------------------------------------------------------- + # WebSocket + # --------------------------------------------------------------------------- + /ws: + get: + operationId: connectWebSocket + tags: [system] + summary: WebSocket connection for real-time updates + description: | + Upgrades to a WebSocket connection that streams execution progress, + node status, and output messages. The server sends an initial `status` + message with the session ID (SID) on connect. + + ## Message types (server → client) + The server sends JSON messages with a `type` field. See the + `x-websocket-messages` list below for the schema of each message type. + parameters: + - name: clientId + in: query + required: false + schema: + type: string + description: Client identifier. If omitted the server assigns one. + responses: + "101": + description: WebSocket upgrade successful + x-websocket-messages: + - type: status + schema: + $ref: "#/components/schemas/StatusWsMessage" + - type: progress + schema: + $ref: "#/components/schemas/ProgressWsMessage" + - type: progress_text + schema: + $ref: "#/components/schemas/ProgressTextWsMessage" + - type: progress_state + schema: + $ref: "#/components/schemas/ProgressStateWsMessage" + - type: executing + schema: + $ref: "#/components/schemas/ExecutingWsMessage" + - type: executed + schema: + $ref: "#/components/schemas/ExecutedWsMessage" + - type: execution_start + schema: + $ref: "#/components/schemas/ExecutionStartWsMessage" + - type: execution_success + schema: + $ref: "#/components/schemas/ExecutionSuccessWsMessage" + - type: execution_cached + schema: + $ref: "#/components/schemas/ExecutionCachedWsMessage" + - type: execution_interrupted + schema: + $ref: "#/components/schemas/ExecutionInterruptedWsMessage" + - type: execution_error + schema: + $ref: "#/components/schemas/ExecutionErrorWsMessage" + - type: logs + schema: + $ref: "#/components/schemas/LogsWsMessage" + - type: notification + schema: + $ref: "#/components/schemas/NotificationWsMessage" + - type: feature_flags + schema: + $ref: "#/components/schemas/FeatureFlagsWsMessage" + - type: asset_download + schema: + $ref: "#/components/schemas/AssetDownloadWsMessage" + - type: asset_export + schema: + $ref: "#/components/schemas/AssetExportWsMessage" + + # --------------------------------------------------------------------------- + # Prompt + # --------------------------------------------------------------------------- + /api/prompt: + get: + operationId: getPromptInfo + tags: [prompt] + summary: Get queue status + description: Returns how many items remain in the execution queue. + responses: + "200": + description: Queue info + content: + application/json: + schema: + $ref: "#/components/schemas/PromptInfo" + post: + operationId: executePrompt + tags: [prompt] + summary: Submit a workflow for execution + description: Submits a workflow for execution. The server validates the graph, assigns a `prompt_id`, and enqueues it. Clients listen on `/ws` for execution progress and output messages. + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/PromptRequest" + responses: + "200": + description: Prompt accepted + content: + application/json: + schema: + $ref: "#/components/schemas/PromptResponse" + "400": + description: Validation or node errors + content: + application/json: + schema: + $ref: "#/components/schemas/PromptErrorResponse" + + # --------------------------------------------------------------------------- + # Queue + # --------------------------------------------------------------------------- + /api/queue: + get: + operationId: getQueue + tags: [queue] + summary: Get running and pending queue items + description: Returns the server's current execution queue, split into the currently-running prompt and the list of pending prompts. + responses: + "200": + description: Queue contents + content: + application/json: + schema: + $ref: "#/components/schemas/QueueInfo" + post: + operationId: manageQueue + tags: [queue] + summary: Clear or delete items from the queue + description: Mutates the execution queue. Supports clearing all queued prompts or deleting individual prompts by ID. + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/QueueManageRequest" + responses: + "200": + description: Queue updated + + /api/interrupt: + post: + operationId: interruptExecution + tags: [queue] + summary: Interrupt current execution + description: Interrupts the prompt that is currently executing. The next queued prompt (if any) will start immediately after. + requestBody: + required: false + content: + application/json: + schema: + type: object + properties: + prompt_id: + type: string + format: uuid + description: "If provided, only interrupts this specific running prompt. Otherwise interrupts all." + responses: + "200": + description: Interrupt signal sent + + /api/free: + post: + operationId: freeMemory + tags: [queue] + summary: Free GPU memory and/or unload models + description: Frees GPU memory by unloading models and/or freeing the resident model cache, controlled by the request flags. + requestBody: + required: false + content: + application/json: + schema: + type: object + properties: + unload_models: + type: boolean + description: Unload all models from VRAM/RAM + free_memory: + type: boolean + description: Run garbage collection and free cached memory + responses: + "200": + description: Memory freed + + # --------------------------------------------------------------------------- + # Jobs + # --------------------------------------------------------------------------- + /api/jobs: + get: + operationId: listJobs + tags: [queue] + summary: List jobs with filtering and pagination + description: Returns a paginated list of completed prompt executions, newest first. + parameters: + - name: status + in: query + schema: + type: string + description: Filter by job status + - name: workflow_id + in: query + schema: + type: string + description: Filter by workflow ID + - name: sort_by + in: query + schema: + type: string + description: Field to sort by + - name: sort_order + in: query + schema: + type: string + enum: [asc, desc] + description: Sort direction + - name: limit + in: query + schema: + type: integer + description: Maximum number of results (default is unlimited/None) + - name: offset + in: query + schema: + type: integer + default: 0 + description: Pagination offset + responses: + "200": + description: Jobs list + content: + application/json: + schema: + type: object + properties: + jobs: + type: array + items: + $ref: "#/components/schemas/JobEntry" + pagination: + $ref: "#/components/schemas/PaginationInfo" + + /api/jobs/{job_id}: + get: + operationId: getJob + tags: [queue] + summary: Get a single job by ID + description: Returns the full record for a single completed prompt execution, including its outputs, status, and metadata. + parameters: + - name: job_id + in: path + description: The job (prompt) ID to fetch. + required: true + schema: + type: string + format: uuid + responses: + "200": + description: Job detail + content: + application/json: + schema: + $ref: "#/components/schemas/JobDetailResponse" + "404": + description: Job not found + + # --------------------------------------------------------------------------- + # History + # --------------------------------------------------------------------------- + /api/history: + get: + operationId: getHistory + tags: [history] + summary: Get execution history + deprecated: true + description: | + **Deprecated.** Superseded by `GET /api/jobs`, which returns the same + execution records in a paginated, filterable format. Planned for removal + no earlier than a future major release; sunset timeline TBD. + + Returns a dictionary keyed by prompt_id. Each value is a HistoryEntry + containing prompt metadata, outputs, status, and node meta. + parameters: + - $ref: "#/components/parameters/ComfyUserHeader" + - name: max_items + in: query + schema: + type: integer + description: Maximum number of history entries to return + - name: offset + in: query + schema: + type: integer + description: Pagination offset (number of entries to skip) + responses: + "200": + description: History dictionary keyed by prompt_id + content: + application/json: + schema: + type: object + additionalProperties: + $ref: "#/components/schemas/HistoryEntry" + post: + operationId: manageHistory + tags: [history] + summary: Clear or delete history entries + deprecated: true + description: | + **Deprecated.** Superseded by the forthcoming job-management endpoints + under `/api/jobs`. Planned for removal no earlier than a future major + release; sunset timeline TBD. + parameters: + - $ref: "#/components/parameters/ComfyUserHeader" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/HistoryManageRequest" + responses: + "200": + description: History updated + + /api/history/{prompt_id}: + get: + operationId: getHistoryByPromptId + tags: [history] + summary: Get history for a specific prompt + deprecated: true + description: | + **Deprecated.** Superseded by `GET /api/jobs/{job_id}`, which returns + the same execution record. Planned for removal no earlier than a future + major release; sunset timeline TBD. + parameters: + - $ref: "#/components/parameters/ComfyUserHeader" + - name: prompt_id + in: path + description: The prompt ID to fetch history for. + required: true + schema: + type: string + format: uuid + responses: + "200": + description: Single-entry history dictionary. Returns an empty object `{}` if the prompt_id is not found. + content: + application/json: + schema: + type: object + additionalProperties: + $ref: "#/components/schemas/HistoryEntry" + + # --------------------------------------------------------------------------- + # Upload + # --------------------------------------------------------------------------- + /api/upload/image: + post: + operationId: uploadImage + tags: [upload] + summary: Upload an image file + description: Uploads an image file into one of the input/output/temp directories so it can be referenced by workflow nodes. + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: + - image + properties: + image: + type: string + format: binary + description: Image file to upload + type: + type: string + enum: [input, temp, output] + default: input + description: Target directory type + overwrite: + type: string + description: 'Set to "true" to overwrite existing files' + subfolder: + type: string + description: Subfolder within the target directory + responses: + "200": + description: Upload result + content: + application/json: + schema: + $ref: "#/components/schemas/UploadResult" + "400": + description: No file provided or invalid request + + /api/upload/mask: + post: + operationId: uploadMask + tags: [upload] + summary: Upload a mask image + description: Uploads a mask image associated with a previously-uploaded reference image. + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: + - image + - original_ref + properties: + image: + type: string + format: binary + description: Mask image (alpha channel is used) + original_ref: + type: object + description: Reference to the original image file + required: + - filename + properties: + filename: + type: string + description: Filename of the original image + additionalProperties: true + type: + type: string + enum: [input, temp, output] + default: input + description: Target directory type + overwrite: + type: string + description: 'Set to "true" to overwrite existing files' + subfolder: + type: string + description: Subfolder within the target directory + responses: + "200": + description: Upload result + content: + application/json: + schema: + $ref: "#/components/schemas/UploadResult" + "400": + description: No file provided or invalid request + + # --------------------------------------------------------------------------- + # View + # --------------------------------------------------------------------------- + /api/view: + get: + operationId: viewFile + tags: [view] + summary: View or download a file + description: Serves a file (image, audio, or video) from the input/output/temp directory identified by the query parameters. + parameters: + - name: filename + in: query + required: true + schema: + type: string + description: Name of the file to view + - name: type + in: query + schema: + type: string + enum: [input, output, temp] + default: output + description: Directory type + - name: subfolder + in: query + schema: + type: string + description: Subfolder within the directory + - name: preview + in: query + schema: + type: string + description: Preview format hint (e.g. "webp;90") + - name: channel + in: query + schema: + type: string + enum: [rgba, rgb, a] + description: Channel extraction mode + responses: + "200": + description: File content + content: + image/*: + schema: + type: string + format: binary + video/*: + schema: + type: string + format: binary + audio/*: + schema: + type: string + format: binary + application/octet-stream: + schema: + type: string + format: binary + "404": + description: File not found + + /api/view_metadata/{folder_name}: + get: + operationId: viewMetadata + tags: [view] + summary: Get metadata for a file (e.g. safetensors header) + description: Returns embedded metadata parsed from a file in the given folder — for example, the header of a safetensors model. + parameters: + - name: folder_name + in: path + required: true + schema: + type: string + description: Folder type (output, input, temp, etc.) + - name: filename + in: query + required: true + schema: + type: string + description: Filename to read metadata from + responses: + "200": + description: File metadata + content: + application/json: + schema: + type: object + additionalProperties: true + "404": + description: File or metadata not found + + # --------------------------------------------------------------------------- + # System + # --------------------------------------------------------------------------- + /api/system_stats: + get: + operationId: getSystemStats + tags: [system] + summary: Get system statistics + description: Returns hardware, Python, VRAM, and runtime statistics for the running ComfyUI process. + responses: + "200": + description: System stats + content: + application/json: + schema: + $ref: "#/components/schemas/SystemStatsResponse" + + /api/features: + get: + operationId: getFeatures + tags: [system] + summary: Get enabled feature flags + description: Returns a dictionary of feature flag names to their enabled state. + responses: + "200": + description: Feature flags + content: + application/json: + schema: + type: object + additionalProperties: + type: boolean + + # --------------------------------------------------------------------------- + # Node / Object Info + # --------------------------------------------------------------------------- + /api/object_info: + get: + operationId: getObjectInfo + tags: [node] + summary: Get all node definitions + description: | + Returns a dictionary of every registered node class, keyed by class name. + Each value is a NodeInfo object describing inputs, outputs, category, etc. + responses: + "200": + description: All node definitions + content: + application/json: + schema: + type: object + additionalProperties: + $ref: "#/components/schemas/NodeInfo" + + /api/object_info/{node_class}: + get: + operationId: getObjectInfoByClass + tags: [node] + summary: Get a single node definition + description: Returns the `NodeInfo` definition for a single registered node class. + parameters: + - name: node_class + in: path + required: true + schema: + type: string + description: Node class name (e.g. "KSampler") + responses: + "200": + description: Single node definition + content: + application/json: + schema: + type: object + additionalProperties: + $ref: "#/components/schemas/NodeInfo" + "404": + description: Node class not found + + /api/embeddings: + get: + operationId: getEmbeddings + tags: [node] + summary: List available embedding names + description: Returns the list of text-encoder embeddings available on disk. + responses: + "200": + description: Embedding names + content: + application/json: + schema: + type: array + items: + type: string + + # --------------------------------------------------------------------------- + # Models + # --------------------------------------------------------------------------- + /api/models: + get: + operationId: getModelTypes + tags: [model] + summary: List model folder type names + description: Returns an array of model type names (e.g. checkpoints, loras, vae). + responses: + "200": + description: Model type names + content: + application/json: + schema: + type: array + items: + type: string + + /api/models/{folder}: + get: + operationId: getModelsByFolder + tags: [model] + summary: List model filenames in a folder + description: Returns the names of model files in the given folder. This endpoint predates `/api/experiment/models/{folder}` and returns names only — prefer the experiment endpoint for new integrations. + parameters: + - name: folder + in: path + required: true + schema: + type: string + description: Model folder type name + responses: + "200": + description: Model filenames + content: + application/json: + schema: + type: array + items: + type: string + "404": + description: Unknown folder type + + /api/experiment/models: + get: + operationId: getExperimentModels + tags: [model] + summary: List model folders with paths + description: Returns an array of model folder objects with name and folder paths. + responses: + "200": + description: Model folders + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ModelFolder" + + /api/experiment/models/{folder}: + get: + operationId: getExperimentModelsByFolder + tags: [model] + summary: List model files with metadata + description: Returns the model files in the given folder with richer metadata (path index, mtime, size) than the legacy `/api/models/{folder}` endpoint. + parameters: + - name: folder + in: path + required: true + schema: + type: string + description: Model folder type name + responses: + "200": + description: Model files with metadata + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ModelFile" + "404": + description: Unknown folder type + + /api/experiment/models/preview/{folder}/{path_index}/{filename}: + get: + operationId: getModelPreview + tags: [model] + summary: Get model preview image + description: Returns the preview image associated with a model file, if one exists alongside the model on disk. + parameters: + - name: folder + in: path + required: true + schema: + type: string + description: Model folder type name + - name: path_index + in: path + required: true + schema: + type: integer + description: Path index within the folder + - name: filename + in: path + required: true + schema: + type: string + description: Model filename + responses: + "200": + description: Preview image (WebP) + content: + image/webp: + schema: + type: string + format: binary + "404": + description: Preview not found + + # --------------------------------------------------------------------------- + # Users + # --------------------------------------------------------------------------- + /api/users: + get: + operationId: getUsers + tags: [user] + summary: Get user storage info + description: | + Returns user storage configuration. In single-user mode returns + `{"storage": "server", "migrated": true/false}`. In multi-user mode + returns `{"storage": "server", "users": {"user_id": "user_dir", ...}}`. + parameters: + - $ref: "#/components/parameters/ComfyUserHeader" + responses: + "200": + description: User info + content: + application/json: + schema: + type: object + properties: + storage: + type: string + description: Storage backend type (always "server") + migrated: + type: boolean + description: Whether migration from browser storage is complete (single-user) + users: + type: object + additionalProperties: + type: string + description: Map of user_id to directory name (multi-user) + post: + operationId: createUser + tags: [user] + summary: Create a new user (multi-user mode) + description: Creates a new user entry. Only meaningful when ComfyUI is running in multi-user mode. + parameters: + - $ref: "#/components/parameters/ComfyUserHeader" + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - username + properties: + username: + type: string + description: Username for the new user + responses: + "200": + description: Created user ID + content: + application/json: + schema: + type: string + description: The generated user_id + "400": + description: Username already exists or invalid + + # --------------------------------------------------------------------------- + # Userdata + # --------------------------------------------------------------------------- + /api/userdata: + get: + operationId: listUserdata + tags: [userdata] + summary: List files in a userdata directory + description: Lists files in the authenticated user's data directory. Returns either filename strings or full objects depending on the `full_info` query parameter. + parameters: + - $ref: "#/components/parameters/ComfyUserHeader" + - name: dir + in: query + required: true + schema: + type: string + description: Directory path relative to the user's data folder + - name: recurse + in: query + schema: + type: boolean + description: Recurse into subdirectories + - name: full_info + in: query + schema: + type: boolean + description: Return full file info objects instead of just names + - name: split + in: query + schema: + type: boolean + description: Split paths into directory components + responses: + "200": + description: File listing + content: + application/json: + schema: + $ref: "#/components/schemas/ListUserdataResponse" + "404": + description: Directory not found + + /api/v2/userdata: + get: + operationId: listUserdataV2 + tags: [userdata] + summary: List files in userdata (v2 format) + description: Lists files in the authenticated user's data directory using the v2 response shape, which always returns full objects. + parameters: + - $ref: "#/components/parameters/ComfyUserHeader" + - name: path + in: query + schema: + type: string + description: Directory path relative to user data root + responses: + "200": + description: File listing with metadata + content: + application/json: + schema: + type: array + items: + type: object + properties: + name: + type: string + path: + type: string + type: + type: string + enum: [file, directory] + size: + type: integer + modified: + type: number + description: Unix timestamp + + /api/userdata/{file}: + get: + operationId: getUserdataFile + tags: [userdata] + summary: Read a userdata file + description: Reads the contents of a file from the authenticated user's data directory. + parameters: + - $ref: "#/components/parameters/ComfyUserHeader" + - name: file + in: path + required: true + schema: + type: string + description: File path relative to user data directory + responses: + "200": + description: File content + content: + application/octet-stream: + schema: + type: string + format: binary + "404": + description: File not found + post: + operationId: writeUserdataFile + tags: [userdata] + summary: Write or create a userdata file + description: Writes (creates or replaces) a file in the authenticated user's data directory. + parameters: + - $ref: "#/components/parameters/ComfyUserHeader" + - name: file + in: path + required: true + schema: + type: string + description: File path relative to user data directory + - name: overwrite + in: query + schema: + type: boolean + description: Allow overwriting existing files + - name: full_info + in: query + schema: + type: boolean + description: Return full file info in response + requestBody: + required: true + content: + application/octet-stream: + schema: + type: string + format: binary + application/json: + schema: {} + responses: + "200": + description: File written + content: + application/json: + schema: + $ref: "#/components/schemas/UserDataResponse" + "409": + description: File exists and overwrite not set + delete: + operationId: deleteUserdataFile + tags: [userdata] + summary: Delete a userdata file + description: Deletes a file from the authenticated user's data directory. + parameters: + - $ref: "#/components/parameters/ComfyUserHeader" + - name: file + in: path + required: true + schema: + type: string + description: File path relative to user data directory + responses: + "204": + description: File deleted + "404": + description: File not found + + /api/userdata/{file}/move/{dest}: + post: + operationId: moveUserdataFile + tags: [userdata] + summary: Move or rename a userdata file + description: Renames or moves a file within the authenticated user's data directory. + parameters: + - $ref: "#/components/parameters/ComfyUserHeader" + - name: file + in: path + required: true + schema: + type: string + description: Source file path + - name: dest + in: path + required: true + schema: + type: string + description: Destination file path + - name: overwrite + in: query + schema: + type: boolean + description: Allow overwriting at destination + - name: full_info + in: query + schema: + type: boolean + description: Return full file info in response + responses: + "200": + description: File moved + content: + application/json: + schema: + $ref: "#/components/schemas/UserDataResponse" + "404": + description: Source file not found + "409": + description: Destination exists and overwrite not set + + # --------------------------------------------------------------------------- + # Settings + # --------------------------------------------------------------------------- + /api/settings: + get: + operationId: getSettings + tags: [settings] + summary: Get all user settings + description: Returns all settings for the authenticated user. + parameters: + - $ref: "#/components/parameters/ComfyUserHeader" + responses: + "200": + description: Settings object + content: + application/json: + schema: + type: object + additionalProperties: true + post: + operationId: updateSettings + tags: [settings] + summary: Update user settings (partial merge) + description: Replaces the authenticated user's settings with the provided object. + parameters: + - $ref: "#/components/parameters/ComfyUserHeader" + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: true + description: Partial settings to merge + responses: + "200": + description: Settings updated + + /api/settings/{id}: + get: + operationId: getSetting + tags: [settings] + summary: Get a single setting by key + description: Returns the value of a single setting, identified by key. + parameters: + - $ref: "#/components/parameters/ComfyUserHeader" + - name: id + in: path + required: true + schema: + type: string + description: Setting key + responses: + "200": + description: Setting value (null if the setting does not exist) + content: + application/json: + schema: + nullable: true + description: The setting value (any JSON type), or null if not set + post: + operationId: updateSetting + tags: [settings] + summary: Set a single setting value + description: Sets the value of a single setting, identified by key. + parameters: + - $ref: "#/components/parameters/ComfyUserHeader" + - name: id + in: path + required: true + schema: + type: string + description: Setting key + requestBody: + required: true + content: + application/json: + schema: + description: The setting value (any JSON type) + responses: + "200": + description: Setting updated + + # --------------------------------------------------------------------------- + # Extensions / Templates / i18n + # --------------------------------------------------------------------------- + /api/extensions: + get: + operationId: getExtensions + tags: [extensions] + summary: List frontend extension JS file paths + description: Returns the list of frontend extension JS URLs registered by custom nodes, to be loaded by the frontend on startup. + responses: + "200": + description: Array of JS file paths + content: + application/json: + schema: + type: array + items: + type: string + description: Relative path to extension JS file + + /api/workflow_templates: + get: + operationId: getWorkflowTemplates + tags: [extensions] + summary: Get workflow template mappings + description: Returns a map of custom node names to their provided workflow template names. + responses: + "200": + description: Template mappings + content: + application/json: + schema: + type: object + additionalProperties: + type: array + items: + type: string + description: Map of node pack name to array of template names + + /api/i18n: + get: + operationId: getI18n + tags: [extensions] + summary: Get internationalisation translation strings + description: Returns the URLs of translation files contributed by custom nodes, keyed by locale. + responses: + "200": + description: Translation map + content: + application/json: + schema: + type: object + additionalProperties: true + description: Nested map of locale to translation key-value pairs + + # --------------------------------------------------------------------------- + # Subgraphs + # --------------------------------------------------------------------------- + /api/global_subgraphs: + get: + operationId: getGlobalSubgraphs + tags: [subgraph] + summary: List global subgraph blueprints + description: Returns a dictionary of subgraph IDs to their metadata. + responses: + "200": + description: Subgraph metadata dictionary + content: + application/json: + schema: + type: object + additionalProperties: + $ref: "#/components/schemas/GlobalSubgraphInfo" + + /api/global_subgraphs/{id}: + get: + operationId: getGlobalSubgraph + tags: [subgraph] + summary: Get a global subgraph with full data + description: Returns the blueprint for a globally-registered subgraph, used by the frontend to materialize the subgraph node. + parameters: + - name: id + in: path + required: true + schema: + type: string + description: Subgraph identifier + responses: + "200": + description: Full subgraph data + content: + application/json: + schema: + $ref: "#/components/schemas/GlobalSubgraphData" + "404": + description: Subgraph not found + + # --------------------------------------------------------------------------- + # Node Replacements + # --------------------------------------------------------------------------- + /api/node_replacements: + get: + operationId: getNodeReplacements + tags: [node] + summary: Get node replacement mappings + description: | + Returns a dictionary mapping deprecated or replaced node class names + to their replacement node information. + responses: + "200": + description: Replacement mappings + content: + application/json: + schema: + type: object + additionalProperties: true + + # --------------------------------------------------------------------------- + # Internal (x-internal: true) + # --------------------------------------------------------------------------- + /internal/logs: + get: + operationId: getInternalLogs + tags: [internal] + summary: Get server logs as text + description: Returns structured ComfyUI log entries from the in-memory log buffer. + x-internal: true + responses: + "200": + description: Log text + content: + text/plain: + schema: + type: string + + /internal/logs/raw: + get: + operationId: getInternalLogsRaw + tags: [internal] + summary: Get raw structured log entries + description: Returns the raw ComfyUI log buffer as text, together with metadata about the current size limit. + x-internal: true + responses: + "200": + description: Structured log data + content: + application/json: + schema: + type: object + properties: + entries: + type: array + items: + type: object + properties: + t: + type: number + description: Timestamp + m: + type: string + description: Message + size: + type: object + properties: + cols: + type: integer + rows: + type: integer + + /internal/logs/subscribe: + patch: + operationId: subscribeToLogs + tags: [internal] + summary: Subscribe or unsubscribe a WebSocket client to log streaming + description: Subscribes or unsubscribes the current client from live log streaming over the WebSocket. + x-internal: true + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - clientId + - enabled + properties: + clientId: + type: string + description: WebSocket client ID + enabled: + type: boolean + description: Enable or disable log streaming for this client + responses: + "200": + description: Subscription updated + + /internal/folder_paths: + get: + operationId: getInternalFolderPaths + tags: [internal] + summary: Get configured folder paths + description: Returns the filesystem paths ComfyUI is configured to load models and other assets from, keyed by folder type. + x-internal: true + responses: + "200": + description: Dictionary of folder type to paths + content: + application/json: + schema: + type: object + additionalProperties: + type: array + items: + type: array + items: + type: string + description: Map of folder type name to list of [path, ...] entries + + /internal/files/{directory_type}: + get: + operationId: getInternalFiles + tags: [internal] + summary: List files in a directory type + description: Lists the files present in one of ComfyUI's known directories (input, output, or temp). + x-internal: true + parameters: + - name: directory_type + in: path + required: true + schema: + type: string + description: Directory type (e.g. output, input, temp) + responses: + "200": + description: Array of filenames + content: + application/json: + schema: + type: array + items: + type: string + + # --------------------------------------------------------------------------- + # Assets (x-feature-gate: enable-assets) + # --------------------------------------------------------------------------- + /api/assets/hash/{hash}: + head: + operationId: checkAssetByHash + tags: [assets] + summary: Check if an asset with the given hash exists + description: Returns 204 if an asset with the given content hash already exists, 404 otherwise. Used by clients to deduplicate uploads before transferring bytes. + x-feature-gate: enable-assets + parameters: + - name: hash + in: path + required: true + schema: + type: string + description: "Blake3 hash of the asset (e.g. blake3:abc123...)" + responses: + "200": + description: Asset exists + "404": + description: No asset with this hash + + /api/assets: + get: + operationId: listAssets + tags: [assets] + summary: List assets with filtering and pagination + description: Returns a paginated list of assets, optionally filtered by tags, name, or other query parameters. + x-feature-gate: enable-assets + parameters: + - name: limit + in: query + schema: + type: integer + default: 50 + - name: offset + in: query + schema: + type: integer + default: 0 + - name: include_tags + in: query + schema: + type: array + items: + type: string + style: form + explode: true + description: Tags that assets must have (AND logic) + - name: exclude_tags + in: query + schema: + type: array + items: + type: string + style: form + explode: true + description: Tags that assets must not have + - name: name_contains + in: query + schema: + type: string + description: Filter assets whose name contains this substring + - name: metadata_filter + in: query + schema: + type: string + description: JSON-encoded metadata key/value filter + - name: sort + in: query + schema: + type: string + description: Field to sort by + - name: order + in: query + schema: + type: string + enum: [asc, desc] + description: Sort direction + responses: + "200": + description: Asset list + content: + application/json: + schema: + $ref: "#/components/schemas/ListAssetsResponse" + post: + operationId: createAsset + tags: [assets] + summary: Upload a new asset + description: Uploads a new asset (binary content plus metadata) and registers it in the asset database. + x-feature-gate: enable-assets + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: + - file + properties: + file: + type: string + format: binary + description: Asset file to upload + name: + type: string + description: Display name for the asset + tags: + type: string + description: Comma-separated tags + user_metadata: + type: string + description: JSON-encoded user metadata + hash: + type: string + description: "Blake3 hash of the file content (e.g. blake3:abc123...)" + mime_type: + type: string + description: MIME type of the file (overrides auto-detected type) + preview_id: + type: string + format: uuid + description: ID of an existing asset to use as the preview image + responses: + "201": + description: Asset created + content: + application/json: + schema: + $ref: "#/components/schemas/AssetCreated" + + /api/assets/from-hash: + post: + operationId: createAssetFromHash + tags: [assets] + summary: Create an asset reference from an existing hash + description: Registers a new asset that references existing content by hash, without re-uploading the bytes. + x-feature-gate: enable-assets + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - hash + - name + properties: + hash: + type: string + description: Blake3 hash of existing content + name: + type: string + description: Display name + tags: + type: array + items: + type: string + user_metadata: + type: object + additionalProperties: true + responses: + "201": + description: Asset created from hash + content: + application/json: + schema: + $ref: "#/components/schemas/AssetCreated" + + /api/assets/{id}: + get: + operationId: getAsset + tags: [assets] + summary: Get asset metadata + description: Returns the metadata for a single asset. + x-feature-gate: enable-assets + parameters: + - name: id + in: path + description: The asset ID. + required: true + schema: + type: string + format: uuid + responses: + "200": + description: Asset metadata + content: + application/json: + schema: + $ref: "#/components/schemas/Asset" + "404": + description: Asset not found + put: + operationId: updateAsset + tags: [assets] + summary: Update asset metadata + description: Updates the mutable metadata of an asset (name, tags, etc.). Binary content is immutable. + x-feature-gate: enable-assets + parameters: + - name: id + in: path + description: The asset ID. + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: New display name for the asset + user_metadata: + type: object + additionalProperties: true + description: Custom user metadata to set + preview_id: + type: string + format: uuid + description: ID of the asset to use as the preview + responses: + "200": + description: Asset updated + content: + application/json: + schema: + $ref: "#/components/schemas/AssetUpdated" + delete: + operationId: deleteAsset + tags: [assets] + summary: Delete an asset + description: Removes an asset entry. Depending on the server configuration, the underlying content may also be deleted. + x-feature-gate: enable-assets + parameters: + - name: id + in: path + description: The asset ID. + required: true + schema: + type: string + format: uuid + - name: delete_content + in: query + schema: + type: boolean + description: Also delete the underlying content file + responses: + "204": + description: Asset deleted + + /api/assets/{id}/content: + get: + operationId: getAssetContent + tags: [assets] + summary: Download asset file content + description: Returns the binary content of an asset. Supports range requests. + x-feature-gate: enable-assets + parameters: + - name: id + in: path + description: The asset ID. + required: true + schema: + type: string + format: uuid + responses: + "200": + description: Asset file content + content: + application/octet-stream: + schema: + type: string + format: binary + "404": + description: Asset not found + + /api/assets/{id}/tags: + post: + operationId: addAssetTags + tags: [assets] + summary: Add tags to an asset + description: Adds one or more tags to an asset. + x-feature-gate: enable-assets + parameters: + - name: id + in: path + description: The asset ID. + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - tags + properties: + tags: + type: array + items: + type: string + responses: + "200": + description: Tags added + content: + application/json: + schema: + $ref: "#/components/schemas/TagsModificationResponse" + delete: + operationId: removeAssetTags + tags: [assets] + summary: Remove tags from an asset + description: Removes one or more tags from an asset. + x-feature-gate: enable-assets + parameters: + - name: id + in: path + description: The asset ID. + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - tags + properties: + tags: + type: array + items: + type: string + responses: + "200": + description: Tags removed + content: + application/json: + schema: + $ref: "#/components/schemas/TagsModificationResponse" + + /api/tags: + get: + operationId: listTags + tags: [assets] + summary: List all known tags with counts + description: Returns the list of all tags known to the asset database, with counts. + x-feature-gate: enable-assets + parameters: + - name: limit + in: query + schema: + type: integer + - name: offset + in: query + schema: + type: integer + - name: search + in: query + schema: + type: string + description: Search term for tag name + responses: + "200": + description: Tag list + content: + application/json: + schema: + $ref: "#/components/schemas/ListTagsResponse" + + /api/assets/tags/refine: + get: + operationId: refineAssetTags + tags: [assets] + summary: Get tag counts for assets matching current filters + description: Returns suggested additional tags that would refine a filtered asset query, together with the count of assets each tag would select. + x-feature-gate: enable-assets + parameters: + - name: include_tags + in: query + schema: + type: array + items: + type: string + style: form + explode: true + description: Tags that assets must have (AND logic) + - name: exclude_tags + in: query + schema: + type: array + items: + type: string + style: form + explode: true + description: Tags that assets must not have + - name: name_contains + in: query + schema: + type: string + description: Filter assets whose name contains this substring + - name: metadata_filter + in: query + schema: + type: string + description: JSON-encoded metadata key/value filter + - name: limit + in: query + schema: + type: integer + - name: offset + in: query + schema: + type: integer + - name: sort + in: query + schema: + type: string + description: Field to sort by + - name: order + in: query + schema: + type: string + enum: [asc, desc] + description: Sort direction + responses: + "200": + description: Tag histogram + content: + application/json: + schema: + $ref: "#/components/schemas/AssetTagHistogramResponse" + + /api/assets/seed: + post: + operationId: seedAssets + tags: [assets] + summary: Trigger asset scan/seed from filesystem + description: Starts a background job that scans the configured directories and registers any assets not yet present in the asset database. + x-feature-gate: enable-assets + requestBody: + required: false + content: + application/json: + schema: + type: object + properties: + roots: + type: array + items: + type: string + description: Root folder paths to scan (if omitted, scans all) + responses: + "200": + description: Seed started + content: + application/json: + schema: + type: object + properties: + status: + type: string + + /api/assets/seed/status: + get: + operationId: getAssetSeedStatus + tags: [assets] + summary: Get asset scan progress + description: Returns the progress and status of the most recently-started asset seed job. + x-feature-gate: enable-assets + responses: + "200": + description: Scan progress + content: + application/json: + schema: + type: object + additionalProperties: true + description: Scan progress details (files scanned, total, status, etc.) + + /api/assets/seed/cancel: + post: + operationId: cancelAssetSeed + tags: [assets] + summary: Cancel an in-progress asset scan + description: Requests cancellation of the currently-running asset seed job. + x-feature-gate: enable-assets + responses: + "200": + description: Scan cancelled + content: + application/json: + schema: + type: object + properties: + status: + type: string + + /api/assets/prune: + post: + operationId: pruneAssets + tags: [assets] + summary: Mark assets whose backing files no longer exist on disk + description: Starts a background job that removes asset entries whose underlying content no longer exists on disk. + x-feature-gate: enable-assets + responses: + "200": + description: Prune result + content: + application/json: + schema: + type: object + properties: + status: + type: string + marked: + type: integer + description: Number of assets marked as missing + +components: + parameters: + ComfyUserHeader: + name: Comfy-User + in: header + required: false + schema: + type: string + description: | + Identifies the active user in multi-user mode. Used for settings, + userdata, and history isolation. This is not a security mechanism — + it is an organisational convenience with no authentication behind it. + + schemas: + # ------------------------------------------------------------------- + # Prompt + # ------------------------------------------------------------------- + PromptRequest: + type: object + description: A workflow submission. Wraps the prompt graph plus optional client identifier and extra per-request data. + required: + - prompt + properties: + prompt: + type: object + description: | + The workflow graph to execute. Keys are node IDs (strings); + values are objects with class_type and inputs. + additionalProperties: true + number: + type: number + description: Priority number for the queue (lower numbers have higher priority) + front: + type: boolean + description: If true, adds the prompt to the front of the queue + extra_data: + type: object + description: Extra data associated with the prompt (e.g. extra_pnginfo) + additionalProperties: true + client_id: + type: string + description: WebSocket client ID to receive progress updates + prompt_id: + type: string + format: uuid + description: "Client-supplied prompt ID. Server generates a UUID if omitted." + partial_execution_targets: + type: array + items: + type: string + description: List of node IDs to execute (partial graph execution) + + PromptResponse: + type: object + description: Server acknowledgement of a workflow submission. Includes the assigned `prompt_id` and current queue position. + properties: + prompt_id: + type: string + format: uuid + description: Unique identifier for the prompt execution + number: + type: number + description: Priority number in the queue + node_errors: + type: object + description: Validation errors keyed by node ID + additionalProperties: + $ref: "#/components/schemas/NodeError" + error: + description: Top-level prompt error (string message or structured error) + oneOf: + - type: string + - $ref: "#/components/schemas/PromptError" + + PromptErrorResponse: + type: object + description: Error response when prompt validation fails + additionalProperties: true + + PromptError: + type: object + description: Structured prompt validation error + properties: + type: + type: string + message: + type: string + details: + type: string + + Error: + type: object + description: Detailed node-level error + properties: + type: + type: string + message: + type: string + details: + type: string + extra_info: + type: object + properties: + input_name: + type: string + additionalProperties: true + + NodeError: + type: object + description: Error details for a single node + properties: + errors: + type: array + items: + $ref: "#/components/schemas/Error" + class_type: + type: string + description: The node's class type + dependent_outputs: + type: array + items: {} + + PromptInfo: + type: object + description: Summary of a queued or recently-executed prompt, as returned by the queue and history endpoints. + properties: + exec_info: + type: object + properties: + queue_remaining: + type: integer + description: Number of items remaining in the queue + + # ------------------------------------------------------------------- + # Queue + # ------------------------------------------------------------------- + QueueInfo: + type: object + description: Queue information with pending and running items + properties: + queue_running: + type: array + description: Currently running queue items + items: + type: array + description: | + Queue item tuple: [number, prompt_id, prompt, extra_data, outputs_to_execute, sensitive] + items: {} + prefixItems: + - type: number + description: Priority number + - type: string + format: uuid + description: prompt_id + - type: object + description: prompt graph + additionalProperties: true + - type: object + description: extra_data + additionalProperties: true + - type: array + description: outputs_to_execute (list of output node IDs) + items: + type: string + - type: object + description: sensitive data (may be omitted) + additionalProperties: true + queue_pending: + type: array + description: Pending queue items (oldest first) + items: + type: array + description: | + Queue item tuple: [number, prompt_id, prompt, extra_data, outputs_to_execute, sensitive] + items: {} + prefixItems: + - type: number + description: Priority number + - type: string + format: uuid + description: prompt_id + - type: object + description: prompt graph + additionalProperties: true + - type: object + description: extra_data + additionalProperties: true + - type: array + description: outputs_to_execute (list of output node IDs) + items: + type: string + - type: object + description: sensitive data (may be omitted) + additionalProperties: true + + QueueManageRequest: + type: object + description: Request to clear or delete from queue + properties: + clear: + type: boolean + description: If true, clear all pending items + delete: + type: array + items: + type: string + description: Array of prompt IDs to delete from queue + + # ------------------------------------------------------------------- + # History + # ------------------------------------------------------------------- + HistoryEntry: + type: object + description: A single execution history entry + properties: + prompt: + type: array + description: | + Prompt tuple: [number, prompt_id, prompt_graph, extra_data, output_node_ids] + items: {} + outputs: + type: object + description: Output data from execution keyed by node ID + additionalProperties: true + status: + type: object + description: Execution status (status_str, completed, messages, etc.) + additionalProperties: true + meta: + type: object + description: Metadata about the execution and nodes + additionalProperties: true + + HistoryManageRequest: + type: object + description: Request to clear or delete history entries + properties: + clear: + type: boolean + description: If true, clear all history + delete: + type: array + items: + type: string + description: Array of prompt IDs to delete from history + + # ------------------------------------------------------------------- + # Jobs + # ------------------------------------------------------------------- + JobEntry: + type: object + description: Lightweight job data for list views + required: + - id + - status + properties: + id: + type: string + format: uuid + description: Unique job identifier (same as prompt_id) + status: + type: string + description: Current job status + create_time: + type: number + description: Job creation timestamp + execution_start_time: + type: number + description: Workflow execution start timestamp + execution_end_time: + type: number + description: Workflow execution end timestamp + preview_output: + type: object + additionalProperties: true + description: Primary preview output + outputs_count: + type: integer + description: Total number of output files + + JobDetailResponse: + type: object + description: Full job details including workflow and outputs + required: + - id + - status + properties: + id: + type: string + format: uuid + status: + type: string + workflow: + type: object + additionalProperties: true + description: Full ComfyUI workflow + outputs: + type: object + additionalProperties: true + description: Full outputs object from execution + execution_error: + $ref: "#/components/schemas/ExecutionError" + create_time: + type: number + update_time: + type: number + execution_start_time: + type: number + execution_end_time: + type: number + preview_output: + type: object + additionalProperties: true + outputs_count: + type: integer + execution_status: + type: object + additionalProperties: true + execution_meta: + type: object + additionalProperties: true + + ExecutionError: + type: object + description: Detailed execution error from ComfyUI + properties: + node_id: + type: string + description: ID of the node that failed + node_type: + type: string + description: Type name of the node + exception_message: + type: string + description: Human-readable error message + exception_type: + type: string + description: Python exception type + traceback: + type: array + items: + type: string + description: Traceback lines + current_inputs: + type: object + additionalProperties: true + current_outputs: + type: object + additionalProperties: true + + PaginationInfo: + type: object + description: Pagination metadata returned alongside list responses. + properties: + offset: + type: integer + limit: + type: integer + total: + type: integer + has_more: + type: boolean + + # ------------------------------------------------------------------- + # Upload / View + # ------------------------------------------------------------------- + UploadResult: + type: object + description: Response body returned by the image/mask upload endpoints, describing where the uploaded file now lives. + properties: + name: + type: string + description: Saved filename (may be renamed to avoid collisions) + subfolder: + type: string + description: Subfolder the file was saved to + type: + type: string + description: Directory type (input, temp) + + # ------------------------------------------------------------------- + # System + # ------------------------------------------------------------------- + DeviceStats: + type: object + description: GPU/compute device statistics + required: + - name + - type + - index + properties: + name: + type: string + description: Device name + type: + type: string + description: Device type (cuda, mps, cpu, etc.) + index: + type: number + description: Device index + vram_total: + type: number + description: Total VRAM in bytes + vram_free: + type: number + description: Free VRAM in bytes + torch_vram_total: + type: number + description: Total PyTorch-managed VRAM in bytes + torch_vram_free: + type: number + description: Free PyTorch-managed VRAM in bytes + + SystemStatsResponse: + type: object + description: Hardware, VRAM, Python, and ComfyUI version information for the running process. + required: + - system + - devices + properties: + system: + type: object + required: + - os + - python_version + - embedded_python + - comfyui_version + - pytorch_version + - argv + - ram_total + - ram_free + properties: + os: + type: string + description: Operating system + python_version: + type: string + description: Python version + embedded_python: + type: boolean + description: Whether using embedded Python + comfyui_version: + type: string + description: ComfyUI version string + pytorch_version: + type: string + description: PyTorch version + required_frontend_version: + type: string + description: Required frontend version + argv: + type: array + items: + type: string + description: Command line arguments + ram_total: + type: number + description: Total RAM in bytes + ram_free: + type: number + description: Free RAM in bytes + installed_templates_version: + type: string + nullable: true + description: Version of the currently installed workflow templates + required_templates_version: + type: string + nullable: true + description: Minimum required workflow templates version for this ComfyUI build + devices: + type: array + items: + $ref: "#/components/schemas/DeviceStats" + + # ------------------------------------------------------------------- + # Node / Object Info + # ------------------------------------------------------------------- + NodeInfo: + type: object + description: 'Definition of a registered node class: its inputs, outputs, category, and display metadata.' + properties: + input: + type: object + description: Input specifications (required and optional groups) + additionalProperties: true + input_order: + type: object + description: Ordered input names per group + additionalProperties: + type: array + items: + type: string + output: + type: array + items: + type: string + description: Output type names + output_is_list: + type: array + items: + type: boolean + description: Whether each output is a list + output_name: + type: array + items: + type: string + description: Display names of outputs + name: + type: string + description: Internal class name + display_name: + type: string + description: Human-readable display name + description: + type: string + description: Node description + python_module: + type: string + description: Python module implementing the node + category: + type: string + description: Node category path + output_node: + type: boolean + description: Whether this is an output node + output_tooltips: + type: array + items: + type: string + description: Tooltips for each output + deprecated: + type: boolean + description: Whether the node is deprecated + experimental: + type: boolean + description: Whether the node is experimental + api_node: + type: boolean + description: Whether this is an API node + is_input_list: + type: boolean + description: Whether the node accepts list inputs + dev_only: + type: boolean + description: Whether the node is developer-only (hidden in production UI) + has_intermediate_output: + type: boolean + description: Whether the node emits intermediate output during execution + search_aliases: + type: array + items: + type: string + description: Alternative search terms for finding this node + essentials_category: + type: string + description: Category override used by the essentials pack + + # ------------------------------------------------------------------- + # Models + # ------------------------------------------------------------------- + ModelFolder: + type: object + description: A configured model folder and the list of disk paths it resolves to. + required: + - name + - folders + properties: + name: + type: string + description: Model folder type name (e.g. "checkpoints") + folders: + type: array + items: + type: string + description: Filesystem paths for this model type + + ModelFile: + type: object + description: A single model file in a folder, with filesystem metadata. + required: + - name + - pathIndex + properties: + name: + type: string + description: Model filename + pathIndex: + type: integer + description: Index into the folder's paths array + modified: + type: number + description: File modification timestamp + created: + type: number + description: File creation timestamp + size: + type: integer + format: int64 + description: File size in bytes + + # ------------------------------------------------------------------- + # Subgraphs + # ------------------------------------------------------------------- + GlobalSubgraphInfo: + type: object + description: Metadata for a global subgraph blueprint (without full data) + required: + - source + - name + - info + properties: + source: + type: string + description: Source type ("templates" or "custom_node") + name: + type: string + description: Display name of the subgraph blueprint + info: + type: object + description: Additional information about the subgraph + required: + - node_pack + properties: + node_pack: + type: string + description: The node pack/module providing this subgraph + data: + type: string + description: The full subgraph JSON data (may be empty in list view) + + GlobalSubgraphData: + type: object + description: Full data for a global subgraph blueprint + required: + - source + - name + - info + - data + properties: + source: + type: string + description: Source type ("templates" or "custom_node") + name: + type: string + description: Display name of the subgraph blueprint + info: + type: object + description: Additional information about the subgraph + required: + - node_pack + properties: + node_pack: + type: string + description: The node pack/module providing this subgraph + data: + type: string + description: The full subgraph JSON data as a string + + # ------------------------------------------------------------------- + # Userdata + # ------------------------------------------------------------------- + UserDataResponse: + description: | + Response body for the POST endpoints `/api/userdata/{file}` and + `/api/userdata/{file}/move/{dest}`. Returns a single item whose + shape depends on the `full_info` query parameter. + x-variant-selector: + full_info=true: file-info object (`GetUserDataResponseFullFile`) + default: relative path string + oneOf: + - $ref: "#/components/schemas/GetUserDataResponseFullFile" + - type: string + description: Relative path of the written or moved file. Returned when `full_info` is absent or false. + + ListUserdataResponse: + description: | + Response body for `GET /api/userdata`. The array item shape is + determined by the `full_info` and `split` query parameters. + x-variant-selector: + full_info=true: array of file-info objects (`GetUserDataResponseFullFile`) + split=true: array of `[relative_path, ...path_components]` arrays + default: array of relative path strings + oneOf: + - type: array + items: + $ref: "#/components/schemas/GetUserDataResponseFullFile" + description: Returned when `full_info=true`. + - type: array + items: + type: array + items: + type: string + minItems: 2 + description: | + Returned when `split=true` and `full_info=false`. Each inner + array is `[relative_path, ...path_components]`. + - type: array + items: + type: string + description: Default shape — array of file paths relative to the user data root. + + GetUserDataResponseFullFile: + type: object + description: A single entry in a full-info user data listing. + properties: + path: + type: string + description: File name or path relative to the user directory + created: + type: number + description: Unix timestamp of file creation + size: + type: integer + description: File size in bytes + modified: + type: integer + format: int64 + description: Unix timestamp of last modification in milliseconds + + # ------------------------------------------------------------------- + # Assets + # ------------------------------------------------------------------- + Asset: + type: object + description: A registered asset — an input/output file tracked in the asset database with content hash and metadata. + required: + - id + - name + - size + - created_at + - updated_at + properties: + id: + type: string + format: uuid + description: Unique identifier for the asset + name: + type: string + description: Name of the asset file + asset_hash: + type: string + description: Blake3 hash of the asset content + pattern: "^blake3:[a-f0-9]{64}$" + size: + type: integer + format: int64 + description: Size of the asset in bytes + mime_type: + type: string + description: MIME type of the asset + tags: + type: array + items: + type: string + description: Tags associated with the asset + user_metadata: + type: object + description: Custom user metadata + additionalProperties: true + metadata: + type: object + description: System-managed metadata (read-only) + additionalProperties: true + readOnly: true + preview_url: + type: string + format: uri + description: URL for asset preview/thumbnail + preview_id: + type: string + format: uuid + description: ID of the preview asset if available + prompt_id: + type: string + format: uuid + description: ID of the prompt that created this asset + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + last_access_time: + type: string + format: date-time + is_immutable: + type: boolean + description: Whether this asset is immutable + + AssetCreated: + description: Response body returned after successfully registering a new asset. + allOf: + - $ref: "#/components/schemas/Asset" + - type: object + required: + - created_new + properties: + created_new: + type: boolean + description: Whether this was a new creation (true) or returned existing (false) + + AssetUpdated: + type: object + description: Response body returned after updating an asset's metadata. + required: + - id + - updated_at + properties: + id: + type: string + format: uuid + name: + type: string + asset_hash: + type: string + pattern: "^blake3:[a-f0-9]{64}$" + tags: + type: array + items: + type: string + mime_type: + type: string + user_metadata: + type: object + additionalProperties: true + updated_at: + type: string + format: date-time + + ListAssetsResponse: + type: object + description: Paginated list of assets. + required: + - assets + - total + - has_more + properties: + assets: + type: array + items: + $ref: "#/components/schemas/Asset" + total: + type: integer + has_more: + type: boolean + + TagInfo: + type: object + description: A tag known to the asset database, with the number of assets bearing it. + required: + - name + - count + properties: + name: + type: string + count: + type: integer + + ListTagsResponse: + type: object + description: Flat list of all tags, with counts. + required: + - tags + - total + - has_more + properties: + tags: + type: array + items: + $ref: "#/components/schemas/TagInfo" + total: + type: integer + has_more: + type: boolean + + AssetTagHistogramResponse: + type: object + description: Tags that would refine a filtered asset query, with the count of assets each tag would additionally select. + required: + - tag_counts + properties: + tag_counts: + type: object + additionalProperties: + type: integer + description: Map of tag names to occurrence counts + + TagsModificationResponse: + type: object + description: Response body returned after adding or removing tags on an asset. + required: + - total_tags + properties: + added: + type: array + items: + type: string + description: Tags successfully added + removed: + type: array + items: + type: string + description: Tags successfully removed + already_present: + type: array + items: + type: string + description: Tags already present (for add) + not_present: + type: array + items: + type: string + description: Tags not present (for remove) + total_tags: + type: array + items: + type: string + description: All tags on the asset after the operation + + # ------------------------------------------------------------------- + # Result / Output types + # ------------------------------------------------------------------- + ResultItem: + type: object + description: A single output file reference + properties: + filename: + type: string + subfolder: + type: string + type: + type: string + enum: [input, output, temp] + display_name: + type: string + + NodeOutputs: + type: object + description: | + Outputs from a single node execution. Known keys are listed below, + but custom nodes may add arbitrary keys (additionalProperties). + properties: + images: + type: array + items: + $ref: "#/components/schemas/ResultItem" + audio: + type: array + items: + $ref: "#/components/schemas/ResultItem" + video: + type: array + items: + $ref: "#/components/schemas/ResultItem" + animated: + type: array + items: + type: boolean + text: + oneOf: + - type: string + - type: array + items: + type: string + additionalProperties: true + + TerminalSize: + type: object + description: Terminal dimensions + properties: + cols: + type: number + row: + type: number + + LogEntry: + type: object + description: A single log entry + properties: + t: + type: string + description: Timestamp + m: + type: string + description: Log message + + StatusWsMessageStatus: + type: object + description: Inner payload of a `status` WebSocket message, describing the execution queue state. + properties: + exec_info: + type: object + required: + - queue_remaining + properties: + queue_remaining: + type: integer + + StatusWsMessage: + type: object + description: Initial status message sent on connect + queue status updates + properties: + status: + $ref: "#/components/schemas/StatusWsMessageStatus" + sid: + type: string + description: Session ID assigned by the server + + ProgressWsMessage: + type: object + description: Node execution progress (step N of M) + required: + - value + - max + - prompt_id + - node + properties: + value: + type: integer + description: Current step + max: + type: integer + description: Total steps + prompt_id: + type: string + node: + type: string + description: Node ID currently executing + + ProgressTextWsMessage: + type: object + description: Text-based progress update from a node + properties: + nodeId: + type: string + text: + type: string + prompt_id: + type: string + + NodeProgressState: + type: object + description: Progress state for a single node + properties: + value: + type: number + max: + type: number + state: + type: string + enum: [pending, running, finished, error] + node_id: + type: string + prompt_id: + type: string + display_node_id: + type: string + parent_node_id: + type: string + real_node_id: + type: string + + ProgressStateWsMessage: + type: object + description: Bulk progress state for all nodes in a prompt + required: + - prompt_id + - nodes + properties: + prompt_id: + type: string + nodes: + type: object + description: Map of node ID to progress state + additionalProperties: + $ref: "#/components/schemas/NodeProgressState" + + ExecutingWsMessage: + type: object + description: Fired when a node begins execution + required: + - node + - display_node + - prompt_id + properties: + node: + type: string + description: Node ID + display_node: + type: string + description: Display node ID (may differ for subgraphs) + prompt_id: + type: string + + ExecutedWsMessage: + type: object + description: Fired when a node completes execution with output + required: + - node + - display_node + - prompt_id + - output + properties: + node: + type: string + display_node: + type: string + prompt_id: + type: string + output: + $ref: "#/components/schemas/NodeOutputs" + merge: + type: boolean + description: Whether to merge with existing output + + ExecutionWsMessageBase: + type: object + description: Base fields for execution lifecycle messages + required: + - prompt_id + - timestamp + properties: + prompt_id: + type: string + timestamp: + type: integer + description: Unix timestamp in milliseconds + + ExecutionStartWsMessage: + allOf: + - $ref: "#/components/schemas/ExecutionWsMessageBase" + description: Fired when prompt execution begins + + ExecutionSuccessWsMessage: + allOf: + - $ref: "#/components/schemas/ExecutionWsMessageBase" + description: Fired when prompt execution completes successfully + + ExecutionCachedWsMessage: + allOf: + - $ref: "#/components/schemas/ExecutionWsMessageBase" + - type: object + properties: + nodes: + type: array + items: + type: string + description: List of node IDs that were cached + description: Fired when nodes are served from cache + + ExecutionInterruptedWsMessage: + allOf: + - $ref: "#/components/schemas/ExecutionWsMessageBase" + - type: object + properties: + node_id: + type: string + node_type: + type: string + executed: + type: array + items: + type: string + description: Node IDs that completed before interruption + description: Fired when execution is interrupted by user + + ExecutionErrorWsMessage: + allOf: + - $ref: "#/components/schemas/ExecutionWsMessageBase" + - type: object + properties: + node_id: + type: string + node_type: + type: string + executed: + type: array + items: + type: string + exception_message: + type: string + exception_type: + type: string + traceback: + type: array + items: + type: string + current_inputs: {} + current_outputs: {} + description: Fired when a node throws an exception during execution + + LogsWsMessage: + type: object + description: Streaming log entries from the server + properties: + size: + $ref: "#/components/schemas/TerminalSize" + entries: + type: array + items: + $ref: "#/components/schemas/LogEntry" + + NotificationWsMessage: + type: object + description: Server notification (e.g. model download complete) + properties: + value: + type: string + id: + type: string + + FeatureFlagsWsMessage: + type: object + description: Feature flags sent on connect + additionalProperties: true + + AssetDownloadWsMessage: + type: object + description: Asset download progress + required: + - task_id + - asset_name + - bytes_total + - bytes_downloaded + - progress + - status + properties: + task_id: + type: string + asset_name: + type: string + bytes_total: + type: number + bytes_downloaded: + type: number + progress: + type: number + description: 0.0 to 1.0 + status: + type: string + enum: [created, running, completed, failed] + asset_id: + type: string + error: + type: string + + AssetExportWsMessage: + type: object + description: Bulk asset export progress + required: + - task_id + - assets_total + - assets_attempted + - assets_failed + - bytes_total + - bytes_processed + - progress + - status + properties: + task_id: + type: string + export_name: + type: string + assets_total: + type: number + assets_attempted: + type: number + assets_failed: + type: number + bytes_total: + type: number + bytes_processed: + type: number + progress: + type: number + description: 0.0 to 1.0 + status: + type: string + enum: [created, running, completed, failed] + error: + type: string diff --git a/requirements.txt b/requirements.txt index 44648dc0f..c32c6bf0e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -comfyui-frontend-package==1.42.14 +comfyui-frontend-package==1.42.15 comfyui-workflow-templates==0.9.62 comfyui-embedded-docs==0.4.4 torch