Skip to content

Commit 0f349ac

Browse files
haranrkcopybara-github
authored andcommitted
refactor(interactions): extract previous-interaction-id lookup helper
Extract the previous-interaction-id lookup into reusable, private module-level helpers. No behavior change. Co-authored-by: Haran Rajkumar <haranrk@google.com> PiperOrigin-RevId: 937636496
1 parent e66eaf5 commit 0f349ac

2 files changed

Lines changed: 111 additions & 72 deletions

File tree

src/google/adk/flows/llm_flows/interactions_processor.py

Lines changed: 55 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,56 @@
2929
logger = logging.getLogger('google_adk.' + __name__)
3030

3131

32+
def _is_event_in_branch(current_branch: Optional[str], event: Event) -> bool:
33+
"""Return True if ``event`` belongs to ``current_branch`` (or the root)."""
34+
if not current_branch:
35+
# No branch means we're at the root; include all events without a branch.
36+
return not event.branch
37+
return event.branch == current_branch or not event.branch
38+
39+
40+
def _find_previous_interaction_id(
41+
events: list[Event],
42+
*,
43+
agent_name: str,
44+
current_branch: Optional[str],
45+
) -> Optional[str]:
46+
"""Find the most recent interaction_id authored by ``agent_name``.
47+
48+
Scans ``events`` in reverse, skipping events outside ``current_branch``, and
49+
returns the first ``interaction_id`` from an event authored by this agent.
50+
"""
51+
logger.debug(
52+
'Finding previous_interaction_id: agent=%s, branch=%s, num_events=%d',
53+
agent_name,
54+
current_branch,
55+
len(events),
56+
)
57+
for event in reversed(events):
58+
if not _is_event_in_branch(current_branch, event):
59+
logger.debug(
60+
'Skipping event not in branch: author=%s, branch=%s, current=%s',
61+
event.author,
62+
event.branch,
63+
current_branch,
64+
)
65+
continue
66+
logger.debug(
67+
'Checking event: author=%s, interaction_id=%s, branch=%s',
68+
event.author,
69+
event.interaction_id,
70+
event.branch,
71+
)
72+
if event.author == agent_name and event.interaction_id:
73+
logger.debug(
74+
'Found interaction_id from agent %s: %s',
75+
agent_name,
76+
event.interaction_id,
77+
)
78+
return event.interaction_id
79+
return None
80+
81+
3282
class InteractionsRequestProcessor(BaseLlmRequestProcessor):
3383
"""Request processor for Interactions API stateful conversations.
3484
This processor extracts the previous_interaction_id from session events
@@ -75,66 +125,12 @@ async def run_async(
75125
def _find_previous_interaction_id(
76126
self, invocation_context: 'InvocationContext'
77127
) -> Optional[str]:
78-
"""Find the previous interaction ID from session events.
79-
For interactions API stateful mode, we need to find the most recent
80-
interaction_id from model responses to chain interactions.
81-
Args:
82-
invocation_context: The invocation context containing session events.
83-
Returns:
84-
The previous interaction ID if found, None otherwise.
85-
"""
86-
events = invocation_context.session.events
87-
current_branch = invocation_context.branch
88-
agent_name = invocation_context.agent.name
89-
logger.debug(
90-
'Finding previous_interaction_id: agent=%s, branch=%s, num_events=%d',
91-
agent_name,
92-
current_branch,
93-
len(events),
128+
"""Find the previous interaction ID from session events."""
129+
return _find_previous_interaction_id(
130+
invocation_context.session.events,
131+
agent_name=invocation_context.agent.name,
132+
current_branch=invocation_context.branch,
94133
)
95-
# Iterate backwards through events to find the most recent interaction_id
96-
for event in reversed(events):
97-
# Skip events not in current branch
98-
if not self._is_event_in_branch(current_branch, event):
99-
logger.debug(
100-
'Skipping event not in branch: author=%s, branch=%s, current=%s',
101-
event.author,
102-
event.branch,
103-
current_branch,
104-
)
105-
continue
106-
# Look for model responses with interaction_id from this agent
107-
logger.debug(
108-
'Checking event: author=%s, interaction_id=%s, branch=%s',
109-
event.author,
110-
event.interaction_id,
111-
event.branch,
112-
)
113-
# Only consider events from this agent (skip sub-agent events)
114-
if event.author == agent_name and event.interaction_id:
115-
logger.debug(
116-
'Found interaction_id from agent %s: %s',
117-
agent_name,
118-
event.interaction_id,
119-
)
120-
return event.interaction_id
121-
return None
122-
123-
def _is_event_in_branch(
124-
self, current_branch: Optional[str], event: Event
125-
) -> bool:
126-
"""Check if an event belongs to the current branch.
127-
Args:
128-
current_branch: The current branch name.
129-
event: The event to check.
130-
Returns:
131-
True if the event belongs to the current branch.
132-
"""
133-
if not current_branch:
134-
# No branch means we're at the root, include all events without branch
135-
return not event.branch
136-
# Event must be in the same branch or have no branch (root level)
137-
return event.branch == current_branch or not event.branch
138134

139135

140136
# Module-level processor instance for use in flow configuration

tests/unittests/flows/llm_flows/test_interactions_processor.py

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,13 @@ def test_find_previous_interaction_id_skips_user_events(self):
168168

169169
def test_is_event_in_branch_no_branch(self):
170170
"""Test branch filtering with no current branch."""
171-
processor = interactions_processor.InteractionsRequestProcessor()
172-
173171
# Event without branch should be included when no current branch
174172
event = Event(
175173
invocation_id="inv1",
176174
author="test",
177175
content=types.ModelContent("test"),
178176
)
179-
assert processor._is_event_in_branch(None, event) is True
177+
assert interactions_processor._is_event_in_branch(None, event) is True
180178

181179
# Event with branch should be excluded when no current branch
182180
event_with_branch = Event(
@@ -185,39 +183,84 @@ def test_is_event_in_branch_no_branch(self):
185183
content=types.ModelContent("test"),
186184
branch="some_branch",
187185
)
188-
assert processor._is_event_in_branch(None, event_with_branch) is False
186+
assert (
187+
interactions_processor._is_event_in_branch(None, event_with_branch)
188+
is False
189+
)
189190

190191
def test_is_event_in_branch_same_branch(self):
191192
"""Test that events in the same branch are included."""
192-
processor = interactions_processor.InteractionsRequestProcessor()
193-
194193
event = Event(
195194
invocation_id="inv1",
196195
author="test",
197196
content=types.ModelContent("test"),
198197
branch="root.child",
199198
)
200-
assert processor._is_event_in_branch("root.child", event) is True
199+
assert (
200+
interactions_processor._is_event_in_branch("root.child", event) is True
201+
)
201202

202203
def test_is_event_in_branch_different_branch(self):
203204
"""Test that events in different branches are excluded."""
204-
processor = interactions_processor.InteractionsRequestProcessor()
205-
206205
event = Event(
207206
invocation_id="inv1",
208207
author="test",
209208
content=types.ModelContent("test"),
210209
branch="root.other",
211210
)
212-
assert processor._is_event_in_branch("root.child", event) is False
211+
assert (
212+
interactions_processor._is_event_in_branch("root.child", event) is False
213+
)
213214

214215
def test_is_event_in_branch_root_events_included(self):
215216
"""Test that root events (no branch) are included in child branches."""
216-
processor = interactions_processor.InteractionsRequestProcessor()
217-
218217
event = Event(
219218
invocation_id="inv1",
220219
author="test",
221220
content=types.ModelContent("test"),
222221
)
223-
assert processor._is_event_in_branch("root.child", event) is True
222+
assert (
223+
interactions_processor._is_event_in_branch("root.child", event) is True
224+
)
225+
226+
227+
def _evt(author: str, interaction_id: str | None, branch: str | None) -> Event:
228+
return Event(author=author, interaction_id=interaction_id, branch=branch)
229+
230+
231+
def test_find_previous_interaction_id_returns_latest_for_agent():
232+
events = [
233+
_evt("my_agent", "int_1", None),
234+
_evt("user", None, None),
235+
_evt("my_agent", "int_2", None),
236+
_evt("other_agent", "int_3", None),
237+
]
238+
239+
result = interactions_processor._find_previous_interaction_id(
240+
events, agent_name="my_agent", current_branch=None
241+
)
242+
243+
assert result == "int_2"
244+
245+
246+
def test_find_previous_interaction_id_respects_branch():
247+
events = [
248+
_evt("my_agent", "int_main", None),
249+
_evt("my_agent", "int_other_branch", "branch_b"),
250+
]
251+
252+
result = interactions_processor._find_previous_interaction_id(
253+
events, agent_name="my_agent", current_branch="branch_a"
254+
)
255+
256+
assert result == "int_main"
257+
258+
259+
def test_find_previous_interaction_id_none_when_absent():
260+
events = [_evt("user", None, None)]
261+
262+
result = interactions_processor._find_previous_interaction_id(
263+
events, agent_name="my_agent", current_branch=None
264+
)
265+
266+
assert result is None

0 commit comments

Comments
 (0)