# Python 3.10+: pip install comfy-sdk==0.4.0
import os
from comfy_sdk import Comfy
api_key = os.environ.get("COMFY_API_KEY", "").strip()
if not api_key: raise RuntimeError("Set COMFY_API_KEY")
os.environ.setdefault("COMFY_BASE_URL", "https://cloud.comfy.org")
client = Comfy(api_key=api_key)
workflow = client.workflows.from_json({
"1": {
"class_type": "SaveAudioMP3",
"inputs": {
"audio": [
"2:18",
0
],
"filename_prefix": "audio/ace-step-1.5",
"quality": "V0"
},
"_meta": {
"title": "Save Audio (MP3) (DEPRECATED)"
}
},
"7": {
"class_type": "PreviewAny",
"inputs": {
"source": [
"11",
0
]
},
"_meta": {
"title": "Preview as Text"
}
},
"11": {
"class_type": "GeminiNode",
"inputs": {
"model": "gemini-3-pro-preview",
"prompt": "A blues song, to rival the great Robert Johnson.",
"seed": 1046438542938789,
"system_prompt": "**Role:** You are a professional Music Architect for **ACE-Step 1.5**. \n\n**Task:** Generate a music creation plan.\n\n**Strict Output Constraint (IMPORTANT):**\n1. Return the output as **RAW PLAIN TEXT** only.\n2. DO NOT use Markdown code blocks (no backticks ```).\n3. DO NOT use any headers, labels, or bold text.\n4. Use ONLY the separator `---` to divide the two parts.\n5. Part 1: Comma-separated style keywords.\n6. Part 2: Structured lyrics with [Tags].\n\n**My Topic/Requirement:**\n[USER INPUT]"
},
"_meta": {
"title": "Google Gemini"
}
},
"18": {
"class_type": "RegexExtract",
"inputs": {
"case_insensitive": True,
"dotall": False,
"group_index": 1,
"mode": "First Group",
"multiline": False,
"regex_pattern": "(?sm)^(.*?)^---$(.*)",
"string": [
"11",
0
]
},
"_meta": {
"title": "Extract Text"
}
},
"19": {
"class_type": "RegexExtract",
"inputs": {
"case_insensitive": True,
"dotall": False,
"group_index": 1,
"mode": "First Group",
"multiline": False,
"regex_pattern": "(?s)(?<=\\n---\\n)([\\s\\S]*)$",
"string": [
"11",
0
]
},
"_meta": {
"title": "Extract Text"
}
},
"2:104": {
"class_type": "UNETLoader",
"inputs": {
"unet_name": "acestep_v1.5_turbo.safetensors",
"weight_dtype": "default"
},
"_meta": {
"title": "Load Diffusion Model"
}
},
"2:105": {
"class_type": "DualCLIPLoader",
"inputs": {
"clip_name1": "qwen_0.6b_ace15.safetensors",
"clip_name2": "qwen_4b_ace15.safetensors",
"device": "default",
"type": "ace"
},
"_meta": {
"title": "Load CLIP (Dual)"
}
},
"2:106": {
"class_type": "VAELoader",
"inputs": {
"vae_name": "ace_1.5_vae.safetensors"
},
"_meta": {
"title": "Load VAE"
}
},
"2:110": {
"class_type": "PrimitiveFloat",
"inputs": {
"value": 120
},
"_meta": {
"title": "Song Duration"
}
},
"2:18": {
"class_type": "VAEDecodeAudio",
"inputs": {
"samples": [
"2:3",
0
],
"vae": [
"2:106",
0
]
},
"_meta": {
"title": "VAE Decode Audio"
}
},
"2:3": {
"class_type": "KSampler",
"inputs": {
"cfg": 1,
"denoise": 1,
"latent_image": [
"2:98",
0
],
"model": [
"2:78",
0
],
"negative": [
"2:47",
0
],
"positive": [
"2:94",
0
],
"sampler_name": "euler",
"scheduler": "simple",
"seed": 27660246420877,
"steps": 8
},
"_meta": {
"title": "KSampler"
}
},
"2:47": {
"class_type": "ConditioningZeroOut",
"inputs": {
"conditioning": [
"2:94",
0
]
},
"_meta": {
"title": "Conditioning Zero Out"
}
},
"2:78": {
"class_type": "ModelSamplingAuraFlow",
"inputs": {
"model": [
"2:104",
0
],
"sampling": "flow",
"shift": 3
},
"_meta": {
"title": "ModelSamplingAuraFlow"
}
},
"2:94": {
"class_type": "TextEncodeAceStepAudio1.5",
"inputs": {
"bpm": 190,
"cfg_scale": 2,
"clip": [
"2:105",
0
],
"duration": [
"2:110",
0
],
"generate_audio_codes": True,
"keyscale": "E minor",
"language": "en",
"lyrics": [
"19",
0
],
"min_p": 0,
"seed": 27660246420877,
"tags": [
"18",
0
],
"temperature": 0.85,
"timesignature": "4",
"top_k": 0,
"top_p": 0.9
},
"_meta": {
"title": "TextEncodeAceStepAudio1.5"
}
},
"2:98": {
"class_type": "EmptyAceStep1.5LatentAudio",
"inputs": {
"batch_size": 1,
"seconds": [
"2:110",
0
]
},
"_meta": {
"title": "Empty Ace Step 1.5 Latent Audio"
}
}
})
# run() also passes api_key to the partner nodes in this workflow.
job = client.run(workflow, api_key=api_key)