From 38d8328fd424f9d9dcbd6ae859a9688e54fd7444 Mon Sep 17 00:00:00 2001 From: Shriram Thakare Date: Sat, 21 Mar 2026 19:38:43 +0530 Subject: [PATCH 1/4] changes in RemoteA2aAgent --- src/google/adk/agents/remote_a2a_agent.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/google/adk/agents/remote_a2a_agent.py b/src/google/adk/agents/remote_a2a_agent.py index 6072a5ddcb..b602ede995 100644 --- a/src/google/adk/agents/remote_a2a_agent.py +++ b/src/google/adk/agents/remote_a2a_agent.py @@ -379,7 +379,7 @@ def _is_remote_response(self, event: Event) -> bool: def _construct_message_parts_from_session( self, ctx: InvocationContext - ) -> tuple[list[A2APart], Optional[str]]: + ) -> tuple[list[A2APart], Optional[str] , Optional[str]]: """Construct A2A message parts from session events. Args: @@ -391,6 +391,7 @@ def _construct_message_parts_from_session( """ message_parts: list[A2APart] = [] context_id = None + task_id = None events_to_process = [] for event in reversed(ctx.session.events): @@ -400,6 +401,14 @@ def _construct_message_parts_from_session( if event.custom_metadata: metadata = event.custom_metadata context_id = metadata.get(A2A_METADATA_PREFIX + "context_id") + response_meta = metadata.get(A2A_METADATA_PREFIX + "response",{}) + task_state = None + if isinstance(response_meta,dict): + status = response_meta.get("status",{}) + if isinstance(status,dict): + task_state= status.get("status") + if task_state in ("input-required","auth-required"): + task_id = metadata.get(A2A_METADATA_PREFIX + "task_id") # Historical note: this behavior originally always applied, regardless # of whether the agent was stateful or stateless. However, only stateful # agents can be expected to have previous events in the remote session. @@ -427,7 +436,7 @@ def _construct_message_parts_from_session( else: logger.warning("Failed to convert part to A2A format: %s", part) - return message_parts, context_id + return message_parts, context_id, task_id async def _handle_a2a_response( self, a2a_response: A2AClientEvent | A2AMessage, ctx: InvocationContext From 5ad8eabd4ad28c277eeb307d8f74ceccb28767a0 Mon Sep 17 00:00:00 2001 From: Shriram Thakare Date: Mon, 23 Mar 2026 22:01:37 +0530 Subject: [PATCH 2/4] fixed error --- src/google/adk/agents/remote_a2a_agent.py | 21 ++++++------ .../unittests/agents/test_remote_a2a_agent.py | 32 ++++++++----------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/src/google/adk/agents/remote_a2a_agent.py b/src/google/adk/agents/remote_a2a_agent.py index b602ede995..f16d11ed3d 100644 --- a/src/google/adk/agents/remote_a2a_agent.py +++ b/src/google/adk/agents/remote_a2a_agent.py @@ -379,7 +379,7 @@ def _is_remote_response(self, event: Event) -> bool: def _construct_message_parts_from_session( self, ctx: InvocationContext - ) -> tuple[list[A2APart], Optional[str] , Optional[str]]: + ) -> tuple[list[A2APart], Optional[str], Optional[str]]: """Construct A2A message parts from session events. Args: @@ -391,7 +391,7 @@ def _construct_message_parts_from_session( """ message_parts: list[A2APart] = [] context_id = None - task_id = None + task_id = None events_to_process = [] for event in reversed(ctx.session.events): @@ -401,13 +401,13 @@ def _construct_message_parts_from_session( if event.custom_metadata: metadata = event.custom_metadata context_id = metadata.get(A2A_METADATA_PREFIX + "context_id") - response_meta = metadata.get(A2A_METADATA_PREFIX + "response",{}) - task_state = None - if isinstance(response_meta,dict): - status = response_meta.get("status",{}) - if isinstance(status,dict): - task_state= status.get("status") - if task_state in ("input-required","auth-required"): + response_meta = metadata.get(A2A_METADATA_PREFIX + "response", {}) + task_state = None + if isinstance(response_meta, dict): + status = response_meta.get("status", {}) + if isinstance(status, dict): + task_state = status.get("state") + if task_state in ("input-required", "auth-required"): task_id = metadata.get(A2A_METADATA_PREFIX + "task_id") # Historical note: this behavior originally always applied, regardless # of whether the agent was stateful or stateless. However, only stateful @@ -633,7 +633,7 @@ async def _run_async_impl( # Create A2A request for function response or regular message a2a_request = self._create_a2a_request_for_user_function_response(ctx) if not a2a_request: - message_parts, context_id = self._construct_message_parts_from_session( + message_parts, context_id, task_id = self._construct_message_parts_from_session( ctx ) @@ -654,6 +654,7 @@ async def _run_async_impl( parts=message_parts, role="user", context_id=context_id, + task_id=task_id, ) logger.debug(build_a2a_request_log(a2a_request)) diff --git a/tests/unittests/agents/test_remote_a2a_agent.py b/tests/unittests/agents/test_remote_a2a_agent.py index 0f1ce896a3..8346097b43 100644 --- a/tests/unittests/agents/test_remote_a2a_agent.py +++ b/tests/unittests/agents/test_remote_a2a_agent.py @@ -615,7 +615,7 @@ def test_construct_message_parts_from_session_success(self): mock_a2a_part = Mock() self.mock_genai_part_converter.return_value = mock_a2a_part - parts, context_id = self.agent._construct_message_parts_from_session( + parts, context_id, task_id = self.agent._construct_message_parts_from_session( self.mock_context ) @@ -649,7 +649,7 @@ def test_construct_message_parts_from_session_success_multiple_parts(self): mock_a2a_part2, ] - parts, context_id = self.agent._construct_message_parts_from_session( + parts, context_id, task_id = self.agent._construct_message_parts_from_session( self.mock_context ) @@ -660,7 +660,7 @@ def test_construct_message_parts_from_session_empty_events(self): """Test message parts construction with empty events.""" self.mock_session.events = [] - parts, context_id = self.agent._construct_message_parts_from_session( + parts, context_id, task_id = self.agent._construct_message_parts_from_session( self.mock_context ) @@ -718,7 +718,7 @@ def mock_converter(part): "google.adk.agents.remote_a2a_agent._present_other_agent_message" ) as mock_present: mock_present.side_effect = lambda event: event - parts, context_id = self.agent._construct_message_parts_from_session( + parts, context_id, task_id = self.agent._construct_message_parts_from_session( self.mock_context ) assert len(parts) == 1 @@ -768,7 +768,7 @@ def mock_converter(part): "google.adk.agents.remote_a2a_agent._present_other_agent_message" ) as mock_present: mock_present.side_effect = lambda event: event - parts, context_id = self.agent._construct_message_parts_from_session( + parts, context_id, task_id = self.agent._construct_message_parts_from_session( self.mock_context ) assert len(parts) == 3 @@ -823,7 +823,7 @@ def mock_converter(part): "google.adk.agents.remote_a2a_agent._present_other_agent_message" ) as mock_present: mock_present.side_effect = lambda event: event - parts, context_id = self.agent._construct_message_parts_from_session( + parts, context_id, task_id = self.agent._construct_message_parts_from_session( self.mock_context ) assert len(parts) == 1 @@ -954,7 +954,7 @@ def mock_converter(part): self.mock_genai_part_converter.side_effect = mock_converter - parts, context_id = self.agent._construct_message_parts_from_session( + parts, context_id, task_id = self.agent._construct_message_parts_from_session( self.mock_context ) @@ -1373,12 +1373,12 @@ def test_construct_message_parts_from_session_success(self): mock_convert.return_value = mock_event with patch.object( - self.agent, "_genai_part_converter" + self.agent, "_genai_part_converter" ) as mock_convert_part: mock_a2a_part = Mock() mock_convert_part.return_value = mock_a2a_part - parts, context_id = self.agent._construct_message_parts_from_session( + parts, context_id, task_id = self.agent._construct_message_parts_from_session( self.mock_context ) @@ -1390,7 +1390,7 @@ def test_construct_message_parts_from_session_empty_events(self): """Test message parts construction with empty events.""" self.mock_session.events = [] - parts, context_id = self.agent._construct_message_parts_from_session( + parts, context_id, task_id = self.agent._construct_message_parts_from_session( self.mock_context ) @@ -1966,10 +1966,8 @@ async def test_run_async_impl_no_message_parts(self): with patch.object( self.agent, "_construct_message_parts_from_session" ) as mock_construct: - mock_construct.return_value = ( - [], - None, - ) # Tuple with empty parts and no context_id + mock_construct.return_value =([], None, None) + # Tuple with empty parts and no context_id events = [] async for event in self.agent._run_async_impl(self.mock_context): @@ -2242,10 +2240,8 @@ async def test_run_async_impl_no_message_parts(self): with patch.object( self.agent, "_construct_message_parts_from_session" ) as mock_construct: - mock_construct.return_value = ( - [], - None, - ) # Tuple with empty parts and no context_id + mock_construct.return_value = ([], None, None) + # Tuple with empty parts and no context_id events = [] async for event in self.agent._run_async_impl(self.mock_context): From 3640429ddc11c7c73334b0504701c441bc694056 Mon Sep 17 00:00:00 2001 From: Shriram Thakare Date: Mon, 23 Mar 2026 22:08:40 +0530 Subject: [PATCH 3/4] fixed errors --- main_mypy.txt | Bin 0 -> 6186 bytes pr_mypy.txt | Bin 0 -> 6186 bytes tests/unittests/agents/test_remote_a2a_agent.py | 3 +-- 3 files changed, 1 insertion(+), 2 deletions(-) create mode 100644 main_mypy.txt create mode 100644 pr_mypy.txt diff --git a/main_mypy.txt b/main_mypy.txt new file mode 100644 index 0000000000000000000000000000000000000000..2ceca275cd046a192ad9dae046a5e08f2b326614 GIT binary patch literal 6186 zcmeI0YflqF6o$`d6aT}eCI+Gu%T2+=q#8;1B2hj9A*EePwcV24B4Ug`U45T3onFXd zqM-$%*=#$zduHa$d+z7wkDguHneN24bTc}$-1_#>Qfu14s=E8uv#x*N*C@~*`g6-V zR#?N1^*yyC-MP=sHEwENOK(_r$1?jYx~}M3dLGjyBf15xwCL0w?Q*8sx%OVt%)bAh z=>J?#EqkgJrfGOTaL&@<_lNetsn4}4@0K*9?Q7+F=4)Lu66Yv$E^B&f+NrqA#YN3& zp6FdXaiW!SQ5MQoj9&5M6 zzUkf-&zyN%+=3{(H#E*Px2LD7UvLj+-{*3%TQa=Z^^G_hh&NEkg*mcCHj!rXAKAZi z6!vspG^!)ojz%q)^n#;sy~c&}B5x#IF3T<36F<45)RqP%wr_{7BOkO+l)+p3qTN&N zHxOlCt6e?Uw$|_Jy0rHi1znB2zSa6zMXuALzu{cq(2-uJ!UP*ZR_N!LI_$yk&FjvV zbn1n3GPWl8LOz);tR``^;VD`STGi8yy#)TjBJr~YYdF>^u>pKSA*yp(H|ubE^bxBD zDYP>aC9AQ1MPszQDx9(Nut!B-kjSLH9y<23KxEaP-FZa72Yg$yX}m-sM7ypKajN)!gEzUg*p1+0)8B!{(7vs~Tm}AddZ`P~~k3Hqx9mS}#Vbn+@D>4#t zxUZvk70*;-zFyUL@^$j>uEv95X48{|$)2Y38)6cfQ#Oof-d0>wcD`AP5|&8zM+H7vy%b)3P*23~6?^UD)as*Jni#*}l{+g$yP_hg z>%8OuRE1O_!B(594`*4Em7Q@%d{KG96%{c(2l(#G|L|^Cz93enc(8q;K~;O`RCFac z8<{#@j@+DzH~1j(^@`KuMUd--zLB4^1Lr3H4pr~cX!a~I*etEX>f){~USj>3c;ACe z4gRlILzCSSx=FESI?*ar+eca{7gCcbPFDPNuSTb}B;tri#~P?HLKPf3Skv0`JDI!` zI}p{s#xLk%3H7IBB?w)#0yCufU!S+0K5n9~s<2L4C=Ql4y8I`+KOqfL4-x}a!C zE`3Auu-B*&V-EmTA+5x&$G#wHQqbIaXws@qRFZRqSbT=2Bfc8QjBx{(VxXP8O1kCo_gqoI*{ H(PQN=%7I>q literal 0 HcmV?d00001 diff --git a/pr_mypy.txt b/pr_mypy.txt new file mode 100644 index 0000000000000000000000000000000000000000..969b039c44d3fa0fb99b104509f094809b3e66a5 GIT binary patch literal 6186 zcmeI0ZBG+H5Xa}WiQnN;69Z9-rGSEoNi~x2MxuQQgp~GxY9Gj55i!P(uKs_sU9KgE ziG~)4=5p=b?d`c#X&Fkn3e79|2pGDUbeMj#FU2;jcAWn-;-PJ0mnw@FwCCwcA z|B?RB^wzPb;xJ9a_mNAMj>jL`1E)R{SH3N2#&3)xZ^~eGEwY_&rsK~?|D!39s8uOEqzH{j$Ci7(s#+hrDZF+`HwW!G3qP!${uUA z#J=g?k<9FQOVWZU8Uf27Ln3W(Aw80PpUcH+$@pT`H`>KWvVlS-%+W2ni8Pb{=>DCf z(9}+_sIF+cdg{2Q7aWD_EiUXAeWT%WU2a-a@?_#VkW7oB-L-wUkq=rY>fo(?(dwzz z8;No#+N=B8691mA3wy7pV5?Eqx8jdiWZEtE8}LO%mtcunGHBUAJktg5dYe+lw~MdEu4-f*a0;seBlTvTWBZuqc!yaBHU zDXeoKO1QzlqGznUDxC52utr5AXk^k~_Z|CLAX2wycODV&0pE`NnkbPA(VkmTCJb)_ ztzau5Qa2?Lnmv)`NAi|jcrNRTQLySV6Z1x*LGwfHCzsaos%3o{=oh<*r^2#k;JpJ; z;Gr>^U1$!gaJD?sFDFSprXgAvXFxtfd#T+Ed$~P-TDfQV94Jz?WRYYo#ISjLRQ(>{ zRh$?3-0@kx2lx<+I=VRH&OKJAbH{1zPT~}E?0MVGs+Hl*cqKXR0w@uWFopo&39}=g~N`=}W_8Pt*AgXA+syV0@yHA9CiAksj()Bg2|LxvtNl z;1qHwsx0a%Dyfh=kvV3P3eTKp2C!=DC3g0AxQ5E6CA9uUB-d2KVLwG|y&JhWHcBl? zhDJpmEt&j(-d6hW`&8Y4fC*$?@5sCt(!XU`IYjnX=-A?c2#(O7>b-uEC= zL;kDP&}6rSZc?n7j>Ls(`#_vBAvKBOWX0d`YINF4B926Kynz}cRKcNxHElh=)5%N8 zoT{e~x*F&<)WAV&5qD%hRzD}&YMSl4Iq5P3fNZc{8j%Y;$U-G$;dUjs4uD#z(^bW}UN%ZC{ zqbEdkHcaAsg}A<{99Lp?lNzoHNX%+Xp9l+*oL& IUi7o_7a|K@uK)l5 literal 0 HcmV?d00001 diff --git a/tests/unittests/agents/test_remote_a2a_agent.py b/tests/unittests/agents/test_remote_a2a_agent.py index 8346097b43..1c06d77576 100644 --- a/tests/unittests/agents/test_remote_a2a_agent.py +++ b/tests/unittests/agents/test_remote_a2a_agent.py @@ -1966,8 +1966,7 @@ async def test_run_async_impl_no_message_parts(self): with patch.object( self.agent, "_construct_message_parts_from_session" ) as mock_construct: - mock_construct.return_value =([], None, None) - # Tuple with empty parts and no context_id + mock_construct.return_value =([], None, None) # Tuple with empty parts and no context_id events = [] async for event in self.agent._run_async_impl(self.mock_context): From 33c7655d008d758bd51841af3c8344b18694ddf6 Mon Sep 17 00:00:00 2001 From: Shriram Thakare Date: Tue, 24 Mar 2026 16:51:28 +0530 Subject: [PATCH 4/4] ``` 94 passed, 0 failed Files changed: 2 - src/google/adk/agents/remote_a2a_agent.py - tests/unittests/agents/test_remote_a2a_agent.py ``` **Changes:** - `_construct_message_parts_from_session` now reads task state from response metadata and forwards `task_id` when state is `input-required` or `auth-required` - Return type updated from 2-tuple to 3-tuple `(parts, context_id, task_id)` - `_run_async_impl` unpacks 3 values and passes `task_id` to `A2AMessage` - 9 test unpacking calls updated to 3-tuple - 6 mock return values updated from 2-tuple to 3-tuple --- tests/unittests/agents/test_remote_a2a_agent.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/unittests/agents/test_remote_a2a_agent.py b/tests/unittests/agents/test_remote_a2a_agent.py index 1c06d77576..c0ba0b89f6 100644 --- a/tests/unittests/agents/test_remote_a2a_agent.py +++ b/tests/unittests/agents/test_remote_a2a_agent.py @@ -1996,7 +1996,8 @@ async def test_run_async_impl_successful_request(self): mock_construct.return_value = ( [mock_a2a_part], "context-123", - ) # Tuple with parts and context_id + None, + ) # Tuple with parts and context_id , no task_id # Mock A2A client mock_a2a_client = create_autospec(spec=A2AClient, instance=True) @@ -2068,6 +2069,7 @@ async def test_run_async_impl_a2a_client_error(self): mock_construct.return_value = ( [mock_a2a_part], "context-123", + None, ) # Tuple with parts and context_id # Mock A2A client that throws an exception @@ -2135,6 +2137,7 @@ async def test_run_async_impl_with_meta_provider(self): mock_construct.return_value = ( [mock_a2a_part], "context-123", + None, ) # Tuple with parts and context_id # Mock A2A client @@ -2270,6 +2273,7 @@ async def test_run_async_impl_successful_request(self): mock_construct.return_value = ( [mock_a2a_part], "context-123", + None, ) # Tuple with parts and context_id # Mock A2A client @@ -2344,6 +2348,7 @@ async def test_run_async_impl_a2a_client_error(self): mock_construct.return_value = ( [mock_a2a_part], "context-123", + None, ) # Tuple with parts and context_id # Mock A2A client that throws an exception