From a34121d451b7eac83af6cf1ad0c2218835563e5a Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Fri, 24 Apr 2026 14:21:04 -0700 Subject: [PATCH] fix(a2a_executor): remove shadowing local `Part` import that broke streaming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python scoping rule: any name assigned anywhere in a function body is local for the entire body. The outbound-files block at ~L442 had `from a2a.types import ... Part ...`, which made `Part` a local name throughout the execute() function. The astream_events loop at L358 — which runs BEFORE that import — then raised: UnboundLocalError: cannot access local variable 'Part' where it is not associated with a value Every streaming A2A reply died with "Agent error: cannot access local variable 'Part' where it is not associated with a value" instead of the actual agent text. 5 tests caught it: - test_streaming_plain_string_content - test_streaming_anthropic_content_blocks - test_non_stream_events_ignored - test_core_execute_on_chat_model_end_captures_last_ai_message - test_core_execute_pii_redaction_when_pii_found Fix: drop `Part` from the function-scope import (it is already imported at module level on line 42) and leave a comment pinning the rationale so a future refactor doesn't re-introduce the shadow. All 43 test_a2a_executor tests pass locally. Co-Authored-By: Claude Opus 4.7 (1M context) --- workspace/a2a_executor.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/workspace/a2a_executor.py b/workspace/a2a_executor.py index b95558ce..a981ec2d 100644 --- a/workspace/a2a_executor.py +++ b/workspace/a2a_executor.py @@ -439,7 +439,14 @@ class LangGraphA2AExecutor(AgentExecutor): # deepagents, future ReAct variants) inherits it. _outbound = collect_outbound_files(final_text) if _outbound: - from a2a.types import FilePart, FileWithUri, Message, Part, Role, TextPart + # NOTE: do NOT re-import `Part` here. It is already imported + # at module scope (line 42). A function-scope `from a2a.types + # import ... Part ...` would mark `Part` as a local name + # throughout this function under Python's scoping rules, + # making the earlier `Part(text=text)` call (line ~358, inside + # the astream_events loop) raise UnboundLocalError because + # the local binding is not yet in scope at that point. + from a2a.types import FilePart, FileWithUri, Message, Role, TextPart _parts: list[Part] = [Part(root=TextPart(text=final_text))] if final_text else [] for f in _outbound: _parts.append(Part(root=FilePart(file=FileWithUri(