feat(gamedev): sistema de style presets reutilizable (gameboy/ghibli/pixel-art-retro)

Calidad por ESTILO en vez de por tipo: un dev fija el look del juego una vez
y todos los assets salen coherentes. Diseño (A) datos puros + helper, no
pipeline monolítico (issue 0087, crecer por composición).

- comfyui_get_gamedev_style_preset(name=None): recetas curadas o catálogo.
  gameboy (sin LoRA, post pixelize paleta game-boy 4 tonos), ghibli (degrada
  a watercolor_style_sd15 gratis instalado, sin LoRA Ghibli gated), pixel-art-retro
  (reutiliza pixel-art-xl SDXL + juggernaut + post pixelize 16 colores). Extensible.
- comfyui_apply_style_preset(preset, subject): traduce a kwargs **spread-ables
  para cualquier builder de sujeto + size/transparent/post. Pura, no muta.
- 16 tests offline verdes. Validado e2e GPU: mismo 'knight character' en 3
  estilos visiblemente distintos (4 vs 78552 vs 16 colores).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-27 12:36:18 +02:00
parent 9f0d2e2338
commit 0eefb7cfcd
7 changed files with 713 additions and 0 deletions
@@ -0,0 +1,83 @@
---
name: comfyui_apply_style_preset
kind: function
lang: py
domain: ml
version: "1.0.0"
purity: pure
signature: "def comfyui_apply_style_preset(preset: dict, subject: str, *, style: str | None = None, negative: str | None = None) -> dict"
description: "Traduce un STYLE PRESET gamedev (de comfyui_get_gamedev_style_preset) + un subject del usuario a lo que necesita un builder de sujeto del grupo gamedev-2d: el subject combinado con el prefijo/sufijo del estilo, los kwargs comunes (style, checkpoint, lora, lora_strength, negative) listos para **spread, la resolucion y el recorte recomendados (size, transparent) y la spec de post-proceso (post, p.ej. pixelize) que el caller aplica al PNG. Asi el mismo estilo se aplica a CUALQUIER builder (item_icon, enemy_creature, prop_object, ...) y al pipeline comfyui_generate_asset_pack_oneshot sin acoplar firmas. Pura, sin red ni I/O; no muta el preset."
tags: [comfyui, ml, gamedev-2d, style, preset, theme]
uses_functions: [comfyui_get_gamedev_style_preset_py_ml]
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
params:
- name: preset
desc: "Receta de estilo (dict de comfyui_get_gamedev_style_preset). Debe traer los campos del preset (se valida que esten). No se muta."
- name: subject
desc: "Lo que el usuario quiere generar (ej. 'knight character', 'health potion'). Se combina con el prefijo/sufijo del estilo. No puede estar vacio."
- name: style
desc: "Override puntual: si se pasa, sustituye al style del preset. None usa el del preset. keyword-only."
- name: negative
desc: "Negativo extra del caller; se MERGEA (sin duplicar) con el negativo del estilo, no lo reemplaza. None = solo el del estilo. keyword-only."
output: "dict con: name (estilo aplicado), subject (combinado con prefijo/sufijo), builder_kwargs ({style, checkpoint, lora, lora_strength, negative} para **spread en el builder), size (resolucion recomendada), transparent (recorte recomendado), post (post-proceso CPU: {'pixelize': {...}} o {})."
tested: true
tests: ["golden gameboy: subject combina suffix (8-bit), builder_kwargs con las 5 claves comunes, checkpoint dreamshaper, lora None, post pixelize paleta game-boy", "golden contrato: los builder_kwargs hacen **spread en comfyui_build_item_icon_workflow sin TypeError y el LoRA del preset aparece en el grafo", "edge style override sustituye el del preset", "edge negative se mergea con el del estilo (no se pierde photorealistic) y deduplica", "edge no muta el preset de entrada", "error subject vacio -> ValueError", "error preset incompleto -> ValueError"]
test_file_path: "python/functions/ml/comfyui_apply_style_preset_test.py"
file_path: "python/functions/ml/comfyui_apply_style_preset.py"
---
## Ejemplo
```python
import sys, os
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
from ml.comfyui_get_gamedev_style_preset import comfyui_get_gamedev_style_preset
from ml.comfyui_apply_style_preset import comfyui_apply_style_preset
from ml.comfyui_build_enemy_creature_workflow import comfyui_build_enemy_creature_workflow
# 1. Elegir estilo + aplicarlo a un subject
preset = comfyui_get_gamedev_style_preset("gameboy")
ap = comfyui_apply_style_preset(preset, "knight character")
# 2. Construir el workflow con cualquier builder de sujeto (kwargs por **spread)
wf = comfyui_build_enemy_creature_workflow(
ap["subject"], size=ap["size"], transparent=ap["transparent"], **ap["builder_kwargs"]
)
# 3. Generar (submit/wait/fetch) y, si el estilo lo pide, post-proceso:
# if ap["post"].get("pixelize"):
# comfyui_pixelize_image(raw_png, dst_png, **ap["post"]["pixelize"])
```
O lanzable directo con: `./fn run comfyui_apply_style_preset` (aplica pixel-art-retro a "knight character").
## Cuando usarla
Justo despues de elegir un estilo con `comfyui_get_gamedev_style_preset`, para convertir
esa receta en los argumentos exactos de un builder. Es el puente entre "que estilo quiero"
y "como lo paso a item_icon/enemy_creature/prop_object/...". El mismo `ap` sirve para
generar N assets distintos en el MISMO estilo (varia solo el `subject`). Para overrides
puntuales sin tocar el preset, usa `style=`/`negative=`.
## Gotchas
- Devuelve `builder_kwargs` con EXACTAMENTE las 5 claves comunes a los builders de SUJETO
(`style`, `checkpoint`, `lora`, `lora_strength`, `negative`). Builders que NO las acepten
todas (p.ej. `seamless_tile`, `parallax_background` no tienen `transparent`/`lora` igual)
exigen filtrar las claves; este helper esta pensado para los builders de sujeto cuadrado.
- `size` y `transparent` van FUERA de `builder_kwargs` (son recomendaciones del estilo): el
caller los pasa explicitos o decide otros. `transparent=False` en los presets de demo es
para que el look (paleta/pintura) cubra todo el frame; para un sprite con alpha pon
`transparent=True` (el recorte es ortogonal al estilo).
- El `post` NO se aplica solo: el caller debe llamar `comfyui_pixelize_image(raw, dst,
**ap["post"]["pixelize"])` tras descargar el PNG si `ap["post"].get("pixelize")`. Sin eso,
estilos como gameboy/pixel-art-retro no sellan su grid/paleta.
- Es **pura**: no llama a ningun builder ni toca la GPU; solo arma kwargs. No muta el
`preset` de entrada (lo que devuelve es independiente).
## Capability growth log
(v1.0.0 — sin cambios todavia.)