Repository navigation
Python: document the shape contract of FunctionInvocationContext.result - #8283
Merged
Eduard van Valkenburg (eavanvalkenburg) merged 2 commits intoSep 11, 2026
Conversation
NekoPunch (orangeCatDeveloper)
temporarily deployed
to
github-app-auth
September 11, 2026 06:41 — with
GitHub Actions
Inactive
NekoPunch (orangeCatDeveloper)
temporarily deployed
to
github-app-auth
September 11, 2026 06:41 — with
GitHub Actions
Inactive
NekoPunch (orangeCatDeveloper)
temporarily deployed
to
github-app-auth
September 11, 2026 06:41 — with
GitHub Actions
Inactive
NekoPunch (orangeCatDeveloper)
temporarily deployed
to
github-app-auth
September 11, 2026 06:42 — with
GitHub Actions
Inactive
FunctionInvocationContext.result documents one contract but carries two: middleware observe FunctionTool.invoke's parsed output and may write back a value of any type, so a type check written from the docs silently never fires. The shared-state sample modeled the wrong side and sent a Python repr of the Content list to the model instead of the tool's text.
NekoPunch (orangeCatDeveloper)
force-pushed
the
docs/function-middleware-result-shape
branch
from
September 11, 2026 06:43
65d261f to
01cf950
Compare
NekoPunch (orangeCatDeveloper)
temporarily deployed
to
github-app-auth
September 11, 2026 06:43 — with
GitHub Actions
Inactive
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The documentation incorrectly omits the bare function_approval_request content that passes through unchanged.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Clarifies the Python function middleware result-shape contract and corrects the shared-state sample’s result handling.
Changes:
- Documents parsed, raw, and overridden result shapes.
- Preserves
list[Content]when enhancing sample output.
File summaries
| File | Description |
|---|---|
python/packages/core/agent_framework/_middleware.py |
Expands middleware result documentation. |
python/samples/02-agents/middleware/shared_state_middleware.py |
Enhances text content without stringifying the list. |
Review details
Suppressed comments (1)
python/packages/core/agent_framework/_middleware.py:782
- The override rule omits the
function_approval_requestcontrol object, which the invocation layer deliberately passes through as a bareContentinstead of collapsing. Please include this exception so the method documentation agrees with the supported middleware approval path.
middleware left. Overriding with anything but ``list[Content]`` or ``str``
collapses the result into a single stringified text item.
- Files reviewed: 2/2 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.
A bare Content of type function_approval_request is returned unchanged by the invocation layer on both the normal and termination paths, so the override rule as written misdirects middleware that drive approval flows.
NekoPunch (orangeCatDeveloper)
temporarily deployed
to
github-app-auth
September 11, 2026 06:47 — with
GitHub Actions
Inactive
Eduard van Valkenburg (eavanvalkenburg)
approved these changes
Sep 11, 2026
Eduard van Valkenburg (eavanvalkenburg)
enabled auto-merge
September 11, 2026 08:24
NekoPunch (orangeCatDeveloper)
deleted the
docs/function-middleware-result-shape
branch
September 11, 2026 08:42
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation & Context
FunctionInvocationContext.resultis documented with one sentence that covers twodifferent contracts. After
call_next()it holdsFunctionTool.invoke's parsed output —a
list[Content]— but the same attribute accepts a raw value of any type when middlewareoverride the result. A middleware author reading "Function execution result. Can be
observed after calling
call_next()to see the actual execution result" reasonably writesif not isinstance(context.result, str): return, which silently never fires: no error, nolog line. The natural unit test for that middleware — build a context, assign
result = "text", assert — passes, because the write side really does accept a string.Test and production disagree and nothing reports it.
The repo's own function-middleware sample models the wrong side of that contract, so the
mistake is inherited by imitation.
Description & Review Guide
The docstrings now describe the attribute as what it is: untyped. The pipeline assigns
FunctionTool.invoke's output at the innermost link of the chain only(
FunctionMiddlewarePipeline.execute), so an outer middleware observes whatever the innerones left behind, not the tool's output. The docs say that, name
result_parser=SKIP_PARSINGas the one thing that changes the parsed shape, and state the override rule that is easy to
get wrong: only
list[Content]andstrsurviveContent.from_function_resultintact —a bare
Contentor a partially rebuilt list is stringified into one text item.shared_state_middleware.pywas f-stringing thelist[Content]and assigning the stringback, which sent a Python repr to the model instead of the tool's text. It now rebuilds
the text items.
Captured by driving the real
FunctionMiddlewarePipelinewith a real@toolandFunctionTool.invokeas the final handler, then wrapping the result the way_finalize_function_resultdoes.No runtime behavior change in the framework — docstrings plus one sample. The sample's
output text changes, for the better.
test_middleware_context_result.py::test_function_middleware_post_execution_overrideis worth a look while reviewing: its
final_handlerreturns astr, so the middleware'sif "modify" in context.resultpasses there while the same check would be silently Falseagainst
FunctionTool.invoke'slist[Content]in production. That is the issue's pointabout stub-shaped middleware tests, in this repo's own suite. I left it alone rather than
grow this PR into a test rewrite.
Deliberately not covered: the issue's suggestion 2 (a public
result_textaccessor, whichwould let
security.pydrop its private_ensure_content_list) and suggestion 3 (makingthe type honest at the seam, a breaking change). Both are API decisions worth their own
issue rather than a doc PR.
security.py's "typicallylist[Content]" hedge is left asis — with the chain behavior now documented, that wording turns out to be accurate.
Related Issue
Part of #7575
Contribution Checklist
ruff check/ruff format --checkclean)uvon this machine), leaving it to CI.breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.