feat(ml): núcleo subsistema comfyui-skill + ask_llm_vision

Grupo nuevo comfyui-skill: recetas versionadas de generación ComfyUI que
compilan a un workflow cambiando solo el subject.

- comfyui_build_skill_workflow (pura): receta -> workflow API format,
  despacha base (txt2img/flux/sdxl_refiner), sustituye {subject}+triggers,
  encadena loras e inject blocks (facedetailer, hires_fix). SkillWorkflowError tipada.
- comfyui_inject_hires_fix (pura): inyecta 2ª pasada UltimateSDUpscale sobre dict.
- comfyui_save/load/list_skill (impuras): CRUD de la librería en disco con
  versionado por snapshots, round-trip idéntico, filtro NSFW.
- ask_llm_vision (core, claude-direct): pregunta multimodal imagen+texto via
  API directa Anthropic, para puntuar generaciones.
- Página madre docs/capabilities/comfyui-skill.md con schema canónico de recipe.json.

Tests offline: 11 verdes (6 builder + 5 inject_hires_fix). Sin GPU.
This commit is contained in:
agent
2026-06-24 14:35:46 +02:00
parent e8a66f0dad
commit 70d541fca9
15 changed files with 1666 additions and 0 deletions
+63
View File
@@ -0,0 +1,63 @@
---
name: comfyui_load_skill
kind: function
lang: py
domain: ml
version: "1.0.0"
purity: impure
signature: "def comfyui_load_skill(slug: str, *, version=None, library_dir: str = None) -> dict"
description: "Lee una receta de skill ComfyUI de la libreria de disco: recipe.json (version actual) o un snapshot versions/vN.json. Hermana inversa de comfyui_save_skill; el round-trip save(recipe)->load(slug) devuelve un dict identico. library_dir default ~/ComfyUI/skills_library. Slug, version o archivo inexistente -> {ok:False} sin lanzar."
error_type: error_go_core
tags: [comfyui, comfyui-skill, ml, skill, library]
uses_functions: []
uses_types: []
params:
- name: slug
desc: "Slug de la skill (nombre de su carpeta en la libreria)."
- name: version
desc: "Si None, lee recipe.json (version actual). Si se pasa (int 1, '1' o 'v1'), lee el snapshot versions/vN.json. keyword-only."
- name: library_dir
desc: "Raiz de la libreria en disco. Default ~/ComfyUI/skills_library. keyword-only."
output: "dict {ok, recipe, slug, path, version, error}. En exito ok=True y recipe es el dict guardado; si slug/version/archivo no existen ok=False y recipe=None."
file_path: python/functions/ml/comfyui_load_skill.py
---
# comfyui_load_skill
Carga una receta de skill guardada por [`comfyui_save_skill`](comfyui_save_skill.md). Paso de
entrada del flujo del grupo `comfyui-skill`: `load_skill`
[`comfyui_build_skill_workflow`](comfyui_build_skill_workflow.md) → submit → wait.
## Ejemplo
```python
import sys, os
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
from ml.comfyui_load_skill import comfyui_load_skill
# Version actual
res = comfyui_load_skill("portrait_cinematic_sdxl", library_dir="/tmp/skills_demo")
if res["ok"]:
recipe = res["recipe"]
# Un snapshot concreto (v1, v2, ...)
old = comfyui_load_skill("portrait_cinematic_sdxl", version=1, library_dir="/tmp/skills_demo")
```
O directo: `./fn run comfyui_load_skill demo_skill --library-dir /tmp/skills_demo`.
## Cuando usarla
- Para recuperar una receta antes de compilarla con `comfyui_build_skill_workflow`.
- Para inspeccionar un snapshot histórico concreto (`version=N`) y comparar cómo evolucionó una
skill.
## Gotchas
- **No lanza excepción**: slug inexistente, versión inválida o archivo ausente devuelven
`{ok: False, recipe: None, error: ...}`. Comprueba `res["ok"]` antes de usar `res["recipe"]`.
- **`version` acepta varios formatos**: int `1`, str `"1"` o `"v1"` apuntan a `versions/v1.json`.
Cualquier otra cosa da error de versión inválida.
- **Round-trip exacto con save**: lee el JSON tal cual se guardó; no normaliza ni rellena campos.
- **library_dir por defecto `~/ComfyUI/skills_library`**: pásalo explícito para librerías de
test o aisladas.