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,68 @@
|
||||
---
|
||||
name: comfyui_inject_controlnet
|
||||
kind: function
|
||||
lang: py
|
||||
domain: ml
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "def comfyui_inject_controlnet(workflow: dict, control_image: str, cn_name: str, *, strength: float = 1.0, positive_node: str | None = None) -> dict"
|
||||
description: "Inyecta una rama ControlNet (LoadImage + ControlNetLoader + ControlNetApply) en un workflow ComfyUI ya construido (API format), repuntando el KSampler.positive al condicionamiento condicionado por la imagen de control. Version ENCADENABLE-sobre-dict del builder comfyui_build_controlnet_workflow (que construye desde cero). Pensada para componerse con inject_lora/inject_ipadapter/inject_hires_fix. Pura: no muta el dict de entrada."
|
||||
tags: [comfyui, comfyui-skill, ml, controlnet, stable-diffusion, workflow]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
params:
|
||||
- name: workflow
|
||||
desc: "dict en API format (ej. salida de comfyui_build_txt2img_workflow). No se muta; se devuelve una copia."
|
||||
- name: control_image
|
||||
desc: "Nombre del archivo de la imagen de control en input/ del servidor ComfyUI (mapa preprocesado canny/depth/openpose). No puede estar vacio (raise ValueError)."
|
||||
- name: cn_name
|
||||
desc: "Nombre del modelo ControlNet en models/controlnet/ (control_net_name de ControlNetLoader)."
|
||||
- name: strength
|
||||
desc: "Fuerza con la que el ControlNet condiciona la generacion (0.0 nula, 1.0 plena). keyword-only."
|
||||
- name: positive_node
|
||||
desc: "node_id cuya salida CONDITIONING (slot 0) sera el positivo de entrada del ControlNetApply. Si None, se detecta la fuente que hoy alimenta el KSampler.positive. keyword-only."
|
||||
output: "copia del workflow con LoadImage + ControlNetLoader + ControlNetApply insertados y el KSampler.positive repuntado a la salida del ControlNetApply. node_ids = max id numerico + 1/2/3."
|
||||
tested: true
|
||||
tests: ["inyecta los 3 nodos (LoadImage, ControlNetLoader, ControlNetApply) y repunta KSampler.positive", "ControlNetApply toma el positivo original como conditioning", "respeta strength y cn_name", "no muta el dict de entrada (pureza)", "control_image vacio lanza ValueError", "workflow sin KSampler lanza ValueError", "api format valido"]
|
||||
test_file_path: "python/functions/ml/tests/test_comfyui_inject_controlnet.py"
|
||||
file_path: "python/functions/ml/comfyui_inject_controlnet.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
|
||||
from ml.comfyui_build_txt2img_workflow import comfyui_build_txt2img_workflow
|
||||
from ml.comfyui_inject_controlnet import comfyui_inject_controlnet
|
||||
|
||||
base = comfyui_build_txt2img_workflow("dreamshaper_8.safetensors", "a knight, dramatic light")
|
||||
wf = comfyui_inject_controlnet(base, "pose_canny.png", "control_v11p_sd15_canny.pth", strength=0.8)
|
||||
# KSampler.positive ahora viene de ControlNetApply(conditioning=CLIPTextEncode+, image=pose_canny)
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Cuando quieras **guiar la composicion** (pose, bordes, profundidad) de un
|
||||
workflow txt2img ya construido con una imagen de control, sin reconstruir el
|
||||
grafo desde cero. Es la pieza ControlNet del mixer
|
||||
`comfyui_compose_capabilities`: encadena sobre el mismo dict que las LoRAs y el
|
||||
IPAdapter. Para construir un workflow ControlNet aislado desde cero usa
|
||||
`comfyui_build_controlnet_workflow`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- Pura: no muta el `workflow` de entrada y NO valida que `cn_name`/`control_image`
|
||||
existan en el servidor. La imagen de control debe estar subida al `input/` del
|
||||
servidor ANTES de submit; si no, el LoadImage falla en ejecucion.
|
||||
- **control_image obligatorio**: una llamada con `control_image=""` lanza
|
||||
`ValueError` (ControlNet sin imagen de control no tiene sentido). Es el error
|
||||
path tipico cuando se activa la capacidad sin proveer el mapa de control.
|
||||
- Repunta solo `KSampler.positive`. Si el workflow tiene varios KSampler, opera
|
||||
sobre el primero que encuentra; pasa `positive_node` para casos no triviales.
|
||||
- Apila coste de VRAM (carga el modelo ControlNet). En 8GB con SD1.5 cabe junto a
|
||||
1-2 LoRAs; combinandolo con IPAdapter + hires vigila la memoria.
|
||||
Reference in New Issue
Block a user