---
name: comfyui_harvest_civitai_skill_oneshot
kind: pipeline
lang: py
domain: pipelines
version: "1.0.0"
purity: impure
signature: "def comfyui_harvest_civitai_skill_oneshot(*, query: str | None = None, model_version_id: int | None = None, nsfw: str = \"None\", dest_dir: str, library_dir: str = \"~/ComfyUI/skills_library\", comfyui_dir: str = \"~/ComfyUI\", token: str | None = None, slug: str | None = None, pick_index: int = 0, search_limit: int = 20) -> dict"
description: "One-shot Civitai -> skill candidata: search_images -> fetch (segrega NSFW) -> extract_recipe -> save_skill (candidata, score_n=0, provenance.source='civitai'). Itera los items hasta encontrar uno con receta destilable (preferentemente workflow ComfyUI embebido), descartando los PNG intermedios sin receta. Si se filtro por modelo y ninguno trae workflow, hace un 2o pase al feed global 'Most Reactions'. NO baja modelos a ciegas: si la receta referencia un checkpoint/LoRA ausente lo reporta en missing_models. Doctrina issue 0087 aplicada a cosechar assets de internet. Pipeline impuro: HTTP + escritura en disco."
tags: [comfyui, civitai, pipelines, ml, skill, harvest, comfyui-skill]
uses_functions: ["comfyui_search_civitai_images_py_ml", "comfyui_fetch_civitai_image_py_ml", "comfyui_extract_recipe_from_png_py_ml", "comfyui_save_skill_py_ml"]
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: ["os", "sys"]
params:
  - name: query
    desc: "Texto de busqueda (best-effort: se resuelve a una version de modelo via search). keyword-only."
  - name: model_version_id
    desc: "Filtra por una version de modelo concreta (filtro fiable). keyword-only."
  - name: nsfw
    desc: "Nivel NSFW del search Civitai ('None'=SFW por defecto, 'Soft'/'Mature'/'X'). keyword-only."
  - name: dest_dir
    desc: "Carpeta donde se descarga el PNG (NSFW se segrega a nsfw/). keyword-only, obligatorio."
  - name: library_dir
    desc: "Libreria de skills. Default ~/ComfyUI/skills_library. keyword-only."
  - name: comfyui_dir
    desc: "Raiz de ComfyUI para chequear modelos instalados (missing_models). keyword-only."
  - name: token
    desc: "Token Civitai. None lo resuelve de pass civitai/api-token. No hardcodear. keyword-only."
  - name: slug
    desc: "Slug de la skill. Si None se deriva del prompt cosechado. keyword-only."
  - name: pick_index
    desc: "Indice del item del search por el que empezar a iterar (default 0). keyword-only."
  - name: search_limit
    desc: "Cuantas imagenes pedir al search. keyword-only."
output: "dict {ok, slug, skill_path, png_path, recipe, nsfw, missing_models, image_id, has_workflow, attempts, error}. ok=False con error claro en fallo de red/guardado o si ningun item produjo receta; 0 resultados -> ok=False."
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/pipelines/comfyui_harvest_civitai_skill_oneshot.py"
---

## Ejemplo

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

res = comfyui_harvest_civitai_skill_oneshot(
    query="cinematic portrait", nsfw="None",
    dest_dir=os.path.expanduser("~/ComfyUI/civitai_harvest"),
)
print(res["ok"], res["slug"], "workflow_embebido=", res["has_workflow"])
print("guardada en:", res["skill_path"])          # ~/ComfyUI/skills_library/<slug>/recipe.json
print("modelos ausentes:", res["missing_models"]) # checkpoints/LoRAs a bajar (NO bajados)
# La receta queda como CANDIDATA (score_n=0). El bucle del grupo comfyui-skill
# (generate -> judge -> bump) la valida y promueve si funciona.
```

## Cuando usarla

Cuando quieras cosechar de Civitai una imagen con su workflow+receta embebidos y
guardarla como skill candidata de un tiron, sin reconstruir la receta a mano. Es
la 3a pieza del sistema `comfyui-skill`: alimenta la libreria con recetas reales
de internet para clonar su calidad. Luego juzga/promueve con
`comfyui_generate_with_skill_oneshot` + `comfyui_bump_skill_version`.

## Gotchas

- **NO baja modelos a ciegas**: si la receta cosechada referencia un checkpoint o
  LoRA no instalado en `<comfyui_dir>/models/`, lo lista en `missing_models` y NO
  descarga nada. Bajarlos (con `comfyui_search_civitai_models` +
  `comfyui_download_model`) es una decision aparte del caller.
- **NSFW segregado**: el PNG cosechado se descarga a `<dest_dir>/nsfw/` si el item
  es NSFW (politica del sistema: permitido pero SIEMPRE separado). El `dest_dir`
  vive fuera del repo (`~/ComfyUI/`) y NO se commitea: trata las imagenes como
  datos.
- **Itera y descarta**: como muchas imagenes de Civitai no traen workflow ComfyUI
  (JPEG recomprimido / A1111), el pipeline prueba varios items y borra los PNG sin
  receta. `attempts` indica cuantos probo; `has_workflow` si la elegida traia el
  grafo embebido.
- **2o pase global**: si filtras por un modelo (query resuelto o model_version_id)
  y ninguna imagen de esa version trae workflow, reintenta en el feed global "Most
  Reactions" (donde abundan los workflows ComfyUI de usuarios flux). Por eso un
  query tematico acaba dando candidata aunque no filtre fuerte.
- **La skill nace CANDIDATA** (`score_n=0`, `provenance.source='civitai'`): no esta
  validada. El prompt es concreto, no un scaffold con `{subject}`.
- El token es secreto: viene de `pass civitai/api-token`, nunca en claro.
- Pipeline impuro: HTTP + escritura en disco. Requiere internet y la libreria de
  skills es estado local (no se indexa, vive bajo `~/ComfyUI/skills_library`).
