16
16
17
17
from __future__ import annotations
18
18
19
+ import logging
19
20
from typing import AsyncGenerator
20
21
from typing import ClassVar
21
22
from typing import Type
24
25
25
26
from ..events .event import Event
26
27
from ..utils .context_utils import Aclosing
28
+ from ..utils .feature_decorator import experimental
27
29
from .base_agent import BaseAgent
28
- from .base_agent import BaseAgentConfig
30
+ from .base_agent import BaseAgentState
31
+ from .base_agent_config import BaseAgentConfig
29
32
from .invocation_context import InvocationContext
30
33
from .llm_agent import LlmAgent
31
34
from .sequential_agent_config import SequentialAgentConfig
32
35
36
+ logger = logging .getLogger ('google_adk.' + __name__ )
37
+
38
+
39
+ @experimental
40
+ class SequentialAgentState (BaseAgentState ):
41
+ """State for SequentialAgent."""
42
+
43
+ current_sub_agent : str = ''
44
+ """The name of the current sub-agent to run."""
45
+
33
46
34
47
class SequentialAgent (BaseAgent ):
35
48
"""A shell agent that runs its sub-agents in sequence."""
@@ -41,18 +54,70 @@ class SequentialAgent(BaseAgent):
41
54
async def _run_async_impl (
42
55
self , ctx : InvocationContext
43
56
) -> AsyncGenerator [Event , None ]:
44
- for sub_agent in self .sub_agents :
45
- should_exit = False
57
+ if not self .sub_agents :
58
+ return
59
+
60
+ # Initialize or resume the execution state from the agent state.
61
+ agent_state = self ._load_agent_state (ctx , SequentialAgentState )
62
+ start_index = self ._get_start_index (agent_state )
63
+
64
+ pause_invocation = False
65
+ resuming_sub_agent = agent_state is not None
66
+ for i in range (start_index , len (self .sub_agents )):
67
+ sub_agent = self .sub_agents [i ]
68
+ if not resuming_sub_agent :
69
+ # If we are resuming from the current event, it means the same event has
70
+ # already been logged, so we should avoid yielding it again.
71
+ if ctx .is_resumable :
72
+ agent_state = SequentialAgentState (current_sub_agent = sub_agent .name )
73
+ yield self ._create_agent_state_event (ctx , agent_state = agent_state )
74
+
75
+ # Reset the sub-agent's state in the context to ensure that each
76
+ # sub-agent starts fresh.
77
+ ctx .reset_agent_state (sub_agent .name )
78
+
46
79
async with Aclosing (sub_agent .run_async (ctx )) as agen :
47
80
async for event in agen :
48
81
yield event
49
- if event .actions .escalate :
50
- should_exit = True
51
- break
52
-
53
- if should_exit :
82
+ if ctx .should_pause_invocation (event ):
83
+ pause_invocation = True
84
+ if event .actions and event .actions .escalate :
85
+ return
86
+ # Skip the rest of the sub-agents if the invocation is paused.
87
+ if pause_invocation :
54
88
return
55
89
90
+ # Reset the flag for the next sub-agent.
91
+ resuming_sub_agent = False
92
+
93
+ if ctx .is_resumable :
94
+ yield self ._create_agent_state_event (ctx , end_of_agent = True )
95
+
96
+ def _get_start_index (
97
+ self ,
98
+ agent_state : SequentialAgentState ,
99
+ ) -> int :
100
+ """Calculates the start index for the sub-agent loop."""
101
+ if not agent_state :
102
+ return 0
103
+
104
+ if not agent_state .current_sub_agent :
105
+ # This means the process was finished.
106
+ return len (self .sub_agents )
107
+
108
+ try :
109
+ sub_agent_names = [sub_agent .name for sub_agent in self .sub_agents ]
110
+ return sub_agent_names .index (agent_state .current_sub_agent )
111
+ except ValueError :
112
+ # A sub-agent was removed so the agent name is not found.
113
+ # For now, we restart from the beginning.
114
+ logger .warning (
115
+ 'Sub-agent %s was removed so the agent name is not found. Restarting'
116
+ ' from the beginning.' ,
117
+ agent_state .current_sub_agent ,
118
+ )
119
+ return 0
120
+
56
121
@override
57
122
async def _run_live_impl (
58
123
self , ctx : InvocationContext
@@ -68,6 +133,9 @@ async def _run_live_impl(
68
133
Args:
69
134
ctx: The invocation context of the agent.
70
135
"""
136
+ if not self .sub_agents :
137
+ return
138
+
71
139
# There is no way to know if it's using live during init phase so we have to init it here
72
140
for sub_agent in self .sub_agents :
73
141
# add tool
0 commit comments