Skip to content

Python: fix(declarative): treat omitted workflow inputs as blank in PowerFx - #9730

Draft
Naveen Chatlapalli (1aifanatic) wants to merge 5 commits into
microsoft:mainfrom
1aifanatic:contrib/python-declarative-missing-inputs-blank
Draft

Naveen Chatlapalli (1aifanatic) wants to merge 5 commits into
microsoft:mainfrom
1aifanatic:contrib/python-declarative-missing-inputs-blank

Conversation

@1aifanatic

@1aifanatic Naveen Chatlapalli (1aifanatic) commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Motivation & Context

=If(IsBlank(inputs.name), "World", inputs.name) never applies its default when the caller omits name, so the simple_workflow sample prints Hello, ! for workflow.run({}). PowerFx fails to compile inputs.name when the inputs record has no such field, and DeclarativeWorkflowState.eval() returns None for the whole expression. Details and an input matrix are in #9729.

Description & Review Guide

  • What are the major changes?
    • Evaluate formulas against the original symbols first. If PowerFx reports an unrecognized name, use its diagnostic spans to find missing input references and retry once with those fields bound to blank.
    • Support inputs and Workflow.Inputs, quoted identifiers, comments between tokens, and nested paths. Valid locally scoped references do not add fields to the global input record. Custom inputs namespaces remain independent from Workflow.Inputs.
    • Keep caller-supplied values and workflow state unchanged. If the synthesized bindings cannot compile, or diagnostic locations are unavailable, retain the existing None fallback.
    • Print the outputs of the simple workflow sample's second run with "Alice".
  • What is the impact of these changes? Omitted inputs can use IsBlank defaults. Formulas that already evaluate successfully retain their existing behavior.
  • What do you want reviewers to focus on? Blank semantics for omitted inputs, preserving independent namespaces and local scopes, and the fallback when missing paths cannot be completed.

Regression coverage includes both aliases, quoted/comment-separated references, literals, custom namespaces, nested paths, and local bindings through With, As, and ForAll. Diagnostic offsets are converted from UTF-16 so preceding non-BMP characters do not shift reference matching. The latest local-scope regressions fail before the fix. All 1,699 declarative tests pass with the .NET runtime; Ruff lint/format and source Pyright checks pass.

Related Issue

Fixes #9729

AI Assistance

  • No material AI assistance was used.
  • This is an AI-assisted contribution. I reviewed, understood, and verified all submitted content and accept responsibility for it.

AI assistance details: Claude Code helped find the bug by running the declarative samples locally, trace it to DeclarativeWorkflowState.eval(), implement the initial fix and tests, and draft the linked issue. Codex helped address review feedback, reproduce and fix local-scope handling during retries, add regression coverage, run validation, and update this description.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR links to an agreed issue with no competing open PR, or the Related Issue section documents a trivial-change or repository-automation exception.
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

🤖 Generated with Claude Code

…owerFx

When a caller omits an input that a formula references, PowerFx fails to
compile inputs.<name> / Workflow.Inputs.<name> ("Name isn't valid") and
eval() returns None for the whole expression. That breaks the documented
default pattern =If(IsBlank(inputs.name), "World", inputs.name): the
simple_workflow sample printed "Hello, !" for workflow.run({}).

Bind referenced-but-missing inputs to blank for that evaluation only, the
same result as passing the input as None, without writing them to state.
Also print the simple_workflow sample's second run, which was discarded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 26, 2026 18:36
@agent-framework-automation agent-framework-automation Bot added the python Usage: [Issues, PRs], Target: Python label Oct 26, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Reference discovery matches literal and comment text while missing valid quoted and whitespace-separated references.

2 open issues
What changed in this PR

This PR addresses #9729 by binding omitted workflow inputs as blank during PowerFx evaluation, allowing default expressions to run without modifying workflow state.

Changes:

  • Adds evaluation-only blank bindings for references through both input aliases.
  • Tests omitted-input defaults and verifies state remains unchanged.
  • Prints the custom-name run’s outputs in the simple workflow sample.
File Description
python/​samples/​03-workflows/​declarative/​simple_workflow/​main.py Prints outputs from the “Alice” run.
python/​packages/​declarative/​tests/​test_powerfx_yaml_compatibility.py Adds missing-input regression tests for both aliases.
python/​packages/​declarative/​agent_framework_declarative/​_workflows/​_declarative_base.py Discovers missing input references and binds evaluation-only blanks.

Review effort: Balanced

Skip comments and string literals when looking for inputs.<name> and
Workflow.Inputs.<name>, and accept single-quoted field names and
whitespace or comments around the dots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Formulas that already compile keep their base result, so member names and
local bindings called inputs are unaffected. A custom inputs namespace is
no longer replaced by the workflow inputs record.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A formula such as If(IsBlank(inputs.user.name), "World", inputs.user.name)
with omitted inputs bound `user` to blank, so the retry failed with
"Deprecated use of '.'" and raised instead of returning None as before.

Input references are now read as whole paths, and a missing parent is
bound as a record whose missing fields are blank. A parent that the
formula also uses as a value is left unbound, since a record would not
be blank. If the retry still fails for any reason, eval keeps the
undefined-reference fallback and returns None.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Use compiler name-error spans to keep valid locally shadowed inputs and Workflow references from adding global blank fields during retry. Convert UTF-16 diagnostic offsets and retain the undefined-reference fallback for unsupported diagnostic formats.

This branch was successfully deployed

1 active deployment
github-app-auth — c98d7796 Deployed Nov 6, 2026 by 1aifanatic via add_label #27333
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

.NET: Python: [Bug]: Declarative workflow IsBlank defaults ignore inputs the caller omitted

3 participants