mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-07-10 00:17:16 +08:00
Merge branch '20260623a_text_overlay' of https://github.com/drozbay/ComfyUI into 20260623a_text_overlay
This commit is contained in:
commit
af82f1bfd4
@ -4,12 +4,12 @@ early_access: false
|
|||||||
tone_instructions: "Only comment on issues introduced by this PR's changes. Do not flag pre-existing problems in moved, re-indented, or reformatted code."
|
tone_instructions: "Only comment on issues introduced by this PR's changes. Do not flag pre-existing problems in moved, re-indented, or reformatted code."
|
||||||
|
|
||||||
reviews:
|
reviews:
|
||||||
profile: "chill"
|
profile: "assertive"
|
||||||
request_changes_workflow: false
|
request_changes_workflow: true
|
||||||
high_level_summary: false
|
high_level_summary: false
|
||||||
poem: false
|
poem: false
|
||||||
review_status: false
|
review_status: false
|
||||||
review_details: false
|
review_details: true
|
||||||
commit_status: true
|
commit_status: true
|
||||||
collapse_walkthrough: true
|
collapse_walkthrough: true
|
||||||
changed_files_summary: false
|
changed_files_summary: false
|
||||||
@ -39,6 +39,14 @@ reviews:
|
|||||||
- path: "**"
|
- path: "**"
|
||||||
instructions: |
|
instructions: |
|
||||||
IMPORTANT: Only comment on issues directly introduced by this PR's code changes.
|
IMPORTANT: Only comment on issues directly introduced by this PR's code changes.
|
||||||
|
Treat AGENTS.md as mandatory repository policy, not optional style guidance.
|
||||||
|
Flag PR changes that violate AGENTS.md even when the code is otherwise functional.
|
||||||
|
In particular, enforce architecture boundaries, dtype/device/memory rules,
|
||||||
|
interface contracts, import style, no unnecessary try/except blocks, no inline
|
||||||
|
imports, no outbound internet paths in core ComfyUI, and narrow scoped fixes.
|
||||||
|
Prefer direct findings over suggestions when a rule is violated. Only ignore
|
||||||
|
AGENTS.md when it clearly conflicts with a newer explicit maintainer instruction
|
||||||
|
in the PR.
|
||||||
Do NOT flag pre-existing issues in code that was merely moved, re-indented,
|
Do NOT flag pre-existing issues in code that was merely moved, re-indented,
|
||||||
de-indented, or reformatted without logic changes. If code appears in the diff
|
de-indented, or reformatted without logic changes. If code appears in the diff
|
||||||
only due to whitespace or structural reformatting (e.g., removing a `with:` block),
|
only due to whitespace or structural reformatting (e.g., removing a `with:` block),
|
||||||
@ -123,5 +131,10 @@ chat:
|
|||||||
|
|
||||||
knowledge_base:
|
knowledge_base:
|
||||||
opt_out: false
|
opt_out: false
|
||||||
|
code_guidelines:
|
||||||
|
enabled: true
|
||||||
|
filePatterns:
|
||||||
|
- files: "AGENTS.md"
|
||||||
|
applyTo: "**"
|
||||||
learnings:
|
learnings:
|
||||||
scope: "auto"
|
scope: "auto"
|
||||||
|
|||||||
@ -543,18 +543,24 @@ class SDTokenizer:
|
|||||||
def _try_get_embedding(self, embedding_name:str):
|
def _try_get_embedding(self, embedding_name:str):
|
||||||
'''
|
'''
|
||||||
Takes a potential embedding name and tries to retrieve it.
|
Takes a potential embedding name and tries to retrieve it.
|
||||||
Returns a Tuple consisting of the embedding and any leftover string, embedding can be None.
|
Returns a Tuple consisting of the embedding, the cleaned embedding name, and any leftover string, embedding can be None.
|
||||||
'''
|
'''
|
||||||
split_embed = embedding_name.split()
|
split_embed = embedding_name.split()
|
||||||
embedding_name = split_embed[0]
|
embedding_name = split_embed[0]
|
||||||
leftover = ' '.join(split_embed[1:])
|
leftover = ' '.join(split_embed[1:])
|
||||||
|
|
||||||
|
match = re.search(r'[<\[]', embedding_name)
|
||||||
|
if match is not None:
|
||||||
|
leftover = embedding_name[match.start():] + (" " + leftover if leftover else "")
|
||||||
|
embedding_name = embedding_name[:match.start()]
|
||||||
|
|
||||||
embed = load_embed(embedding_name, self.embedding_directory, self.embedding_size, self.embedding_key)
|
embed = load_embed(embedding_name, self.embedding_directory, self.embedding_size, self.embedding_key)
|
||||||
if embed is None:
|
if embed is None:
|
||||||
stripped = embedding_name.strip(',')
|
stripped = embedding_name.strip(',')
|
||||||
if len(stripped) < len(embedding_name):
|
if len(stripped) < len(embedding_name):
|
||||||
embed = load_embed(stripped, self.embedding_directory, self.embedding_size, self.embedding_key)
|
embed = load_embed(stripped, self.embedding_directory, self.embedding_size, self.embedding_key)
|
||||||
return (embed, "{} {}".format(embedding_name[len(stripped):], leftover))
|
return (embed, embedding_name, "{} {}".format(embedding_name[len(stripped):], leftover))
|
||||||
return (embed, leftover)
|
return (embed, embedding_name, leftover)
|
||||||
|
|
||||||
def pad_tokens(self, tokens, amount):
|
def pad_tokens(self, tokens, amount):
|
||||||
if self.pad_left:
|
if self.pad_left:
|
||||||
@ -585,7 +591,7 @@ class SDTokenizer:
|
|||||||
tokens = []
|
tokens = []
|
||||||
for weighted_segment, weight in parsed_weights:
|
for weighted_segment, weight in parsed_weights:
|
||||||
to_tokenize = unescape_important(weighted_segment)
|
to_tokenize = unescape_important(weighted_segment)
|
||||||
split = re.split(' {0}|\n{0}'.format(self.embedding_identifier), to_tokenize)
|
split = re.split(r'(?<=\s){}'.format(re.escape(self.embedding_identifier)), to_tokenize)
|
||||||
to_tokenize = [split[0]]
|
to_tokenize = [split[0]]
|
||||||
for i in range(1, len(split)):
|
for i in range(1, len(split)):
|
||||||
to_tokenize.append("{}{}".format(self.embedding_identifier, split[i]))
|
to_tokenize.append("{}{}".format(self.embedding_identifier, split[i]))
|
||||||
@ -595,7 +601,7 @@ class SDTokenizer:
|
|||||||
# if we find an embedding, deal with the embedding
|
# if we find an embedding, deal with the embedding
|
||||||
if word.startswith(self.embedding_identifier) and self.embedding_directory is not None:
|
if word.startswith(self.embedding_identifier) and self.embedding_directory is not None:
|
||||||
embedding_name = word[len(self.embedding_identifier):].strip('\n')
|
embedding_name = word[len(self.embedding_identifier):].strip('\n')
|
||||||
embed, leftover = self._try_get_embedding(embedding_name)
|
embed, embedding_name, leftover = self._try_get_embedding(embedding_name)
|
||||||
if embed is None:
|
if embed is None:
|
||||||
logging.warning(f"warning, embedding:{embedding_name} does not exist, ignoring")
|
logging.warning(f"warning, embedding:{embedding_name} does not exist, ignoring")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -937,22 +937,41 @@ class BaseGenerate:
|
|||||||
return torch.argmax(logits, dim=-1, keepdim=True)
|
return torch.argmax(logits, dim=-1, keepdim=True)
|
||||||
|
|
||||||
# Sampling mode
|
# Sampling mode
|
||||||
if repetition_penalty != 1.0:
|
if len(token_history) > 0 and (repetition_penalty != 1.0 or (presence_penalty is not None and presence_penalty != 0.0)):
|
||||||
for i in range(logits.shape[0]):
|
token_ids = torch.tensor(list(set(token_history)), device=logits.device)
|
||||||
for token_id in set(token_history):
|
token_logits = logits[:, token_ids]
|
||||||
logits[i, token_id] *= repetition_penalty if logits[i, token_id] < 0 else 1/repetition_penalty
|
if repetition_penalty != 1.0:
|
||||||
|
token_logits = torch.where(token_logits < 0, token_logits * repetition_penalty, token_logits / repetition_penalty)
|
||||||
if presence_penalty is not None and presence_penalty != 0.0:
|
if presence_penalty is not None and presence_penalty != 0.0:
|
||||||
for i in range(logits.shape[0]):
|
token_logits = token_logits - presence_penalty
|
||||||
for token_id in set(token_history):
|
logits[:, token_ids] = token_logits
|
||||||
logits[i, token_id] -= presence_penalty
|
|
||||||
|
|
||||||
if temperature != 1.0:
|
if temperature != 1.0:
|
||||||
logits = logits / temperature
|
logits = logits / temperature
|
||||||
|
|
||||||
if top_k > 0:
|
if top_k > 0:
|
||||||
indices_to_remove = logits < torch.topk(logits, top_k)[0][..., -1, None]
|
top_k = min(top_k, logits.shape[-1])
|
||||||
logits[indices_to_remove] = torch.finfo(logits.dtype).min
|
logits, top_indices = torch.topk(logits, top_k)
|
||||||
|
|
||||||
|
if min_p > 0.0:
|
||||||
|
probs_before_filter = torch.nn.functional.softmax(logits, dim=-1)
|
||||||
|
top_probs, _ = probs_before_filter.max(dim=-1, keepdim=True)
|
||||||
|
min_threshold = min_p * top_probs
|
||||||
|
indices_to_remove = probs_before_filter < min_threshold
|
||||||
|
logits[indices_to_remove] = torch.finfo(logits.dtype).min
|
||||||
|
|
||||||
|
if top_p < 1.0:
|
||||||
|
sorted_logits, sorted_indices = torch.sort(logits, descending=True)
|
||||||
|
cumulative_probs = torch.cumsum(torch.nn.functional.softmax(sorted_logits, dim=-1), dim=-1)
|
||||||
|
sorted_indices_to_remove = cumulative_probs > top_p
|
||||||
|
sorted_indices_to_remove[..., 0] = False
|
||||||
|
indices_to_remove = torch.zeros_like(logits, dtype=torch.bool)
|
||||||
|
indices_to_remove.scatter_(1, sorted_indices, sorted_indices_to_remove)
|
||||||
|
logits[indices_to_remove] = torch.finfo(logits.dtype).min
|
||||||
|
|
||||||
|
probs = torch.nn.functional.softmax(logits, dim=-1)
|
||||||
|
next_token = torch.multinomial(probs, num_samples=1, generator=generator)
|
||||||
|
return top_indices.gather(1, next_token)
|
||||||
|
|
||||||
if min_p > 0.0:
|
if min_p > 0.0:
|
||||||
probs_before_filter = torch.nn.functional.softmax(logits, dim=-1)
|
probs_before_filter = torch.nn.functional.softmax(logits, dim=-1)
|
||||||
|
|||||||
@ -9,6 +9,7 @@ from typing import Any
|
|||||||
import folder_paths
|
import folder_paths
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
_SENSITIVE_HEADERS = {"authorization", "x-api-key"}
|
||||||
|
|
||||||
|
|
||||||
def get_log_directory():
|
def get_log_directory():
|
||||||
@ -73,6 +74,10 @@ def _format_data_for_logging(data: Any) -> str:
|
|||||||
return str(data)
|
return str(data)
|
||||||
|
|
||||||
|
|
||||||
|
def _redact_headers(headers: dict) -> dict:
|
||||||
|
return {k: ("***" if k.lower() in _SENSITIVE_HEADERS else v) for k, v in headers.items()}
|
||||||
|
|
||||||
|
|
||||||
def log_request_response(
|
def log_request_response(
|
||||||
operation_id: str,
|
operation_id: str,
|
||||||
request_method: str,
|
request_method: str,
|
||||||
@ -101,7 +106,7 @@ def log_request_response(
|
|||||||
log_content.append(f"Method: {request_method}")
|
log_content.append(f"Method: {request_method}")
|
||||||
log_content.append(f"URL: {request_url}")
|
log_content.append(f"URL: {request_url}")
|
||||||
if request_headers:
|
if request_headers:
|
||||||
log_content.append(f"Headers:\n{_format_data_for_logging(request_headers)}")
|
log_content.append(f"Headers:\n{_format_data_for_logging(_redact_headers(request_headers))}")
|
||||||
if request_params:
|
if request_params:
|
||||||
log_content.append(f"Params:\n{_format_data_for_logging(request_params)}")
|
log_content.append(f"Params:\n{_format_data_for_logging(request_params)}")
|
||||||
if request_data is not None:
|
if request_data is not None:
|
||||||
|
|||||||
@ -16,23 +16,30 @@ class ColorToRGBInt(io.ComfyNode):
|
|||||||
],
|
],
|
||||||
outputs=[
|
outputs=[
|
||||||
io.Int.Output(display_name="rgb_int"),
|
io.Int.Output(display_name="rgb_int"),
|
||||||
io.Color.Output(display_name="hex")
|
io.Color.Output(display_name="hex"),
|
||||||
|
io.Float.Output(display_name="alpha"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def execute(cls, color: str) -> io.NodeOutput:
|
def execute(cls, color: str) -> io.NodeOutput:
|
||||||
# expect format #RRGGBB
|
# expect format #RRGGBB or #RRGGBBAA
|
||||||
if len(color) != 7 or color[0] != "#":
|
if len(color) not in (7, 9) or color[0] != "#":
|
||||||
raise ValueError("Color must be in format #RRGGBB")
|
raise ValueError("Color must be in format #RRGGBB or #RRGGBBAA")
|
||||||
try:
|
try:
|
||||||
int(color[1:], 16)
|
int(color[1:], 16)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValueError("Color must be in format #RRGGBB") from None
|
raise ValueError("Color must be in format #RRGGBB or #RRGGBBAA") from None
|
||||||
|
|
||||||
|
alpha = 1.0
|
||||||
|
if len(color) == 9:
|
||||||
|
alpha = int(color[7:9], 16) / 255.0
|
||||||
|
color = color[:7]
|
||||||
|
|
||||||
r, g, b = hex_to_rgb(color)
|
r, g, b = hex_to_rgb(color)
|
||||||
|
|
||||||
rgb_int = r * 256 * 256 + g * 256 + b
|
rgb_int = r * 256 * 256 + g * 256 + b
|
||||||
return io.NodeOutput(rgb_int, color)
|
return io.NodeOutput(rgb_int, color, alpha)
|
||||||
|
|
||||||
|
|
||||||
class ColorExtension(ComfyExtension):
|
class ColorExtension(ComfyExtension):
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
comfyui-frontend-package==1.45.20
|
comfyui-frontend-package==1.45.20
|
||||||
comfyui-workflow-templates==0.11.2
|
comfyui-workflow-templates==0.11.2
|
||||||
comfyui-embedded-docs==0.5.6
|
comfyui-embedded-docs==0.5.7
|
||||||
torch
|
torch
|
||||||
torchsde
|
torchsde
|
||||||
torchvision
|
torchvision
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user