239 lines
9.6 KiB
Python
239 lines
9.6 KiB
Python
from transformers import AutoTokenizer, AutoModelForCausalLM, GenerationConfig, LogitsProcessorList
|
|
from transformers.generation.logits_process import UnbatchedClassifierFreeGuidanceLogitsProcessor
|
|
import comfy
|
|
import torch
|
|
import re
|
|
from comfy_api.v0_0_2 import io
|
|
from ... import ROOT_NAME, NODE_SURFIX, SYMBOL
|
|
|
|
CATEGORY_NAME = ROOT_NAME + "dart"
|
|
|
|
class LoadDart(io.ComfyNode):
|
|
@classmethod
|
|
def define_schema(cls) -> io.Schema:
|
|
return io.Schema(
|
|
node_id=f"LoadDart{NODE_SURFIX}",
|
|
display_name=f"Load Dart {SYMBOL}",
|
|
category=CATEGORY_NAME,
|
|
inputs=[
|
|
io.String.Input("tokenizer", default="p1atdev/dart-v1-sft"),
|
|
io.String.Input("model", default="p1atdev/dart-v1-sft"),
|
|
],
|
|
outputs=[
|
|
io.Custom("DART_TOKENIZER").Output(),
|
|
io.Custom("DART_MODEL").Output(),
|
|
],
|
|
)
|
|
|
|
@classmethod
|
|
def execute(cls, tokenizer, model) -> io.NodeOutput:
|
|
tokenizer = AutoTokenizer.from_pretrained(tokenizer, trust_remote_code=True)
|
|
model = AutoModelForCausalLM.from_pretrained(model, trust_remote_code=True)
|
|
return io.NodeOutput(tokenizer, model)
|
|
|
|
class DartPrompt(io.ComfyNode):
|
|
@classmethod
|
|
def define_schema(cls) -> io.Schema:
|
|
return io.Schema(
|
|
node_id=f"DartPrompt{NODE_SURFIX}",
|
|
display_name=f"Dart Prompt {SYMBOL}",
|
|
category=CATEGORY_NAME,
|
|
inputs=[
|
|
io.Combo.Input("rating", options=["general", "sensitive", "questionable", "explicit", "sfw", "nsfw"]),
|
|
io.String.Input("copyright", default="original"),
|
|
io.String.Input("character", default=""),
|
|
io.String.Input("general", multiline=True),
|
|
io.Combo.Input("long", options=["very_short", "short", "long", "very_long"], default="long"),
|
|
],
|
|
outputs=[
|
|
io.String.Output(),
|
|
],
|
|
)
|
|
|
|
@classmethod
|
|
def execute(cls, rating, copyright, character, general, long) -> io.NodeOutput:
|
|
prompt = "<|bos|>"
|
|
prompt += f"<rating>rating:{rating}</rating>"
|
|
prompt += f"<copylight>{copyright}</copyright>"
|
|
prompt += f"<character>{character}</character>"
|
|
prompt += "<general>" + f"<|{long}|>"
|
|
prompt += f"{general}"
|
|
prompt += "<|input_end|>"
|
|
|
|
return io.NodeOutput(prompt)
|
|
|
|
class DartPromptV2(io.ComfyNode):
|
|
@classmethod
|
|
def define_schema(cls) -> io.Schema:
|
|
return io.Schema(
|
|
node_id=f"DartPromptV2{NODE_SURFIX}",
|
|
display_name=f"Dart Prompt V2 {SYMBOL}",
|
|
category=CATEGORY_NAME,
|
|
inputs=[
|
|
io.Combo.Input("rating", options=["general", "sensitive", "questionable", "explicit", "sfw", "nsfw"]),
|
|
io.String.Input("copyright", default="original"),
|
|
io.String.Input("character", default=""),
|
|
io.String.Input("general", multiline=True),
|
|
io.Combo.Input("aspect_ratio", options=["ultra_wide", "wide", "square", "tall", "ultra_tall"], default="tall"),
|
|
io.Combo.Input("length", options=["very_short", "short", "medium", "long", "very_long"], default="medium"),
|
|
io.Combo.Input("identity", options=["none", "lax", "strict"], default="none"),
|
|
],
|
|
outputs=[
|
|
io.String.Output(),
|
|
],
|
|
)
|
|
|
|
@classmethod
|
|
def execute(cls, rating, copyright, character, general, aspect_ratio, length, identity) -> io.NodeOutput:
|
|
prompt = "<|bos|>"
|
|
prompt += f"<copylight>{copyright}</copyright>"
|
|
prompt += f"<character>{character}</character>"
|
|
prompt += f"<|rating:{rating}|>" + f"<|aspect_ratio:{aspect_ratio}|>" + f"<|length:{length}|>" + f"<|identity:{identity}|>"
|
|
prompt += f"<general>{general}<|identity:{identity}|><|input_end|>"
|
|
|
|
return io.NodeOutput(prompt)
|
|
|
|
class DartConfig(io.ComfyNode):
|
|
@classmethod
|
|
def define_schema(cls) -> io.Schema:
|
|
return io.Schema(
|
|
node_id=f"DartConfig{NODE_SURFIX}",
|
|
display_name=f"Dart Config {SYMBOL}",
|
|
category=CATEGORY_NAME,
|
|
inputs=[
|
|
io.Int.Input("max_new_tokens", default=128, min=1, max=256, step=1),
|
|
io.Int.Input("min_new_tokens", default=0, min=0, max=255, step=1),
|
|
io.Float.Input("temperature", default=1.0, min=0.0, max=5.0, step=0.01),
|
|
io.Float.Input("top_p", default=1.0, min=0.0, max=1.0, step=0.01),
|
|
io.Int.Input("top_k", default=20, min=1, max=500, step=1),
|
|
io.Int.Input("num_beams", default=1, min=1, max=10, step=1),
|
|
io.Float.Input("cfg_scale", default=1.0, min=0.0, max=10.0, step=0.01),
|
|
],
|
|
outputs=[
|
|
io.Custom("DART_CONFIG").Output(),
|
|
],
|
|
)
|
|
|
|
@classmethod
|
|
def execute(cls, max_new_tokens, min_new_tokens, temperature, top_p, top_k, num_beams, cfg_scale) -> io.NodeOutput:
|
|
kwargs = {
|
|
"max_new_tokens": max_new_tokens,
|
|
"min_new_tokens": min_new_tokens,
|
|
"temperature": temperature,
|
|
"top_p": top_p,
|
|
"top_k": top_k,
|
|
"num_beams": num_beams,
|
|
"cfg_scale": cfg_scale,
|
|
}
|
|
kwargs["temperature"] = float(kwargs["temperature"]) # avoid error
|
|
return io.NodeOutput(kwargs)
|
|
|
|
class BanTags(io.ComfyNode):
|
|
@classmethod
|
|
def define_schema(cls) -> io.Schema:
|
|
return io.Schema(
|
|
node_id=f"BanTags{NODE_SURFIX}",
|
|
display_name=f"Ban Tags {SYMBOL}",
|
|
category=CATEGORY_NAME,
|
|
inputs=[
|
|
io.Custom("DART_TOKENIZER").Input("tokenizer"),
|
|
io.String.Input("ban_tags", multiline=True),
|
|
],
|
|
outputs=[
|
|
io.String.Output(),
|
|
],
|
|
)
|
|
|
|
@classmethod
|
|
def execute(cls, tokenizer, ban_tags) -> io.NodeOutput:
|
|
ban_tags_result = set()
|
|
patterns = [re.compile(ban_tag) for ban_tag in ban_tags.splitlines()]
|
|
for pattern in patterns:
|
|
for tag in tokenizer.vocab:
|
|
if pattern.match(tag):
|
|
ban_tags_result.add(tag)
|
|
return io.NodeOutput(", ".join(ban_tags_result))
|
|
|
|
class DartGenerate(io.ComfyNode):
|
|
@classmethod
|
|
def define_schema(cls) -> io.Schema:
|
|
return io.Schema(
|
|
node_id=f"DartGenerate{NODE_SURFIX}",
|
|
display_name=f"Dart Generate {SYMBOL}",
|
|
category=CATEGORY_NAME,
|
|
inputs=[
|
|
io.Custom("DART_TOKENIZER").Input("tokenizer"),
|
|
io.Custom("DART_MODEL").Input("model"),
|
|
io.String.Input("prompt", default=""),
|
|
io.Int.Input("batch_size", default=1, min=1, max=4096),
|
|
io.Int.Input("seed", default=0, min=0, max=0xffffffffffffffff),
|
|
io.Custom("DART_CONFIG").Input("config", optional=True),
|
|
io.String.Input("negative", default="", optional=True),
|
|
io.String.Input("ban_tags", default="", optional=True),
|
|
],
|
|
outputs=[
|
|
io.Custom("BATCH_STRING").Output(),
|
|
io.String.Output(),
|
|
],
|
|
)
|
|
|
|
@classmethod
|
|
def execute(cls, tokenizer, model, prompt, batch_size, seed, config=None, negative=None, ban_tags=None) -> io.NodeOutput:
|
|
if config:
|
|
config = config
|
|
else:
|
|
config = {
|
|
"max_new_tokens": 128,
|
|
"min_new_tokens": 0,
|
|
"temperature": 1.0,
|
|
"top_p": 1.0,
|
|
"top_k": 100,
|
|
"num_beams": 1,
|
|
}
|
|
|
|
rng_state = torch.get_rng_state()
|
|
cuda_rng_state = torch.cuda.get_rng_state()
|
|
|
|
if seed is not None:
|
|
torch.manual_seed(seed)
|
|
torch.cuda.manual_seed(seed)
|
|
|
|
generation_config = GenerationConfig.from_pretrained("p1atdev/dart-v1-sft", **config) # こんなんでいいの?
|
|
model.to(comfy.model_management.get_torch_device(), dtype=torch.float16).eval()
|
|
inputs = tokenizer([prompt], return_tensors="pt").input_ids.to(comfy.model_management.get_torch_device()).repeat(batch_size, 1)
|
|
if config["cfg_scale"] != 1.0:
|
|
negative_inputs = tokenizer([negative], return_tensors="pt").input_ids.to(comfy.model_management.get_torch_device()).repeat(batch_size, 1)
|
|
loggits_processor = LogitsProcessorList([
|
|
UnbatchedClassifierFreeGuidanceLogitsProcessor(
|
|
guidance_scale=config["cfg_scale"],
|
|
model=model,
|
|
unconditional_ids=negative_inputs,
|
|
)
|
|
])
|
|
else:
|
|
loggits_processor = None
|
|
|
|
if ban_tags:
|
|
ban_tags_ids = tokenizer([ban_tags]).input_ids
|
|
bad_words_ids = [[token_id] for token_id in ban_tags_ids[0]]
|
|
else:
|
|
bad_words_ids = None
|
|
|
|
with torch.no_grad():
|
|
outputs = model.generate(inputs, generation_config=generation_config, bad_words_ids=bad_words_ids, logits_processor=loggits_processor)
|
|
|
|
prompts = [", ".join([tag for tag in tokenizer.batch_decode(output, skip_special_tokens=True) if tag.strip() != ""]) for output in outputs]
|
|
|
|
if "rating:" in prompts[0]:
|
|
# delete rating
|
|
prompts = [", ".join(prompt.split(", ")[1:]) for prompt in prompts]
|
|
|
|
strings = "" # for checking
|
|
for i, prompt in enumerate(prompts):
|
|
strings += f"Prompt {i + 1}:\n{prompt}\n\n"
|
|
|
|
torch.set_rng_state(rng_state)
|
|
torch.cuda.set_rng_state(cuda_rng_state)
|
|
|
|
return io.NodeOutput(prompts, strings)
|