Skip to content

Python: [BREAKING] bound PowerFx state construction - #8511

Open
Jose Alvarez (jpalvarezl) wants to merge 5 commits into
mainfrom
jpalvarezl-publish-state-construction
Open

Jose Alvarez (jpalvarezl) wants to merge 5 commits into
mainfrom
jpalvarezl-publish-state-construction

Conversation

@jpalvarezl

@jpalvarezl Jose Alvarez (jpalvarezl) commented Sep 18, 2026

Copy link
Copy Markdown
Member

Motivation & Context

Declarative expression evaluation copies workflow state and constructs PowerFx
symbols, including data not referenced by the expression. Bound that
construction work and report explicit errors instead of silently truncating
values.

This PR includes both the Core pre-copy validation hook and its Declarative
consumer, with regression coverage for both packages.

Description & Review Guide

  • What are the major changes? Add a shared structural budget for raw state
    and converted symbols. Validate stored state before defensive copying through
    an internal Core State._validate hook, retaining pending-first visibility
    and copy isolation. Count converted output incrementally, reject cycles,
    avoid eager whole-value debug formatting, and propagate budget errors through
    legacy evaluation rather than taking its fallback path. Preserve MessageText
    source/data separation and temporary-binding cleanup. Add small boundary and
    real-State regressions. Document counting semantics beside the implementation,
    without adding a package README section.

  • What is the impact of these changes? The proposed fixed limits are depth
    64 (root depth zero), 10,000 visited values including mapping keys and
    containers, and 1,048,576 aggregate string characters or binary bytes per
    traversal. Repeated references and compatibility aliases count at each
    occurrence. These are proposed compatibility choices for maintainer review,
    not limits inherited from the .NET implementation. Previously accepted larger
    or cyclic state raises ValueError, including during declarative snapshot/write
    paths. Normal within-budget types, namespaces, and temporary-state cleanup are
    preserved. This patch does not introduce retained-engine caching, select only
    referenced namespaces, impose a hard expression deadline, sandbox application-defined
    Python copy/conversion hooks, or establish a global workflow/YAML loading budget.

  • What do you want reviewers to focus on? Review the proposed limits and
    pre-copy validation boundary. The Core implementation and tests are already
    included in this same PR; no separate Core implementation PR is required.

    Release preparation requirement: the updated Declarative package must
    require a Core release that contains State._validate. The current
    agent-framework-core>=1.19.0,<2 metadata admits published Core 1.19.0,
    which lacks that hook. During the next coordinated release, include both
    changes, assign the package versions, raise Declarative's minimum Core
    version accordingly while preserving <2, update the lockfile, and validate
    the packaged dependency combination. Publish the compatible Core package
    before publishing the updated Declarative package. Do not ship the new
    Declarative code with the unchanged minimum dependency.

    This version-floor update is handled during coordinated release preparation,
    rather than guessing a future version or requiring that it already be on
    PyPI before this source change can merge. Documenting that release obligation
    does not mean the metadata has already been updated or that mixed-package
    installations have been validated.

Related Issue

No public issue is linked.

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 is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • 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.

The original patch's selected state, PowerFx, executor, and factory suites
passed (585 tests, no skips) using an editable Core/Declarative pair.
Source/test Pyright and changed-file Ruff checks also passed on that original
baseline. The earlier package-wide Poe hook had a documented exception for
unrelated formatting; that historical result is not an all-hooks pass.

After merging upstream main in 7768d9f2c039ed361c2d40b642ca06efafc16231,
the combined state, traversal, expression-output, and function-tool selection
passed on Python 3.11: 701 tests, no failures or skips. Merged-source Pyright
and all applicable conflict-file and merge-commit hooks passed without skip
overrides. The full repository test suite and released-package compatibility
checks have not been run locally.

Validate structural budgets before declarative state copies and PowerFx symbol conversion. Preserve copy isolation and temporary bindings, reject budget failures explicitly, and add focused boundary regressions. Fixed limits and the new Core hook require compatibility and dependency-floor review before release.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Validate structural budgets before declarative state copies and PowerFx symbol conversion. Preserve copy isolation and temporary bindings, reject budget failures explicitly, and add focused boundary regressions. Fixed limits and the new Core hook require compatibility and dependency-floor review before release.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings September 18, 2026 11:29
@agent-framework-automation agent-framework-automation Bot added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python breaking change Usage: [PRs], Target: all PRs that introduce changes that are not backward compatible labels Sep 18, 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

Declarative’s dependency range still permits Core releases without the newly required State._validate hook.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds bounded traversal and conversion for PowerFx state to prevent excessive or cyclic state construction.

Changes:

  • Adds shared depth, node, and text-size budgets.
  • Validates Core state before copying and propagates budget errors.
  • Documents limits and adds boundary/regression tests.
File summaries
File Description
python/packages/core/agent_framework/_workflows/_state.py Adds pre-copy state validation hook.
python/packages/core/tests/workflow/test_state.py Tests validation visibility and failure behavior.
python/packages/declarative/agent_framework_declarative/_workflows/_powerfx_limits.py Implements structural budgets and cycle detection.
python/packages/declarative/agent_framework_declarative/_workflows/_declarative_base.py Applies budgets to state, configuration, and symbols.
python/packages/declarative/agent_framework_declarative/_workflows/_state.py Applies limits to legacy PowerFx evaluation.
python/packages/declarative/tests/test_powerfx_safe.py Adds budget and state-integrity regressions.
python/packages/declarative/README.md Documents PowerFx state limits.
Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

@github-code-quality

github-code-quality Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: Python

Python / code-coverage/python

The overall line coverage in commit 7768d9f in the jpalvarezl-publish-s... branch is 91%. Line coverage data for the main branch is not yet available.

Show a line coverage summary of the most covered files.
File main jpalvarezl-publish-s... 7768d9f +/-
packages/core/a...ework/_tools.py 96%
packages/core/a...work/_skills.py 95%
packages/openai..._chat_client.py 94%
packages/core/a.../_compaction.py 94%
packages/core/a...ework/_types.py 93%
packages/core/a...ork/_vectors.py 93%
packages/core/a...bservability.py 93%
packages/core/a...amework/_mcp.py 92%
packages/ag-ui/...i/_agent_run.py 89%
packages/core/a...ork/security.py 89%

Updated September 18, 2026 14:29 UTC

Preserve the original implementation commit and adopt the published review baseline without changing the published source tree.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@jpalvarezl

Copy link
Copy Markdown
Member Author

Reviewer attention: proposed state-construction limits

Please review the numerical defaults and their compatibility impact explicitly. The reasons for having separate depth, cardinality, and payload-size bounds are stronger than the evidence for these exact numbers: they are provisional engineering choices, not production-workload-derived thresholds, latency/memory guarantees, or limits copied from the .NET implementation.

Proposed limit What it controls Reasoning behind the starting value
64 levels of nesting (root at depth zero) Depth of recursive state traversal. A finite, readily understandable ceiling that still permits nested records and tables, rather than relying on Python recursion failure to stop traversal. The exact value 64 has not been established as the maximum needed by customer workflows.
10,000 visited values per traversal Breadth and aggregate traversal work, even when nesting is shallow. Containers and mapping keys count as well as values. A round initial work budget intended to accommodate thousands of small values while placing a finite ceiling on conversion work. This is not a measured performance breakpoint, and it does not mean 10,000 table rows.
1,048,576 aggregate text characters / binary bytes per traversal Large scalar payloads that a node-count limit alone would miss; mapping keys also contribute. A simple binary-sized starting budget, independent of collection shape. Strings contribute len(str) and binary values contribute their byte length, so this is not a 1 MiB process-memory or UTF-8-size guarantee. The exact capacity remains a compatibility choice for review.

Counting and failure semantics matter: shared values count at each occurrence, including the inputs / Workflow.Inputs compatibility aliases. Raw-state validation and symbol/output construction have their own traversal budgets; this is not one cumulative budget across an entire workflow. Rejection is explicit (ValueError), never silent truncation. Because whole state is currently copied/bound, oversized state can reject an expression even if that expression does not reference the oversized value. Temporary MessageText bindings and configured Env values also contribute to the relevant symbol budget.

.NET's configurable expression-length and formula-call-depth limits address different dimensions; they do not justify these state-size numbers. Likewise, these checks do not impose a hard expression deadline or sandbox application-defined Python copy/conversion hooks.

The boundary regressions verify that the implementation enforces its chosen contract and preserves ordinary behavior; they do not establish that these exact defaults suit every supported workload. Please flag legitimate state shapes that require different defaults, especially conversation histories and structured tool results, so we can adjust the proposed policy before this is accepted.

@github-actions github-actions Bot 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.

MAF Automated Review — Iteration 1

Result: No findings
Scope: full PR (1 commit(s)): 3182665daf58
Model: gpt-5.6-sol-fast

Overview

This PR adds per-traversal depth, node, and text budgets around declarative state copying and PowerFx symbol conversion. Pre-copy validation, incremental converted-output accounting, cycle detection, explicit budget-error propagation, and temporary-binding cleanup are backed by focused boundary and state-isolation tests. No non-duplicative Critical, High, or Medium finding remains after source and test verification.

Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
No publishable findings remained after source verification for this scope.

Remove the detailed package README section and document per-traversal counting next to the budget implementation. Preserve the limits, runtime behavior, and method error documentation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Preserve both the workflow traversal error import and the state-construction budget imports. Retain upstream traversal and expression-output changes alongside the existing state bounds.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Usage: [PRs], Target: all PRs that introduce changes that are not backward compatible documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants