Prompt Director (API: edit config.json)
Three keywords in, a whole shot out
- positive_prompt
- negative_prompt
- aspect_ratio
Type cyberpunk, rainy night, red-haired girl and get back a positive prompt, a negative prompt, and an aspect ratio - the three things you need to press go. It's a good pitch, because the blank-page problem is real: most people can describe a vibe but stall on turning it into "subject, scene, mood, composition, camera, lighting."
It is one LLM call. A system prompt tells the model it's "an elite prompt director for AI image generation," demands a single JSON object with exactly those three keys, and forbids anything else. That's the whole trick, and it's the right shape: asking for JSON and parsing the keys is how you avoid the classic enhancer failure where the model prepends "Here is your enhanced prompt:" and that preamble becomes literal prompt tokens.
The widget that earns the node its place: model_style
Flux, SDXL, or Qwen-Image. This is the difference between a generic rewriter and something useful, because it switches the prompt dialect:
- Flux - flowing natural-language prose in full descriptive sentences. The system prompt explicitly forbids booru tag spam and quality-word stacks; detail gets baked into the sentences.
- SDXL - comma-separated Danbooru-style short tags: subject first, then composition, then lighting/style, ending with
masterpiece, best quality, highres, extremely detailed. - Qwen-Image - natural language, Chinese/English mixed is fine, vivid sentences over bare tags.
That mapping matches what the architecture actually wants. On an LLM-encoder model the encoder reads your prompt like an instruction, so prose works and masterpiece, 8k does measurably nothing; on an SDXL-lineage checkpoint, prose is a waste and the tag order matters. Getting the dialect wrong is the main reason people conclude "the LLM makes my prompts worse." Pick the branch that matches your checkpoint - including the 2026 trick of choosing the new branch on an old model.
Everything else is plain: keywords is your multiline input, temperature (0.7) is the usual randomness dial, and thinking_mode runs Ours (8-step) (default, your instruction, thinking off), Model native (built-in minimal JSON-shape instruction, enable_thinking true), or Both. There's one optional field, extra_notes, for steering - "no text in the image, keep the background empty" - and it's genuinely worth using instead of stuffing constraints into keywords.
Outputs, and the one that's better than it looks
Three strings: positive_prompt, negative_prompt, aspect_ratio. The first two wire into your positive and negative CLIP Text Encode.
aspect_ratio is a string, and it's clamped in code to the pack's nine-value ratio list (1:1, 1:2, 2:1, 3:4, 4:3, 9:16, 16:9, 9:21, 21:9). It normalises the model's formatting - full-width colons, slashes, whitespace - and then matches on value, so 16.0:16.0 resolves to 1:1 and an 8:16 written any reasonable way lands on 1:2. Anything that doesn't match falls back to 1:1, which is a deliberate floor rather than a surprise. Then convert the aspect_ratio widget on Aspect Ratio (1024) to an input and wire this into it, and your LLM-chosen composition drives the actual latent size.
That chain is the reason to use this node over a chat window: the director picks the ratio for the composition, and the sizing node - same list, same math, shared code - makes the frame agree.
Setting it up
The endpoint is not a widget. It reads api_base / api_key / model from config.json in the pack folder, optionally overridden per node:
{
"api_base": "http://127.0.0.1:8080",
"api_key": "",
"per_node": { "FeiFeiPromptDirector": { "model": "qwen3-8b" } }
}
Empty per_node values inherit the top level; the file is re-read every run, so no ComfyUI restart. No environment variable is read anywhere in the pack, by design - a key in config.json never ends up baked into a workflow or an image's metadata, which is exactly what would happen if it were a widget.
Install is the usual:
cd ComfyUI/custom_nodes
git clone https://github.com/aifeifei798/ComfyUI-FeiFei.git
# optional: pip install openai (otherwise a urllib fallback handles the call)
What to expect, and where it lies to you
- It will invent detail. That's the feature until it isn't. Give it the three anchors you care about and use
extra_notesfor hard constraints, or you'll get a beautiful prompt for a shot you didn't want. - The negative prompt is often inert. On any guidance-distilled model running at CFG 1 - Z-Image Turbo, Klein distilled, Turbo/Lightning checkpoints - the negative field does nothing. The director dutifully writes one because that's what the JSON asks for. Either you're on a model where it matters, or you rewrite the intent as presence in the positive prompt.
API Error:in the output. The node returns errors as strings rather than raising, so a bad endpoint doesn't kill your run - it just hands you an error where a prompt should be. Checkconfig.json, then whether the server is up.- Local, small and obedient beats clever. This is a format-following job, not a writing job. A reasoning model is slower and more likely to leak its scratch-work into your prompt.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| keywords | STRING | cyberpunk, rainy night, red-haired girl | — |
| model_style | COMBO | Flux | 3 options: Flux, SDXL, Qwen-Image |
| temperature | FLOAT | 0.700.1–1.5 | — |
| thinking_mode | COMBO | Ours (8-step) | 3 options: Ours (8-step), Model native, Both |
| extra_notesopt | STRING | — | |
| timeoutopt | INT | 12010–600 | Request timeout in seconds. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| positive_prompt | STRING | — |
| negative_prompt | STRING | — |
| aspect_ratio | STRING | — |