Skip to content

Python: Expose forwardedProps to agents and tools via session metadata#5264

Open
moonbox3 wants to merge 5 commits intomicrosoft:mainfrom
moonbox3:agent/fix-5239-1
Open

Python: Expose forwardedProps to agents and tools via session metadata#5264
moonbox3 wants to merge 5 commits intomicrosoft:mainfrom
moonbox3:agent/fix-5239-1

Conversation

@moonbox3
Copy link
Copy Markdown
Contributor

Motivation and Context

forwardedProps from the AG-UI request was parsed but silently dropped—agents, tools, and workflows had no way to access request-level metadata (e.g., invocation-source flags from CopilotKit), forcing users to write custom FastAPI endpoints instead of using the built-in add_agent_framework_fastapi_endpoint().

Fixes #5239

Description

In _agent_run.py, forwarded_props is now extracted from the input data (accepting both snake_case and camelCase keys) and injected into session.metadata, and the key is added to AG_UI_INTERNAL_METADATA_KEYS so it is filtered from LM-bound metadata. In _workflow_run.py, the same extraction is performed and the props are passed through to the workflow via function_invocation_kwargs. A new state_update() helper and _state.py module were also added to let tools push deterministic AG-UI state updates from actual tool output, with corresponding StateSnapshotEvent coalescing in _run_common.py. Tests cover both the forwarded-props metadata plumbing and the new tool-driven state update path.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

Note: PR autogenerated by moonbox3's agent

Copilot and others added 2 commits April 15, 2026 05:47
…osoft#5239)

Include forwarded_props from AG-UI request input_data in session.metadata
(agent runner) and function_invocation_kwargs (workflow runner) so that
agents, tools, and workflow executors can access request-level metadata
such as invocation source flags from CopilotKit.

- Add forwarded_props to base_metadata in _agent_run.py when present
- Add 'forwarded_props' to AG_UI_INTERNAL_METADATA_KEYS to filter it
  from LLM-bound client metadata
- Extract forwarded_props in _workflow_run.py and pass via
  function_invocation_kwargs to workflow.run()
- Accept both snake_case and camelCase keys (forwarded_props/forwardedProps)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…esolution (microsoft#5239)

The previous fix passed stream=True via **kwargs dict, which prevented
pyright from resolving the Workflow.run() overload to the streaming
variant. Pass stream=True as an explicit keyword argument so pyright
can correctly infer the ResponseStream return type.

Also remove unused pytest import in test file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 15, 2026 05:59
@moonbox3 moonbox3 self-assigned this Apr 15, 2026
Copy link
Copy Markdown
Contributor Author

@moonbox3 moonbox3 left a comment

Choose a reason for hiding this comment

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

Automated Code Review

Reviewers: 4 | Confidence: 94%

✓ Correctness

The change is a clean refactoring that separates the always-present stream=True keyword from the conditionally-present function_invocation_kwargs. Before, both lived in run_kwargs; now stream=True is passed as an explicit keyword argument and fwd_kwargs only carries forwarded-prop data when present. The behavior is identical in all cases. The test file change removes an unused import pytest, which is correct since no pytest decorators or fixtures are used in that module.

✓ Security Reliability

This is a clean, low-risk refactor that separates the always-present stream=True keyword argument from the conditionally-present function_invocation_kwargs. The rename from run_kwargs to fwd_kwargs improves clarity by scoping the variable name to its actual content (forwarded props only). The test file change removes an unused pytest import. The behavior is functionally identical before and after — stream=True is always passed, and function_invocation_kwargs is only passed when forwarded_props is present. No security or reliability concerns found.

✓ Test Coverage

The code change refactors how kwargs are built for workflow.run(), separating 'stream=True' from the forwarded_props kwargs dict. The behavioral change is purely structural — both old and new code pass the same arguments to workflow.run(). Test coverage forwarded_props forwarding is thorough (present, absent, camelCase variants) via CapturingWorkflow tests in test_workflow_run.py. The test file change just removes an unused pytest import. The one minor gap: no existing test asserts that stream=True is passed to workflow.run(), though this is pre-existing and not introduced by this PR — the CapturingWorkflow already captures it but tests don't assert on it.

✓ Design Approach

The diff makes two unrelated but both correct cleanups: (1) it refactors run_kwargs into fwd_kwargs so that stream=True — which is unconditional — is always passed explicitly rather than being hidden inside a dict that is only populated when forwarded_props is present, and (2) it removes an unused pytest import from the test file. Both changes are straightforward and correct. No design-approach issues were found.

Suggestions

  • Consider adding an assertion like assert workflow.captured_kwargs['stream'] is True in the existing CapturingWorkflow tests (e.g., test_workflow_run_passes_forwarded_props_as_function_invocation_kwargs) to verify stream=True is always passed. This is a pre-existing gap, but would guard against accidental removal of the explicit kwarg.

Automated review by moonbox3's agents

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR exposes AG-UI forwardedProps/forwarded_props to downstream agent/workflow execution by extracting it from request input and threading it into session/workflow invocation metadata, with tests validating the plumbing.

Changes:

  • Extract forwarded_props (snake_case + camelCase) and inject into agent session metadata, while filtering it from LLM-bound metadata via AG_UI_INTERNAL_METADATA_KEYS.
  • Pass forwarded_props into workflow.run() via function_invocation_kwargs.
  • Add/extend tests to cover forwarded-props propagation paths.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

File Description
python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py Adds forwarded_props to internal metadata keys and injects it into session.metadata.
python/packages/ag-ui/agent_framework_ag_ui/_workflow_run.py Extracts forwarded_props and forwards it to workflow.run() via function_invocation_kwargs.
python/packages/ag-ui/tests/ag_ui/test_workflow_run.py Adds workflow-stream tests asserting function_invocation_kwargs behavior.
python/packages/ag-ui/tests/ag_ui/test_forwarded_props_in_metadata.py Adds tests around internal key filtering and safe metadata serialization.

Comment thread python/packages/ag-ui/agent_framework_ag_ui/_workflow_run.py Outdated
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_workflow_run.py
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_workflow_run.py Outdated
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py Outdated
Comment thread python/packages/ag-ui/tests/ag_ui/test_forwarded_props_in_metadata.py Outdated
Comment thread python/packages/ag-ui/tests/ag_ui/test_forwarded_props_in_metadata.py Outdated
Copilot and others added 3 commits April 15, 2026 06:13
- Use key-presence checks instead of truthiness for forwarded_props so
  empty dict {} is forwarded correctly
- Gate function_invocation_kwargs on workflow.run() signature inspection
  to avoid TypeError for workflows without **kwargs
- Change _build_safe_metadata to drop (with warning) keys whose
  serialized values exceed 512 chars instead of truncating into invalid
  JSON
- Rewrite metadata tests to exercise _build_safe_metadata directly with
  JSON-decodability and truncation assertions
- Add workflow tests for empty dict forwarded_props, stream=True
  assertion, and signature-gated kwarg dropping

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…t#5239)

Guard against accidental removal of the explicit stream=True kwarg
in all forwarded_props CapturingWorkflow test cases.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rops to agents and tools via session metadata
Copy link
Copy Markdown
Contributor Author

@moonbox3 moonbox3 left a comment

Choose a reason for hiding this comment

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

Automated Code Review

Reviewers: 4 | Confidence: 95% | Result: All clear

Reviewed: Correctness, Security Reliability, Test Coverage, Design Approach


Automated review by moonbox3's agents

@moonbox3
Copy link
Copy Markdown
Contributor Author

moonbox3 commented Apr 15, 2026

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/ag-ui/agent_framework_ag_ui
   _agent_run.py4925389%159–166, 205–206, 213, 310, 322, 326, 328–329, 345, 372–373, 409–413, 527–529, 541–543, 641, 649, 762, 764–765, 803, 805, 822, 839–840, 847, 915, 938, 946, 948, 951, 957, 1010, 1013, 1023–1024, 1031, 1077
   _workflow_run.py5273094%182, 218–221, 249, 254, 282, 292, 303, 308, 311, 324, 364–365, 386, 435, 455, 463, 466, 471, 486, 559, 601–602, 623–624, 695, 738, 757
TOTAL27567320388% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
5587 20 💤 0 ❌ 0 🔥 1m 30s ⏱️

@moonbox3 moonbox3 enabled auto-merge April 16, 2026 08:38
@moonbox3 moonbox3 requested a review from giles17 April 16, 2026 08:43
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.

Python: Expose forwardedProps to agents and tools via session metadata

3 participants