Extensions/ComfyUI-Pi-LLM
ComfyUI Extension

ComfyUI-Pi-LLM

ComfyUI custom nodes for calling Pi as a text and optional image LLM from a workflow, with local caching for reproducible results and companion text extraction utilities.

By felixowens·Created about a month ago·Updated about a month ago· 0
felixowens/ComfyUI-Pi-LLM
Nodes
On cloudLocal install
Stars0
Updatedabout a month ago
Readme

ComfyUI Pi LLM

ComfyUI custom nodes for calling Pi as a text and optional image LLM from a workflow.

The main node runs the local pi CLI in print mode and returns the model response as a ComfyUI STRING. It can optionally pass connected ComfyUI images to vision-capable Pi models. A companion extractor node helps remove common LLM wrapper text, such as XML tags or Markdown code fences.

Features

  • Call Pi from ComfyUI with a system instruction, model dropdown, prompt, and optional image input.
  • Defaults to minimax/MiniMax-M3.
  • Includes OpenRouter dropdown options for Grok, Kimi, GLM, and Gemini Flash/Flash Lite models.
  • LLM-only execution: tools, context files, and sessions are disabled for safer/predictable workflow behavior.
  • Deterministic local response cache keyed by LLM inputs and seed for hands-off reproducible reruns.
  • Reproducible saved-response mode for portable/manual freezing when needed.
  • Cache-busting seed input so the same prompt can be regenerated.
  • Optional run_every_queue mode to force reruns.
  • Extract generated text from <prompt>...</prompt>, code fences, or custom delimiters.
  • Compose seeded wildcard prompts from inline lists of likes, dislikes, characters, clothing, styles, and extras.

Requirements

  • ComfyUI.
  • Pi installed and available on the PATH used by the ComfyUI process.
  • Pi already authenticated/configured for the model provider you want to use.

Install Pi if needed:

npm install -g --ignore-scripts @earendil-works/pi-coding-agent

Then authenticate/configure Pi as usual, for example:

pi /login

Or use supported provider API key environment variables. See the Pi docs for provider setup.

Verify Pi works outside ComfyUI:

pi -p --no-tools --no-context-files --no-session "Say hello in five words."

Installation

From your ComfyUI directory:

cd custom_nodes
git clone https://github.com/felixowens/ComfyUI-Pi-LLM.git

Restart ComfyUI, then search for the Pi category.

If ComfyUI cannot find pi, make sure the shell/service that starts ComfyUI has Pi on its PATH. You can also symlink Pi into a common path, for example:

which pi
# then, if needed:
sudo ln -s /path/to/pi /usr/local/bin/pi

Nodes

Pi LLM Text

Category: Pi

Inputs:

  • system_instruction: system prompt passed to Pi via --system-prompt.
  • model_name: Pi model selector dropdown, passed via --model. Defaults to minimax/MiniMax-M3.
  • prompt: what the LLM should do or produce. Type text here or connect a STRING output from another node.
  • connected_text: optional extra STRING input appended after prompt when non-empty.
  • image: optional ComfyUI IMAGE input passed to Pi as a temporary PNG attachment. Use a vision-capable model.
  • response_mode:
    • use_saved_text_if_present default: use saved_response when non-empty, otherwise use the cache/Pi path.
    • call_pi: ignore saved_response and use the cache/Pi path.
    • use_saved_text: always output saved_response and never call Pi.
  • cache_mode:
    • use_cache_or_generate default: use the deterministic local cache when present, otherwise call Pi and save the response.
    • refresh_cache: call Pi and overwrite the cached response for this input/seed.
    • cache_only: only use the cache; error if missing.
    • disable_cache: call Pi without reading or writing cache.
  • saved_response: optional manual/portable freeze text. Local cache handles normal reproducibility without using this.
  • seed: part of the cache key. Change it to intentionally get/cache a new response for the same prompt.
  • timeout_seconds: maximum time to wait for Pi.
  • run_every_queue: when enabled, disables ComfyUI caching for this node.

Output:

  • text: generated response as a ComfyUI STRING.

Behavior:

pi -p --no-tools --no-context-files --no-session --system-prompt "..." --model "minimax/MiniMax-M3" "...prompt..."

# With optional image input, the node writes temporary PNGs and passes them before the prompt:
pi -p --no-tools --no-context-files --no-session --system-prompt "..." --model "openrouter/google/gemini-2.5-flash" @/tmp/image-0.png "...prompt..."

Tools, context files, and sessions are disabled so the node behaves like a text/image LLM call and avoids file/system side effects. The seed is used for ComfyUI cache invalidation; Pi does not currently receive it as a model sampling seed.

Reproducible reruns

By default, Pi LLM Text uses a deterministic local cache. The cache key includes the system instruction, model name, combined prompt text, optional image content digests, and seed. On first run it calls Pi and stores the response under cache/<sha256>.txt; on future runs with the same inputs and seed it returns that cached response without calling Pi.

This means dragging an image/workflow back into ComfyUI on the same machine should reproduce the same LLM text as long as the local cache directory is still present.

Use cache_mode = refresh_cache or change seed when you intentionally want a new LLM response. Use cache_mode = disable_cache if you always want live Pi calls.

saved_response remains available for manual/portable freezing when you want the exact text stored in the workflow/image metadata itself.

Pi Text Extractor

Extracts useful text from an LLM response when the model adds fluff around the generated content.

Inputs:

  • text: LLM response to extract from.
  • extraction_mode:
    • auto: try XML tag, then code fence, then custom delimiters.
    • xml_tag: extract from tags like <prompt>...</prompt>.
    • code_fence: extract from triple backticks like text ... .
    • between_delimiters: extract between custom start/end markers.
  • xml_tag: tag name for XML extraction, defaults to prompt.
  • fence_language: optional code fence language. Empty matches any code fence.
  • start_delimiter / end_delimiter: custom markers for delimiter extraction.
  • occurrence: choose the first or last match.
  • strip_whitespace: trims leading/trailing whitespace from extracted text.
  • fail_if_missing: raise an error if no wrapper is found. If disabled, returns the original text.

Outputs:

  • text: extracted text, or original text when not found and fail_if_missing is disabled.
  • found: boolean indicating whether an extraction wrapper was found.

Recommended LLM instruction examples:

Return only the final prompt inside <prompt>...</prompt> tags.

or:

Return only the final prompt in a ```text code fence.

Pi Wildcard Prompt

Seeded prompt composer for randomly injecting fragments into positive and negative prompts.

Inputs:

  • prompt_template: optional positive prompt template. Use placeholders like {base_prompt}, {characters}, {clothing}, {styles}, {likes}, {extra}, or {all_positive}. If blank, selected fragments are appended to base_prompt.
  • negative_template: optional negative prompt template. Use placeholders like {base_negative} and {dislikes}. If blank, selected dislikes are appended to base_negative.
  • base_prompt: base positive text. Used as {base_prompt} in templates, or included at the start when prompt_template is blank.
  • base_negative: base negative text. Used as {base_negative} in templates, or included at the start when negative_template is blank.
  • likes: positive fragments you like, one per line.
  • dislikes: negative fragments, one per line.
  • characters: character/subject options, one per line.
  • clothing: clothing/accessory options, one per line.
  • styles: visual/style options, one per line.
  • extra: any other fragments, one per line.
  • seed: deterministic random seed. Same inputs + same seed produce the same selections.
  • *_count: how many entries to select from each category. Counts larger than the list are clamped safely.
  • separator: comma, space, or newline.
  • dedupe: removes duplicate fragments while preserving order.
  • shuffle_positive: shuffles the selected positive fragments while keeping base_prompt first.

Template placeholders:

  • {base_prompt}
  • {base_negative}
  • {likes}
  • {dislikes}
  • {characters}
  • {clothing}
  • {styles}
  • {extra}
  • {all_positive}: base prompt plus all selected positive categories.

Example prompt_template:

{base_prompt} of {characters}, wearing {clothing}, {styles}, {likes}, {extra}

Example negative_template:

{base_negative}, {dislikes}

Outputs:

  • positive_prompt: composed positive prompt.
  • negative_prompt: composed negative prompt from template/base negative plus selected dislikes.
  • selected: readable debug text showing exactly what was selected for the seed.

List parsing rules:

  • Blank lines are ignored.
  • Lines starting with # are ignored as comments.

Example styles list:

# lighting/style options
cinematic lighting
soft watercolor illustration
retro anime screencap
35mm film still

Model list

The dropdown is currently a static list generated from pi --list-models on the original development machine. It includes selected OpenRouter models such as openrouter/x-ai/grok-4.3, openrouter/~moonshotai/kimi-latest, openrouter/z-ai/glm-4.7-flash, openrouter/~google/gemini-flash-latest, and openrouter/google/gemini-2.5-flash-lite.

If your Pi installation has different model access, edit models.py and update AVAILABLE_MODELS.

Development

Run syntax checks and unit tests from this repository:

python -m py_compile __init__.py nodes.py models.py pi_runner.py extractors.py wildcards.py
python -m unittest discover -s tests -v

The ComfyUI node classes in nodes.py are intentionally thin adapters. Most behavior lives in pure modules:

  • pi_runner.py: prompt joining, saved-response mode decisions, deterministic response caching, Pi command construction, and Pi subprocess execution.
  • image_inputs.py: conversion of optional ComfyUI IMAGE inputs into temporary PNG attachments and cache digests.
  • extractors.py: XML/code-fence/delimiter extraction.
  • wildcards.py: deterministic seeded wildcard prompt composition.
  • models.py: static model dropdown data.

License

No license has been added yet.