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:
@@ -256,6 +256,74 @@ Notas de uso:
|
||||
commitea ni se indexa.
|
||||
- El token Civitai es secreto: viene de `pass civitai/api-token`, nunca hardcodeado.
|
||||
|
||||
## Mezclar capacidades (mixer)
|
||||
|
||||
Una skill fija *una* receta. El **mixer** resuelve el otro eje: combinar **a la carta** todas las
|
||||
capacidades de generación sobre un mismo workflow base y activar/desactivar cada una para iterar.
|
||||
Misma doctrina del issue 0087 (componer piezas probadas, no reescribir el grafo), pero aplicada a
|
||||
mezclar capacidades en vez de a guardar una receta.
|
||||
|
||||
Dos funciones:
|
||||
|
||||
| ID | firma corta | qué hace |
|
||||
|---|---|---|
|
||||
| `comfyui_compose_capabilities_py_ml` | `compose_capabilities(base, *, loras, controlnet, ipadapter, hires, facedetailer) -> dict` | **PURA.** Aplica EN ORDEN las capacidades activadas (cada arg `None` = desactivada) sobre un dict base, componiendo los inyectores/builders encadenables. Reconecta MODEL/CLIP/positive/IMAGE. Sin ninguna = base intacto. |
|
||||
| `comfyui_generate_mixed_oneshot_py_pipelines` | `generate_mixed_oneshot(base, subject, *, capabilities, server, judge, ...) -> dict` | **Pipeline.** base (skill slug / `'txt2img'` / dict) → compose → submit → wait → fetch → (si `judge`) juzga. Devuelve `{ok, prompt_id, image_path, capabilities_active, judge, error}`. |
|
||||
|
||||
El mixer se apoya en los **inyectores encadenables-sobre-dict** (cada uno la versión componible de
|
||||
su builder-desde-cero hermano):
|
||||
|
||||
| Capacidad | Inyector | Reconecta |
|
||||
|---|---|---|
|
||||
| LoRAs (N) | `comfyui_inject_multi_lora_py_ml` | cadena MODEL/CLIP tras el checkpoint |
|
||||
| ControlNet | `comfyui_inject_controlnet_py_ml` | `KSampler.positive` ← `ControlNetApply` |
|
||||
| IPAdapter (style/faceid) | `comfyui_inject_ipadapter_py_ml` | `KSampler.model` ← IPAdapter (tras las LoRAs) |
|
||||
| hires/upscale | `comfyui_inject_hires_fix_py_ml` | `UltimateSDUpscale` tras el `VAEDecode` |
|
||||
| FaceDetailer | `comfyui_build_facedetailer_workflow_py_ml` | regenera caras del `VAEDecode` |
|
||||
|
||||
Orden fijo: `loras → controlnet → ipadapter → facedetailer → hires`. El IPAdapter se aplica sobre
|
||||
el MODEL ya modificado por los LoRAs (orden correcto). Tras FaceDetailer el mixer deja un único
|
||||
`SaveImage` (el del detailer).
|
||||
|
||||
### Ejemplo canónico (≥3 capacidades, juzgado)
|
||||
|
||||
```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). Activar/desactivar = cambiar args.
|
||||
res = comfyui_generate_mixed_oneshot(
|
||||
"txt2img",
|
||||
"a heroic knight portrait, 3d render style, dramatic lighting, detailed face",
|
||||
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, "strength_clip": 0.5},
|
||||
],
|
||||
"facedetailer": {"denoise": 0.45},
|
||||
# "ipadapter": {"ref_image": "face.png", "mode": "faceid"}, # se activa con solo añadirla
|
||||
# "hires": {"upscale_by": 1.5},
|
||||
},
|
||||
dest="/tmp/comfy_mixed", seed=42, judge=True,
|
||||
)
|
||||
print(res["ok"], res["prompt_id"], res["capabilities_active"], res["judge"])
|
||||
```
|
||||
|
||||
### Límite conocido (8GB / piezas actuales)
|
||||
|
||||
- **hires + facedetailer no encadenan**: ambos toman su imagen del `VAEDecode` del render base, así
|
||||
que combinarlos deja a uno sin efecto sobre la salida final (con los dos activos, hires "gana" y
|
||||
facedetailer queda sin consumidor). Usa uno U otro por workflow. El resto de combinaciones
|
||||
(LoRAs + ControlNet + IPAdapter + uno de los dos post-procesos) encadenan limpio.
|
||||
- **VRAM**: en 8GB lowvram con SD1.5 entran ~2-3 capacidades modestas (p.ej. 2 LoRAs + FaceDetailer
|
||||
a 512px). Apilar IPAdapter FaceID + ControlNet + hires + facedetailer a la vez puede dar OOM —
|
||||
baja resolución o reduce capacidades. `mixer` no valida VRAM; el OOM aflora en `wait`.
|
||||
- **Incompatibilidad explícita, no silenciosa**: ControlNet sin `control_image` o IPAdapter sin
|
||||
`ref_image` lanzan `ValueError` del inyector (no petan a medias). Las imágenes de control/referencia
|
||||
deben estar en el `input/` del servidor antes de encolar.
|
||||
|
||||
## Fronteras
|
||||
|
||||
- **No genera ni descarga modelos**: una skill referencia checkpoints/LoRAs por nombre; deben
|
||||
|
||||
Reference in New Issue
Block a user