2727from google .adk .workflow ._base_node import START
2828from google .adk .workflow ._workflow import Workflow
2929from google .genai import types
30+ from pydantic import Field
3031import pytest
3132
3233from . import testing_utils
@@ -39,6 +40,7 @@ class _MockNonLiveNode(BaseNode):
3940
4041 called : bool = False
4142 actual_input : Any = None
43+ shared_state : dict [str , Any ] = Field (default_factory = dict )
4244
4345 def __init__ (self , * , name : str ):
4446 super ().__init__ (name = name )
@@ -51,6 +53,8 @@ async def _run_impl(
5153 ) -> AsyncGenerator [Any , None ]:
5254 self .called = True
5355 self .actual_input = node_input
56+ self .shared_state ["called" ] = True
57+ self .shared_state ["actual_input" ] = node_input
5458 yield Event (output = f"{ self .name } _output" )
5559
5660
@@ -72,29 +76,6 @@ async def _run_impl(
7276 yield Event (output = self .output_value )
7377
7478
75- class _DynamicLiveSchedulerNode (BaseNode ):
76- """A node that dynamically schedules a child live node using ctx.run_node()."""
77-
78- child_node : BaseNode | None = None
79- child_output : Any = None
80-
81- def __init__ (self , * , name : str , child_node : BaseNode ):
82- super ().__init__ (name = name , rerun_on_resume = True )
83- self .child_node = child_node
84-
85- async def _run_impl (
86- self ,
87- * ,
88- ctx : Context ,
89- node_input : Any ,
90- ) -> AsyncGenerator [Any , None ]:
91- if self .child_node :
92- self .child_output = await ctx .run_node (
93- self .child_node , node_input = node_input
94- )
95- yield Event (output = f"{ self .name } _output" )
96-
97-
9879# --- Live Workflow Unit Tests (TDD) ---
9980
10081
@@ -468,6 +449,30 @@ async def test_nested_live_node_and_outer_live_node():
468449@pytest .mark .asyncio
469450async def test_dynamic_node_scheduling_of_live_node ():
470451 """CUJ 4: A node in workflow dynamically schedules a live node using ctx.run_node()."""
452+
453+ class _DynamicLiveSchedulerNode (BaseNode ):
454+ """A node that dynamically schedules a child live node using ctx.run_node()."""
455+
456+ child_node : BaseNode | None = None
457+ child_output : Any = None
458+ shared_state : dict [str , Any ] = Field (default_factory = dict )
459+
460+ def __init__ (self , * , name : str , child_node : BaseNode ):
461+ super ().__init__ (name = name , rerun_on_resume = True )
462+ self .child_node = child_node
463+
464+ async def _run_impl (
465+ self ,
466+ * ,
467+ ctx : Context ,
468+ node_input : Any ,
469+ ) -> AsyncGenerator [Any , None ]:
470+ if self .child_node :
471+ output = await ctx .run_node (self .child_node , node_input = node_input )
472+ self .child_output = output
473+ self .shared_state ["child_output" ] = output
474+ yield Event (output = f"{ self .name } _output" )
475+
471476 mock_model = testing_utils .MockModel .create (
472477 responses = [
473478 LlmResponse (
@@ -531,7 +536,9 @@ async def test_dynamic_node_scheduling_of_live_node():
531536 {"result" : "DynamicLiveNode_output" },
532537 "SchedulerNode_output" ,
533538 ]
534- assert scheduler_node .child_output == {"result" : "DynamicLiveNode_output" }
539+ assert scheduler_node .shared_state .get ("child_output" ) == {
540+ "result" : "DynamicLiveNode_output"
541+ }
535542
536543 # Assert content events
537544 content_texts = [
@@ -663,7 +670,7 @@ async def test_single_turn_agent_runs_as_non_live_in_live_session():
663670
664671 outputs = [e .output for e in events if e .output is not None ]
665672 assert outputs == ["initial_text_input" , "capture_output" ]
666- assert capture .actual_input == "SingleTurn_output"
673+ assert capture .shared_state . get ( " actual_input" ) == "SingleTurn_output"
667674 # Verify that the model received the initial_text_input (node_input) and NOT the live queue audio
668675 assert len (mock_model .requests ) == 1
669676 assert (
0 commit comments