docs(comfyui): consolidar las 5 funciones nuevas del grupo (tests + capability page)

Higiene del grupo comfyui sobre las 5 funciones de la sesión:
comfyui_build_audio_workflow, comfyui_fetch_output_audio,
comfyui_build_flux_workflow, comfyui_list_templates, comfyui_extract_template.

- Tests nuevos para list_templates y extract_template (lógica pura: localización
  del intérprete, error-path sin el paquete instalado, contrato del dict; golden
  condicional con skip si no hay ComfyUI con comfyui-workflow-templates). 10 tests,
  todos verdes.
- comfyui_list_templates.md / comfyui_extract_template.md: tested true + tests +
  test_file_path.
- Fix drift de test_file_path en comfyui_fetch_output_audio.md (apuntaba a un
  *_test.py inexistente; corregido a tests/test_*.py). Elimina el WARN de fn index.
- docs/capabilities/comfyui.md: subsecciones Audio (ACE-Step) y Templates oficiales.
- docs/capabilities/comfyui-overview.md: sección 05b audio, fetch_output_audio en
  Outputs, Templates oficiales en Workflows I/O. (flux ya estaba documentada.)

fn index limpio (las 5 sin WARN); sin drift nuevo en fn doctor uses-functions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-28 04:46:47 +02:00
parent a1074d32e7
commit 7d33b39859
7 changed files with 224 additions and 8 deletions
@@ -24,9 +24,13 @@ params:
- name: server
desc: "host:port del servidor ComfyUI usado para la conversion to_api (default '127.0.0.1:8188')."
output: "dict {ok, name, format, class_types, has_subgraphs, n_nodes, graph, api_workflow, api_error, bundle, version, assets, error}. graph = dict del template (formato UI o API). class_types = lista ordenada de tipos de nodo reales. api_workflow = dict API si to_api tuvo exito, si no {}. Nunca lanza: nombre inexistente -> ok=False con error + sugerencias."
tested: false
tests: []
test_file_path: ""
tested: true
tests:
- "sin el paquete instalado -> ok=False con error que menciona comfyui-workflow-templates"
- "el nombre pedido se preserva y el dict trae todas sus claves aun en fallo"
- "golden (skip si no hay ComfyUI con el paquete): extrae un template real con graph + class_types no vacios"
- "golden (skip si no hay ComfyUI con el paquete): nombre inexistente -> ok=False con error legible"
test_file_path: "python/functions/ml/tests/test_comfyui_extract_template.py"
file_path: "python/functions/ml/comfyui_extract_template.py"
---
@@ -33,7 +33,7 @@ tests:
- "test_find_saveaudiomp3_bajo_audio"
- "test_find_prioriza_clave_audio"
- "test_find_sin_audio_devuelve_none"
test_file_path: "python/functions/ml/comfyui_fetch_output_audio_test.py"
test_file_path: "python/functions/ml/tests/test_comfyui_fetch_output_audio.py"
file_path: "python/functions/ml/comfyui_fetch_output_audio.py"
---
@@ -28,9 +28,15 @@ params:
- name: limit
desc: "Si > 0, trunca a los primeros N templates tras filtrar y ordenar por nombre."
output: "dict {ok: bool, count: int, package_version: str, templates: list, error: str}. Cada template: {name, category, bundle, version, path, n_nodes, node_types, is_workflow}. Nunca lanza: paquete ausente o interprete no hallado -> ok=False con error legible que indica como instalar (pip install comfyui-workflow-templates)."
tested: false
tests: []
test_file_path: ""
tested: true
tests:
- "_find_comfyui_python: interprete existente se devuelve tal cual"
- "_find_comfyui_python: ruta inexistente cae al fallback (sys.executable)"
- "sin el paquete instalado -> ok=False con error que menciona comfyui-workflow-templates"
- "el dict de retorno conserva todas sus claves aun en fallo"
- "golden (skip si no hay ComfyUI con el paquete): catalogo no vacio, cada template con name+bundle"
- "golden (skip si no hay ComfyUI con el paquete): bundle inexistente filtra a lista vacia con ok=True"
test_file_path: "python/functions/ml/tests/test_comfyui_list_templates.py"
file_path: "python/functions/ml/comfyui_list_templates.py"
---
@@ -0,0 +1,86 @@
"""Tests para comfyui_extract_template.
Cubre, sin tocar red ni GPU:
- El camino de error legible cuando el paquete `comfyui-workflow-templates` no
esta instalado: subprocess local contra el python del venv del registry (que no
lo tiene) -> `ok=False` con mensaje accionable, sin lanzar.
- El contrato del dict de retorno (claves presentes, nombre preservado) aun en
fallo.
El golden path (extraer un template real con sus class_types) y el error
'template inexistente -> sugerencias' solo se ejecutan si hay un ComfyUI con el
paquete instalado; si no, se omiten con `pytest.skip`. Nunca dependen de GPU ni
de un servidor ComfyUI vivo (la conversion to_api, que si necesita servidor, no
se ejercita aqui).
"""
import os
import subprocess
import sys
import pytest
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
from comfyui_extract_template import _find_comfyui_python, comfyui_extract_template
_PKG = "comfyui_workflow_templates_core"
_RET_KEYS = {
"ok", "name", "format", "class_types", "has_subgraphs", "n_nodes",
"graph", "api_workflow", "api_error", "bundle", "version", "assets", "error",
}
def _python_con_paquete():
"""Devuelve un interprete que importa el paquete, o None (para omitir el golden)."""
py = _find_comfyui_python(None)
if not py:
return None
r = subprocess.run([py, "-c", f"import {_PKG}"], capture_output=True)
return py if r.returncode == 0 else None
def test_extract_sin_paquete_error_legible():
# El venv del registry no tiene el paquete -> ok=False con error que lo menciona.
res = comfyui_extract_template("image_sdxl", comfyui_python=sys.executable)
assert res["ok"] is False
assert res["graph"] == {}
assert res["class_types"] == []
assert "comfyui-workflow-templates" in res["error"]
def test_extract_preserva_nombre_y_claves():
# El nombre pedido se preserva y el dict trae siempre todas sus claves.
res = comfyui_extract_template("cualquier_nombre", comfyui_python=sys.executable)
assert res["name"] == "cualquier_nombre"
assert _RET_KEYS <= set(res)
def test_extract_golden_template_real():
py = _python_con_paquete()
if not py:
pytest.skip("no hay ComfyUI con comfyui-workflow-templates instalado")
# Toma el primer template real del catalogo y extraelo (to_api=False: sin servidor).
from comfyui_list_templates import comfyui_list_templates
cat = comfyui_list_templates(comfyui_python=py, with_nodes=False, limit=1)
assert cat["ok"] and cat["count"] >= 1
name = cat["templates"][0]["name"]
res = comfyui_extract_template(name, comfyui_python=py)
assert res["ok"] is True
assert res["name"] == name
assert isinstance(res["graph"], dict) and res["graph"]
assert len(res["class_types"]) > 0
assert res["format"] in ("ui_graph", "api")
def test_extract_nombre_inexistente_error_con_sugerencias():
py = _python_con_paquete()
if not py:
pytest.skip("no hay ComfyUI con comfyui-workflow-templates instalado")
res = comfyui_extract_template(
"zzz_template_que_no_existe_jamas", comfyui_python=py
)
assert res["ok"] is False
assert "no existe" in res["error"]
@@ -0,0 +1,87 @@
"""Tests para comfyui_list_templates.
Cubre dos cosas sin tocar red ni GPU:
- La localizacion del interprete (`_find_comfyui_python`), que solo consulta el
sistema de ficheros.
- El camino de error legible cuando el paquete `comfyui-workflow-templates` no
esta instalado: se ejecuta un subprocess local contra el python indicado (el
del propio venv del registry, que no tiene el paquete) y se comprueba que la
funcion devuelve `ok=False` con un mensaje accionable, sin lanzar.
El golden path (catalogo de templates no vacio) y un edge de filtrado solo se
ejecutan si hay un ComfyUI con el paquete instalado; si no, se omiten con
`pytest.skip`. Nunca dependen de GPU ni de un servidor ComfyUI vivo.
"""
import os
import subprocess
import sys
import pytest
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
from comfyui_list_templates import _find_comfyui_python, comfyui_list_templates
_PKG = "comfyui_workflow_templates_core"
_RET_KEYS = {"ok", "count", "package_version", "templates", "error"}
def _python_con_paquete():
"""Devuelve un interprete que importa el paquete, o None (para omitir el golden)."""
py = _find_comfyui_python(None)
if not py:
return None
r = subprocess.run([py, "-c", f"import {_PKG}"], capture_output=True)
return py if r.returncode == 0 else None
def test_find_comfyui_python_explicit_valido():
# Un interprete que existe en disco se devuelve tal cual.
assert _find_comfyui_python(sys.executable) == sys.executable
def test_find_comfyui_python_inexistente_cae_a_fallback():
# Una ruta inexistente no rompe: cae al siguiente candidato (sys.executable existe).
got = _find_comfyui_python("/ruta/que/no/existe/python")
assert got is not None and os.path.isfile(got)
def test_list_sin_paquete_error_legible():
# El venv del registry no tiene el paquete -> ok=False con error que lo menciona.
res = comfyui_list_templates(comfyui_python=sys.executable)
assert res["ok"] is False
assert res["count"] == 0
assert res["templates"] == []
assert "comfyui-workflow-templates" in res["error"]
def test_list_retorno_tiene_todas_las_claves():
# El contrato del dict de retorno se mantiene aun en fallo.
res = comfyui_list_templates(comfyui_python=sys.executable)
assert _RET_KEYS <= set(res)
def test_list_golden_catalogo_no_vacio():
py = _python_con_paquete()
if not py:
pytest.skip("no hay ComfyUI con comfyui-workflow-templates instalado")
res = comfyui_list_templates(comfyui_python=py, with_nodes=False, limit=5)
assert res["ok"] is True
assert res["count"] > 0
assert len(res["templates"]) == res["count"]
# Cada template trae al menos nombre y bundle.
for t in res["templates"]:
assert t.get("name")
assert t.get("bundle")
def test_list_golden_filtro_bundle_inexistente_vacio():
py = _python_con_paquete()
if not py:
pytest.skip("no hay ComfyUI con comfyui-workflow-templates instalado")
# Un bundle que no existe filtra a una lista vacia pero la llamada sigue siendo ok.
res = comfyui_list_templates(comfyui_python=py, bundle="bundle-inexistente-xyz")
assert res["ok"] is True
assert res["count"] == 0
assert res["templates"] == []