Skip to content

Commit 6afdfd9

Browse files
DN6sayakpaul
andauthored
Update diffusers-cli for agentic use (#13966)
* update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * pdate * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update --------- Co-authored-by: Sayak Paul <spsayakpaul@gmail.com>
1 parent b48d49d commit 6afdfd9

14 files changed

Lines changed: 3161 additions & 21 deletions

File tree

.ai/skills/custom-blocks/SKILL.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
---
2+
name: custom-blocks
3+
description: >
4+
Use when the user has written (or wants to write) a `ModularPipelineBlocks`
5+
subclass in a local Python file and needs to package it into a Hub-uploadable
6+
directory. Covers the workflow from a single `block.py` file to a published
7+
custom-block repo that consumers can load via
8+
`ModularPipeline.from_pretrained(<repo>, trust_remote_code=True)`.
9+
---
10+
11+
## What this skill is for
12+
13+
A `ModularPipelineBlocks` subclass is a unit of pipeline logic — input/output spec plus a `__call__` — that
14+
slots into diffusers' modular pipeline composition. Once you have one defined locally, you almost always want to
15+
publish it as a small Hub repo so others can `from_pretrained` it. `diffusers-cli custom_blocks` automates the
16+
packaging step: it parses your Python file, instantiates the chosen block class, and writes a
17+
`save_pretrained`-style directory in your cwd that's ready to push to the Hub.
18+
19+
Use this skill when:
20+
21+
- The user is writing a custom modular block and asks "how do I publish this?" or "package this for the Hub".
22+
- The user has a `block.py` (or similar) file with one or more `ModularPipelineBlocks` subclasses.
23+
- You're scaffolding a new modular pipeline repo and need the on-disk layout that `ModularPipelineBlocks.from_pretrained`
24+
expects.
25+
26+
Don't use this skill for: running an existing modular pipeline (`diffusers-cli run`), introspecting one
27+
(`diffusers-cli schema`), or writing the block class itself — this skill packages an *already-written* block.
28+
29+
## The end-to-end workflow
30+
31+
```
32+
[you: write block.py] → diffusers-cli custom_blocks → [packaged dir in cwd]
33+
34+
hf upload <repo> .
35+
36+
consumers: ModularPipeline.from_pretrained(<repo>, trust_remote_code=True)
37+
diffusers-cli schema --model <repo> --trust-remote-code
38+
diffusers-cli run --model <repo> --trust-remote-code ...
39+
```
40+
41+
The skill covers the middle box. The bookends (writing the block and uploading) are out of scope.
42+
43+
## Command surface
44+
45+
```bash
46+
diffusers-cli custom_blocks [--block_module_name <file.py>] [--block_class_name <ClassName>]
47+
```
48+
49+
### Flags
50+
51+
- `--block_module_name <file>` — Python file containing the block class. Defaults to `block.py` in the cwd.
52+
- `--block_class_name <name>` — Which class in the file to package. Optional: if omitted, the CLI parses the
53+
file with `ast`, finds every class that inherits from `ModularPipelineBlocks`, and uses the first one (with
54+
an info log naming the others). Specify explicitly when the file defines more than one block and you want a
55+
specific one.
56+
57+
### What it does
58+
59+
1. **AST scan**: parses `<file>` without executing it, walks top-level `ClassDef` nodes, and collects every
60+
class whose `bases` include `ModularPipelineBlocks`.
61+
2. **Pick a class**: uses `--block_class_name` if given, else the first found. Errors with the list of available
62+
classes if your name doesn't match.
63+
3. **Load and save**: imports the file via `importlib.util.spec_from_file_location` (this does execute the
64+
module — make sure your block.py is something you trust to run), instantiates the chosen class with no
65+
constructor args, and calls `.save_pretrained(os.getcwd())`.
66+
67+
The result is a Hub-uploadable directory laid out the way `ModularPipelineBlocks.from_pretrained` expects:
68+
your block source, an `auto_map` in the config so consumers know to load it with `trust_remote_code=True`,
69+
and any artifacts `save_pretrained` writes for that block class.
70+
71+
## End-to-end example
72+
73+
Given a `block.py` like:
74+
75+
```python
76+
from diffusers.modular_pipelines import ModularPipelineBlocks, InputParam, OutputParam
77+
78+
class MyDenoiseBlock(ModularPipelineBlocks):
79+
model_name = "my-denoise"
80+
81+
@property
82+
def inputs(self):
83+
return [
84+
InputParam("latents", type_hint="torch.Tensor", required=True, description="Noisy latents."),
85+
InputParam("guidance_scale", type_hint="float", default=7.5),
86+
]
87+
88+
@property
89+
def intermediate_outputs(self):
90+
return [OutputParam("latents", type_hint="torch.Tensor")]
91+
92+
def __call__(self, components, state):
93+
# ... denoising logic ...
94+
return components, state
95+
```
96+
97+
Package it:
98+
99+
```bash
100+
diffusers-cli custom_blocks --block_module_name block.py
101+
```
102+
103+
Output in cwd:
104+
105+
```
106+
./
107+
├── block.py
108+
├── modular_config.json # contains auto_map → MyDenoiseBlock
109+
└── (any state files MyDenoiseBlock.save_pretrained writes)
110+
```
111+
112+
Upload to the Hub:
113+
114+
```bash
115+
hf upload my-user/my-denoise-block .
116+
```
117+
118+
Consumers can now use it:
119+
120+
```python
121+
from diffusers import ModularPipeline
122+
pipe = ModularPipeline.from_pretrained("my-user/my-denoise-block", trust_remote_code=True)
123+
```
124+
125+
Or via CLI:
126+
127+
```bash
128+
diffusers-cli schema --model my-user/my-denoise-block --trust-remote-code
129+
diffusers-cli run --model my-user/my-denoise-block --trust-remote-code \
130+
--pipeline-kwargs '{"latents": "...", "guidance_scale": 7.5}'
131+
```
132+
133+
## Common errors
134+
135+
- **`Could not parse '<file>': SyntaxError`** — the file isn't valid Python. Fix the syntax; the AST step runs
136+
before any execution.
137+
- **`block_class_name could not be retrieved. Available classes from <file>: [ClassA, ClassB]`** — your
138+
`--block_class_name` doesn't match any `ModularPipelineBlocks` subclass found. Pick from the list shown.
139+
- **No classes found**: silent — the command will try to use the first entry in an empty list and raise
140+
`IndexError`. If you hit that, double-check your class actually inherits from `ModularPipelineBlocks`
141+
(the AST scan looks for that literal base-class name; aliased imports like `from diffusers import ...
142+
as MPB` won't be picked up).
143+
- **Block requires constructor args**: the command calls `<ClassName>()` with no args. If your block needs
144+
`__init__` parameters, refactor to take them from `state`/`components` at `__call__` time instead, or
145+
hardcode defaults in `__init__`.
146+
147+
## Verifying the install
148+
149+
If `diffusers-cli` isn't on PATH, see the install verification section of
150+
[`../diffusers-cli/SKILL.md`](../diffusers-cli/SKILL.md#verifying-the-cli-is-installed).
151+
152+
## Related
153+
154+
- [`diffusers-cli` skill](../diffusers-cli/SKILL.md) — once your block is uploaded, `schema`/`run`
155+
let you call it from the terminal without writing Python.
156+
- diffusers' [modular pipelines docs](../../../docs/source/en/modular_diffusers) — for writing the block
157+
class itself.

.ai/skills/diffusers-cli/SKILL.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
name: diffusers-cli
3+
description: >
4+
Use when the user wants to run a diffusers pipeline from a terminal (one-off
5+
generation, batch jobs, smoke-testing a new model), run on HF Sandbox
6+
hardware via `--remote`, introspect a pipeline's input schema before
7+
calling it, or attach a LoRA at inference time. Prefer this over writing
8+
ad-hoc Python scripts for generation tasks.
9+
---
10+
11+
## Overview
12+
13+
`diffusers-cli` is the shipped CLI in `src/diffusers/commands/`. Subcommands relevant to agentic use:
14+
15+
| Command | Purpose |
16+
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
17+
| `run` | Run any `DiffusionPipeline` or `ModularPipeline`. Forwards `--pipeline-kwargs` verbatim, saves output by detecting its runtime type, optionally runs on HF Jobs via `--remote`. |
18+
| `schema` | Print the input schema for a pipeline repo (kwarg names, types, defaults, descriptions). **No weights downloaded** — only the small index file. |
19+
| `custom_blocks` | Package a local `ModularPipelineBlocks` subclass for the Hub. |
20+
| `env` | Print versions of diffusers + torch + transformers + accelerate + safetensors + CUDA + GPU info. Use when investigating environment issues, dtype/precision support, or building bug reports. |
21+
22+
## When to read which file
23+
24+
Most agentic work goes through `run`. Read the matching reference file before constructing a command:
25+
26+
- **[`run.md`](run.md)** — full reference for `diffusers-cli run`. Covers `--pipeline-kwargs`
27+
semantics and the shell-quoting gotcha, LoRA via `--lora`, optimization flags (`--dtype`, `--cpu-offload`,
28+
`--attention-backend`, `--vae-tiling/slicing`), output handling and `--push-to` bucket uploads, the full
29+
`--remote` HF Jobs flow (image, container command, log streaming, timing payload, artifact download), and
30+
context parallel (`--context-parallel`) for both local-torchrun and `--remote` paths.
31+
32+
The other commands are small enough that `diffusers-cli <command> --help` is the canonical reference:
33+
34+
```bash
35+
diffusers-cli schema --help
36+
diffusers-cli custom_blocks --help
37+
diffusers-cli env --help
38+
```
39+
40+
## When NOT to use this skill
41+
42+
- Multi-stage workflows where you need intermediate tensor manipulation between pipelines → write Python.
43+
- Training or fine-tuning → CLI only covers inference.
44+
- Anything requiring `quantization_config` or other low-level loader knobs not exposed by the CLI flags → write
45+
Python. (`device_map` is exposed as `--device-map`; see [run.md](run.md#optimization-flags).)
46+
47+
## Verifying the CLI is installed
48+
49+
The console entry point is registered in `pyproject.toml` (`diffusers-cli =
50+
"diffusers.commands.diffusers_cli:main"`). If `diffusers-cli` is not on PATH after `pip install -e .`, reinstall
51+
with `pip install -e . --force-reinstall --no-deps` and check `which diffusers-cli`. If the installed binary is
52+
missing recent features (e.g. you see `unrecognized arguments: --lora`), reinstall.
53+
54+
## Output formats
55+
56+
`--format {auto, human, agent, json}` (top-level flag, must appear before the subcommand):
57+
58+
- **`human`** — plain-text indented output for terminals (default when not running under an agent harness). No ANSI color.
59+
- **`agent`** — TSV tables and `key=value` lines. Auto-selected when an agent env var is present
60+
(`CLAUDECODE`, `CLAUDE_CODE`, `CODEX_SANDBOX`, `CURSOR_AI`, `AIDER_AI_CONTEXT`, `GH_COPILOT_AGENT`,
61+
`AI_AGENT`). Token-cheap for LLM agents to read.
62+
- **`json`** — compact JSON. Use for programmatic parsing (scripts, services) where type fidelity and nested
63+
structures matter.
64+
65+
`stdout` carries data; `stderr` carries hints/warnings/progress — parseable output is never polluted.
66+
67+
Rule of thumb: `--format json` for scripts that will `json.loads()` the output, otherwise leave it on
68+
auto-detect (`agent` for LLMs, `human` for terminals).

0 commit comments

Comments
 (0)