mirror of
https://github.com/encounter/adk-python.git
synced 2026-07-09 18:19:28 -07:00
fix: Make compactor optional in EventsCompactionConfig and add a default
If `EventsCompactionConfig` is provided without a `compactor`, a `SlidingWindowCompactor` is now automatically instantiated using the `root_agent`'s LLM. This simplifies configuration by providing a sensible default. PiperOrigin-RevId: 816038579
This commit is contained in:
committed by
Copybara-Service
parent
238472d083
commit
3f4bd67b49
@@ -21,7 +21,7 @@ from pydantic import Field
|
|||||||
|
|
||||||
from ..agents.base_agent import BaseAgent
|
from ..agents.base_agent import BaseAgent
|
||||||
from ..agents.context_cache_config import ContextCacheConfig
|
from ..agents.context_cache_config import ContextCacheConfig
|
||||||
from ..apps.base_events_compactor import BaseEventsCompactor
|
from ..apps.base_events_summarizer import BaseEventsSummarizer
|
||||||
from ..plugins.base_plugin import BasePlugin
|
from ..plugins.base_plugin import BasePlugin
|
||||||
from ..utils.feature_decorator import experimental
|
from ..utils.feature_decorator import experimental
|
||||||
|
|
||||||
@@ -56,11 +56,13 @@ class EventsCompactionConfig(BaseModel):
|
|||||||
extra="forbid",
|
extra="forbid",
|
||||||
)
|
)
|
||||||
|
|
||||||
compactor: BaseEventsCompactor
|
summarizer: Optional[BaseEventsSummarizer] = None
|
||||||
"""The event compactor strategy for the application."""
|
"""The event summarizer to use for compaction."""
|
||||||
|
|
||||||
compaction_interval: int
|
compaction_interval: int
|
||||||
"""The number of *new* user-initiated invocations that, once
|
"""The number of *new* user-initiated invocations that, once
|
||||||
fully represented in the session's events, will trigger a compaction."""
|
fully represented in the session's events, will trigger a compaction."""
|
||||||
|
|
||||||
overlap_size: int
|
overlap_size: int
|
||||||
"""The number of preceding invocations to include from the
|
"""The number of preceding invocations to include from the
|
||||||
end of the last compacted range. This creates an overlap between consecutive
|
end of the last compacted range. This creates an overlap between consecutive
|
||||||
|
|||||||
+2
-2
@@ -23,11 +23,11 @@ from ..utils.feature_decorator import experimental
|
|||||||
|
|
||||||
|
|
||||||
@experimental
|
@experimental
|
||||||
class BaseEventsCompactor(abc.ABC):
|
class BaseEventsSummarizer(abc.ABC):
|
||||||
"""Base interface for compacting events."""
|
"""Base interface for compacting events."""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def maybe_compact_events(
|
async def maybe_summarize_events(
|
||||||
self, *, events: list[Event]
|
self, *, events: list[Event]
|
||||||
) -> Optional[Event]:
|
) -> Optional[Event]:
|
||||||
"""Compact a list of events into a single event.
|
"""Compact a list of events into a single event.
|
||||||
@@ -17,6 +17,7 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from google.adk.apps.app import App
|
from google.adk.apps.app import App
|
||||||
|
from google.adk.apps.llm_event_summarizer import LlmEventSummarizer
|
||||||
from google.adk.sessions.base_session_service import BaseSessionService
|
from google.adk.sessions.base_session_service import BaseSessionService
|
||||||
from google.adk.sessions.session import Session
|
from google.adk.sessions.session import Session
|
||||||
|
|
||||||
@@ -180,8 +181,13 @@ async def _run_compaction_for_sliding_window(
|
|||||||
if not events_to_compact:
|
if not events_to_compact:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
if not app.events_compaction_config.summarizer:
|
||||||
|
app.events_compaction_config.summarizer = LlmEventSummarizer(
|
||||||
|
llm=app.root_agent.canonical_model
|
||||||
|
)
|
||||||
|
|
||||||
compaction_event = (
|
compaction_event = (
|
||||||
await app.events_compaction_config.compactor.maybe_compact_events(
|
await app.events_compaction_config.summarizer.maybe_summarize_events(
|
||||||
events=events_to_compact
|
events=events_to_compact
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,15 +19,15 @@ from google.genai import types
|
|||||||
from google.genai.types import Content
|
from google.genai.types import Content
|
||||||
from google.genai.types import Part
|
from google.genai.types import Part
|
||||||
|
|
||||||
|
from ..apps.base_events_summarizer import BaseEventsSummarizer
|
||||||
from ..events.event import Event
|
from ..events.event import Event
|
||||||
from ..events.event_actions import EventActions
|
from ..events.event_actions import EventActions
|
||||||
from ..events.event_actions import EventCompaction
|
from ..events.event_actions import EventCompaction
|
||||||
from ..models.base_llm import BaseLlm
|
from ..models.base_llm import BaseLlm
|
||||||
from ..models.llm_request import LlmRequest
|
from ..models.llm_request import LlmRequest
|
||||||
from .base_events_compactor import BaseEventsCompactor
|
|
||||||
|
|
||||||
|
|
||||||
class LlmEventSummarizer(BaseEventsCompactor):
|
class LlmEventSummarizer(BaseEventsSummarizer):
|
||||||
"""An LLM-based event summarizer for sliding window compaction.
|
"""An LLM-based event summarizer for sliding window compaction.
|
||||||
|
|
||||||
This class is responsible for summarizing a provided list of events into a
|
This class is responsible for summarizing a provided list of events into a
|
||||||
@@ -81,7 +81,7 @@ class LlmEventSummarizer(BaseEventsCompactor):
|
|||||||
formatted_history.append(f'{event.author}: {part.text}')
|
formatted_history.append(f'{event.author}: {part.text}')
|
||||||
return '\\n'.join(formatted_history)
|
return '\\n'.join(formatted_history)
|
||||||
|
|
||||||
async def maybe_compact_events(
|
async def maybe_summarize_events(
|
||||||
self, *, events: list[Event]
|
self, *, events: list[Event]
|
||||||
) -> Optional[Event]:
|
) -> Optional[Event]:
|
||||||
"""Compacts given events and returns the compacted content.
|
"""Compacts given events and returns the compacted content.
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class TestCompaction(unittest.IsolatedAsyncioTestCase):
|
|||||||
await _run_compaction_for_sliding_window(
|
await _run_compaction_for_sliding_window(
|
||||||
app, session, self.mock_session_service
|
app, session, self.mock_session_service
|
||||||
)
|
)
|
||||||
self.mock_compactor.maybe_compact_events.assert_not_called()
|
self.mock_compactor.maybe_summarize_events.assert_not_called()
|
||||||
self.mock_session_service.append_event.assert_not_called()
|
self.mock_session_service.append_event.assert_not_called()
|
||||||
|
|
||||||
async def test_run_compaction_for_sliding_window_not_enough_new_invocations(
|
async def test_run_compaction_for_sliding_window_not_enough_new_invocations(
|
||||||
@@ -82,7 +82,7 @@ class TestCompaction(unittest.IsolatedAsyncioTestCase):
|
|||||||
name='test',
|
name='test',
|
||||||
root_agent=Mock(spec=BaseAgent),
|
root_agent=Mock(spec=BaseAgent),
|
||||||
events_compaction_config=EventsCompactionConfig(
|
events_compaction_config=EventsCompactionConfig(
|
||||||
compactor=self.mock_compactor,
|
summarizer=self.mock_compactor,
|
||||||
compaction_interval=3,
|
compaction_interval=3,
|
||||||
overlap_size=1,
|
overlap_size=1,
|
||||||
),
|
),
|
||||||
@@ -100,7 +100,7 @@ class TestCompaction(unittest.IsolatedAsyncioTestCase):
|
|||||||
await _run_compaction_for_sliding_window(
|
await _run_compaction_for_sliding_window(
|
||||||
app, session, self.mock_session_service
|
app, session, self.mock_session_service
|
||||||
)
|
)
|
||||||
self.mock_compactor.maybe_compact_events.assert_not_called()
|
self.mock_compactor.maybe_summarize_events.assert_not_called()
|
||||||
self.mock_session_service.append_event.assert_not_called()
|
self.mock_session_service.append_event.assert_not_called()
|
||||||
|
|
||||||
async def test_run_compaction_for_sliding_window_first_compaction(self):
|
async def test_run_compaction_for_sliding_window_first_compaction(self):
|
||||||
@@ -108,7 +108,7 @@ class TestCompaction(unittest.IsolatedAsyncioTestCase):
|
|||||||
name='test',
|
name='test',
|
||||||
root_agent=Mock(spec=BaseAgent),
|
root_agent=Mock(spec=BaseAgent),
|
||||||
events_compaction_config=EventsCompactionConfig(
|
events_compaction_config=EventsCompactionConfig(
|
||||||
compactor=self.mock_compactor,
|
summarizer=self.mock_compactor,
|
||||||
compaction_interval=2,
|
compaction_interval=2,
|
||||||
overlap_size=1,
|
overlap_size=1,
|
||||||
),
|
),
|
||||||
@@ -124,14 +124,16 @@ class TestCompaction(unittest.IsolatedAsyncioTestCase):
|
|||||||
mock_compacted_event = self._create_compacted_event(
|
mock_compacted_event = self._create_compacted_event(
|
||||||
1.0, 4.0, 'Summary inv1-inv4'
|
1.0, 4.0, 'Summary inv1-inv4'
|
||||||
)
|
)
|
||||||
self.mock_compactor.maybe_compact_events.return_value = mock_compacted_event
|
self.mock_compactor.maybe_summarize_events.return_value = (
|
||||||
|
mock_compacted_event
|
||||||
|
)
|
||||||
|
|
||||||
await _run_compaction_for_sliding_window(
|
await _run_compaction_for_sliding_window(
|
||||||
app, session, self.mock_session_service
|
app, session, self.mock_session_service
|
||||||
)
|
)
|
||||||
|
|
||||||
# Expected events to compact: inv1, inv2, inv3, inv4
|
# Expected events to compact: inv1, inv2, inv3, inv4
|
||||||
compacted_events_arg = self.mock_compactor.maybe_compact_events.call_args[
|
compacted_events_arg = self.mock_compactor.maybe_summarize_events.call_args[
|
||||||
1
|
1
|
||||||
]['events']
|
]['events']
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@@ -147,7 +149,7 @@ class TestCompaction(unittest.IsolatedAsyncioTestCase):
|
|||||||
name='test',
|
name='test',
|
||||||
root_agent=Mock(spec=BaseAgent),
|
root_agent=Mock(spec=BaseAgent),
|
||||||
events_compaction_config=EventsCompactionConfig(
|
events_compaction_config=EventsCompactionConfig(
|
||||||
compactor=self.mock_compactor,
|
summarizer=self.mock_compactor,
|
||||||
compaction_interval=2,
|
compaction_interval=2,
|
||||||
overlap_size=1,
|
overlap_size=1,
|
||||||
),
|
),
|
||||||
@@ -174,7 +176,9 @@ class TestCompaction(unittest.IsolatedAsyncioTestCase):
|
|||||||
mock_compacted_event = self._create_compacted_event(
|
mock_compacted_event = self._create_compacted_event(
|
||||||
2.0, 5.0, 'Summary inv2-inv5'
|
2.0, 5.0, 'Summary inv2-inv5'
|
||||||
)
|
)
|
||||||
self.mock_compactor.maybe_compact_events.return_value = mock_compacted_event
|
self.mock_compactor.maybe_summarize_events.return_value = (
|
||||||
|
mock_compacted_event
|
||||||
|
)
|
||||||
|
|
||||||
await _run_compaction_for_sliding_window(
|
await _run_compaction_for_sliding_window(
|
||||||
app, session, self.mock_session_service
|
app, session, self.mock_session_service
|
||||||
@@ -183,7 +187,7 @@ class TestCompaction(unittest.IsolatedAsyncioTestCase):
|
|||||||
# New invocations are inv3, inv4, inv5 (3 new) > threshold (2).
|
# New invocations are inv3, inv4, inv5 (3 new) > threshold (2).
|
||||||
# Overlap size is 1, so start from 1 inv before inv3, which is inv2.
|
# Overlap size is 1, so start from 1 inv before inv3, which is inv2.
|
||||||
# Compact range: inv2 to inv5.
|
# Compact range: inv2 to inv5.
|
||||||
compacted_events_arg = self.mock_compactor.maybe_compact_events.call_args[
|
compacted_events_arg = self.mock_compactor.maybe_summarize_events.call_args[
|
||||||
1
|
1
|
||||||
]['events']
|
]['events']
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@@ -201,7 +205,7 @@ class TestCompaction(unittest.IsolatedAsyncioTestCase):
|
|||||||
name='test',
|
name='test',
|
||||||
root_agent=Mock(spec=BaseAgent),
|
root_agent=Mock(spec=BaseAgent),
|
||||||
events_compaction_config=EventsCompactionConfig(
|
events_compaction_config=EventsCompactionConfig(
|
||||||
compactor=self.mock_compactor,
|
summarizer=self.mock_compactor,
|
||||||
compaction_interval=1,
|
compaction_interval=1,
|
||||||
overlap_size=0,
|
overlap_size=0,
|
||||||
),
|
),
|
||||||
@@ -209,11 +213,11 @@ class TestCompaction(unittest.IsolatedAsyncioTestCase):
|
|||||||
events = [self._create_event(1.0, 'inv1', 'e1')]
|
events = [self._create_event(1.0, 'inv1', 'e1')]
|
||||||
session = Session(app_name='test', user_id='u1', id='s1', events=events)
|
session = Session(app_name='test', user_id='u1', id='s1', events=events)
|
||||||
|
|
||||||
self.mock_compactor.maybe_compact_events.return_value = None
|
self.mock_compactor.maybe_summarize_events.return_value = None
|
||||||
|
|
||||||
await _run_compaction_for_sliding_window(
|
await _run_compaction_for_sliding_window(
|
||||||
app, session, self.mock_session_service
|
app, session, self.mock_session_service
|
||||||
)
|
)
|
||||||
|
|
||||||
self.mock_compactor.maybe_compact_events.assert_called_once()
|
self.mock_compactor.maybe_summarize_events.assert_called_once()
|
||||||
self.mock_session_service.append_event.assert_not_called()
|
self.mock_session_service.append_event.assert_not_called()
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class TestLlmEventSummarizer(unittest.IsolatedAsyncioTestCase):
|
|||||||
|
|
||||||
self.mock_llm.generate_content_async.return_value = async_gen()
|
self.mock_llm.generate_content_async.return_value = async_gen()
|
||||||
|
|
||||||
compacted_event = await self.compactor.maybe_compact_events(events=events)
|
compacted_event = await self.compactor.maybe_summarize_events(events=events)
|
||||||
|
|
||||||
self.assertIsNotNone(compacted_event)
|
self.assertIsNotNone(compacted_event)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@@ -101,11 +101,11 @@ class TestLlmEventSummarizer(unittest.IsolatedAsyncioTestCase):
|
|||||||
|
|
||||||
self.mock_llm.generate_content_async.return_value = async_gen()
|
self.mock_llm.generate_content_async.return_value = async_gen()
|
||||||
|
|
||||||
compacted_event = await self.compactor.maybe_compact_events(events=events)
|
compacted_event = await self.compactor.maybe_summarize_events(events=events)
|
||||||
self.assertIsNone(compacted_event)
|
self.assertIsNone(compacted_event)
|
||||||
|
|
||||||
async def test_maybe_compact_events_empty_input(self):
|
async def test_maybe_compact_events_empty_input(self):
|
||||||
compacted_event = await self.compactor.maybe_compact_events(events=[])
|
compacted_event = await self.compactor.maybe_summarize_events(events=[])
|
||||||
self.assertIsNone(compacted_event)
|
self.assertIsNone(compacted_event)
|
||||||
self.mock_llm.generate_content_async.assert_not_called()
|
self.mock_llm.generate_content_async.assert_not_called()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user