From 71dee8a355e7f40ac2ad97ceada1cbc8608cee29 Mon Sep 17 00:00:00 2001 From: Wiktoria Walczak Date: Mon, 13 Jul 2026 03:15:57 -0700 Subject: [PATCH] refactor: extract shared compaction window helpers into CompactionUtils PiperOrigin-RevId: 946920085 --- .../adk/kt/summarizer/CompactionUtils.kt | 47 +++++++++++++++++++ .../summarizer/SlidingWindowEventCompactor.kt | 31 +----------- 2 files changed, 48 insertions(+), 30 deletions(-) create mode 100644 core/src/commonMain/kotlin/com/google/adk/kt/summarizer/CompactionUtils.kt diff --git a/core/src/commonMain/kotlin/com/google/adk/kt/summarizer/CompactionUtils.kt b/core/src/commonMain/kotlin/com/google/adk/kt/summarizer/CompactionUtils.kt new file mode 100644 index 00000000..1d544330 --- /dev/null +++ b/core/src/commonMain/kotlin/com/google/adk/kt/summarizer/CompactionUtils.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.adk.kt.summarizer + +import com.google.adk.kt.events.Event + +/** Returns true when this event carries a context-compaction summary. */ +internal fun Event.isCompactionEvent(): Boolean = actions.compaction != null + +/** + * Returns the longest prefix of the given window that can be summarized without splitting a + * function-call/response pair or leaving a tool-confirmation (HITL) request unresolved. + * + * Performs a single left-to-right pass tracking "open" obligations keyed by call ID: a function + * call or a tool-confirmation request opens one, and a function response with the same ID closes + * it. The prefix is safe to summarize exactly at the points where no obligation is open, so the + * longest prefix ending at such a balanced point is returned (empty if the window never reaches a + * balanced point). + */ +internal fun longestSelfContainedPrefix(events: List): List { + val openIds = mutableSetOf() + var safeLength = 0 + for ((index, event) in events.withIndex()) { + for (response in event.functionResponses()) { + response.id?.let(openIds::remove) + } + for (call in event.functionCalls()) { + call.id?.let(openIds::add) + } + openIds.addAll(event.actions.requestedToolConfirmations.keys) + if (openIds.isEmpty()) safeLength = index + 1 + } + return events.subList(0, safeLength) +} diff --git a/core/src/commonMain/kotlin/com/google/adk/kt/summarizer/SlidingWindowEventCompactor.kt b/core/src/commonMain/kotlin/com/google/adk/kt/summarizer/SlidingWindowEventCompactor.kt index 7f20500f..e0621783 100644 --- a/core/src/commonMain/kotlin/com/google/adk/kt/summarizer/SlidingWindowEventCompactor.kt +++ b/core/src/commonMain/kotlin/com/google/adk/kt/summarizer/SlidingWindowEventCompactor.kt @@ -111,35 +111,6 @@ class SlidingWindowEventCompactor(private val config: EventsCompactionConfig) : } if (invocationsToCompact.size < compactionInterval) return null - return eventsToCompact.asReversed().longestSelfContainedPrefix().ifEmpty { null } + return longestSelfContainedPrefix(eventsToCompact.asReversed()).ifEmpty { null } } } - -private fun Event.isCompactionEvent(): Boolean = actions.compaction != null - -/** - * Returns the longest prefix of the given window that can be summarized without splitting a - * function-call/response pair or leaving a tool-confirmation (HITL) request unresolved. - * - * Performs a single left-to-right pass tracking "open" obligations keyed by call ID: a function - * call or a tool-confirmation request opens one, and a function response with the same ID closes - * it. The prefix is safe to summarize exactly at the points where no obligation is open, so the - * longest prefix ending at such a balanced point is returned (empty if the window never reaches a - * balanced point). - */ -private fun List.longestSelfContainedPrefix(): List { - val openIds = mutableSetOf() - var safeLength = 0 - for ((index, event) in withIndex()) { - for (response in event.functionResponses()) { - response.id?.let(openIds::remove) - } - for (call in event.functionCalls()) { - call.id?.let(openIds::add) - } - openIds.addAll(event.actions.requestedToolConfirmations.keys) - // TODO(b/505630632): Add authentication requests handling. - if (openIds.isEmpty()) safeLength = index + 1 - } - return subList(0, safeLength) -}