---
name: comfyui_build_inpaint_asset_workflow
kind: function
lang: py
domain: ml
purity: pure
version: 1.0.0
signature: "def comfyui_build_inpaint_asset_workflow(input_image: str, mask_image: str, prompt: str, *, checkpoint: str = \"IMG_dreamshaper_8.safetensors\", denoise: float = 1.0, style: str = \"game asset\", grow_mask: int = 6, size: int | None = None, seed: int = 0, lora: str | None = None, lora_strength: float = 1.0, mode: str = \"vae_encode\", negative: str | None = None, steps: int = 28, cfg: float = 7.0, sampler_name: str = \"dpmpp_2m\", scheduler: str = \"karras\", img_upscale_method: str = \"bilinear\", mask_upscale_method: str = \"nearest-exact\", crop: str = \"disabled\", filename_prefix: str = \"inpaint_asset\") -> dict"
description: "Construye el dict (API format) del workflow que EDITA SOLO UNA REGION de un asset 2D ya pintado (inpaint): recibe el asset + una mascara (blanco = editar, negro = conservar) + un prompt de que poner en esa zona, y repinta UNICAMENTE la region enmascarada dejando el resto del sprite intacto. Es el cuarto vertice del eje transform de gamedev-2d: distinto de txt2img (enemy_creature/item_icon, inventan la forma desde texto), de img2img (asset_variant, reescribe TODO el asset), y de ControlNet (sprite_from_sketch, pinta un sprite nuevo desde un boceto). Mecanismo (modo vae_encode): VAEEncodeForInpaint codifica el latente respetando la mascara y dilata su borde grow_mask px para difuminar la costura; el KSampler (denoise alto) regenera solo esa zona con '{prompt}, {style}, seamless blend'. Modo noise_mask degrada a VAEEncode + SetLatentNoiseMask (+ GrowMask) para servidores sin VAEEncodeForInpaint. size escala imagen Y mascara de forma consistente (escalar solo una las desalinearia). Compone comfyui_build_inpaint_workflow + comfyui_inject_lora (estilo opcional). Pura, sin red ni I/O. class_types verificados contra /object_info (8GB lowvram)."
tags: [comfyui, ml, gamedev-2d, inpaint, asset-transform, mask-edit, stable-diffusion, workflow]
uses_functions: [comfyui_build_inpaint_workflow_py_ml, comfyui_inject_lora_py_ml]
uses_types: []
returns: []
returns_optional: false
error_type: ""
params:
  - name: input_image
    desc: "Nombre del archivo del asset a editar dentro de la carpeta input/ del servidor ComfyUI (un sprite/icono YA pintado). Lo carga el nodo LoadImage. Subelo antes con POST /upload/image o copialo a ~/ComfyUI/input/. No puede estar vacio."
  - name: mask_image
    desc: "Nombre del archivo de la mascara dentro de input/ del servidor. BLANCO = region a editar/repintar, NEGRO = region a conservar. Debe tener la MISMA resolucion que input_image (salvo que pases size, que reescala ambas consistentemente). No puede estar vacio: sin mascara no es inpaint (para reescribir el asset entero usa comfyui_build_asset_variant_workflow)."
  - name: prompt
    desc: "Que poner en la region enmascarada ('a golden sword', 'a blue shield', 'empty background', 'intact armor'). Es lo unico que se regenera; el resto del asset se conserva. No puede estar vacio."
  - name: checkpoint
    desc: "Checkpoint del servidor. 'IMG_dreamshaper_8.safetensors' (SD1.5, holgado en 8GB lowvram) por defecto. keyword-only."
  - name: denoise
    desc: "Fuerza de denoising del KSampler dentro de la mascara. En inpaint suele ser alto: 1.0 (por defecto) reescribe la region por completo; <1.0 conserva parte de los pixeles originales (reparacion suave). Se clampa a [0.0, 1.0]. keyword-only."
  - name: style
    desc: "Descriptor de estilo que mantiene la region coherente con el asset y el set ('game asset', 'dark fantasy creature', 'pixel art'). Mismo style + checkpoint + (lora) que el resto del set. keyword-only."
  - name: grow_mask
    desc: "Pixeles que se dilata el borde de la mascara para difuminar la costura entre lo viejo y lo nuevo (evita bordes duros). Se clampa a [0, 64] (limite del nodo VAEEncodeForInpaint). En modo noise_mask se aplica via GrowMask. keyword-only."
  - name: size
    desc: "Lado en px al que se NORMALIZAN imagen Y mascara antes de inpaint. None (por defecto) = no escala: conserva la resolucion nativa del asset y exige que mascara e imagen ya coincidan en tamano (lo recomendado; los assets del set ya salen a 512). Un int reescala AMBAS a size x size de forma consistente. Solo aplica al modo vae_encode. keyword-only."
  - name: seed
    desc: "Semilla del KSampler. keyword-only."
  - name: lora
    desc: "LoRA de estilo opcional en models/loras (ej. 'SD15_dark_fantasy.safetensors'). None = sin LoRA. keyword-only."
  - name: lora_strength
    desc: "Fuerza del LoRA sobre model y clip. Se clampa a [0.0, 2.0]. keyword-only."
  - name: mode
    desc: "'vae_encode' (por defecto, VAEEncodeForInpaint con grow_mask nativo) o 'noise_mask' (degrada a VAEEncode + SetLatentNoiseMask + GrowMask para servidores sin VAEEncodeForInpaint). El caller decide tras consultar /object_info. keyword-only."
  - name: negative
    desc: "Prompt negativo. None usa el negativo por defecto pensado para edicion local (mezcla limpia, sin costuras ni bordes de mascara, una figura). keyword-only."
  - name: steps
    desc: "Pasos de sampling del KSampler. keyword-only."
  - name: cfg
    desc: "Classifier-free guidance scale. keyword-only."
  - name: sampler_name
    desc: "Nombre del sampler (ej. 'dpmpp_2m', 'euler'). keyword-only."
  - name: scheduler
    desc: "Scheduler del sampler (ej. 'karras', 'normal'). keyword-only."
  - name: img_upscale_method
    desc: "Metodo de ImageScale para la imagen cuando size no es None ('bilinear' por defecto; 'lanczos' NO esta disponible en este servidor). keyword-only."
  - name: mask_upscale_method
    desc: "Metodo de ImageScale para la mascara cuando size no es None ('nearest-exact' por defecto, preserva bordes nitidos blanco/negro de la mascara). keyword-only."
  - name: crop
    desc: "Modo de recorte de ImageScale ('disabled' o 'center'). Solo si size no es None. keyword-only."
  - name: filename_prefix
    desc: "Prefijo del archivo de salida en SaveImage. keyword-only."
output: "dict en API format listo para comfyui_submit_workflow: inpaint que repinta SOLO la region marcada en blanco por la mascara, conservando el resto del asset, con grow_mask para difuminar la costura, escalado consistente opcional (img+mask) y LoRA de estilo opcional. Nodos modo vae_encode: CheckpointLoaderSimple '4', LoadImage '10', LoadImageMask '12', VAEEncodeForInpaint '11', CLIPTextEncode '6'/'7', KSampler '3', VAEDecode '8', SaveImage '9' (+ ImageScale/ImageToMask si size, + LoraLoader si lora). Modo noise_mask sustituye VAEEncodeForInpaint por VAEEncode + SetLatentNoiseMask (+ GrowMask)."
tested: true
tests: ["estructura vae_encode (LoadImage+LoadImageMask+VAEEncodeForInpaint)", "prompt de region + grow_mask reflejados", "grow_mask se clampa a [0,64]", "mode noise_mask degrada a VAEEncode+SetLatentNoiseMask+GrowMask", "size inserta ImageScale a imagen y mascara + ImageToMask", "lora opcional + filename_prefix", "errores: input/mask/prompt vacios, mode invalido", "determinismo"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_inpaint_asset_workflow.py"
file_path: python/functions/ml/comfyui_build_inpaint_asset_workflow.py
---

Construye el dict (API format) del workflow que **edita SOLO una región** de un asset
2D ya pintado (inpaint). Cuarto vértice del eje **transform** de `gamedev-2d`, junto a
sus hermanos `comfyui_build_asset_variant_workflow` (img2img: reescribe todo) y
`comfyui_build_sprite_from_sketch_workflow` (ControlNet: pinta desde un boceto). Aquí
el dev tiene un sprite terminado y quiere cambiar **una parte** —ponerle otra arma,
quitarle el casco, añadir un escudo, reparar una zona dañada— dejando el resto del
pixel intacto. Una máscara delimita qué se repinta (blanco = editar, negro = conservar)
y el sampler solo regenera ahí.

## Ejemplo

```python
import sys, os
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
from ml.comfyui_build_inpaint_asset_workflow import comfyui_build_inpaint_asset_workflow
from ml.comfyui_submit_workflow import comfyui_submit_workflow
from ml.comfyui_wait_result import comfyui_wait_result

# Editar SOLO la mano del goblin: poner un orbe mágico, conservar el resto del sprite.
# (input_image y mask_image deben estar ya subidos al input/ del server, misma resolución)
wf = comfyui_build_inpaint_asset_workflow(
    "enemy_creature_00001_.png",          # el goblin (512x512, ya en ~/ComfyUI/input/)
    "goblin_hand_mask.png",               # máscara: blanco sobre la mano, negro el resto
    "a glowing blue magic orb",           # qué poner en la región enmascarada
    style="dark fantasy creature, game asset",
    grow_mask=8,                          # difumina la costura 8 px
    denoise=1.0,                          # reescribe la región por completo
    seed=7,
)
resp = comfyui_submit_workflow(wf, server="127.0.0.1:8188")
out = comfyui_wait_result(resp["prompt_id"], server="127.0.0.1:8188")
# La imagen sale en ~/ComfyUI/output/inpaint_asset_*.png: SOLO la mano cambió.
```

## Cuando usarla

Úsala cuando tengas un asset 2D **ya pintado** y quieras cambiar **una zona concreta**
sin regenerar el resto: cambiar/añadir un objeto que sostiene un personaje, quitar una
pieza de equipo, reparar una región dañada o limpiar un fondo detrás del sujeto. Elige
entre los hermanos del eje transform así:

- **una región, el resto intacto** → este builder (inpaint con máscara).
- **todo el asset, mismo diseño/pose, otro material/tier** → `asset_variant` (img2img).
- **un sprite nuevo cuya silueta marca un boceto** → `sprite_from_sketch` (ControlNet).
- **un asset de cero desde texto** → `enemy_creature` / `item_icon` (txt2img).

## Gotchas

- **Necesita una máscara real.** `mask_image` es obligatorio (blanco = editar, negro =
  conservar). Sin máscara no es inpaint: lanza `ValueError`. Para reescribir el asset
  entero usa `asset_variant`. La máscara y el asset deben tener la **misma resolución**
  (o pasa `size`, que reescala ambos de forma consistente; escalar solo uno los
  desalinea y la edición cae en el sitio equivocado).
- **`grow_mask` evita costuras.** Sin dilatar el borde de la máscara aparece una línea
  dura entre lo viejo y lo nuevo. 6–10 px suele bastar; se clampa a `[0, 64]`.
- **`denoise` alto por defecto (1.0).** La región se reescribe por completo. Baja a
  ~0.5–0.7 para reparaciones suaves que conserven parte de los píxeles originales bajo
  la máscara.
- **`mode="noise_mask"` es el plan B.** Si el servidor no expone `VAEEncodeForInpaint`
  (compruébalo con `/object_info`), pásalo: arma el equivalente con `VAEEncode` +
  `SetLatentNoiseMask` (+ `GrowMask`). El camino por defecto `vae_encode` da mejores
  bordes y es el recomendado.
- **`ImageScale` no ofrece 'lanczos' en este servidor** (8GB lowvram): métodos válidos
  `nearest-exact`, `bilinear`, `area`, `bicubic`. La máscara se escala con
  `nearest-exact` por defecto para no difuminar sus bordes blanco/negro.
- **Función pura.** No sube ni valida que `input_image`/`mask_image`/`checkpoint`/`lora`
  existan en el servidor: súbelos antes (`POST /upload/image`) y valida con
  `comfyui_validate_workflow` si quieres atrapar nombres inexistentes antes de enviar.
