feat(ml): mixer de capacidades comfyui (compose + generate_mixed_oneshot + inject controlnet/ipadapter)
Mezclador del grupo comfyui-skill que promueve a una sola llamada la secuencia base -> compose -> submit -> wait -> fetch -> judge (issue 0087): - comfyui_compose_capabilities_py_ml (PURA): aplica en orden las capacidades activadas (loras, controlnet, ipadapter, facedetailer, hires) sobre un workflow base, sin mutar la entrada. - comfyui_generate_mixed_oneshot_py_pipelines: one-shot que resuelve el base (skill/txt2img/dict), compone, encola, espera, descarga el PNG y lo puntua con el panel comfyui-judge. - comfyui_inject_controlnet_py_ml, comfyui_inject_ipadapter_py_ml: inyectores encadenables que consume el compose. - Tests (24 passed) + pagina madre docs/capabilities/comfyui-skill.md. Prueba real en GPU: txt2img dreamshaper_8 + 2 LoRAs (3d_render_redmond + detail_tweaker) + FaceDetailer -> imagen 512x512 en ~24s, juez verdict 'good' (score 4.69, votos aesthetic+clip good; voto llm degradado por rate-limit 429). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
---
|
||||
name: comfyui_generate_mixed_oneshot
|
||||
kind: pipeline
|
||||
lang: py
|
||||
domain: pipelines
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def comfyui_generate_mixed_oneshot(base, subject: str, *, capabilities: dict | None = None, server: str = \"127.0.0.1:8188\", dest: str | None = None, seed: int = 0, judge: bool = True, checkpoint: str | None = None, negative: str = \"\", library_dir: str | None = None, wait_timeout: float = 600.0) -> dict"
|
||||
description: "Pipeline one-shot del mixer comfyui-skill: parte de un workflow base (skill slug, builder 'txt2img', o dict ya construido), aplica el conjunto de capacidades elegido con comfyui_compose_capabilities (LoRAs + ControlNet + IPAdapter + hires + FaceDetailer, cada una activable), encola, espera, descarga el PNG y si judge=True lo puntua con el panel comfyui-judge. Promueve a una llamada la secuencia base->compose->submit->wait->fetch->judge (issue 0087). Devuelve {ok, prompt_id, image_path, capabilities_active, judge, error}. Impuro: HTTP + disco + API Anthropic."
|
||||
tags: [comfyui, comfyui-skill, pipelines, mixer, txt2img, lora, ipadapter, controlnet, facedetailer, judge, launcher]
|
||||
uses_functions: [comfyui_build_txt2img_workflow_py_ml, comfyui_load_skill_py_ml, comfyui_build_skill_workflow_py_ml, comfyui_compose_capabilities_py_ml, comfyui_submit_workflow_py_ml, comfyui_wait_result_py_ml, comfyui_fetch_output_image_py_ml, comfyui_judge_image_py_ml]
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: error_py_core
|
||||
imports: [comfyui_build_txt2img_workflow_py_ml, comfyui_compose_capabilities_py_ml, comfyui_submit_workflow_py_ml, comfyui_wait_result_py_ml, comfyui_fetch_output_image_py_ml, comfyui_judge_image_py_ml]
|
||||
params:
|
||||
- name: base
|
||||
desc: "Workflow base: dict (API format ya construido), la cadena 'txt2img' (construye con checkpoint+subject), o un slug de skill guardada (carga su receta y la compila con subject)."
|
||||
- name: subject
|
||||
desc: "Sujeto/prompt principal. En 'txt2img' es el prompt positivo; en una skill sustituye {subject} en el scaffold."
|
||||
- name: capabilities
|
||||
desc: "Dict de capacidades a mezclar tal cual las acepta comfyui_compose_capabilities: {loras, controlnet, ipadapter, hires, facedetailer}. Ausentes/None = desactivadas. None = solo el base. keyword-only."
|
||||
- name: server
|
||||
desc: "host:port del servidor ComfyUI (sin esquema). keyword-only."
|
||||
- name: dest
|
||||
desc: "Directorio local donde guardar el PNG (None = cwd). keyword-only."
|
||||
- name: seed
|
||||
desc: "Semilla de generacion. keyword-only."
|
||||
- name: judge
|
||||
desc: "Si True, puntua el PNG con el panel comfyui-judge. keyword-only."
|
||||
- name: checkpoint
|
||||
desc: "Checkpoint para base='txt2img' (obligatorio en ese caso). keyword-only."
|
||||
- name: negative
|
||||
desc: "Prompt negativo para base='txt2img'. keyword-only."
|
||||
- name: library_dir
|
||||
desc: "Raiz de la libreria de skills (base = slug). keyword-only."
|
||||
- name: wait_timeout
|
||||
desc: "Segundos maximos esperando al servidor. keyword-only."
|
||||
output: "dict {ok, base, prompt_id, image_path, prompt_resolved, capabilities_active, judge, error}. capabilities_active = lista de capacidades activadas (evidencia de la mezcla). judge = {verdict, score, votes} o None. Si falla un paso, ok=False y error explica cual."
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/pipelines/comfyui_generate_mixed_oneshot.py"
|
||||
---
|
||||
|
||||
# comfyui_generate_mixed_oneshot
|
||||
|
||||
One-shot del **mixer** del grupo [`comfyui-skill`](../../../docs/capabilities/comfyui-skill.md):
|
||||
de un workflow base + un conjunto de capacidades activables a un PNG **ya puntuado** por el
|
||||
panel [`comfyui-judge`](../../../docs/capabilities/comfyui-judge.md), en una llamada. El bucle
|
||||
del juez afina qué capacidades y pesos dan mejor resultado.
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
|
||||
from pipelines.comfyui_generate_mixed_oneshot import comfyui_generate_mixed_oneshot
|
||||
|
||||
# txt2img dreamshaper + 2 LoRAs + FaceDetailer (3 capacidades), juzgado:
|
||||
res = comfyui_generate_mixed_oneshot(
|
||||
"txt2img",
|
||||
"a heroic knight in 3d render style, dramatic lighting",
|
||||
checkpoint="dreamshaper_8.safetensors",
|
||||
capabilities={
|
||||
"loras": [
|
||||
{"name": "3d_render_redmond_sd15.safetensors", "strength_model": 0.9},
|
||||
{"name": "detail_tweaker_sd15.safetensors", "strength_model": 0.5},
|
||||
],
|
||||
"facedetailer": {"denoise": 0.45},
|
||||
# "ipadapter": {"ref_image": "face.png", "mode": "faceid"}, # activar/desactivar
|
||||
# "hires": {"upscale_by": 1.5},
|
||||
},
|
||||
dest="/tmp/comfy_mixed", seed=42, judge=True,
|
||||
)
|
||||
print(res["ok"], res["prompt_id"], res["capabilities_active"], res["judge"])
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Cuando quieras **generar mezclando varias capacidades** y obtener de vuelta el
|
||||
PNG ya puntuado, en una sola llamada — para iterar (activar/desactivar/ajustar
|
||||
capacidades) guiado por el score del juez. Es la promocion a one-shot de
|
||||
`compose_capabilities` + el ciclo submit/wait/fetch/judge.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- Impuro: necesita el servidor ComfyUI vivo (`server`) y, si `judge=True`, la API
|
||||
Anthropic para el juez critico. Las imagenes de referencia/control de IPAdapter
|
||||
y ControlNet deben estar en el `input/` del servidor antes de llamar.
|
||||
- `base='txt2img'` exige `checkpoint`. Un slug de skill exige que la skill exista
|
||||
en `library_dir`. Un `base` dict se usa tal cual.
|
||||
- Hereda la limitacion del mixer: **hires + facedetailer juntos no encadenan**
|
||||
(ver `comfyui_compose_capabilities`). Activa uno U otro.
|
||||
- En 8GB lowvram, apilar muchas capacidades (IPAdapter FaceID + ControlNet + hires
|
||||
+ facedetailer) puede dar OOM y `wait` devolvera el error del servidor: baja
|
||||
resolucion (`width`/`height` via un base dict) o reduce capacidades.
|
||||
- Si el juez falla pero la imagen se genero, `ok=True` con `error` describiendo el
|
||||
fallo del panel (la imagen no se pierde).
|
||||
Reference in New Issue
Block a user