# ModularPipeline

[ModularPipeline](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline) преобразует [ModularPipelineBlocks](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_blocks#diffusers.ModularPipelineBlocks) в исполняемый конвейер, который загружает модели и выполняет вычислительные шаги, определённые в блоках. Это основной интерфейс для запуска конвейера, и его API очень похож на [DiffusionPipeline](/docs/diffusers/main/ru/api/pipelines/overview#diffusers.DiffusionPipeline), но с несколькими ключевыми отличиями.

- **Loading is lazy.** With [DiffusionPipeline](/docs/diffusers/main/ru/api/pipelines/overview#diffusers.DiffusionPipeline), [from_pretrained()](/docs/diffusers/main/ru/api/pipelines/overview#diffusers.DiffusionPipeline.from_pretrained) creates the pipeline and loads all models at the same time. With [ModularPipeline](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline), creating and loading are two separate steps: [from_pretrained()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline.from_pretrained) reads the configuration and knows where to load each component from, but doesn't actually load the model weights. You load the models later with [load_components()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline.load_components), which is where you pass loading arguments like `dtype` and `quantization_config`.

- **Два способа создания пайплайна.** Можно использовать [from_pretrained()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline.from_pretrained) с существующим репозиторием модели diffusers — он автоматически сопоставляется с блоками пайплайна по умолчанию, а затем преобразуется в [ModularPipeline](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline) без дополнительной настройки. Список поддерживаемых моделей можно найти в [modular_pipelines_directory](https://github.com/huggingface/diffusers/tree/main/src/diffusers/modular_pipelines). Также можно собрать собственный пайплайн из [ModularPipelineBlocks](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_blocks#diffusers.ModularPipelineBlocks) и преобразовать его с помощью метода [init_pipeline()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_blocks#diffusers.ModularPipelineBlocks.init_pipeline) (подробнее см. [Создание пайплайна](#creating-a-pipeline)).

- **Running the pipeline is the same.** Once loaded, you call the pipeline with the same arguments you're used to. A single [ModularPipeline](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline) can support multiple workflows (text-to-image, image-to-image, inpainting, etc.) when the pipeline blocks use [`AutoPipelineBlocks`](./auto_pipeline_blocks) to automatically select the workflow based on your inputs.

Ниже приведены полные примеры для генерации изображений по тексту, преобразования изображения в изображение и инпейнтинга с SDXL.

```py
import torch
from diffusers import ModularPipeline

pipeline = ModularPipeline.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0")
pipeline.load_components(dtype=torch.float16)
pipeline.to("cuda")  # or "mps", "xpu", "cpu"

image = pipeline(prompt="Astronaut in a jungle, cold color palette, muted colors, detailed, 8k").images[0]
image.save("modular_t2i_out.png")
```

```py
import torch
from diffusers import ModularPipeline
from diffusers.utils import load_image

pipeline = ModularPipeline.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0")
pipeline.load_components(dtype=torch.float16)
pipeline.to("cuda")  # or "mps", "xpu", "cpu"

url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/sdxl-text2img.png"
init_image = load_image(url)
prompt = "a dog catching a frisbee in the jungle"
image = pipeline(prompt=prompt, image=init_image, strength=0.8).images[0]
image.save("modular_i2i_out.png")
```

```py
import torch
from diffusers import ModularPipeline
from diffusers.utils import load_image

pipeline = ModularPipeline.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0")
pipeline.load_components(dtype=torch.float16)
pipeline.to("cuda")  # or "mps", "xpu", "cpu"

img_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/sdxl-text2img.png"
mask_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/sdxl-inpaint-mask.png"

init_image = load_image(img_url)
mask_image = load_image(mask_url)

prompt = "A deep sea diver floating"
image = pipeline(prompt=prompt, image=init_image, mask_image=mask_image, strength=0.85).images[0]
image.save("modular_inpaint_out.png")
```

В этом руководстве показано, как создать [ModularPipeline](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline), управлять его компонентами и запустить конвейер.

## Создание конвейера

There are two ways to create a [ModularPipeline](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline). Assemble and create a pipeline from [ModularPipelineBlocks](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_blocks#diffusers.ModularPipelineBlocks) with [init_pipeline()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_blocks#diffusers.ModularPipelineBlocks.init_pipeline), or load an existing pipeline with [from_pretrained()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline.from_pretrained).

Вы также можете инициализировать [`ComponentsManager`](./components_manager) для управления размещением на устройствах и управлением памятью. Если автоматическое выгружение не требуется, вы можете пропустить этот шаг и вручную переместить конвейер на ваше устройство с помощью `pipeline.to("cuda")`.

> [!TIP]
> См. документацию [ComponentsManager](./components_manager) для получения подробной информации о том, как он помогает управлять компонентами в разных рабочих процессах.

### init_pipeline

[init_pipeline()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_blocks#diffusers.ModularPipelineBlocks.init_pipeline) преобразует любой [ModularPipelineBlocks](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_blocks#diffusers.ModularPipelineBlocks) в [ModularPipeline](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline).

Определим минимальный блок, чтобы посмотреть, как он работает:

```py
from transformers import CLIPTextModel
from diffusers.modular_pipelines import (
    ComponentSpec,
    ModularPipelineBlocks,
    PipelineState,
)

class MyBlock(ModularPipelineBlocks):
    @property
    def expected_components(self):
        return [
            ComponentSpec(
                name="text_encoder",
                type_hint=CLIPTextModel,
                pretrained_model_name_or_path="openai/clip-vit-large-patch14",
            ),
        ]

    def __call__(self, components, state: PipelineState) -> PipelineState:
        return components, state
```

Вызовите [init_pipeline()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_blocks#diffusers.ModularPipelineBlocks.init_pipeline), чтобы преобразовать его в пайплайн. Атрибут `blocks` пайплайна содержит блоки, из которых он был создан, — они определяют ожидаемые входные и выходные данные, а также логику вычислений.

```py
block = MyBlock()
pipe = block.init_pipeline()
pipe.blocks
```

```
MyBlock {
  "_class_name": "MyBlock",
  "_diffusers_version": "0.37.0.dev0"
}
```

> [!WARNING]
> Блоки изменяемы — вы можете свободно добавлять, удалять или заменять блоки до создания конвейера. Однако, как только конвейер создан, изменение `pipeline.blocks` не повлияет на конвейер, так как возвращается копия. Если вам нужна другая структура блоков, создайте новый конвейер после изменения блоков.

При вызове [init_pipeline()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_blocks#diffusers.ModularPipelineBlocks.init_pipeline) без указания репозитория используется `pretrained_model_name_or_path`, определённый в [ComponentSpec](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_components#diffusers.ComponentSpec) блока, чтобы определить, откуда загружать каждый компонент. Вывод пайплайна показывает конфигурацию загрузки компонентов.

```py
pipe
ModularPipeline {
  "_blocks_class_name": "MyBlock",
  "_class_name": "ModularPipeline",
  "_diffusers_version": "0.37.0.dev0",
  "text_encoder": [
    null,
    null,
    {
      "pretrained_model_name_or_path": "openai/clip-vit-large-patch14",
      "revision": null,
      "subfolder": "",
      "type_hint": [
        "transformers",
        "CLIPTextModel"
      ],
      "variant": null
    }
  ]
}
```

Если вы передадите репозиторий в [init_pipeline()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_blocks#diffusers.ModularPipelineBlocks.init_pipeline), он переопределит путь загрузки, сопоставляя компоненты вашего блока с конфигурацией пайплайна в этом репозитории (`model_index.json` или `modular_model_index.json`). См. [Modular repository](#modular-repository), чтобы узнать, как спецификации загрузки записываются и сохраняются.

В примере ниже `pretrained_model_name_or_path` будет обновлено до `"stabilityai/stable-diffusion-xl-base-1.0"`.

```py
pipe = block.init_pipeline("stabilityai/stable-diffusion-xl-base-1.0")
pipe
ModularPipeline {
  "_blocks_class_name": "MyBlock",
  "_class_name": "ModularPipeline",
  "_diffusers_version": "0.37.0.dev0",
  "text_encoder": [
    null,
    null,
    {
      "pretrained_model_name_or_path": "stabilityai/stable-diffusion-xl-base-1.0",
      "revision": null,
      "subfolder": "text_encoder",
      "type_hint": [
        "transformers",
        "CLIPTextModel"
      ],
      "variant": null
    }
  ]
}
```

Если компонент в вашем блоке отсутствует в репозитории, он остаётся `null` и пропускается при вызове [load_components()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline.load_components).

### from_pretrained

[from_pretrained()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline.from_pretrained) — удобный способ создать [ModularPipeline](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline), не определяя блоки самостоятельно.

Он работает с тремя типами репозиториев.

**Обычный репозиторий diffusers.** Передайте любой поддерживаемый репозиторий модели, и он автоматически сопоставится с блоками стандартного пайплайна. В настоящее время поддерживаются модели SDXL, Wan, Qwen, Z-Image, Flux и Flux2.

```py
from diffusers import ModularPipeline, ComponentsManager

components = ComponentsManager()
pipeline = ModularPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", components_manager=components
)
```

**Модульный репозиторий.** Эти репозитории содержат файл `modular_model_index.json`, в котором указаны классы каждого компонента и источники их загрузки, при этом сами компоненты хранятся рядом с этим файлом. См. [Модульный репозиторий](#modular-repository), чтобы узнать, как его создать.

```py
from diffusers import ModularPipeline, ComponentsManager

components = ComponentsManager()
pipeline = ModularPipeline.from_pretrained(
    "MiniMaxAI/MiniMax-H3", components_manager=components
)
```

**Модульный репозиторий с собственным кодом.** Некоторые репозитории содержат наряду с конфигурацией загрузки собственные блоки конвейера. Добавьте `trust_remote_code=True`, чтобы загрузить их. О том, как создать собственные блоки, см. в разделе [Собственные блоки](./custom_blocks).

```py
from diffusers import ModularPipeline, ComponentsManager

components = ComponentsManager()
pipeline = ModularPipeline.from_pretrained(
    "diffusers/Florence2-image-Annotator", trust_remote_code=True, components_manager=components
)
```

Когда блоки пайплайна определяют рабочие процессы (см. `pipeline.blocks.available_workflows`), передайте `workflow=`, чтобы оставить только блоки этого рабочего процесса — это та же обрезка, что и в [get_workflow()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_blocks#diffusers.ModularPipelineBlocks.get_workflow). Тогда пайплайн объявляет только те компоненты, которые использует данный рабочий процесс, а его docstring описывает именно его входные данные.

```py
pipeline = ModularPipeline.from_pretrained("Qwen/Qwen-Image", workflow="inpainting")
```

## Загрузка компонентов

[ModularPipeline](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline) не инициализируется автоматически с компонентами. Он загружает только конфигурацию и спецификации компонентов. Загрузить компоненты можно с помощью [load_components()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline.load_components).

Это загрузит все компоненты, для которых определена корректная спецификация загрузки.

```py
import torch

pipeline.load_components(dtype=torch.float16)
```

Вы также можете загрузить конкретные компоненты по имени. В примере ниже загружается только `text_encoder`.

```py
pipeline.load_components(names=["text_encoder"], dtype=torch.float16)
```

В конвейере, блоки которого определяют рабочие процессы, параметр `workflow=` загружает только те компоненты, которые используются этим рабочим процессом. Конвейер сохраняет все свои блоки, поэтому это удобный способ запускать один конвейер для разных рабочих процессов: каждый вызов добавляет лишь то, чего ещё не хватает новому рабочему процессу.

```py
pipeline.load_components(workflow="inpainting", dtype=torch.float16)
```

После загрузки вывод пайплайна показывает, какие компоненты загружены — первые два поля меняются с `null` на библиотеку и класс компонента.

```py
pipeline
```

```
# text_encoder is loaded - shows library and class
"text_encoder": [
  "transformers",
  "CLIPTextModel",
  { ... }
]

# unet is not loaded yet - still null
"unet": [
  null,
  null,
  { ... }
]
```

Параметры, передаваемые по имени, такие как `dtype`, `variant`, `revision` и `quantization_config`, передаются в `from_pretrained()` для каждого компонента. Можно передать одно значение, чтобы применить его ко всем компонентам, или словарь, чтобы задать значения для каждого компонента отдельно.

```py
# apply bfloat16 to all components
pipeline.load_components(dtype=torch.bfloat16)

# different dtypes per component
pipeline.load_components(dtype={"transformer": torch.bfloat16, "default": torch.float32})
```

[load_components()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline.load_components) загружает только те компоненты, которые ещё не были загружены и для которых задано корректное описание загрузки. Это означает, что если вы уже установили компонент в конвейере, повторный вызов [load_components()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline.load_components) не перезагрузит его.

## Обновление компонентов

[update_components()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline.update_components) заменяет компонент в конвейере новым. При обновлении компонента спецификации загрузки также обновляются в конфигурации конвейера, и [load_components()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline.load_components) будет пропускать его при последующих вызовах.

### Из AutoModel

Вы можете передать объект модели, загруженный с помощью `AutoModel.from_pretrained()`. Модели, загруженные таким образом, автоматически помечаются информацией о загрузке.

```py
from diffusers import AutoModel

unet = AutoModel.from_pretrained(
    "RunDiffusion/Juggernaut-XL-v9", subfolder="unet", variant="fp16", dtype=torch.float16
)
pipeline.update_components(unet=unet)
```

### Из ComponentSpec

Используйте [get_component_spec()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline.get_component_spec), чтобы получить копию текущей спецификации компонента, изменить её и загрузить новый компонент.

```py
unet_spec = pipeline.get_component_spec("unet")

# modify to load from a different repository
unet_spec.pretrained_model_name_or_path = "RunDiffusion/Juggernaut-XL-v9"

# load and update
unet = unet_spec.load(dtype=torch.float16)
pipeline.update_components(unet=unet)
```

Вы также можете создать [ComponentSpec](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_components#diffusers.ComponentSpec) с нуля.

Не все компоненты загружаются из предобученных весов — некоторые создаются на основе конфигурации (перечислены в `pipeline.config_component_names`). Для них используйте [create()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_components#diffusers.ComponentSpec.create) вместо [load()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_components#diffusers.ComponentSpec.load).

```py
guider_spec = pipeline.get_component_spec("guider")
guider_spec.config = {"guidance_scale": 5.0}
guider = guider_spec.create()
pipeline.update_components(guider=guider)
```

Или просто передайте объект напрямую.

```py
from diffusers.guiders import ClassifierFreeGuidance

guider = ClassifierFreeGuidance(guidance_scale=5.0)
pipeline.update_components(guider=guider)
```

См. руководство [Guiders](../using-diffusers/guiders) для получения подробной информации о доступных guiders и способах их настройки.

## Разделение конвейера на этапы

Поскольку блоки комбинируемы, вы можете разобрать пайплайн и собрать из него отдельные пайплайны для каждого этапа. В примере ниже показано, как можно выделить блок text encoder из остальной части пайплайна, чтобы независимо закодировать промпт и передать эмбеддинги в основной пайплайн.

```py
from diffusers import ModularPipeline, ComponentsManager
import torch

device = "cuda"  # or "mps", "xpu", "cpu"
dtype = torch.bfloat16
repo_id = "black-forest-labs/FLUX.2-klein-4B"

# get the blocks and separate out the text encoder
blocks = ModularPipeline.from_pretrained(repo_id).blocks
text_block = blocks.sub_blocks.pop("text_encoder")

# use ComponentsManager to handle offloading across multiple pipelines
manager = ComponentsManager()
manager.enable_auto_cpu_offload(device=device)

# create separate pipelines for each stage
text_encoder_pipeline = text_block.init_pipeline(repo_id, components_manager=manager)
pipeline = blocks.init_pipeline(repo_id, components_manager=manager)

# encode text
text_encoder_pipeline.load_components(dtype=dtype)
text_embeddings = text_encoder_pipeline(prompt="a cat").get_by_kwargs("denoiser_input_fields")

# denoise and decode
pipeline.load_components(dtype=dtype)
output = pipeline(
    **text_embeddings,
    num_inference_steps=4,
).images[0]
```

[ComponentsManager](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_components#diffusers.ComponentsManager) управляет памятью в нескольких конвейерах. В отличие от стратегий выгрузки в [DiffusionPipeline](/docs/diffusers/main/ru/api/pipelines/overview#diffusers.DiffusionPipeline), которые следуют фиксированному порядку, [ComponentsManager](/docs/diffusers/main/ru/api/modular_diffusers/pipeline_components#diffusers.ComponentsManager) принимает решения о выгрузке динамически при каждом проходе модели вперёд, исходя из текущего состояния памяти. Это означает, что он работает независимо от количества созданных конвейеров и порядка их выполнения. Подробнее см. в руководстве по [ComponentsManager](./components_manager).

Если этапы конвейера используют общие компоненты (например, одна и та же VAE для кодирования и декодирования), вы можете использовать [update_components()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline.update_components), чтобы передать уже загруженный компонент в другой конвейер, а не загружать его заново.

## Модульный репозиторий

Репозиторий требуется, если блоки конвейера используют *предобученные компоненты*. Репозиторий предоставляет спецификации загрузки и метаданные.

[ModularPipeline](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline) работает с обычными репозиториями diffusers без дополнительной настройки. Однако для большей гибкости можно создать *модульный репозиторий*. Модульный репозиторий содержит файл `modular_model_index.json` с тремя элементами.

- `library` и `class` показывают, из какой библиотеки был загружен компонент и его класс. Если `null`, компонент ещё не был загружен.
- `loading_specs_dict` содержит информацию, необходимую для загрузки компонента, например, репозиторий и подпапку, из которых он загружается.

```py
from diffusers import ModularPipeline

# load from a regular repo
pipeline = ModularPipeline.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0")
pipeline.load_components()

# push as a modular repository
pipeline.save_pretrained("local/path", repo_id="my-username/sdxl-modular", push_to_hub=True)
```

По умолчанию [save_pretrained()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline.save_pretrained) записывает каждый загруженный в данный момент компонент, который Diffusers может сериализовать. Компоненты, которые не загружены или не могут быть сериализованы, не записываются и сохраняют свои существующие спецификации загрузки.

### Сохранить автономную копию

Загрузите все компоненты, а затем сохраните. Каждая спецификация указывает на результат, поэтому он полностью перезагружается из одного места, включая офлайн-режим.

```py
pipe = ModularPipeline.from_pretrained("MiniMaxAI/MiniMax-H3")
pipe.load_components()
pipe.save_pretrained("path/to/local-copy")
```

Путь назначения, записанный в `modular_model_index.json`, — это то место, куда [save_pretrained()](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline.save_pretrained) записывает данные: `save_directory` при сохранении локально или `repo_id`, если вы передаёте `push_to_hub=True`.

Перемещение файлов любым другим способом не обновляет индекс. Копия, загруженная с помощью `hf download --local-dir`, по-прежнему работает: при загрузке пайплайна из локального каталога каждый компонент, файлы которого присутствуют в этом каталоге, загружается из него, а не из указанного в индексе репозитория. Копия, загруженная с помощью `hf upload`, по-прежнему указывает на старое расположение, поэтому в этом случае необходимо вручную обновить индекс.

Начиная с `huggingface_hub>=1.32.0`, идентичные файлы, использующие Xet, могут разделять один кэшированный payload между любыми репозиториями Hub. Это касается отдельных файлов в шардированном чекпоинте: шард с одинаковым Xet-хэшем может быть переиспользован, даже если другие шарды отличаются. Оба процесса загрузки должны использовать один и тот же каталог кэша Hub с поддержкой символьных ссылок. Изменение dtype, способа сериализации или границ шардов может изменить хэши файлов и предотвратить переиспользование. Это экономит трафик загрузки и место на диске, но не память, используемую загруженными компонентами.

Общее кэширование работает в режиме best-effort. Установка переменной `HF_HUB_DISABLE_SHARED_BLOBS=1` по-прежнему поддерживает загрузку, но кэширует отдельные копии. Существующие ссылки на компоненты в `modular_model_index.json` остаются без изменений; сохраняйте внешние ссылки для компонентов, которые не хранятся в модульном репозитории.

### Ссылки на компоненты в других репозиториях

Спецификация загрузки компонента в `modular_model_index.json` может указывать на другой репозиторий. Например, [diffusers/flux2-bnb-4bit-modular](https://huggingface.co/diffusers/flux2-bnb-4bit-modular) загружает свой квантованный трансформер из `diffusers/FLUX.2-dev-bnb-4bit`, а остальные компоненты — из `black-forest-labs/FLUX.2-dev`.

Мы настоятельно рекомендуем использовать самодостаточные репозитории: объединение всех компонентов в одном месте упрощает загрузку, обмен и использование пайплайна в автономном режиме без зависимости от других репозиториев. Используйте ссылки на другие репозитории только в том случае, если вы намеренно хотите разместить компонент в другом месте.

Передайте `overwrite_modular_index=False`, чтобы сохранить спецификации загрузки в `modular_model_index.json` без изменений. Сохранённый компонент с пустой спецификацией загрузки всё равно будет дополнен целевым значением, так как сохранять нечего.

Начиная с версии `huggingface_hub>=1.32.0`, идентичные файлы, использующие Xet, могут разделять один кэшированный payload между репозиториями через символические ссылки, что позволяет избежать дублирования загрузок и использования дискового пространства. Для этого не требуется создавать ссылки между репозиториями. Требования и ограничения для общего кэша описаны в разделе [Сохранение автономной копии](#save-a-self-contained-copy).

#### Обновление существующего модульного индекса

Существующие ссылки на исходный репозиторий продолжают работать. Если ссылка использовалась только для предотвращения дублирования загрузок, рекомендуется обновить её для загрузки из модульного репозитория, как только файлы компонентов станут там доступны.

Сначала убедитесь, что модульный репозиторий содержит ту же конфигурацию компонентов и все необходимые файлы весов, включая индекс весов и каждый шард для шардированного чекпоинта. Совпадение хешей Xet определяет, какие файлы весов можно повторно использовать из общего кэша. Сохраняйте ссылки на компоненты, которые отсутствуют или намеренно загружаются из другого чекпоинта.

Измените только соответствующую спецификацию загрузки в локальной копии `modular_model_index.json`. Например, чтобы использовать VAE, хранящийся в `vae/` в модульном репозитории, с тем же вариантом весов:

```py
import json
from pathlib import Path

index_path = Path("path/to/local-copy/modular_model_index.json")
index = json.loads(index_path.read_text())
spec = index["vae"][2]
spec["pretrained_model_name_or_path"] = "my-username/my-modular-repo"
spec.pop("repo", None)
spec["subfolder"] = "vae"
spec["revision"] = None
index_path.write_text(json.dumps(index, indent=2) + "\n")
```

Это также удаляет устаревшее поле `repo`. Ревизия из исходного репозитория не определяет ревизию в целевом: используйте `None` для ветки по умолчанию целевого репозитория или зафиксируйте ревизию целевого репозитория, содержащую компонент. При необходимости скорректируйте `subfolder` и `variant`, если имена файлов различаются.

Перед публикацией обновленного индекса загрузите его с помощью `ModularPipeline.from_pretrained("path/to/local-copy")`, вызовите `load_components(names="vae")` и убедитесь, что компонент загружается с ожидаемыми параметрами. Загружайте только отредактированный индекс; повторное сохранение весов может изменить их хеши и помешать повторному использованию кэша.

### Пользовательский код

Модульный репозиторий также может включать пользовательские блоки конвейера в виде Python-кода. Это позволяет делиться специализированными блоками, которые не являются нативными для Diffusers. Например, [diffusers/Florence2-image-Annotator](https://huggingface.co/diffusers/Florence2-image-Annotator) содержит пользовательские блоки наряду с конфигурацией загрузки:

```
Florence2-image-Annotator/
├── block.py                    # Custom pipeline blocks implementation
├── config.json                 # Pipeline configuration and auto_map
├── mellon_config.json          # UI configuration for Mellon
└── modular_model_index.json    # Component loading specifications
```

Файл `config.json` содержит ключ `auto_map`, который указывает [ModularPipeline](/docs/diffusers/main/ru/api/modular_diffusers/pipeline#diffusers.ModularPipeline), где искать пользовательские блоки:

```json
{
  "_class_name": "Florence2AnnotatorBlocks",
  "auto_map": {
    "ModularPipelineBlocks": "block.Florence2AnnotatorBlocks"
  }
}
```

Загружайте репозитории с пользовательским кодом с помощью `trust_remote_code=True`, как показано в [from_pretrained](#frompretrained). О том, как создавать и делиться собственными блоками, см. в разделе [Custom blocks](./custom_blocks).

