diff --git a/core/src/main/java/com/google/adk/sessions/VertexAiSessionService.java b/core/src/main/java/com/google/adk/sessions/VertexAiSessionService.java index 6831baefb..c1a665e80 100644 --- a/core/src/main/java/com/google/adk/sessions/VertexAiSessionService.java +++ b/core/src/main/java/com/google/adk/sessions/VertexAiSessionService.java @@ -249,14 +249,11 @@ public Maybe getSession( } /** - * Builds the server-side events filter for {@code afterTimestamp}, mirroring the Python and Go - * implementations (inclusive {@code timestamp>=}). The filter is only applied when {@code - * numRecentEvents} is not set, matching the precedence in {@link #filterEvents}. + * Inclusive server-side {@code timestamp>=} filter for {@code afterTimestamp}, or null. Applied + * independently of {@code numRecentEvents} (see {@link #filterEvents}), so both filters compose. */ private static @Nullable String afterTimestampFilter(Optional config) { - if (config.isPresent() - && config.get().numRecentEvents().isEmpty() - && config.get().afterTimestamp().isPresent()) { + if (config.isPresent() && config.get().afterTimestamp().isPresent()) { return "timestamp>=\"" + config.get().afterTimestamp().get() + "\""; } return null; diff --git a/core/src/test/java/com/google/adk/sessions/VertexAiSessionServiceTest.java b/core/src/test/java/com/google/adk/sessions/VertexAiSessionServiceTest.java index 0bfdd4b48..2271db132 100644 --- a/core/src/test/java/com/google/adk/sessions/VertexAiSessionServiceTest.java +++ b/core/src/test/java/com/google/adk/sessions/VertexAiSessionServiceTest.java @@ -587,6 +587,52 @@ public void getSession_numRecentEventsConfig_returnsMostRecentEvents() { assertThat(session.events().stream().map(Event::id)).containsExactly("e2", "e3").inOrder(); } + @Test + public void getSession_afterTimestampNarrowerThanNumRecentEvents_appliesBothFilters() { + sessionMap.put("10", mockSessionJson("10", "2024-12-12T12:00:30.000000Z")); + eventMap.put( + "10", + mockEventsJson( + mockEventJson("e1", "2024-12-12T12:00:05.000000Z"), + mockEventJson("e2", "2024-12-12T12:00:10.000000Z"), + mockEventJson("e3", "2024-12-12T12:00:15.000000Z"), + mockEventJson("e4", "2024-12-12T12:00:20.000000Z"))); + GetSessionConfig config = + GetSessionConfig.builder() + .afterTimestamp(Instant.parse("2024-12-12T12:00:15.000000Z")) + .numRecentEvents(3) + .build(); + + Session session = + vertexAiSessionService.getSession("123", "user", "10", Optional.of(config)).blockingGet(); + + // afterTimestamp must be applied: without it, numRecentEvents(3) would keep e2, e3, e4. + assertThat(session.events().stream().map(Event::id)).containsExactly("e3", "e4").inOrder(); + } + + @Test + public void getSession_numRecentEventsNarrowerThanAfterTimestamp_appliesBothFilters() { + sessionMap.put("11", mockSessionJson("11", "2024-12-12T12:00:30.000000Z")); + eventMap.put( + "11", + mockEventsJson( + mockEventJson("e1", "2024-12-12T12:00:05.000000Z"), + mockEventJson("e2", "2024-12-12T12:00:10.000000Z"), + mockEventJson("e3", "2024-12-12T12:00:15.000000Z"), + mockEventJson("e4", "2024-12-12T12:00:20.000000Z"))); + GetSessionConfig config = + GetSessionConfig.builder() + .afterTimestamp(Instant.parse("2024-12-12T12:00:10.000000Z")) + .numRecentEvents(2) + .build(); + + Session session = + vertexAiSessionService.getSession("123", "user", "11", Optional.of(config)).blockingGet(); + + // afterTimestamp keeps e2, e3, e4; numRecentEvents must then trim to the 2 most recent. + assertThat(session.events().stream().map(Event::id)).containsExactly("e3", "e4").inOrder(); + } + private static String mockSessionJson(String sessionId, String updateTime) { return String.format( """