Skip to content
1 change: 0 additions & 1 deletion contributing/samples/gepa/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
from tau_bench.types import EnvRunResult
from tau_bench.types import RunConfig
import tau_bench_agent as tau_bench_agent_lib

import utils


Expand Down
1 change: 0 additions & 1 deletion contributing/samples/gepa/run_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
from absl import flags
import experiment
from google.genai import types

import utils

_OUTPUT_DIR = flags.DEFINE_string(
Expand Down
5 changes: 4 additions & 1 deletion src/google/adk/sessions/database_session_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,10 @@ def to_event(self) -> Event:
branch=self.branch,
# This is needed as previous ADK version pickled actions might not have
# value defined in the current version of the EventActions model.
actions=EventActions().model_copy(update=self.actions.model_dump()),
# Use model_validate to properly reconstruct nested Pydantic models.
actions=EventActions.model_validate(self.actions.model_dump())
if self.actions
else None,
timestamp=self.timestamp.timestamp(),
long_running_tool_ids=self.long_running_tool_ids,
partial=self.partial,
Expand Down
65 changes: 65 additions & 0 deletions tests/unittests/sessions/test_session_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from google.adk.errors.already_exists_error import AlreadyExistsError
from google.adk.events.event import Event
from google.adk.events.event_actions import EventActions
from google.adk.events.event_actions import EventCompaction
from google.adk.sessions.base_session_service import GetSessionConfig
from google.adk.sessions.database_session_service import DatabaseSessionService
from google.adk.sessions.in_memory_session_service import InMemorySessionService
Expand Down Expand Up @@ -699,3 +700,67 @@ async def test_partial_events_are_not_persisted(service_type, tmp_path):
app_name=app_name, user_id=user_id, session_id=session.id
)
assert len(session_got.events) == 0


@pytest.mark.asyncio
@pytest.mark.parametrize(
'service_type',
[
SessionServiceType.IN_MEMORY,
SessionServiceType.DATABASE,
SessionServiceType.SQLITE,
],
)
async def test_event_compaction_deserialization(service_type, tmp_path):
"""Test that EventCompaction is properly deserialized as a Pydantic model.

This test verifies the fix for https://github.com/google/adk-python/issues/3633
where EventCompaction was incorrectly deserialized as a dict instead of a
Pydantic model when using DatabaseSessionService.
"""
session_service = get_session_service(service_type, tmp_path)
app_name = 'my_app'
user_id = 'user'

session = await session_service.create_session(
app_name=app_name, user_id=user_id
)

# Create an event with EventCompaction
compaction = EventCompaction(
start_timestamp=1.0,
end_timestamp=2.0,
compacted_content=types.Content(
role='model', parts=[types.Part(text='Compacted summary')]
),
)

event = Event(
invocation_id='inv1',
author='user',
actions=EventActions(compaction=compaction),
)

await session_service.append_event(session=session, event=event)

# Retrieve the session and verify compaction is properly deserialized
session_got = await session_service.get_session(
app_name=app_name, user_id=user_id, session_id=session.id
)

assert len(session_got.events) == 1
retrieved_event = session_got.events[0]

# Verify that compaction is an EventCompaction instance, not a dict
assert retrieved_event.actions is not None
assert retrieved_event.actions.compaction is not None
assert isinstance(
retrieved_event.actions.compaction, EventCompaction
), f'Expected EventCompaction, got {type(retrieved_event.actions.compaction)}'

# Verify we can access attributes (not dict keys)
assert retrieved_event.actions.compaction.start_timestamp == 1.0
assert retrieved_event.actions.compaction.end_timestamp == 2.0
assert retrieved_event.actions.compaction.compacted_content.parts[0].text == (
'Compacted summary'
)