feat: enhance ControlNet integration with Xinsir-compatible pose rendering and update model handling

This commit is contained in:
syntaxbullet
2026-07-11 19:46:02 +02:00
parent ff762b8f17
commit 95043dfbdd
2 changed files with 37 additions and 8 deletions

View File

@@ -65,6 +65,14 @@ describe("Comfy adapter", () => {
expect(workflow["6"]?.inputs).toMatchObject({ latent_image: ["19", 0], positive: ["22", 0], negative: ["22", 1] });
});
test("uses Xinsir-compatible pose rendering for pose ControlNet", () => {
const workflow = buildSdxlWorkflow({
...inpaintRequest({ maskedContent: "neutral" }),
inpaint: { maskedContent: "neutral", structureControl: "pose", controlModel: "controlnet-openpose-sdxl-1.0.safetensors" },
});
expect(workflow["21"]).toMatchObject({ class_type: "OpenposePreprocessor", inputs: { detect_hand: "enable", detect_body: "enable", detect_face: "enable", scale_stick_for_xinsr_cn: "enable" } });
});
test("adds an optional low-denoise detail pass", () => {
const workflow = buildSdxlWorkflow({ ...inpaintRequest({ maskedContent: "neutral" }), refinePass: true, refineStrength: 18, seed: 40 });
expect(workflow["30"]).toMatchObject({ class_type: "KSampler", inputs: { seed: 41, denoise: 0.18, latent_image: ["6", 0] } });
@@ -73,7 +81,7 @@ describe("Comfy adapter", () => {
test("builds native SAM3 point selection as a mask output", () => {
const workflow = buildSemanticSelectionWorkflow({ inputImage: "input.png", model: "sam3.safetensors", x: 24.4, y: 18.6 });
expect(workflow["1"]).toMatchObject({ class_type: "UNETLoader", inputs: { unet_name: "sam3.safetensors" } });
expect(workflow["1"]).toMatchObject({ class_type: "CheckpointLoaderSimple", inputs: { ckpt_name: "sam3.safetensors" } });
expect(workflow["3"]).toMatchObject({ class_type: "SAM3_Detect", inputs: { positive_coords: '[{"x":24,"y":19}]', refine_iterations: 2 } });
expect(workflow["4"]).toMatchObject({ class_type: "MaskToImage", inputs: { mask: ["3", 0] } });
});
@@ -123,7 +131,7 @@ describe("Comfy adapter", () => {
test("lists branded non-Z diffusion models under Anima", async () => {
const originalFetch = globalThis.fetch;
const mockFetch: typeof fetch = Object.assign(async () => new Response(JSON.stringify({
CheckpointLoaderSimple: { input: { required: { ckpt_name: [["sd_xl_base_1.0.safetensors"]] } } },
CheckpointLoaderSimple: { input: { required: { ckpt_name: [["sd_xl_base_1.0.safetensors", "sam3.1_multiplex_fp16.safetensors"]] } } },
KSampler: { input: { required: { sampler_name: [["euler"]], scheduler: [["normal"]] } } },
UNETLoader: {
input: {
@@ -140,12 +148,29 @@ describe("Comfy adapter", () => {
},
CLIPLoader: { input: { required: { clip_name: [["qwen_3_06b_base.safetensors"]] } } },
VAELoader: { input: { required: { vae_name: [["qwen_image_vae.safetensors"]] } } },
ControlNetLoader: { input: { required: { control_net_name: [[
"controlnet-canny-sdxl-1.0-fp16.safetensors",
"controlnet-depth-sdxl-1.0-fp16.safetensors",
"controlnet-openpose-sdxl-1.0.safetensors",
]] } } },
Canny: {},
"MiDaS-DepthMapPreprocessor": {},
OpenposePreprocessor: {},
ControlNetApplyAdvanced: {},
SAM3_Detect: {},
MaskToImage: {},
}), { headers: { "content-type": "application/json" } }), { preconnect: originalFetch.preconnect });
globalThis.fetch = mockFetch;
try {
const response = await handleComfyApi(new Request("http://image-studio.test/api/comfy/models"));
const body = await response.json() as { architectures: { value: string; models: string[] }[] };
const body = await response.json() as {
architectures: { value: string; models: string[] }[];
controlModels: string[];
structureControls: string[];
semanticSelection: boolean;
sam3Models: string[];
};
const anima = body.architectures.find((architecture) => architecture.value === "anima");
expect(response.status).toBe(200);
@@ -153,6 +178,10 @@ describe("Comfy adapter", () => {
expect(anima?.models).toContain("miaomiaoHarem_anima13.safetensors");
expect(anima?.models).not.toContain("z_image_bf16.safetensors");
expect(anima?.models).not.toContain("z_image_turbo_bf16.safetensors");
expect(body.structureControls).toEqual(["canny", "depth", "pose"]);
expect(body.controlModels).toHaveLength(3);
expect(body.semanticSelection).toBeTrue();
expect(body.sam3Models).toEqual(["sam3.1_multiplex_fp16.safetensors"]);
} finally {
globalThis.fetch = originalFetch;
}