Files
alisson-anjos-ComfyUI-BFSNodes/edit_prompt_fixer.py
Claude 8eb8a0546c Add deterministic Edit Anything prompt fixer (1.28.0)
Two formatting rules the VLM breaks consistently, and that no amount of prompt
work fixed — five rounds of rewriting plus temperature 0 still produced the same
violations:

  no reference  -> the marker sentence must not exist, and a swap is a plain
                   Replace. The model emits Add + marker + Remove instead, which
                   is the reference form applied where it does not belong.
  reference     -> the marker may only follow a sentence starting with Add. When
                   it lands on a Replace, that pair appears in zero training
                   samples.

Both are decidable in code: the caller already knows whether it attached a
reference image, so the node takes that boolean rather than asking the model to
infer it. Add+Remove is recomposed into Replace, and Replace+marker into
Add+Remove, preserving the object description the VLM did get right.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 09:51:52 +00:00

89 lines
3.5 KiB
Python

"""Corrige a saida do VLM para o formato que o LoRA foi treinado.
Duas regras que o VLM erra de forma consistente, por mais explicito que o prompt seja, e que
sao deterministicas -- quem chama ja sabe se anexou referencia:
sem referencia -> a frase-marcador nao pode existir, e "trocar" e um Replace simples.
O VLM tende a emitir Add + marcador + Remove, que e a forma da referencia.
com referencia -> o marcador so pode seguir uma frase que comeca com Add. Se vier num
Replace, vira Add + Remove.
"""
import re
MARCADOR = "The object to add is shown in the reference image."
TRIGGER = "edit_anything: "
def _frases(texto):
corpo = texto[len(TRIGGER):] if texto.startswith(TRIGGER) else texto
return [f.strip() for f in re.split(r"(?<=\.)\s+", corpo.strip()) if f.strip()]
def _juntar(frases):
return TRIGGER + " ".join(frases)
def corrigir(prompt, tem_referencia):
frases = _frases(prompt)
mudou = []
if not tem_referencia:
antes = len(frases)
frases = [f for f in frases if f.rstrip() != MARCADOR]
if len(frases) != antes:
mudou.append("marcador removido (sem referencia)")
# Add(X) ... Remove(Y) sem referencia = forma da referencia aplicada no lugar errado.
# Recompoe como Replace Y with X.
i_add = next((i for i, f in enumerate(frases) if f.startswith("Add ")), None)
i_rem = next((i for i, f in enumerate(frases) if f.startswith("Remove ")), None)
if i_add is not None and i_rem is not None:
novo = re.sub(r"^Add\s+", "", frases[i_add]).rstrip(".")
velho = re.sub(r"^Remove\s+", "", frases[i_rem]).rstrip(".")
# descarta localizacao redundante herdada do Add
novo = re.sub(r"\s+(where|parked|standing|placed)\b.*$", "", novo).strip()
frases = [f for k, f in enumerate(frases) if k not in (i_add, i_rem)]
frases.insert(min(i_add, i_rem), f"Replace {velho} with {novo}.")
mudou.append("Add+Remove recomposto como Replace")
else:
for i, f in enumerate(frases):
if f.rstrip() == MARCADOR and i > 0 and not frases[i - 1].startswith("Add "):
anterior = frases[i - 1]
m = re.match(r"^Replace\s+(.*?)\s+with\s+(.*)\.$", anterior)
if m:
velho, novo = m.group(1), m.group(2)
frases[i - 1] = f"Add {novo}."
frases.insert(i + 1, f"Remove {velho}.")
mudou.append("Replace com marcador virou Add + Remove")
break
return _juntar(frases), mudou
class EditAnythingPromptFixer:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"prompt": ("STRING", {"multiline": True, "forceInput": True}),
"has_reference": ("BOOLEAN", {"default": False, "tooltip": "Ligue quando uma imagem de referencia for anexada. Este e o mesmo booleano que decide anexar a imagem."}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("prompt",)
FUNCTION = "fix"
CATEGORY = "BFS/EditAnything"
def fix(self, prompt, has_reference):
saida, mudou = corrigir(prompt, has_reference)
if mudou:
print(f"[BFSNodes] prompt corrigido: {', '.join(mudou)}")
print(f"[BFSNodes] {saida}")
return (saida,)
NODE_CLASS_MAPPINGS = {"EditAnythingPromptFixer": EditAnythingPromptFixer}
NODE_DISPLAY_NAME_MAPPINGS = {"EditAnythingPromptFixer": "Edit Anything Prompt Fixer (BFS)"}