171 lines
6.0 KiB
Python
171 lines
6.0 KiB
Python
import requests
|
|
import re
|
|
import comfy.model_management as model_management
|
|
import lmstudio as lms
|
|
from torchvision.transforms.functional import to_pil_image
|
|
from io import BytesIO
|
|
from tempfile import NamedTemporaryFile
|
|
import torchvision.transforms as T
|
|
import folder_paths
|
|
from pathlib import Path
|
|
|
|
SCRIPT_DIR = Path(__file__).parent
|
|
folder_paths.add_model_folder_path(
|
|
"lms_config", (SCRIPT_DIR / "lms_config").as_posix())
|
|
|
|
|
|
class YANCLMSTUDIO:
|
|
def __init__(self):
|
|
pass
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"prompt": ("STRING", {"forceInput": True}),
|
|
"model_identifier": ("STRING", {"default": ""}),
|
|
"draft_model": ("STRING", {"default": ""}),
|
|
"system_message": ("STRING", {
|
|
"multiline": True,
|
|
"default": "You are an AI assistant specialized in generating detailed and creative image prompts for AI image generation. Your task is to expand a given user prompt into a well-structured, vivid, and highly descriptive prompt while ensuring that all terms from the original prompt are included. Enhance the visual quality and artistic impact by adding relevant details, but do not omit or alter any key elements provided by the user. Follow the given instructions or guidelines and respond only with the refined prompt."
|
|
}),
|
|
"reasoning_tag": ("STRING", {"default": "think"}),
|
|
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
|
"ip": ("STRING", {"default": "localhost"}),
|
|
"port": ("INT", {"default": 1234}),
|
|
"temperature": ("FLOAT", {"default": 0.7, "min": 0.01, "max": 1.0, "step": 0.01}),
|
|
"max_tokens": ("INT", {"default": 600, "min": -1, "max": 0xffffffffffffffff}),
|
|
"unload_llm": ("BOOLEAN", {"default": False}),
|
|
"unload_comfy_models": ("BOOLEAN", {"default": False})
|
|
},
|
|
"optional":
|
|
{
|
|
"image": ("IMAGE", {"forceInput": True})
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING", "STRING")
|
|
RETURN_NAMES = ("Extended Prompt", "Reasoning")
|
|
|
|
OUTPUT_NODE = False
|
|
|
|
FUNCTION = "do_it"
|
|
|
|
CATEGORY = "YANC/😼 LMStudio"
|
|
|
|
def do_it(self, prompt, model_identifier, draft_model, system_message, reasoning_tag, seed, ip, port, temperature, max_tokens, unload_llm, unload_comfy_models, image=None):
|
|
|
|
if unload_comfy_models:
|
|
model_management.unload_all_models()
|
|
model_management.soft_empty_cache(True)
|
|
|
|
server_api_host = f"{ip}:{port}"
|
|
|
|
result = ""
|
|
reasoning = ""
|
|
|
|
with lms.Client(server_api_host) as client:
|
|
|
|
model = client.llm.model(model_identifier)
|
|
|
|
if image is None:
|
|
try:
|
|
chat = lms.Chat(system_message)
|
|
chat.add_user_message(prompt)
|
|
|
|
content = str(model.respond(chat, config={
|
|
"temperature": temperature,
|
|
"maxTokens": max_tokens,
|
|
"draftModel": draft_model
|
|
}))
|
|
except:
|
|
print(
|
|
"Prediction error: Trying alternative approach to prevent prediction error based on wrong template type.")
|
|
chat = lms.Chat()
|
|
chat.add_user_message(
|
|
f"{system_message}: User input: {prompt}")
|
|
|
|
content = str(model.respond(chat, config={
|
|
"temperature": temperature,
|
|
"maxTokens": max_tokens,
|
|
"draftModel": draft_model
|
|
}))
|
|
|
|
# Separate the reasoning block.
|
|
result = re.sub(rf"<{reasoning_tag}>.*?</{reasoning_tag}>", "",
|
|
content, flags=re.DOTALL).strip()
|
|
reasoning = re.sub(
|
|
rf".*<{reasoning_tag}>(.*?)</{reasoning_tag}>.*", r"\1", content, flags=re.DOTALL).strip()
|
|
else:
|
|
info = model.get_info()
|
|
if not info.vision:
|
|
if unload_llm:
|
|
model.unload()
|
|
|
|
raise Exception(
|
|
"The loaded model is not vision enabled. Please try another model.")
|
|
|
|
image_new = image.squeeze(0).permute(2, 0, 1)
|
|
image_pil = to_pil_image(image_new)
|
|
|
|
with NamedTemporaryFile(suffix=".jpg", delete=False) as temp:
|
|
image_pil.save(temp, format="JPEG")
|
|
temp.flush()
|
|
image_handle = client.files.prepare_image(temp.name)
|
|
|
|
chat = lms.Chat()
|
|
chat.add_user_message(
|
|
prompt, images=[image_handle])
|
|
result = model.respond(chat)
|
|
reasoning = result
|
|
|
|
if unload_llm:
|
|
model.unload()
|
|
|
|
return (result, reasoning,)
|
|
|
|
|
|
class YANCSELECTLMSMODEL:
|
|
def __init__(self):
|
|
pass
|
|
|
|
@classmethod
|
|
def get_models(cls, id="models.txt"):
|
|
file_path = folder_paths.get_full_path("lms_config", id)
|
|
with open(file_path, "r", encoding="utf-8") as f:
|
|
return [line.strip() for line in f if line.strip()]
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"model_id": (
|
|
s.get_models(),
|
|
{"tooltip": "Add your favorite model names to the models.txt file in custom_nodes\\YANC_LMStudio\\lms_config\\"}
|
|
)
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING",)
|
|
RETURN_NAMES = ("model_id",)
|
|
|
|
OUTPUT_NODE = False
|
|
|
|
FUNCTION = "do_it"
|
|
|
|
CATEGORY = "YANC/😼 LMStudio"
|
|
|
|
def do_it(self, model_id):
|
|
return (model_id,)
|
|
|
|
|
|
NODE_CLASS_MAPPINGS = {
|
|
"> LMStudio": YANCLMSTUDIO,
|
|
"> Select LMS Model": YANCSELECTLMSMODEL
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"> LMStudio": "😼> LMStudio",
|
|
"> Select LMS Model": "😼> Select LMS Model"
|
|
}
|