From 5f0df32728d6b9908de925e376168bfc8381e1c5 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Wed, 24 Jun 2026 17:37:07 +0200 Subject: [PATCH] fix(browser): limpiar previews/outputs residuales al cargar workflow ComfyUI app.loadApiJson (lo que usa comfyui_load_workflow_ui) reconstruye el grafo pero no llama a app.clean(), por lo que no resetea el store app.nodeOutputs ni los previews de los nodos. Cuando un workflow nuevo reusa un node_id existente en el store, el preview cacheado del workflow anterior se re-pinta sobre el nodo nuevo (visto: imagen 3D pegada bajo un CheckpointLoaderSimple/SaveGLB). - Nueva funcion comfyui_clear_node_outputs_ui: limpieza no destructiva del store app.nodeOutputs + node.imgs/images, sin tocar la topologia del grafo. - comfyui_load_workflow_ui v1.1.0: anade clear_outputs=True (default) que invoca la limpieza antes de loadApiJson, replicando la garantia de loadGraphData. Reproducido y verificado en la UI real (CDP 9222) con evidencia antes/despues. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../browser/comfyui_clear_node_outputs_ui.md | 67 +++++++++++ .../browser/comfyui_clear_node_outputs_ui.py | 105 ++++++++++++++++++ .../browser/comfyui_load_workflow_ui.md | 24 +++- .../browser/comfyui_load_workflow_ui.py | 15 +++ 4 files changed, 207 insertions(+), 4 deletions(-) create mode 100644 python/functions/browser/comfyui_clear_node_outputs_ui.md create mode 100644 python/functions/browser/comfyui_clear_node_outputs_ui.py diff --git a/python/functions/browser/comfyui_clear_node_outputs_ui.md b/python/functions/browser/comfyui_clear_node_outputs_ui.md new file mode 100644 index 00000000..1002ab11 --- /dev/null +++ b/python/functions/browser/comfyui_clear_node_outputs_ui.md @@ -0,0 +1,67 @@ +--- +name: comfyui_clear_node_outputs_ui +kind: function +lang: py +domain: browser +version: "1.0.0" +purity: impure +signature: "def comfyui_clear_node_outputs_ui(*, port: int = 9222, server_url_substr: str = '8188', timeout_s: float = 15.0) -> dict" +description: "Limpia outputs/previews residuales de TODOS los nodos del grafo de ComfyUI en la UI via CDP: vacia app.nodeOutputs (store de previews keyed by node_id) y borra imgs/images de cada nodo vivo, sin tocar la topologia del grafo (no borra nodos ni links). Arregla el bug de imagenes pegadas a nodos que no corresponden tras cargar un workflow nuevo con app.loadApiJson. Compone cdp_eval. Impura: red (CDP) + muta la UI." +tags: [comfyui, browser, cdp, ml, ui-automation, image-generation] +uses_functions: ["cdp_eval_py_browser"] +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: [] +params: + - name: port + desc: "Puerto de remote debugging del Chrome diario. Default 9222." + - name: server_url_substr + desc: "Substring de la URL de la pestana de ComfyUI (default '8188', el puerto del server). Identifica la pestana entre las abiertas." + - name: timeout_s + desc: "Timeout de la conexion CDP en segundos. Default 15.0." +output: "dict {ok, cleared, error, store_cleared, nodes_touched, nodes}. ok/cleared True si la limpieza termino sin excepcion. store_cleared = entradas borradas de app.nodeOutputs; nodes_touched = nodos a los que se les quito un preview; nodes = total de nodos del grafo." +tested: false +tests: [] +test_file_path: "" +file_path: "python/functions/browser/comfyui_clear_node_outputs_ui.py" +--- + +## Ejemplo + +```python +import sys, os +sys.path.insert(0, os.path.join("python", "functions")) +from browser.comfyui_clear_node_outputs_ui import comfyui_clear_node_outputs_ui + +# Requiere la UI de ComfyUI abierta en el Chrome con CDP en el puerto 9222. +print(comfyui_clear_node_outputs_ui()) +# -> {'ok': True, 'cleared': True, 'error': '', 'store_cleared': 6, 'nodes_touched': 2, 'nodes': 12} +``` + +## Cuando usarla + +Cuando ves previews/outputs de imagenes pegados a nodos que no los produjeron +(una imagen bajo un `CheckpointLoaderSimple`, un `SaveGLB`, etc.) tras haber +cargado varios workflows seguidos en la misma pestana. Es la limpieza no +destructiva: borra los previews residuales del grafo actual SIN recargarlo ni +perder la topologia. `comfyui_load_workflow_ui(..., clear_outputs=True)` la +invoca automaticamente antes de cargar, asi que normalmente no hace falta +llamarla a mano; usala solo para limpiar un grafo ya cargado sin recargarlo. + +## Gotchas + +- Requiere la pestana de ComfyUI abierta en un Chrome con + `--remote-debugging-port=9222`. Si no hay target que matchee + `server_url_substr`, `cdp_eval` devuelve error y aqui `ok=False`. +- Borra TODOS los previews del grafo, incluidos los legitimos de la ultima + ejecucion. Si quieres conservar un preview concreto, no la llames; el residuo + cross-workflow se evita de raiz cargando con + `comfyui_load_workflow_ui(..., clear_outputs=True)`. +- No es `app.clean()`: a proposito NO hace `rootGraph.clear()`, por eso es + segura sobre el grafo vivo del usuario (no borra nodos ni conexiones). +- El store que vacia es `app.nodeOutputs`; el nombre interno puede variar entre + versiones de ComfyUI. Si una version renombra el store, el borrado del store + no aplica pero el barrido de `node.imgs`/`node.images` sigue limpiando los + previews visibles. diff --git a/python/functions/browser/comfyui_clear_node_outputs_ui.py b/python/functions/browser/comfyui_clear_node_outputs_ui.py new file mode 100644 index 00000000..b883f8e1 --- /dev/null +++ b/python/functions/browser/comfyui_clear_node_outputs_ui.py @@ -0,0 +1,105 @@ +"""Limpia los outputs/previews residuales de los nodos de ComfyUI en la UI via CDP. + +ComfyUI cachea los outputs de cada ejecucion en `app.nodeOutputs`, un store +indexado por node_id. La ruta de carga `app.loadApiJson` (la que usa +comfyui_load_workflow_ui) reconstruye el grafo pero NO resetea ese store ni los +previews de los nodos. Cuando un workflow nuevo reusa un node_id que ya existia +en el store, el preview cacheado del workflow anterior se vuelve a pintar sobre +el nodo nuevo, que muchas veces es de otro tipo (ej. una imagen pegada bajo un +`CheckpointLoaderSimple` o un `SaveGLB`). + +Esta funcion vacia `app.nodeOutputs` y borra `imgs`/`images` de todos los nodos +vivos del grafo, sin tocar la topologia del grafo (no borra nodos ni links), y +marca el canvas dirty para repintar. Es la version no destructiva de +`app.clean()` (que ademas haria `rootGraph.clear()`). + +Funcion impura: hace red (CDP WebSocket) y muta el estado de la UI. +""" + +try: # ejecucion directa del archivo / fn run (browser/ en sys.path[0]) + from cdp_eval import cdp_eval +except ImportError: # importado como paquete (sys.path = python/functions) + from browser.cdp_eval import cdp_eval + + +def comfyui_clear_node_outputs_ui( + *, + port: int = 9222, + server_url_substr: str = "8188", + timeout_s: float = 15.0, +) -> dict: + """Limpia previews/outputs residuales de todos los nodos del grafo de ComfyUI. + + Args: + port: puerto de remote debugging del Chrome diario. Default 9222. + server_url_substr: substring de la URL de la pestana de ComfyUI (default + "8188", el puerto del server). Identifica la pestana entre las + abiertas. + timeout_s: timeout de la conexion CDP en segundos. + + Returns: + dict {ok: bool, cleared: bool, error: str, store_cleared: int, + nodes_touched: int, nodes: int}. ok/cleared True si la limpieza termino + sin excepcion en la pagina. `store_cleared` es el numero de entradas + eliminadas de `app.nodeOutputs`; `nodes_touched` los nodos a los que se + les quito un preview; `nodes` el total de nodos del grafo. + """ + expr = ( + "(function(){" + " if(!window.app){ return {ok:false, cleared:false, error:'window.app no disponible en la pestana'}; }" + " try{" + " var store=0;" + " if(app.nodeOutputs){ for(var k in app.nodeOutputs){ if(Object.prototype.hasOwnProperty.call(app.nodeOutputs,k)){ delete app.nodeOutputs[k]; store++; } } }" + " var nodes=(app.graph && app.graph._nodes)? app.graph._nodes : [];" + " var touched=0;" + " for(var i=0;i dict" -description: "Carga un workflow ComfyUI (API format) en la UI del navegador via CDP: inyecta app.loadApiJson(, filename) en la pestana de ComfyUI abierta y reconstruye el grafo visual. Compone cdp_eval (transport CDP). Impura: red (CDP WebSocket) + muta el grafo de la UI." +signature: "def comfyui_load_workflow_ui(workflow: dict, *, port: int = 9222, server_url_substr: str = '8188', filename: str = 'workflow.json', clear_outputs: bool = True, timeout_s: float = 20.0) -> dict" +description: "Carga un workflow ComfyUI (API format) en la UI del navegador via CDP: inyecta app.loadApiJson(, filename) en la pestana de ComfyUI abierta y reconstruye el grafo visual. Por defecto (clear_outputs=True) limpia antes los previews/outputs residuales para que un preview cacheado del workflow anterior no se pegue a un nodo nuevo que reusa el mismo node_id. Compone cdp_eval + comfyui_clear_node_outputs_ui. Impura: red (CDP WebSocket) + muta el grafo de la UI." tags: [comfyui, browser, cdp, ml, image-generation, stable-diffusion, ui-automation] -uses_functions: ["cdp_eval_py_browser"] +uses_functions: ["cdp_eval_py_browser", "comfyui_clear_node_outputs_ui_py_browser"] uses_types: [] returns: [] returns_optional: false @@ -23,6 +23,8 @@ params: desc: "Substring de la URL de la pestana de ComfyUI (default '8188', el puerto del server). Identifica la pestana entre las abiertas." - name: filename desc: "Nombre que ComfyUI asocia al workflow cargado. Default 'workflow.json'." + - name: clear_outputs + desc: "Si True (default) limpia previews/outputs residuales (app.nodeOutputs + node.imgs) antes de cargar, evitando que un preview cacheado de un workflow anterior se pegue a un nodo nuevo que reusa el mismo node_id. False conserva los previews previos a proposito." - name: timeout_s desc: "Timeout de la conexion CDP en segundos. Default 20.0." output: "dict {ok: bool, loaded: bool, error: str}. ok/loaded True si app.loadApiJson termino sin excepcion en la pagina." @@ -66,3 +68,17 @@ editarlo en la UI del navegador del usuario antes de encolarlo. Es el puente necesitas. - Espera la Promise de carga (`await_promise=True`). El conteo de nodos cargados se puede verificar con `cdp_eval("app.graph._nodes.length", target_url_substr="8188")`. +- `app.loadApiJson` (a diferencia de la ruta del menu `app.loadGraphData`) NO + llama a `app.clean()`, asi que NO resetea el store `app.nodeOutputs` ni los + previews de los nodos. Sin `clear_outputs=True`, un preview cacheado de un + workflow anterior se re-pinta sobre el nodo nuevo que reuse el mismo node_id + (visto: imagen 3D pegada bajo un `CheckpointLoaderSimple`/`SaveGLB`). El + default `clear_outputs=True` lo evita delegando en + `comfyui_clear_node_outputs_ui`. + +## Capability growth log + +- v1.1.0 (2026-06-24) — anade `clear_outputs=True` (default): limpia los + previews/outputs residuales (`app.nodeOutputs` + `node.imgs`) antes de cargar, + delegando en `comfyui_clear_node_outputs_ui`. Fija el bug de imagenes + residuales pegadas a nodos que reusan node_id entre workflows. diff --git a/python/functions/browser/comfyui_load_workflow_ui.py b/python/functions/browser/comfyui_load_workflow_ui.py index cc2fab91..9e7a1531 100644 --- a/python/functions/browser/comfyui_load_workflow_ui.py +++ b/python/functions/browser/comfyui_load_workflow_ui.py @@ -11,8 +11,10 @@ import json try: # ejecucion directa del archivo / fn run (browser/ en sys.path[0]) from cdp_eval import cdp_eval + from comfyui_clear_node_outputs_ui import comfyui_clear_node_outputs_ui except ImportError: # importado como paquete (sys.path = python/functions) from browser.cdp_eval import cdp_eval + from browser.comfyui_clear_node_outputs_ui import comfyui_clear_node_outputs_ui def comfyui_load_workflow_ui( @@ -21,6 +23,7 @@ def comfyui_load_workflow_ui( port: int = 9222, server_url_substr: str = "8188", filename: str = "workflow.json", + clear_outputs: bool = True, timeout_s: float = 20.0, ) -> dict: """Carga un workflow API format en el grafo de la UI de ComfyUI. @@ -33,12 +36,24 @@ def comfyui_load_workflow_ui( "8188", el puerto del server). Identifica la pestana entre todas las abiertas. filename: nombre que ComfyUI asocia al workflow cargado. + clear_outputs: si True (default), limpia los previews/outputs residuales + (app.nodeOutputs + node.imgs) ANTES de cargar, replicando lo que hace + la ruta del menu (app.clean()). Evita que un preview cacheado de un + workflow anterior se pegue a un nodo nuevo que reusa el mismo node_id + (bug de imagenes residuales). Ponlo en False solo si quieres conservar + a proposito los previews del grafo previo. timeout_s: timeout de la conexion CDP en segundos. Returns: dict {ok: bool, loaded: bool, error: str}. ok/loaded True si app.loadApiJson termino sin excepcion en la pagina. """ + if clear_outputs: + comfyui_clear_node_outputs_ui( + port=port, + server_url_substr=server_url_substr, + timeout_s=timeout_s, + ) expr = ( "(async function(){" " if(!window.app || typeof app.loadApiJson!=='function'){"