diff --git a/core/src/main/java/com/google/adk/sessions/InMemorySessionService.java b/core/src/main/java/com/google/adk/sessions/InMemorySessionService.java index 3f2393c36..cef671de4 100644 --- a/core/src/main/java/com/google/adk/sessions/InMemorySessionService.java +++ b/core/src/main/java/com/google/adk/sessions/InMemorySessionService.java @@ -27,6 +27,7 @@ import io.reactivex.rxjava3.core.Single; import java.time.Instant; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; @@ -35,8 +36,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.jspecify.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * An in-memory implementation of {@link BaseSessionService} assuming {@link Session} objects are @@ -50,9 +49,6 @@ * during retrieval operations ({@code getSession}, {@code createSession}). */ public final class InMemorySessionService implements BaseSessionService { - - private static final Logger logger = LoggerFactory.getLogger(InMemorySessionService.class); - // Structure: appName -> userId -> sessionId -> Session private final ConcurrentMap>> sessions; @@ -178,9 +174,7 @@ public Single listSessions(String appName, String userId) // Create copies with empty events and state for the response List sessionCopies = - userSessionsMap.values().stream() - .map(this::copySessionMetadata) - .collect(toCollection(ArrayList::new)); + prepareSessionsForListResponse(appName, userId, userSessionsMap.values()); return Single.just(ListSessionsResponse.builder().sessions(sessionCopies).build()); } @@ -298,21 +292,6 @@ private Session copySession(Session original) { .build(); } - /** - * Creates a copy of the session containing only metadata fields (ID, appName, userId, timestamp). - * State and Events are explicitly *not* copied. - * - * @param original The session whose metadata to copy. - * @return A new Session instance with only metadata fields populated. - */ - private Session copySessionMetadata(Session original) { - return Session.builder(original.id()) - .appName(original.appName()) - .userId(original.userId()) - .lastUpdateTime(original.lastUpdateTime()) - .build(); - } - /** * Merges the app-specific and user-specific state into the provided *mutable* session's state * map. @@ -338,4 +317,24 @@ private Session mergeWithGlobalState(String appName, String userId, Session sess return session; } + + /** + * Prepares copies of sessions for use in a {@code listSessions} response. + * + *

For each session provided, this method creates a deep copy, clears the copy's event list, + * and merges app-level and user-level state into the copy's state map. + * + * @param appName The application name. + * @param userId The user ID. + * @param sessions The collection of sessions to process. + * @return A list of processed {@link Session} copies. + */ + private List prepareSessionsForListResponse( + String appName, String userId, Collection sessions) { + return sessions.stream() + .map(this::copySession) + .peek(s -> s.events().clear()) + .map(s -> mergeWithGlobalState(appName, userId, s)) + .collect(toCollection(ArrayList::new)); + } } diff --git a/core/src/test/java/com/google/adk/sessions/InMemorySessionServiceTest.java b/core/src/test/java/com/google/adk/sessions/InMemorySessionServiceTest.java index d30081c71..4c35f5b90 100644 --- a/core/src/test/java/com/google/adk/sessions/InMemorySessionServiceTest.java +++ b/core/src/test/java/com/google/adk/sessions/InMemorySessionServiceTest.java @@ -82,13 +82,31 @@ public void lifecycle_getSession() { public void lifecycle_listSessions() { InMemorySessionService sessionService = new InMemorySessionService(); - Session session = sessionService.createSession("app-name", "user-id").blockingGet(); + Session session = + sessionService + .createSession("app-name", "user-id", new ConcurrentHashMap<>(), "session-1") + .blockingGet(); + + ConcurrentMap stateDelta = new ConcurrentHashMap<>(); + stateDelta.put("sessionKey", "sessionValue"); + stateDelta.put("_app_appKey", "appValue"); + stateDelta.put("_user_userKey", "userValue"); + + Event event = + Event.builder().actions(EventActions.builder().stateDelta(stateDelta).build()).build(); + + var unused = sessionService.appendEvent(session, event).blockingGet(); ListSessionsResponse response = sessionService.listSessions(session.appName(), session.userId()).blockingGet(); + Session listedSession = response.sessions().get(0); assertThat(response.sessions()).hasSize(1); - assertThat(response.sessions().get(0).id()).isEqualTo(session.id()); + assertThat(listedSession.id()).isEqualTo(session.id()); + assertThat(listedSession.events()).isEmpty(); + assertThat(listedSession.state()).containsEntry("sessionKey", "sessionValue"); + assertThat(listedSession.state()).containsEntry("_app_appKey", "appValue"); + assertThat(listedSession.state()).containsEntry("_user_userKey", "userValue"); } @Test