Spark Chat (OpenAI-compatible)
Hand the Writing to a Second Machine and Keep Your GPU Rendering
- image
- generated_text
Spark Chat does one thing: it posts a chat request to an OpenAI-compatible endpoint and returns the answer as a STRING. No model runs inside it, nothing to download, zero VRAM added to your graph.
Why bother? Because the text step and the render step usually want the same GPU at once. The intended setup is a DGX Spark (or any box serving an LLM) doing the writing while your desktop card renders. The Spark is an odd machine - built to run mid-to-large LLMs slowly, with no performance mix for image work - which is why you pair it with a card rather than replace one. It's also just as happy pointing at vLLM, llama.cpp or Ollama on your LAN (llm-in-comfyui.md § 3).
How it actually works
A thin HTTP client - the same request works from curl. Give it a base URL and it appends /v1/chat/completions (or takes a .../v1 base, or a full chat URL), sends the usual chat payload, and returns the first choice's content.
Two things matter before you debug. A blank model makes the node call /models and take the first id served - fine for a one-model server, a trap when several are hosted and something boring sorts first. And song mode switches on by graph shape, not node class: the node walks downstream looking for a node with a lyrics input plus a style or duration input. That's why it works across the YuE2 forks, ACE-Step and MiniMax Music.
The inputs you'll actually touch
prompt is the request; its default tells you the author's intent: "Write YuE2 song lyrics only. Use [verse] and [chorus] section tags." base_url and model are the two to get right. system_prompt sets the persona; image lets a vision model write from a picture; temperature, top_p and max_tokens are the usual sampling controls (the last bounds output, not song length).
Three toggles earn their place. strip_thinking (on by default) removes <think>…</think> blocks, because a reasoning model otherwise leaks its scratch-work into your prompt (llm-in-comfyui.md § 3). fit_to_duration caps the lyric to the render length, off by default on purpose: a duration is normally an upper bound, and capping shortens the song. hide_address keeps the address out of the workflow file and off screen recordings. timeout_seconds is the HTTP wait - set it above your slowest expected completion.
Output is one generated_text STRING. Wire it into your song node's lyric input, a prompt-encoding text node, or a save-text node.
Installing it
cd ComfyUI/custom_nodes
git clone https://github.com/TheAwaken1/ComfyUI-SparkStudio
Restart ComfyUI, or search ComfyUI-SparkStudio in ComfyUI Manager. There are no dependencies to install - requirements.txt is a comment saying numpy and Pillow already ship with ComfyUI. Before wiring anything, prove the endpoint answers:
curl http://YOUR-SPARK-ADDRESS:7860/api/engine/v1/models
If that fails, the node will too. base_url takes the Spark Studio gateway (http://host:7860/api/engine/v1), a plain engine base (http://host:8000/v1), or a full chat URL - prefer the gateway, since one URL follows whichever engine is live. To keep the address out of the graph, export SPARK_STUDIO_BASE_URL=... where you launch ComfyUI. A bearer-token endpoint needs SPARK_STUDIO_API_KEY in the environment - never paste a token into a widget, because widget values get saved into the workflow file.
Songwriting mode
Detect a song downstream and the node pulls that node's style text to steer mood and cadence, while forbidding the model from singing instrument names, BPM or artist references. Ask for two verses and two choruses and it writes each section in a separate call, refuses to reuse lines between sections, and repeats the finished chorus verbatim. With fit_to_duration on it revises up to three times, keeps the closest draft, and reports the miss in the console rather than failing the run - correct call, since losing a render to a word count is worse. Around three minutes fits roughly 24 lines, so six sections on a 90-second render will keep missing.
Where people get burned
- "The host name does not exist on this network." You pasted the README's example address.
- "Nothing is listening on that port." The server isn't running, or it's bound to
127.0.0.1on the other machine - use the LAN IP, Tailscale, orssh -L 8000:127.0.0.1:8000 user@host. Same fix for timeouts, which are usually a firewall. - Scratch-work in the output.
strip_thinkingmatches the<think>tag specifically, so a model reasoning in some other wrapper leaks through - check what the server emits. - More than four images on the wire.
imagesends up to four frames as base64 JPEG and errors beyond that.
One caveat for the category: an API node is arbitrary Python that talks to the network, and this ecosystem has seen that shape weaponized once (external-api-nodes.md). This one is small, readable and MIT.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| prompt | STRING | Write YuE2 song lyrics only. Use [verse] and [chorus] section tags. | — |
| base_url | STRING | http://localhost:7860/api/engine/v1 | Your server address. Leave empty to read SPARK_STUDIO_BASE_URL from the environment, which keeps it out of the workflow and off screen recordings. |
| model | STRING | Leave blank to use the first model the endpoint serves | |
| max_tokens | INT | 10241–32768 | — |
| temperature | FLOAT | 0.700–2 | — |
| top_p | FLOAT | 0.950.01–1 | — |
| system_promptopt | STRING | — | |
| imageopt | IMAGE | — | |
| timeout_secondsopt | INT | 1805–3600 | — |
| strip_thinkingopt | BOOLEAN | true | — |
| fit_to_durationopt | BOOLEAN | false | Trim the lyric to the render length. Off by default because the cap makes songs noticeably shorter; style and structure guidance still apply either way. |
| hide_addressopt | BOOLEAN | false | Store the address privately and clear the field. It stays off screen and out of the workflow file. Switch off to see it again. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| generated_text | STRING | — |