---
name: comfyui_import_workflow_json
kind: function
lang: py
domain: ml
version: "1.1.0"
purity: impure
signature: "def comfyui_import_workflow_json(source: str, *, server: str = \"127.0.0.1:8188\", timeout: float = 15.0) -> dict"
description: "Lee un workflow ComfyUI desde una URL (http/https) o un path local y lo normaliza a API format. Si viene en formato UI graph ({nodes, links}) lo convierte a API format usando /object_info para mapear los widgets; si ya es API format lo devuelve tal cual. Omite los nodos virtuales del editor (Note, MarkdownNote, PrimitiveNode, Reroute) tal como hace ComfyUI al pasar UI->API: resuelve los Reroute reconectando la conexion directa origen->destino e inyecta los PrimitiveNode como valor de widget en el consumidor. Compone comfyui_object_info. Impura: HTTP GET / lectura de disco."
tags: [comfyui, ml, import, workflow, stable-diffusion]
uses_functions: [comfyui_object_info_py_ml]
uses_types: []
returns: []
returns_optional: false
error_type: error_go_core
imports: []
params:
  - name: source
    desc: "URL http(s) de un JSON de workflow (OpenArt, ComfyWorkflows, raw GitHub...) o ruta de un archivo local."
  - name: server
    desc: "host:port de ComfyUI usado SOLO para mapear los valores de widget cuando la fuente viene en formato UI graph. keyword-only."
  - name: timeout
    desc: "Timeout HTTP en segundos. keyword-only."
output: "dict {ok, workflow, format_detected, error}. workflow = dict en API format; format_detected = 'api' (passthrough) o 'ui_graph' (convertido) o ''. Si falla la lectura/parse, ok=False y error explica."
tested: true
tests: ["API format se devuelve tal cual (format=api)", "UI graph se normaliza a API (descarta Note, resuelve conexiones)", "JSON invalido -> error", "formato no reconocido -> error", "JSON no es objeto -> error", "archivo inexistente -> error", "determinismo"]
test_file_path: "python/functions/ml/tests/test_comfyui_import_workflow_json.py"
file_path: "python/functions/ml/comfyui_import_workflow_json.py"
---

## Ejemplo

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

# Desde un archivo local en API format (passthrough)
res = comfyui_import_workflow_json("/tmp/mi_workflow.json")
# res == {"ok": True, "workflow": {...}, "format_detected": "api", "error": ""}

# Desde una URL (descarga + normaliza si viene como UI graph)
res2 = comfyui_import_workflow_json("https://raw.githubusercontent.com/user/repo/main/wf.json")
# res2["format_detected"] in ("api", "ui_graph")
```

Lánzalo con el python del venv (import de arriba o heredoc). Nota: `./fn run` directo no aplica porque la firma usa `*` (keyword-only), no soportado por el generador de runner de `fn run`.

## Cuando usarla

Cuando quieras lanzar un workflow ajeno (de OpenArt, ComfyWorkflows, raw GitHub o
un .json local) por la API. Devuelve siempre API format, listo para
`comfyui_validate_workflow` + `comfyui_submit_workflow`. Para workflows embebidos
en un PNG usa `comfyui_import_workflow_png`.

## Gotchas

- Impura: HTTP GET si `source` es URL, lectura de disco si es path. Lectura/JSON
  invalido devuelven `{ok: False, error: ...}` (no lanza).
- La conversion UI graph -> API es best-effort: las CONEXIONES entre nodos se
  reconstruyen siempre, pero el mapeo de los valores de widget (steps, cfg, texto)
  necesita `/object_info` del servidor. Si el servidor esta caido, los widgets del
  UI graph NO se mapean (quedan fuera) — valida el resultado antes de encolar.
- El orden de widgets en object_info se asume = orden de widgets_values del UI
  graph; nodos custom muy raros pueden desalinearse.
- API format se detecta porque todos los valores top-level son dicts con
  `class_type`; UI graph por la clave `nodes`. Otros JSON dan
  "formato no reconocido".
- Los nodos virtuales del editor (`Note`, `MarkdownNote`, `PrimitiveNode`,
  `Reroute` y variantes `Reroute*`) NO aparecen en el API format resultante —
  igual que cuando ComfyUI exporta UI->API. Los `Reroute` se resuelven saltando
  el passthrough y reconectando el origen real al consumidor; una cadena de
  Reroutes rota (entrada sin link) o con ciclo deja el input sin conexion en
  lugar de apuntar a un nodo inexistente. Los `PrimitiveNode` se inyectan como
  valor literal de widget en el consumidor (su `widgets_values[0]`).
- El filtrado es idempotente: un workflow ya en API format (sin nodos virtuales)
  pasa intacto; un UI graph sin virtuales conserva todas sus conexiones.

## Capability growth log

- v1.1.0 (2026-06-24) — la conversion UI->API omite los nodos virtuales del
  editor (Note/MarkdownNote/PrimitiveNode/Reroute), resuelve los Reroute
  reconectando origen->destino e inyecta los PrimitiveNode como valor de widget.
  Antes esos nodos viajaban al API format y `comfyui_validate_workflow` los
  marcaba como `missing_nodes` (falsos positivos). Gap del report 0086.
