ThinkingLLM API (OpenAI Compatible)
Cloud LLMs from ComfyUI — without ever putting a key in a workflow
- image
- RESPONSE
- RAW_TRACE
The name understates the point. This node doesn't take a key you paste in and call some API - it calls an OpenAI-compatible provider using a server-side profile, and your workflow never sees a credential at all. After the ComfyUI_LLMVISION malware mess showed what happens when nodes get to exfiltrate your stuff, "keys stay off the graph" is the right instinct, and this is the cleanest implementation of it in the pack.
You use it when you want a big cloud model doing the LLM work in your pipeline - prompt enhancement, vision analysis, video-prompt writing for LTX or MiniMax - but you don't want to run a multi-gigabyte Qwen locally, or you're sharing a ComfyUI instance with other people and don't want every saved workflow carrying your OpenRouter key.
How it works
Think of it as two halves: the workflow side and the trust boundary. You pick an api_profile (there are 12 built in - OpenAI, OpenRouter, QwenCloud Singapore, Groq, Together, Fireworks, DeepInfra, Featherless, OrcaRouter, plus local Ollama / vLLM / llama.cpp endpoints), and a model_name from a curated list of about 1,200. That's it. The profile binds the endpoint, the auth method, and the caps: an environment variable name like OPENROUTER_API_KEY, a base_url, an allowed-models list, token and timeout ceilings.
The transport is plain stdlib urllib - no OpenAI SDK dependency. It streams, catches mid-stream provider errors instead of silently truncating, refuses HTTP redirects on purpose (a redirect can leak an Authorization header to the wrong host), and enforces HTTPS for cloud profiles. Local profiles hit http://127.0.0.1 and need no key. If you want to lock it down for multi-user use, the pack supports THINKINGLLM_API_PROFILES_FILE to point at your own profile file and THINKINGLLM_DISABLE_BUILTIN_API_PROFILES=1 to kill the convenient built-ins.
The inputs that matter
api_profileandmodel_name- the two you'll touch constantly. Anything not in the curated list goes incustom_model_name, which overrides the dropdown.system_promptandprompt- same mental model as any chat app.max_tokens,temperature,top_p- the usual dials.timeout_secondsis an idle timeout: it aborts only if the provider sends nothing, so long reasoning streams are allowed to run.enable_thinkingandthinking_budget- only meaningful on profiles withthinking_mode: "qwen"(QwenCloud). Elsewhere they're inert.- Optional
image/image_url- feed a ComfyUI IMAGE (encoded as a PNG data URL) or a public HTTPS URL for vision models. Don't connect both.
The outputs
RESPONSE is the cleaned answer with literal <think> blocks stripped - that's what wires downstream into your prompt. RAW_TRACE keeps the raw stream including the reasoning channel, so you can see what a thinking model did before answering. Same two-output pattern as every node in this pack.
Install
It's part of the pack, so you're installing all of it: ComfyUI Manager → Install via Registry → search ThinkingLLM, or
cd ComfyUI/custom_nodes
git clone https://github.com/goodguy1963/ComfyUI-ThinkingLLM.git
cd ComfyUI-ThinkingLLM
pip install -r requirements.txt
Then restart ComfyUI. One honest warning: the pack's requirements drag in the whole local-model stack (transformers>=5.2.0, bitsandbytes, torch, …) even though this node's request path needs none of it. Fine if you might use the local nodes later; annoying if you truly only want cloud calls.
The actual setup step is the key, and it lives in your environment, not ComfyUI:
export OPENROUTER_API_KEY='sk-or-...'
# then launch ComfyUI from that shell
When it bites you
The classic: "API credential is not configured" while the key works fine in your terminal. The ComfyUI process isn't your shell - set the variable in the environment of whatever actually launches ComfyUI (service, task scheduler, container), then restart it. Other common trips: a model name not on the profile's allowed_models list, max_tokens or timeout_seconds over the profile ceiling, and a "redirect rejected" error meaning your base_url isn't the provider's canonical endpoint.
One behavior that surprises people: the node deliberately re-runs the provider call on every queue run via IS_CHANGED. ComfyUI normally caches a node when inputs don't change - wrong for a remote call, since the remote can answer differently. So yes, each run costs you tokens. That's the feature.
Inputs (16)
| Name | Type | Default | Description |
|---|---|---|---|
| api_profile | COMBO | OpenRouter | Server-side profile binding endpoint, authentication, credential reference, capabilities, and safety limits. |
| model_name | COMBO | openrouter/pareto-code | Curated model for the selected profile. For anything not listed, leave this as-is and type the ID in custom_model_name below. |
| system_prompt | STRING | You are a helpful assistant. | — |
| prompt | STRING | — | |
| max_tokens | INT | 81921–65536 | — |
| temperature | FLOAT | 0.600–2 | — |
| top_p | FLOAT | 0.950–1 | — |
| seed | INT | 10–4294967295 | Sent only when the server profile enables send_seed. |
| enable_thinking | BOOLEAN | true | Used by profiles with thinking_mode='qwen'. |
| thinking_budget | INT | 81920–262144 | — |
| stream_tokens_to_terminal | BOOLEAN | false | Print a control-character-sanitized display copy to the ComfyUI terminal. |
| timeout_seconds | INT | 3001–3600 | Idle timeout: aborts only if the provider sends nothing for this many seconds. Long reasoning streams are allowed to continue. Server profile enforces max_timeout_seconds (300 by default; OrcaRouter 900). |
| custom_model_nameopt | STRING | Optional: exact model ID for anything not in the curated list. When set, it overrides model_name. | |
| imageopt | IMAGE | Single ComfyUI IMAGE. Encoded as PNG Base64 data URL and sent as OpenAI-style image_url content. | |
| image_urlopt | STRING | Alternative to IMAGE: public HTTPS image URL. Do not connect IMAGE at the same time. | |
| extra_body_jsonopt | STRING | Every top-level field must be explicitly allowlisted by the server profile. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| RESPONSE | STRING | — |
| RAW_TRACE | STRING | — |