Repository navigation
[modular] ConditionalPipelineBlocks should declare its own trigger inputs #15101
Description
When an input exists only to decide which branch a conditional block runs, no sub-block reads it, so nothing declares it. ConditionalPipelineBlocks.inputs only merges the sub-blocks' inputs and does not add the block's own block_trigger_inputs. The repo has three patterns for this:
-
Declare it nowhere. Ernie's
use_peis only listed inblock_trigger_inputs:class ErnieImageAutoPromptEnhancerStep(ConditionalPipelineBlocks): block_trigger_inputs = ["use_pe"] def select_block(self, use_pe=None): return "prompt_enhancer" if use_pe else None
use_peis missing frominputs, from the generated docstring and fromdefault_call_parameters. And it does not work at call time either:ModularPipeline.__call__only copies declared inputs into the state and warns about the rest, sopipe(prompt=..., use_pe=True)prints "Unexpected input 'dict_keys(['use_pe'])' provided. This input will be ignored.", the conditional block readsNonefrom the state, and the prompt enhancer never runs. Only the staticget_execution_blocks(prompt=True, use_pe=True)resolves to it, because that takes kwargs directly. -
Override
inputson the wrapper. Cosmos 3'senable_sound:class Cosmos3AutoCoreDenoiseStep(ConditionalPipelineBlocks): block_trigger_inputs = ["action", "enable_sound", "control_videos"] @property def inputs(self): inputs = super().inputs inputs.append(InputParam(name="enable_sound", type_hint=bool, default=False, description="...")) return inputs
The input is listed at the top level with a type, a default and a description. And because
enable_soundis also a workflow input,get_workflowdoes the right thing with it:>>> blocks = Cosmos3OmniBlocks() >>> wf = blocks.get_workflow("text2video_with_sound") >>> [name for name in wf.sub_blocks if "sound" in name] ['denoise.prepare_sound_latents', 'denoise.pack_sound_sequence', 'denoise.prepare_sound_denoiser_inputs', 'decode.sound'] >>> "enable_sound" in [p.name for p in wf.inputs] False
The workflow resolves to the sound blocks, and the resolved workflow does not list
enable_soundas an input, which is correct: none of the resolved blocks reads it, the workflow entry has already decided it.get_execution_blocksworks the same way,get_execution_blocks(prompt=True, enable_sound=True)resolves to the same sound blocks, sinceget_workflowis justget_execution_blockscalled with the workflow entry. Ifenable_soundwere not a workflow input,get_workflowwould resolve the wrapper with the default and the input would be lost together with it, see [modular] conditional block triggers should be workflow inputs #15100. -
Declare it on every leaf. LTX-2's
enable_prompt_enhancementis read only byLTX2AutoPromptEnhancerStep.select_block, but it is declared as anInputParamon each of the three prompt-enhancer leaves it chooses between:class LTX2PromptEnhancerStep(ModularPipelineBlocks): @property def inputs(self): return [ InputParam.template("prompt", required=True), InputParam("enable_prompt_enhancement", type_hint=bool, default=False, description="..."), ..., ]
The leaves carry an input they never read, each new leaf has to repeat the declaration, and a resolved workflow that keeps such a leaf lists the input although nothing on that workflow acts on it.
I think the Cosmos 3 pattern is the right one: the wrapper is the block that reads the input, so the wrapper declares it, with a type, a default and a description like any other input, so it shows up in inputs, in the generated docstring and in default_call_parameters. The framework should make this the documented way instead of leaving it to each blockset to work out. The Cosmos 3 inputs override is the shape of it; the base class could offer a small hook for it so the override does not have to re-merge the sub-blocks' inputs, but that is a detail.
Posted by Claude Code on behalf of @yiyixuxu, who reviewed and approved this text.
Activity
Narendra02053 commentedon Oct 26, 2026
Hi! I'd like to contribute a focused fix for this issue. I can reproduce the problem where a conditional trigger input is ignored during a modular pipeline call because it isn't declared in the block's inputs.
Would you be open to a community PR with a regression test and a minimal fix? I'd appreciate your guidance on the preferred implementation approach before I start.
yiyixuxu commentedon Oct 26, 2026
hey, we are looking for discussion only first for this issue
feel free to share your thoughts on best approach etc
Narendra02053 commentedon Oct 27, 2026
Thanks for clarifying! I had a look at the current
inputsimplementation, and I was thinking we could letConditionalPipelineBlocksdeclare its own trigger inputs usingInputParam, then merge them with the inputs from its sub-blocks.That way, inputs like
use_pewould be available to the top-level pipeline without having to declare them on every leaf block. We’d also need to make sure they don’t show up as inputs in a resolved workflow when they’re only used to select the branch.Does this approach make sense, or would you prefer handling it differently?