Nodes/ComfyUI-FeiFei/Prompt Director (API: edit config.json)
ComfyUI Node

Prompt Director (API: edit config.json)

Three keywords in, a whole shot out

By aifeifei798·Created about a month ago·Updated about 16 hours ago· 3
Prompt Director (API: edit config.json)
    • positive_prompt
    • negative_prompt
    • aspect_ratio
    ◄keywordscyberpunk, rainy night, red-haired girl►
    ◄model_styleFlux►
    ◄temperature0.70►
    ◄thinking_modeOurs (8-step)►
    ◄extra_notes►
    ◄timeout120►

    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_notes for 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. Check config.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.
    CategoryFeiFei

    Inputs (6)

    NameTypeDefaultDescription
    keywordsSTRINGcyberpunk, rainy night, red-haired girl—
    model_styleCOMBOFlux3 options: Flux, SDXL, Qwen-Image
    temperatureFLOAT0.700.1–1.5—
    thinking_modeCOMBOOurs (8-step)3 options: Ours (8-step), Model native, Both
    extra_notesoptSTRING—
    timeoutoptINT12010–600Request timeout in seconds.

    Outputs (3)

    NameTypeDescription
    positive_promptSTRING—
    negative_promptSTRING—
    aspect_ratioSTRING—