Skip to content

Commit b7446cd

Browse files
post rebase from main if escalate is true in the sequential_agent then the agent terminates the sequence immediately preventing subsequent agents from running.
1 parent 21b0f49 commit b7446cd

File tree

1 file changed

+76
-8
lines changed

1 file changed

+76
-8
lines changed

src/google/adk/agents/sequential_agent.py

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from __future__ import annotations
1818

19+
import logging
1920
from typing import AsyncGenerator
2021
from typing import ClassVar
2122
from typing import Type
@@ -24,12 +25,24 @@
2425

2526
from ..events.event import Event
2627
from ..utils.context_utils import Aclosing
28+
from ..utils.feature_decorator import experimental
2729
from .base_agent import BaseAgent
28-
from .base_agent import BaseAgentConfig
30+
from .base_agent import BaseAgentState
31+
from .base_agent_config import BaseAgentConfig
2932
from .invocation_context import InvocationContext
3033
from .llm_agent import LlmAgent
3134
from .sequential_agent_config import SequentialAgentConfig
3235

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+
3346

3447
class SequentialAgent(BaseAgent):
3548
"""A shell agent that runs its sub-agents in sequence."""
@@ -41,18 +54,70 @@ class SequentialAgent(BaseAgent):
4154
async def _run_async_impl(
4255
self, ctx: InvocationContext
4356
) -> 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+
4679
async with Aclosing(sub_agent.run_async(ctx)) as agen:
4780
async for event in agen:
4881
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:
5488
return
5589

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+
56121
@override
57122
async def _run_live_impl(
58123
self, ctx: InvocationContext
@@ -68,6 +133,9 @@ async def _run_live_impl(
68133
Args:
69134
ctx: The invocation context of the agent.
70135
"""
136+
if not self.sub_agents:
137+
return
138+
71139
# There is no way to know if it's using live during init phase so we have to init it here
72140
for sub_agent in self.sub_agents:
73141
# add tool

0 commit comments

Comments
 (0)