ApoStudio Chat
A chat box in your ComfyUI graph — prompts, captions, and all the text in between
- history_in
- image
- response
- full_context
- history_out
- elapsed_seconds
- token_usage
The name oversells it slightly: ApoStudio Chat doesn't run a model. It's a thin, honest client for any OpenAI-compatible chat server - LM Studio, Ollama, the OpenAI API, LiteLLM, vLLM - that drops a real chat box into your graph. If you already run an LLM locally (and you should, for this job), the node is ten seconds of setup and then you've got a text generator that sits upstream of your sampler like any other node.
That's the whole point of the LLM-in-the-graph pattern. The LLM isn't your prompt encoder - it's a tool bolted onto the workflow: type a rough idea, get a structured diffusion prompt back; feed it an image, get a caption. Small local models are the community default for exactly this, because a local 8B is free per call, offline, and uncensored. This node doesn't care which philosophy you pick - point it at anything that speaks the OpenAI protocol.
How it works. It builds a messages array, appends your user and system prompts, and POSTs to {api_url}/chat/completions - non-streaming, with a 300-second timeout. When an image is connected, it base64-encodes the tensor as a PNG data URL and tucks it into the user message, so vision only works if the model on the far end is actually a vision model. The node never crashes on a bad call; errors come back as strings starting ERROR: in the response output, which is honestly nicer for iterating than a red console trace.
The inputs that matter. Two of them will bite you. api_url defaults to http://localhost:1234/v1, LM Studio's address - switch to http://localhost:11434/v1 for Ollama or https://api.openai.com/v1 for OpenAI. And model defaults to the placeholder enter-model-id-here, which you must replace with the exact model ID your server reports. temperature (0.7) is the knob you'll actually turn; the rest - top_p, top_k, min_p, repeat_penalty - are llama.cpp-style sampling params the node always sends. Most local servers honor them; a strict hosted API may reject the ones it doesn't know, which shows up as an error naming the parameter. seed of -1 means random every run.
The optional inputs are where this becomes a workflow instead of a toy:
user_prompt_in/system_prompt_in- override the typed boxes when you wire in ApoStudio's User/System Prompt nodeshistory_in- an APO_HISTORY list from a History node, for multi-turnimage- any IMAGE tensor, for captioning
Outputs: response (the reply, wire it to a Display or anywhere text goes), full_context (the raw message array as JSON, for debugging), history_out (the updated conversation - feed it to a History save node), plus elapsed_seconds and token_usage (prompt: X completion: Y total: Z).
Install. ComfyUI Manager (search "ApoStudio") or:
cd ComfyUI/custom_nodes
git clone https://github.com/apoloniart/ApoStudio
pip install -r custom_nodes/ApoStudio/requirements.txt
Dependencies are just requests and Pillow, and they auto-install on first startup. There are no model downloads - the LLM lives on your server, not in ComfyUI. Restart and you'll see [ApoStudio] Loaded - 7 nodes registered.
Where people get burned. Connection errors mean your server isn't running or the URL is wrong. "Model not found" means the model field still has the placeholder or a stale ID. And remember this is an API-reaching node from a category that shipped malware once (ComfyUI_LLMVISION) - this pack is MIT, tiny, and only ever calls the URL you typed, but it's also brand-new with essentially zero community footprint. The source is a few hundred lines; skim it once before you trust it with an API key.
A local prompt enhancer won't write like a frontier model, and it doesn't need to. This node's job is removing the blank-page problem - and it does that well.
Inputs (15)
| Name | Type | Default | Description |
|---|---|---|---|
| api_url | STRING | http://localhost:1234/v1 | — |
| model | STRING | enter-model-id-here | — |
| user_prompt | STRING | — | |
| system_prompt | STRING | — | |
| temperature | FLOAT | 0.700–2 | — |
| max_tokens | INT | 2048128–32000 | — |
| top_p | FLOAT | 0.950–1 | — |
| top_k | INT | 400–200 | — |
| min_p | FLOAT | 0.050–1 | — |
| repeat_penalty | FLOAT | 1.101–2 | — |
| seed | INT | -1-1–2147483647 | — |
| user_prompt_inopt | STRING | — | |
| system_prompt_inopt | STRING | — | |
| history_inopt | APO_HISTORY | — | |
| imageopt | IMAGE | — |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| response | STRING | — |
| full_context | STRING | — |
| history_out | APO_HISTORY | — |
| elapsed_seconds | FLOAT | — |
| token_usage | STRING | — |