Skip to content

Commit 48fb122

Browse files
author
Sourabh Patil
committed
feat(agent): add support for history window in current turn contents
1 parent 3fa2ea7 commit 48fb122

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed

src/google/adk/agents/llm_agent.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,15 @@ class LlmAgent(BaseAgent):
169169
instruction and input
170170
"""
171171

172+
history_window_size: Optional[int] = None
173+
"""The size of the history window (N) to use.
174+
175+
This parameter is ONLY used when `include_contents` is set to 'windowed'.
176+
It must be a positive integer defining the number of recent conversational
177+
turns to include in the model context.
178+
"""
179+
180+
172181
# Controlled input/output configurations - Start
173182
input_schema: Optional[type[BaseModel]] = None
174183
"""The input schema when agent is used as a tool."""
@@ -268,6 +277,31 @@ class LlmAgent(BaseAgent):
268277
"""
269278
# Callbacks - End
270279

280+
@model_validator(mode='after')
281+
def validate_history_window(self) -> 'LlmAgent':
282+
"""Validate that history_window_size is used correctly."""
283+
is_windowed = self.include_contents == 'windowed'
284+
window_size_is_set = self.history_window_size is not None
285+
286+
# Case 1: 'windowed' is set, but the window size is not.
287+
if is_windowed and not window_size_is_set:
288+
raise ValueError(
289+
"When 'include_contents' is 'windowed', 'history_window_size' must be set to a positive integer."
290+
)
291+
292+
# Case 2: 'windowed' is set, but window size is not a positive integer.
293+
if is_windowed and window_size_is_set and self.history_window_size <= 0:
294+
raise ValueError("'history_window_size' must be a positive integer.")
295+
296+
# Case 3: 'windowed' is NOT set, but the window size is.
297+
if not is_windowed and window_size_is_set:
298+
raise ValueError(
299+
"'history_window_size' can only be set when 'include_contents' is 'windowed'."
300+
)
301+
302+
return self
303+
304+
271305
@override
272306
async def _run_async_impl(
273307
self, ctx: InvocationContext

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ async def run_async(
5050
invocation_context.session.events,
5151
agent.name,
5252
)
53+
elif agent.include_contents == 'windowed':
54+
llm_request.contents = _get_windowed_turn_contents(
55+
## Includes windowed turn contexts (windowed conversation history)
56+
invocation_context.branch,
57+
invocation_context.session.events,
58+
agent.name,
59+
history_window=agent.history_window_size
60+
)
5361
else:
5462
# Include current turn context only (no conversation history)
5563
llm_request.contents = _get_current_turn_contents(
@@ -282,6 +290,39 @@ def _get_current_turn_contents(
282290

283291
return []
284292

293+
def _get_windowed_turn_contents(
294+
current_branch: Optional[str],
295+
events: list[Event],
296+
agent_name: str = '',
297+
history_window: int = 5 # New argument
298+
) -> list[types.Content]:
299+
"""Get contents for the current turn, optionally with limited conversation history.
300+
301+
Args:
302+
current_branch: The current branch of the agent.
303+
events: A list of all session events.
304+
agent_name: The name of the agent.
305+
history_window: Number of previous contents to include from conversation history.
306+
307+
Returns:
308+
A list of contents for the current turn and optional recent history.
309+
"""
310+
# Find the start of the current turn
311+
turn_start_index = 0
312+
for i in range(len(events) - 1, -1, -1):
313+
event = events[i]
314+
if event.author == 'user' or _is_other_agent_reply(agent_name, event):
315+
turn_start_index = i
316+
break
317+
318+
# Determine the starting index for history window
319+
if history_window > 0:
320+
history_start_index = max(0, turn_start_index - history_window)
321+
else:
322+
history_start_index = turn_start_index
323+
324+
return _get_contents(current_branch, events[history_start_index:], agent_name)
325+
285326

286327
def _is_other_agent_reply(current_agent_name: str, event: Event) -> bool:
287328
"""Whether the event is a reply from another agent."""

0 commit comments

Comments
 (0)