feat(comfyui): pipeline comfyui_generate_until_quality (loop evaluator-optimizer)

Loop tipo GAN sin entrenar: genera con un builder del registry, juzga con el
panel multi-juez (comfyui_judge_image) y, si no alcanza el umbral, refina (nueva
seed, mas steps/cfg, prompt corregido con el feedback del juez via ask_llm) y
regenera hasta converger (verdict 'good') o agotar max_iters. Devuelve siempre
la mejor candidata por score (best-of-N), nunca lanza excepcion cruda.

Compone comfyui_submit_workflow + comfyui_wait_result + comfyui_fetch_output_image
+ comfyui_judge_image + ask_llm. Filtra kwargs por inspect.signature para ser
robusto entre builders. Caso HUD verificado: itera iter0 bad -> iter1 good.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Egutierrez
2026-06-28 15:01:37 +02:00
parent ec46aae04c
commit 8e9e1e6c8a
2 changed files with 480 additions and 0 deletions
@@ -0,0 +1,131 @@
---
name: comfyui_generate_until_quality
kind: pipeline
lang: py
domain: pipelines
version: "1.0.0"
purity: impure
signature: "comfyui_generate_until_quality(builder, subject, *, threshold=6.0, clip_threshold=0.24, max_iters=4, strategy='reroll+escalate+refine_prompt', server='127.0.0.1:8188', dest_dir='~/ComfyUI/output', judge_prompt=None, seed=0, refine_model='claude-haiku-4-5-20251001', judge_model='claude-opus-4-8', wait_timeout=300.0, **builder_kwargs) -> dict"
description: "Loop evaluator-optimizer (GAN sin entrenar): genera una imagen con un builder del registry, la juzga con el panel multi-juez, y si no alcanza la calidad pedida refina (nueva seed, mas calidad, prompt corregido con el feedback del juez) y regenera hasta pasar el umbral o agotar intentos. Siempre devuelve la mejor candidata por score (best-of-N)."
tags: [comfyui, comfyui-skill, pipeline, launcher, generate, judge, quality-loop, evaluator-optimizer]
uses_functions:
- comfyui_submit_workflow_py_ml
- comfyui_wait_result_py_ml
- comfyui_fetch_output_image_py_ml
- comfyui_judge_image_py_ml
- ask_llm_py_core
uses_types: []
returns: []
returns_optional: false
error_type: error_py_core
imports: [comfyui_submit_workflow_py_ml, comfyui_wait_result_py_ml, comfyui_fetch_output_image_py_ml, comfyui_judge_image_py_ml, ask_llm_py_core]
params:
- name: builder
desc: "Callable o nombre (str) de un builder comfyui_build_*_workflow del registry. El subject se pasa como primer positional (builders de asset: ui_hud, item_icon, enemy_creature...)."
- name: subject
desc: "Descripcion del elemento a generar (p.ej. 'RPG health and mana bars'). Se inyecta en el builder y, si se refina, se reescribe con el feedback del juez."
- name: threshold
desc: "Umbral estetico 0-10 que el juez usa para votar good/bad."
- name: clip_threshold
desc: "Umbral de fidelidad CLIP 0-1 del juez (prompt<->imagen)."
- name: max_iters
desc: "Numero maximo de iteraciones de generacion."
- name: strategy
desc: "Tacticas de mejora separadas por '+': reroll (seed nueva), escalate (mas steps/cfg en iters tardias), refine_prompt (reescribe el subject con ask_llm usando las razones del juez)."
- name: server
desc: "host:port del servidor ComfyUI sin esquema."
- name: dest_dir
desc: "Directorio local donde guardar los PNG."
- name: judge_prompt
desc: "Texto que se pasa al juez para medir fidelidad. None = se extrae el positive del workflow construido."
- name: seed
desc: "Semilla base; los rerolls derivan de ella de forma determinista."
- name: refine_model
desc: "Modelo de ask_llm para el refine del prompt (barato, haiku por defecto)."
- name: judge_model
desc: "Modelo del juez critico LLM-vision."
- name: wait_timeout
desc: "Segundos maximos esperando cada generacion."
- name: builder_kwargs
desc: "Parametros extra del builder (ui_style, checkpoint, size, transparent...). Solo se pasan los que el builder acepta (filtrados por inspect.signature)."
output: "dict {ok, converged, best_image_path, best_score, best_verdict, iterations, error}. iterations = lista de {iter, seed, params, score, verdict, reasons, image, error}. converged=True si alguna iteracion logro verdict 'good'. best_* apuntan a la mejor candidata por score aunque ninguna convergiera."
file_path: "python/functions/pipelines/comfyui_generate_until_quality.py"
tested: false
tests: []
test_file_path: ""
---
# comfyui_generate_until_quality
Loop **evaluator-optimizer** sobre ComfyUI: el patrón de una GAN (generador vs.
discriminador) pero **sin entrenar nada**. Un builder genera una imagen, el panel
multi-juez (`comfyui_judge_image`) la puntúa, y si no llega al umbral el pipeline
**refina** (nueva seed, más calidad, prompt corregido con las quejas del juez) y
regenera, hasta converger (`verdict == 'good'`) o agotar `max_iters`. Devuelve
**siempre la mejor candidata por score** (best-of-N): nunca basura por agotar
intentos.
Es la promoción a pipeline one-shot (issue 0087) del bucle de mejora del grupo
`comfyui-skill`: build → submit → wait → fetch → judge → (refine) → repeat.
## Ejemplo
```python
import sys, json
sys.path.insert(0, "python/functions")
from pipelines.comfyui_generate_until_quality import comfyui_generate_until_quality
res = comfyui_generate_until_quality(
"comfyui_build_ui_hud_workflow", # builder por nombre
"RPG health and mana bars, clean game UI", # subject
ui_style="fantasy game UI, clean vector, high contrast, sharp edges",
threshold=6.5, max_iters=3,
dest_dir="/tmp/comfy_until_quality", transparent=False, seed=1000,
)
print(res["converged"], round(res["best_score"], 2), res["best_verdict"])
print("scores:", [it["score"] for it in res["iterations"]]) # historial subiendo
print("mejor imagen:", res["best_image_path"])
```
```bash
# Lanzar directo (caso HUD del ejemplo __main__)
~/fn_registry/python/.venv/bin/python3 \
python/functions/pipelines/comfyui_generate_until_quality.py
```
## Cuando usarla
- Cuando pides un asset (HUD, icono, sprite) y la primera generación sale
borrosa/floja y quieres que el sistema **itere solo** hasta una versión usable,
en vez de re-tirar seeds a mano.
- Cuando quieres un **gate de calidad objetivo** que devuelva lo mejor de N
intentos rankeado por el panel multi-juez, no la primera que salga.
- Como bloque del bucle reactivo del grupo `comfyui-skill`: un skill no está
"hecho" hasta que su imagen pasa el panel; este pipeline es ese bucle.
## Gotchas
- **Impuro**: red (HTTP a ComfyUI), GPU (generación), disco (PNG), API
(juez crítico LLM + refine de prompt). Necesita ComfyUI vivo en `server` y el
venv de jueces (`~/ComfyUI/.venv`, ver `comfyui-judge`).
- **El `subject` se pasa como PRIMER positional del builder**. Vale para los
builders de asset (`comfyui_build_ui_hud_workflow`, `_item_icon_`,
`_enemy_creature_`...), cuyo primer arg es el elemento. NO para
`comfyui_build_txt2img_workflow` (primer arg = `ckpt`): para texto crudo, envuélvelo
o pasa un builder de asset.
- **Filtra kwargs con `inspect.signature`**: solo pasa al builder los que acepta,
así `escalate` (sube `steps`/`cfg`) y `reroll` (set `seed`) no rompen entre
builders con firmas distintas. Si un builder no expone `steps`/`seed`, esa
táctica simplemente no aplica en él.
- **`escalate` sube `steps`+`cfg`**, no inyecta hires-fix (no todos los builders
lo soportan y ui_hud lleva Rembg). Para upscale dedicado, usar
`comfyui_build_hires_fix_workflow` como builder.
- **Degrada con gracia**: si el juez cae (HTTP 429) la imagen se conserva con
score 0/verdict 'unknown' y el loop sigue; si una iteración falla en
submit/wait/fetch se registra su `error` y se reintenta la siguiente. Solo
devuelve `ok=False` si NINGUNA iteración produjo imagen.
- **VRAM (8GB)**: entre familias de generación, liberar con
`POST /free {"unload_models":true,"free_memory":true}` si el juez estético
(CLIP+LAION en el venv ComfyUI) compite por VRAM con el checkpoint SD.
- **Determinista en estructura**: nunca lanza excepción cruda; siempre dict de
estado. El refine usa `ask_llm` (best-effort): si falla, mantiene el subject.