Skip to content

[modular] fill a missing input from the first block that reads it, keep a passed None - #15135

Open
yiyixuxu wants to merge 3 commits into
mainfrom
conditional-runtime-defaults
Open

yiyixuxu wants to merge 3 commits into
mainfrom
conditional-runtime-defaults

Conversation

@yiyixuxu

@yiyixuxu yiyixuxu commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Related to #14234, which let the branches of a conditional block declare different defaults for the same input. It did that by having get_block_state fill in the block's default whenever the state held None. That made passing None indistinguishable from not passing the argument at all: both got the default of the first block that ran.

This does not cause a bug in any pipeline in the code base today, but we want to fix it because:

  1. It deviates from the usual Python contract, where omitting an argument and passing None are different things.
  2. It can also affect sequential blocks. When two blocks in a sequence declare different defaults and the pipeline-level default resolves to None, the same input is None in the first block and becomes the second block's default in the second. No pipeline in the code base has this pattern so no officially supported pipeline is affected this way.

This PR supports per-branch defaults differently. A None passed by the user is stored in the state as is (Note that it's the behavior before #14234 so we just restore it). An omitted input is filled in by the first block that reads it, and set_block_state records the fill, so the blocks after it read the same value. Conditional branches need no special code: the branch that runs is the first reader.

Tests: a passed None reaches every branch (test_conditional_pipeline_blocks.py); in a sequence the later block reads the recorded value instead of its own default (test_sequential_pipeline_blocks.py).

…ep a passed None

`get_block_state` fell back to the block's own default whenever the state held
None. That let a later block in a sequence swap in its default on top of the
first block's, and made a passed None indistinguishable from "not passed".

Now a missing input is filled in by the first block that reads it and
`set_block_state` records the fill, so every block after it reads the same
value. The pipeline stores a passed None as is and no longer pre-fills a None
default. Conditional branches need no special code: the branch that runs is
the first reader.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions github-actions Bot added tests modular-pipelines size/M PR with diff < 200 LOC labels Oct 26, 2026
@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

yiyixuxu and others added 2 commits October 27, 2026 00:29
It shows that per-branch defaults do not affect sequential blocks: the first
block that reads an input fills in its default and records it, so a later
block with a different default reads the same value.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@yiyixuxu
yiyixuxu requested a review from sayakpaul October 27, 2026 04:42

@sayakpaul sayakpaul left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!

Ran the newly added tests on main and test_explicit_none_reaches_every_branch, test_branch_defaults_do_not_affect_sequential_blocks fail as expected.

Comment on lines +591 to +593
if input_param.name in state.values:
# a value in the state, None included, is what the user passed or an earlier block set
value = state.get(input_param.name)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah!

Comment on lines +626 to +627
# Only add if the value is different from what's in the state, or the state did not hold it yet (the
# default this block filled in, see `get_block_state`)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we also supplement this with an example? Just makes reading the code a bit easier.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants