231 lines
8.0 KiB
Python
231 lines
8.0 KiB
Python
from __future__ import annotations
|
|
|
|
from .folder_registry import (
|
|
NO_MMPROJ,
|
|
full_mmproj_path,
|
|
full_model_path,
|
|
full_system_prompt_path,
|
|
mmproj_options,
|
|
model_options,
|
|
system_prompt_options,
|
|
)
|
|
from .llama_cli import (
|
|
MAX_LLAMA_SEED,
|
|
build_command,
|
|
run_llama_cli,
|
|
split_extra_args,
|
|
)
|
|
|
|
|
|
class LLMTextProcessor:
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"model": (model_options(), {
|
|
"tooltip": "GGUF model loaded from ComfyUI/models/LLM. mmproj files are hidden from this list.",
|
|
}),
|
|
"mmproj": (mmproj_options(), {
|
|
"default": NO_MMPROJ,
|
|
"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.",
|
|
}),
|
|
"prompt": ("STRING", {
|
|
"default": "Describe this image in detail.",
|
|
"multiline": True,
|
|
"dynamicPrompts": True,
|
|
"tooltip": "User prompt sent to the selected model.",
|
|
}),
|
|
"max_tokens": ("INT", {
|
|
"default": 2048,
|
|
"min": 1,
|
|
"max": 32768,
|
|
"tooltip": "Maximum number of tokens to generate.",
|
|
}),
|
|
"temperature": ("FLOAT", {
|
|
"default": 0.7,
|
|
"min": 0.0,
|
|
"max": 2.0,
|
|
"step": 0.05,
|
|
"tooltip": "Sampling temperature. Lower is more deterministic.",
|
|
}),
|
|
"top_p": ("FLOAT", {
|
|
"default": 0.8,
|
|
"min": 0.0,
|
|
"max": 1.0,
|
|
"step": 0.01,
|
|
"tooltip": "Nucleus sampling threshold.",
|
|
}),
|
|
"top_k": ("INT", {
|
|
"default": 20,
|
|
"min": 1,
|
|
"max": 1000,
|
|
"tooltip": "Top-K sampling cutoff.",
|
|
}),
|
|
"repeat_penalty": ("FLOAT", {
|
|
"default": 1.0,
|
|
"min": 0.0,
|
|
"max": 3.0,
|
|
"step": 0.01,
|
|
"tooltip": "Penalty applied to repeated tokens.",
|
|
}),
|
|
"ctx_size": ("INT", {
|
|
"default": 8192,
|
|
"min": 512,
|
|
"max": 1048576,
|
|
"step": 512,
|
|
"tooltip": "Context window size in tokens. Use a value supported by the selected GGUF; larger context uses more VRAM.",
|
|
}),
|
|
"memory_mode": ([
|
|
"auto",
|
|
"gpu_layers",
|
|
"cpu_moe_layers",
|
|
"gpu_and_cpu_moe_layers",
|
|
], {
|
|
"default": "auto",
|
|
"tooltip": "Advanced memory placement mode: auto, gpu_layers, cpu_moe_layers, or gpu_and_cpu_moe_layers.",
|
|
"advanced": True,
|
|
}),
|
|
"n_gpu_layers": ("INT", {
|
|
"default": 99,
|
|
"min": -1,
|
|
"max": 999,
|
|
"tooltip": "Used only in gpu_layers and gpu_and_cpu_moe_layers modes. Number of model layers to place on the GPU.",
|
|
"advanced": True,
|
|
}),
|
|
"n_cpu_moe_layers": ("INT", {
|
|
"default": 1,
|
|
"min": 1,
|
|
"max": 999,
|
|
"tooltip": "Used only in cpu_moe_layers and gpu_and_cpu_moe_layers modes. Number of MoE layers to keep on the CPU.",
|
|
"advanced": True,
|
|
}),
|
|
"seed": ("INT", {
|
|
"default": 1,
|
|
"min": -1,
|
|
"max": MAX_LLAMA_SEED,
|
|
"tooltip": "Random seed. Use -1 for a random seed.",
|
|
}),
|
|
"timeout_seconds": ("INT", {
|
|
"default": 300,
|
|
"min": 10,
|
|
"max": 3600,
|
|
"tooltip": "Maximum time to wait before generation is stopped.",
|
|
}),
|
|
"reasoning": (["auto", "on", "off"], {
|
|
"default": "off",
|
|
"tooltip": "Reasoning output mode.",
|
|
}),
|
|
},
|
|
"optional": {
|
|
"image": ("IMAGE", {
|
|
"tooltip": "Optional image input. A single image or ComfyUI batch is passed to llama.cpp.",
|
|
}),
|
|
"enable_processing": ("BOOLEAN", {
|
|
"default": True,
|
|
"tooltip": "When enabled, run normal node processing. When disabled, forward the input prompt directly as RESPONSE.",
|
|
"advanced": True,
|
|
}),
|
|
"extra_args": ("STRING", {
|
|
"default": "",
|
|
"multiline": False,
|
|
"tooltip": "Optional advanced llama.cpp parameters. Leave empty for normal use.",
|
|
}),
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING", "STRING", "STRING")
|
|
RETURN_NAMES = ("RESPONSE", "REASONING", "PERF")
|
|
OUTPUT_TOOLTIPS = (
|
|
"Final model response with reasoning blocks removed.",
|
|
"Extracted reasoning when present in model output.",
|
|
"llama.cpp prompt and generation speed.",
|
|
)
|
|
FUNCTION = "generate"
|
|
CATEGORY = "LLM Text Processor"
|
|
TITLE = "LLM Text Processor"
|
|
|
|
@classmethod
|
|
def VALIDATE_INPUTS(
|
|
cls,
|
|
model,
|
|
mmproj,
|
|
system_prompt,
|
|
):
|
|
return True
|
|
|
|
def generate(
|
|
self,
|
|
model: str,
|
|
mmproj: str,
|
|
system_prompt: str,
|
|
prompt: str,
|
|
max_tokens: int,
|
|
temperature: float,
|
|
top_p: float,
|
|
top_k: int,
|
|
repeat_penalty: float,
|
|
ctx_size: int,
|
|
memory_mode: str,
|
|
n_gpu_layers: int,
|
|
n_cpu_moe_layers: int,
|
|
seed: int,
|
|
timeout_seconds: int,
|
|
reasoning: str,
|
|
image=None,
|
|
enable_processing: bool = True,
|
|
extra_args: str = "",
|
|
):
|
|
if not enable_processing:
|
|
return (prompt, "", "")
|
|
|
|
current_models = model_options()
|
|
current_mmprojs = mmproj_options()
|
|
current_system_prompts = system_prompt_options()
|
|
|
|
if model not in current_models:
|
|
raise ValueError(f"Model not found: {model}")
|
|
|
|
if mmproj not in current_mmprojs:
|
|
raise ValueError(f"mmproj not found: {mmproj}")
|
|
|
|
if system_prompt not in current_system_prompts:
|
|
raise ValueError(f"System prompt preset not found: {system_prompt}")
|
|
|
|
model_path = full_model_path(model)
|
|
mmproj_path = full_mmproj_path(mmproj)
|
|
system_prompt_path = full_system_prompt_path(system_prompt)
|
|
parsed_extra_args = split_extra_args(extra_args)
|
|
|
|
command, cleanup_paths = build_command(
|
|
model_path=model_path,
|
|
mmproj_path=mmproj_path,
|
|
system_prompt_path=system_prompt_path,
|
|
image=image,
|
|
prompt=prompt,
|
|
max_tokens=max_tokens,
|
|
temperature=temperature,
|
|
top_p=top_p,
|
|
top_k=top_k,
|
|
repeat_penalty=repeat_penalty,
|
|
ctx_size=ctx_size,
|
|
memory_mode=memory_mode,
|
|
n_gpu_layers=n_gpu_layers,
|
|
n_cpu_moe_layers=n_cpu_moe_layers,
|
|
seed=seed,
|
|
reasoning=reasoning,
|
|
extra_args=parsed_extra_args,
|
|
)
|
|
response, reasoning_text, perf = run_llama_cli(
|
|
command=command,
|
|
timeout_seconds=timeout_seconds,
|
|
cleanup_paths=cleanup_paths,
|
|
)
|
|
return (response, reasoning_text, perf)
|
|
|
|
|
|
NODE_CLASS_MAPPINGS = {"LLMTextProcessor": LLMTextProcessor}
|
|
NODE_DISPLAY_NAME_MAPPINGS = {"LLMTextProcessor": "LLM Text Processor"}
|