Diffusers documentation
Pika H3 Flex
Pika H3 Flex
Pika H3 Flex is Pika’s post-training of MiniMax-H3 into a six-step video-and-audio generator. It keeps MiniMax-H3’s packed sequence, its Qwen3-VL conditioner and its two VAEs, and changes two things: the sampler, a fixed six-step schedule that re-noises the clean prediction at every step in place of the Euler update, and the weights, a pair of rank-128 LoRA adapters over the two MiniMax-H3 transformer partitions. Video and its synchronized stereo soundtrack come out of the same six forward passes.
The release is the adapters: the FL2VA adapter (fl2va/adapter.safetensors) drives the transformer/ partition for text and endpoint-frame conditioning, the Ref2VA adapter (ref2va/adapter.safetensors) drives transformer_ref/ for multimodal references. The base components stay in MiniMaxAI/MiniMax-H3.
Pika H3 Flex is integrated as Modular Diffusers blocks only, like MiniMax-H3: the blocks and their PikaH3FlexModularPipeline are the whole integration, and there is no DiffusionPipeline half.
Loading
A pipeline is assembled in two steps: the components are loaded through a modular_model_index.json that names the MiniMax-H3 base components and the two scheduler configs, then the adapter of the selected workflow is loaded on top with load_lora_weights(). The adapters are plain PEFT dumps of the diffusers module names and load at their trained scale (alpha == rank, 1.0).
| Workflow | Inputs | Transformer partition | Adapter |
|---|---|---|---|
t2va | prompt | transformer/ | fl2va/adapter.safetensors |
fl2va | prompt and image and/or last_image | transformer/ | fl2va/adapter.safetensors |
ref2va | prompt and references | transformer_ref/ | ref2va/adapter.safetensors |
import torch
from diffusers import ModularPipeline
pipe = ModularPipeline.from_pretrained("Pika-Labs/Pika-H3-Flex", workflow="fl2va")
pipe.load_components(dtype={"vae": torch.float32, "audio_vae": torch.float32, "default": torch.bfloat16})
pipe.load_lora_weights("Pika-Labs/Pika-H3-Flex", weight_name="fl2va/adapter.safetensors")Passing the workflow prunes the blocks to that task, so only its own transformer partition is fetched, the adapter routes to the one partition present, and the pipeline accepts that task’s requests alone — a fl2va pipeline needs a keyframe, a t2va one takes none. A pipeline that holds both partitions (loaded without a workflow=) needs to be told where the Ref2VA adapter goes: pipe.load_lora_weights(..., weight_name="ref2va/adapter.safetensors", load_into_transformer_ref=True). The two VAEs are released in float32 and decode in float32, as the released package does; everything else runs in bfloat16.
pipe.docprints what the pipeline in front of you takes and returns — every input with its default, the components it expects and the outputs it produces.
What differs from MiniMax-H3
- Six steps, no
num_inference_steps. Both MiniMaxH3Scheduler instances (shift=12.0for video,shift=3.0for audio) run withstochastic_samplingover the pipeline’sdenoising_step_listconfig, the released schedule[1000, 750, 500, 250, 100, 40, 0]: every step predicts the clean sample, and all but the last re-noise it with fresh noise to the next sigma. The step count is the adapter’s contract and is not an input. - Fresh noise at every step. A request draws from its
generatorin a fixed order: one draw per keyframe or visual reference for the conditioning noise, the video noise, the audio noise, and then the video and audio re-noising draws of each of the first five steps. Passinglatentsoraudio_latentsreplaces the two initial draws and leaves the rest in place. - Keyframes are cropped, not stretched. A
fl2vakeyframe is center-cropped to the canvas’ aspect ratio at its own resolution and resized onto the canvas. - References follow the canvas’ resolution tier. Pika H3 Flex generates on a bucket table — a 480p tier and a 768p tier, eleven aspect ratios each — and a
ref2varequest conditions by the tier of its canvas: an image reference is encoded at a 1024 pixel short edge below the 768 tier and 2048 from it upwards, and a video reference is put on the tier’s own canvas rule. Every video or standalone audio reference runs 2 to 15 seconds, and the video durations and the standalone audio durations each sum to at most 15 seconds.
Generation
The released buckets are the canvases Pika H3 Flex was validated on. Pass one as height and width:
| 21:9 | 16:9 | 3:2 | 4:3 | 5:4 | 1:1 | 4:5 | 3:4 | 2:3 | 9:16 | 9:21 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 480p | 960x416 | 832x480 | 704x480 | 640x480 | 608x480 | 480x480 | 480x608 | 480x640 | 480x704 | 480x832 | 416x960 |
| 768p | 1536x672 | 1344x768 | 1152x768 | 1024x768 | 960x768 | 768x768 | 768x960 | 768x1024 | 768x1152 | 768x1344 | 672x1536 |
import torch
from diffusers import ComponentsManager, ModularPipeline
from diffusers.utils import load_image
from diffusers.utils.export_utils import encode_video
# 61.7GB of transformer and 62.1GB of conditioner do not sit on one accelerator, so the components are
# registered in a manager that moves each one on and off as the blocks reach it.
manager = ComponentsManager()
manager.enable_auto_cpu_offload(device="cuda") # or "mps", "xpu", "cpu"
# Create one pipeline for all supported workflows, then load the `fl2va` workflow's components, which is
# enough to run both the fl2va and t2va requests below.
pipe = ModularPipeline.from_pretrained("Pika-Labs/Pika-H3-Flex", components_manager=manager)
pipe.load_components(
workflow="fl2va", dtype={"vae": torch.float32, "audio_vae": torch.float32, "default": torch.bfloat16}
)
pipe.load_lora_weights("Pika-Labs/Pika-H3-Flex", weight_name="fl2va/adapter.safetensors")
prompt = (
"integrated_multimodal_description: A cinematic close shot of a red ceramic cup on a wooden table beside an "
"open window. A soft breeze moves a linen curtain. The camera slowly pushes toward the cup as warm morning "
"light crosses the table.\n"
"overall_soundscape: Gentle wind through the window and faint birdsong outside. No speech.\n"
"non_diegetic_music: None.\n"
)
outputs = ["videos", "audio", "sampling_rate"]
# Text to video + audio on the 480p 16:9 bucket, 5 seconds.
results = pipe(
prompt=prompt,
height=480,
width=832,
num_frames=124,
generator=torch.Generator("cuda").manual_seed(42),
output=outputs
)
# First frame (and optionally `last_image`) to video + audio. The keyframe is center-cropped onto the canvas and
# held as the first frame, so the prompt describes the keyframe and what happens from it.
image = load_image(
"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/astronaut.jpg"
)
i2va_prompt = (
"For the target video, at 0.00 seconds into the target video, <Picture 1> (from [Shot 1]) is fully referenced.\n\n"
"integrated_multimodal_description: [Shot 1] Begin with the supplied image of a small toy astronaut figurine "
"standing on a grey rocky moon surface, holding a white egg-shaped object. Preserve the figurine, its orange "
"visor and the rocks. The astronaut slowly turns its helmet toward the camera and lifts the egg a little; fine "
"dust drifts up around its boots. The camera slowly pushes in in one continuous shot. By the end the astronaut "
"stands still, egg held up.\n\n"
"overall_soundscape: Soft low wind and faint crunch of dust under the boots. No speech.\n\n"
"non_diegetic_music: N/A\n"
)
results = pipe(
prompt=i2va_prompt,
image=image,
height=480,
width=832,
num_frames=124,
generator=torch.Generator("cuda").manual_seed(42),
output=outputs,
)
encode_video(
results["videos"][0],
fps=24,
output_path="pika_h3_flex_fl2va.mp4",
audio=results["audio"][0],
audio_sample_rate=results["sampling_rate"],
)The ref2va workflow takes an ordered list of references — up to 9 images, 3 videos and 3 audio clips, 12 in total — the way MiniMax-H3 does, with one reference class per modality. It is also the request that needs the most memory: the references add their own rows to the sequence, and at 768p the transformer alone fills an 80 GB card. The example below therefore streams the transformer’s blocks from CPU memory one at a time, while the manager moves the other components on and off the device as before. It runs a 768p reference request on one 80 GB card; without the streaming, keep to 480p.
import torch
from diffusers import ComponentsManager, ModularPipeline
from diffusers.modular_pipelines.pika_h3_flex import (
PikaH3FlexAudioReference,
PikaH3FlexImageReference,
PikaH3FlexVideoReference,
)
manager = ComponentsManager()
manager.enable_auto_cpu_offload(device="cuda") # or "mps", "xpu", "cpu"
pipe = ModularPipeline.from_pretrained(
"Pika-Labs/Pika-H3-Flex", workflow="ref2va", components_manager=manager
)
pipe.load_components(dtype={"vae": torch.float32, "audio_vae": torch.float32, "default": torch.bfloat16})
pipe.load_lora_weights("Pika-Labs/Pika-H3-Flex", weight_name="ref2va/adapter.safetensors")
# Keep the transformer in CPU memory and bring in one block at a time while it runs.
pipe.transformer_ref.enable_group_offload(
onload_device=torch.device("cuda"),
offload_device=torch.device("cpu"),
offload_type="block_level",
num_blocks_per_group=1,
use_stream=True,
)
results = pipe(
prompt="subject_definitions: <Subject 1> is the woman in <Picture 1>. summary: [reference generation] <Subject 1> waves at the camera.",
references=[
PikaH3FlexImageReference.from_file("subject.png"),
PikaH3FlexVideoReference.from_file("motion_ref.mp4"), # frames, their `fps` and the soundtrack
PikaH3FlexAudioReference.from_file("voice.wav"), # waveform and its `sample_rate`
],
height=768,
width=1344,
num_frames=124,
generator=torch.Generator("cuda").manual_seed(42),
output=["videos", "audio", "sampling_rate"],
)PikaH3FlexModularPipeline
class diffusers.PikaH3FlexModularPipeline
< source >( blocks: diffusers.modular_pipelines.modular_pipeline.ModularPipelineBlocks | None = Nonepretrained_model_name_or_path: str | os.PathLike | None = Nonecomponents_manager: diffusers.modular_pipelines.components_manager.ComponentsManager | None = Nonecollection: str | None = Noneworkflow: str | None = Nonemodular_config_dict: dict[str, typing.Any] | None = Noneconfig_dict: dict[str, typing.Any] | None = None**kwargs )
A ModularPipeline for joint video + audio generation with Pika H3 Flex, the post-trained six-step MiniMax-H3: the t2va (text only) and fl2va (first and/or last keyframe) workflows against the transformer/ checkpoint
partition with the fl2va adapter, and the ref2va (omni-reference) workflow against transformer_ref/ with the ref2va adapter.
The base components are MiniMax-H3’s, and the Pika H3 Flex release is a pair of rank-128 LoRA adapters on top of
the two transformer partitions, so a pipeline is assembled in two steps: the components are loaded through a modular_model_index.json that names the base components and the two schedulers, and the adapter of the selected
workflow is loaded with load_lora_weights().
import torch
from diffusers import ModularPipeline
pipe = ModularPipeline.from_pretrained("Pika-Labs/Pika-H3-Flex", workflow="fl2va")
pipe.load_components(dtype={"vae": torch.float32, "audio_vae": torch.float32, "default": torch.bfloat16})
pipe.load_lora_weights("Pika-Labs/Pika-H3-Flex", weight_name="fl2va/adapter.safetensors")Like MiniMax-H3, Pika H3 Flex denoises one packed sequence that holds the text conditioning, the keyframe or
reference conditioning latents, the audio latents and the video latents at once, which is why the blocks pass a row
layout around rather than per-modality tensors, and why the pipeline carries two schedulers (shift = 12.0 for
video, shift = 3.0 for audio) that are stepped inside a single transformer call. Unlike MiniMax-H3, the two
schedulers are MiniMaxH3Scheduler instances with stochastic_sampling over the fixed six-step denoising_step_list: there is no num_inference_steps, and every step draws fresh noise from the request’s
generator.
The adapters are guidance-distilled like their base: there is no guider, no negative_prompt and no guidance_scale, and every step runs exactly one forward pass.
This module carries the model facts every block keys off — the config-derived geometry as properties, the canvas, tier and frame-count arithmetic as functions — and the conditioning, encoding and noise contracts live on the blocks themselves.
PikaH3FlexBlocks
Auto Modular pipeline blocks for joint video + audio generation with Pika H3 Flex: the t2va (text only), fl2va (first and/or last keyframe) and ref2va (omni-reference) workflows, selected on the references and keyframe
inputs. The t2va and fl2va workflows run the transformer partition with the fl2va adapter and ref2va the transformer_ref partition with the ref2va adapter, so load the adapter of the workflow the request takes.
Without a workflow=, loading the components pulls both 61.7GB transformer partitions — pass one to load only
what the task needs.
Supported workflows:
t2va: requirespromptfl2va: requiresprompt,imageorprompt,last_imageref2va: requiresprompt,references
Components:
image_processor (VaeImageProcessor) text_encoder (Qwen3VLForConditionalGeneration) tokenizer
(Qwen2Tokenizer) processor (Qwen3VLProcessor) vae (AutoencoderKLMiniMaxH3) audio_vae
(AutoencoderKLMiniMaxH3Audio) scheduler (MiniMaxH3Scheduler) audio_scheduler (MiniMaxH3Scheduler)
transformer_ref (MiniMaxH3Transformer3DModel) transformer (MiniMaxH3Transformer3DModel) video_processor
(VideoProcessor)
Configs: canvas_short_edge (default: 768) canvas_max_pixels (default: 1032192) denoising_step_list (default: [1000, 750, 500, 250, 100, 40, 0])
Inputs:
references (list, optional):
The references to condition on, in the order the model should read them: the order labels them in the
prompt presentation and lays them out on the shared rotary clock, so a different order is a different
request. One dataclass per modality, all holding in-memory media — a PikaH3FlexImageReference (at
most 9), a PikaH3FlexVideoReference at its own fps (at most 3, whose audio soundtrack is
conditioned on as well), or a PikaH3FlexAudioReference at its own sample_rate (at most 3) — for at
most 12 references in total, and audio references cannot be the only ones. Every video or standalone
audio reference runs 2 to 15 seconds, and the video durations and the standalone audio durations each sum
to at most 15 seconds. These blocks never open a media file: decode with each class’s from_file classmethod, which brings the rates along.
height (int, optional):
Height of the generated video in pixels, a multiple of 32.
width (int, optional):
Width of the generated video in pixels, a multiple of 32.
num_frames (int, optional):
Number of frames to generate, at the fixed 24 fps: 120 to 360, i.e. 5 to 15 seconds, snapped up to the
next 17 * n + 5 the video VAE can decode. To generate a video as long as a reference soundtrack, pass round(samples / sample_rate * 24).
image (Image, optional):
Keyframe the video starts from. It is center-cropped to the canvas’ aspect ratio and resized onto it; the
canvas by default is derived from its own aspect ratio.
last_image (Image, optional):
Keyframe the video ends on. Can be passed on its own to generate up to a frame. It is center-cropped to
the canvas’ aspect ratio and resized onto it, like image.
prompt (str):
The prompt to guide generation, a single string.
normalized_references (list, optional):
The references normalized by the setup step, in packed order.
keyframes (list, optional):
The keyframes put onto the target canvas, in packed order.
condition_latents (list, optional):
The encoded video conditioning latents, one per image and video reference in packed order. Their shape is
where every reference block’s geometry comes from.
audio_condition_latents (list, optional):
The encoded audio conditioning rows, one per audio-bearing reference in packed order.
generator (Generator, optional):
The generator of the request. The conditioning noise is drawn from it first, one draw per condition,
before the noise of the generated rows.
latents (Tensor):
Pre-generated video noise of shape (1, 24, num_latent_frames, latent_height, latent_width), used
instead of the draw.
audio_latents (Tensor):
Pre-generated audio noise of shape (2, 32, num_audio_latents).
*denoiser_input_fields (None,optional):
The structural description of the packed sequence the transformer reads by name: token_tags, position_ids and the three row-index tensors.
attention_kwargs (dict,optional):
Additional kwargs for attention processors.
keyframe_anchors (tuple,optional, defaults to ()):
Which end of the video every keyframe is anchored to, in packed order.
output_type (str,optional*, defaults to pil):
Output format: ‘pil’, ‘np’ or ‘pt’.
Outputs:
videos (list):
The generated video.
audio (Tensor):
The generated soundtrack, of shape (1, 2, num_samples).
sampling_rate (int):
Sample rate of the generated soundtrack in Hz.
PikaH3FlexImageReference
class diffusers.modular_pipelines.pika_h3_flex.PikaH3FlexImageReference
< source >( image: typing.Union[PIL.Image.Image, numpy.ndarray, torch.Tensor] )
Parameters
- image (
PIL.Image.Image,np.ndarrayortorch.Tensor) — The reference image: an image, a(height, width, 3)array or a(3, height, width)tensor,uint8or floating point over[0, 1]. It never binds the generated geometry — it is encoded at a short edge of its own, 2048 for the released checkpoint, whatever canvas the request generates at.
A subject, style or scene reference: at most 9 per request.
Load an image file into a PikaH3FlexImageReference, through load_image().
PikaH3FlexVideoReference
class diffusers.modular_pipelines.pika_h3_flex.PikaH3FlexVideoReference
< source >( frames: typing.Union[list[PIL.Image.Image], numpy.ndarray, torch.Tensor]fps: float | None = Noneaudio: typing.Optional[torch.Tensor] = Nonesample_rate: int | None = None )
Parameters
- frames (
list[PIL.Image.Image],np.ndarrayortorch.Tensor) — The reference frames: a list of images, a(num_frames, height, width, 3)array or a(num_frames, 3, height, width)tensor,uint8or floating point over[0, 1]. - fps (
float, optional, defaults to 24.0) — The frame rateframescarries, which is what places the reference’s vision blocks on the conditioner’s 2 fps grid. MiniMax-H3’s own clock is 24 fps, so any other rate is resampled onto it by dropping and duplicating whole frames — which makes this the field to get right when the frames came from a file. - audio (
torch.Tensorof shape(channels, num_samples), optional) — This video’s soundtrack, mono or stereo, conditioned on as the reference’s own rather than as a reference of its own. Left out, the reference conditions on motion alone. - sample_rate (
int, optional) — The rateaudiocarries its samples at. Left out, it is the audio VAE’s own, which leaves the samples untouched; any other rate is resampled onto it.
A motion and camera reference: at most 3 per request, conditioned on together with its own soundtrack.
from_file
< source >( media ) → PikaH3FlexVideoReference
Decode a video file into a PikaH3FlexVideoReference, at the resolution, the frame rate and the soundtrack
it carries.
The rates land on the reference, which is the point of decoding this way rather than with load_video(): MiniMax-H3 resamples a reference onto its own 24 fps, so a frame rate lost on the way in
is a request conditioned at the wrong speed, with nothing to raise about it. A container whose metadata is
wrong is corrected by overriding fps or sample_rate on the returned reference.
Needs PyAV.
PikaH3FlexAudioReference
class diffusers.modular_pipelines.pika_h3_flex.PikaH3FlexAudioReference
< source >( audio: Tensorsample_rate: int | None = None )
A voice or music reference: at most 3 per request, and never on its own — an audio reference has to be paired with at least one image or video reference. It never reaches the conditioner and is encoded by the audio VAE alone.
from_file
< source >( media ) → PikaH3FlexAudioReference
Decode an audio file into a PikaH3FlexAudioReference, at the sample rate it carries.
Needs PyAV.