From a04d1eae2b9e4bf27853dd0aad329ed0f4bbf8d1 Mon Sep 17 00:00:00 2001 From: KingManiya Date: Thu, 23 Apr 2026 19:20:32 +0400 Subject: [PATCH] feat: add support batch images. v1.3.0 --- README.md | 13 ++++++++----- llama_cli.py | 23 +++++++++++++---------- nodes.py | 8 ++++---- pyproject.toml | 2 +- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index f14926e..a7195b4 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,16 @@ Qwen3-VL, Qwen3.5, Qwen3.6, Gemma 4, and gpt-oss. This extension adds a local LLM node for prompt writing, prompt rewriting, translation, captioning, extraction, and other text processing tasks inside ComfyUI. It discovers local GGUF models from `ComfyUI/models/LLM`, and it can -also accept an image for multimodal models that use an external `mmproj`. +also accept a single image or a ComfyUI image batch for multimodal models that +use an external `mmproj`. ![Node UI](https://raw.githubusercontent.com/KingManiya/ComfyUI-LLM-text-processor/refs/heads/images/images/node.png) ## Features - Text generation and text transformation with local GGUF models -- Optional image input for multimodal llama.cpp models that use `mmproj` +- Optional image input for multimodal llama.cpp models that use `mmproj`, + including multi-image batches - Separate `RESPONSE` and `REASONING` outputs - System prompt presets from text files - Recursive model discovery from `ComfyUI/models/LLM` @@ -106,7 +108,8 @@ The `model` dropdown shows model `.gguf` files. The `mmproj` dropdown shows vision projector files and `none`. For image workflows, choose the `mmproj` file that belongs to the selected -model. The node uses only the first image from a ComfyUI image batch. +model. You can connect either one image or a ComfyUI image batch; batch items +are sent together in the same llama.cpp request. ## System Prompt Presets @@ -204,7 +207,7 @@ Reference: [gpt-oss docs](https://github.com/openai/gpt-oss) | Input | Description | | --- | --- | | `model` | GGUF model file from `ComfyUI/models/LLM`. | -| `mmproj` | Vision projector GGUF. Required when using image input. | +| `mmproj` | Vision projector GGUF. Required when using image input, whether it is one image or a batch. | | `system_prompt` | Prompt preset from `models/LLM/prompts`, or `none`. | | `prompt` | User prompt sent to the selected model. | | `max_tokens` | Maximum generated tokens. | @@ -219,7 +222,7 @@ Reference: [gpt-oss docs](https://github.com/openai/gpt-oss) | `seed` | Random seed. Use `-1` for a random seed. | | `timeout_seconds` | Maximum runtime before generation is stopped. | | `reasoning` | Reasoning output mode: `auto`, `on`, or `off`. | -| `image` | Optional ComfyUI image input. Uses the first image in a batch. | +| `image` | Optional image input. Accepts a single image or a ComfyUI batch, and sends the batch together to llama.cpp. | | `enable_processing` | When enabled, the node runs normally. When disabled, the node forwards `prompt` directly to `RESPONSE` and skips all model checks and llama.cpp execution. | | `extra_args` | Optional advanced llama.cpp parameters. Leave empty for normal use. | diff --git a/llama_cli.py b/llama_cli.py index 3424e3f..64c1bab 100644 --- a/llama_cli.py +++ b/llama_cli.py @@ -27,14 +27,10 @@ LLAMA_SEED_MODULUS = 2**32 MAX_LLAMA_SEED = LLAMA_SEED_MODULUS - 1 -def tensor_to_temp_png(image) -> Path: +def _tensor_to_temp_png(tensor) -> Path: import numpy as np from PIL import Image - # ComfyUI IMAGE is normally B,H,W,C. The node uses the first batch item in v1. - tensor = image - if hasattr(tensor, "dim") and tensor.dim() == 4: - tensor = tensor[0] array = (tensor.detach().cpu().numpy() * 255).clip(0, 255).astype(np.uint8) pil_image = Image.fromarray(array) fd, path = tempfile.mkstemp(prefix="llm-text-processor-", suffix=".png") @@ -43,6 +39,13 @@ def tensor_to_temp_png(image) -> Path: return Path(path) +def tensor_to_temp_pngs(image) -> list[Path]: + # ComfyUI IMAGE is normally B,H,W,C. Each batch item becomes one CLI image. + if hasattr(image, "dim") and image.dim() == 4: + return [_tensor_to_temp_png(tensor) for tensor in image] + return [_tensor_to_temp_png(image)] + + def _write_temp_text_file(prefix: str, text: str) -> Path: fd, path = tempfile.mkstemp(prefix=prefix, suffix=".txt") os.close(fd) @@ -92,12 +95,12 @@ def build_command( ) -> tuple[list[str], tuple[Path | None, ...]]: cleanup_paths = [] cli_paths = ensure_llama_cli_paths() - image_path = None + image_paths = [] if image is not None: if mmproj_path is None: raise ValueError("Image input requires a selected mmproj GGUF file.") - image_path = tensor_to_temp_png(image) - cleanup_paths.append(image_path) + image_paths = tensor_to_temp_pngs(image) + cleanup_paths.extend(image_paths) prompt_path = _write_prompt_file(prompt) cleanup_paths.append(prompt_path) @@ -128,9 +131,9 @@ def build_command( command.extend(["-f", str(prompt_path)]) - if image_path: + if image_paths: command.extend(["--mmproj", str(mmproj_path)]) - command.extend(["--image", str(image_path)]) + command.extend(["--image", ",".join(str(path) for path in image_paths)]) if extra_args: command.extend(extra_args) diff --git a/nodes.py b/nodes.py index 9dbe64f..f852525 100644 --- a/nodes.py +++ b/nodes.py @@ -27,7 +27,7 @@ class LLMTextProcessor: }), "mmproj": (mmproj_options(), { "default": NO_MMPROJ, - "tooltip": "Vision projector GGUF. Required when an image is connected.", + "tooltip": "Vision projector GGUF. Required when one or more images are connected.", }), "system_prompt": (system_prompt_options(), { "tooltip": "System prompt preset from ComfyUI/models/LLM/prompts, or none.", @@ -74,9 +74,9 @@ class LLMTextProcessor: "ctx_size": ("INT", { "default": 8192, "min": 512, - "max": 131072, + "max": 1048576, "step": 512, - "tooltip": "Context window size in tokens.", + "tooltip": "Context window size in tokens. Use a value supported by the selected GGUF; larger context uses more VRAM.", }), "memory_mode": ([ "auto", @@ -121,7 +121,7 @@ class LLMTextProcessor: }, "optional": { "image": ("IMAGE", { - "tooltip": "Optional ComfyUI image input. Uses the first image in the batch.", + "tooltip": "Optional image input. A single image or ComfyUI batch is passed to llama.cpp.", }), "enable_processing": ("BOOLEAN", { "default": True, diff --git a/pyproject.toml b/pyproject.toml index 59b29f2..dcbd3d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "ComfyUI-LLM-text-processor" description = "Process text and images with GGUF LLMs in ComfyUI using llama.cpp, including Qwen3-VL, Qwen3.5, Qwen3.6, Gemma 4, and gpt-oss." -version = "1.2.0" +version = "1.3.0" license = {file = "LICENSE"} dependencies = ["numpy", "Pillow"]