Skip to content

feat(session): add EventCompaction type and compaction filtering in contents_processor#1044

Open
QuentinBisson wants to merge 4 commits into
google:mainfrom
QuentinBisson:feat/event-compaction
Open

feat(session): add EventCompaction type and compaction filtering in contents_processor#1044
QuentinBisson wants to merge 4 commits into
google:mainfrom
QuentinBisson:feat/event-compaction

Conversation

@QuentinBisson

Copy link
Copy Markdown

Ports the compaction persistence and content-assembly model from adk-python to adk-go. Fixes #1001.

What's missing today

EventActions has no Compaction field and buildContentsDefault has no filtering step for compaction markers. This means raw events are never replaced by their summaries when building the LLM context — compaction runs but has no visible effect on what gets sent to the model.

Changes

session/session.go

type EventCompaction struct {
    StartTimestamp   time.Time
    EndTimestamp     time.Time
    CompactedContent *genai.Content
}

Added Compaction *EventCompaction to EventActions. The summary lives here, not in Event.Content, so compaction markers are excluded from normal content rendering and distinguishable from real model output.

internal/llminternal/contents_processor.go

New applyCompaction pass called inside buildContentsDefault, after transcription aggregation and before the async-function-response rearrangement step:

  1. Collect all compaction marker events (those with Actions.Compaction != nil).
  2. Remove subsumed markers — if marker C2's range fully contains marker C's range, C is dropped (a later compaction already covers it).
  3. Sort active markers by StartTimestamp.
  4. Walk the event list: events whose timestamp falls within an active marker's range are replaced by a single synthetic event carrying CompactedContent; compaction marker events themselves are always removed from the output.

This mirrors _apply_compaction in adk-python's flows/llm_flows/contents.py.

Downstream reference

kagent PR kagent-dev/kagent#2025 uses this via a replace directive and exercises both sliding-window and token-threshold compaction modes against Anthropic and OpenAI adapters. The downstream compaction driver (watermark-based invocation counting, longestSelfContainedPrefix safety, summarizer LLM) lives in kagent — out of scope for this PR, which only adds the session primitives and content-assembly filtering that the driver depends on.

Add EventCompaction struct to EventActions. When non-nil, the event is a
compaction marker: StartTimestamp/EndTimestamp delimit the range of raw events
that were summarized, and CompactedContent carries the replacement summary.

Add applyCompaction pass in buildContentsDefault (after transcription
aggregation, before async-function-response rearrangement). The pass:
- skips all compaction marker events
- for each non-subsumed compaction, replaces events in [start,end] with one
  synthetic event carrying CompactedContent
- detects subsumed compactions (a later, wider compaction covers the same range)
  and hides them, so only the widest covering summary is injected

Port of adk-python actions.compaction / _replace_events_with_compaction_events.
@google-cla

google-cla Bot commented Jun 15, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

QuentinBisson and others added 3 commits June 16, 2026 08:08
… storage

- Preserve compaction marker events through the content-presence filter in
  buildContentsDefault so applyCompaction actually sees them (markers have
  no LLM content and were silently dropped before reaching the compaction pass).

- Add Compaction to the field-by-field EventActions copy in
  inMemoryService.AppendEvent so compaction markers survive in-memory session
  storage.

- Exclude ranges with nil CompactedContent from active instead of silently
  dropping the events they cover; original events are preserved when the
  summarizer has not yet written a result.

- De-duplicate identical [startTS, endTS] ranges via a seen-map so a retry
  that writes a second marker for the same window no longer causes both
  markers to mutually subsume each other and both be dropped.

- Strengthen the subsumption condition to exclude the case where two ranges
  share identical timestamps, preventing identical ranges from being
  incorrectly treated as subsumed by each other.

- Pin function-call events whose paired function-response falls outside every
  active range so rearrangeEventsForLatestFunctionResponse does not see an
  orphaned response and return a hard error.
Replace inline ev.Actions.Compaction != nil checks in applyCompaction with
ev.IsCompactionMarker() so the exported method is the single authoritative
check point. Drop the call-site reference to buildContentsDefault from the
method doc.
@QuentinBisson QuentinBisson marked this pull request as ready for review June 16, 2026 06:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add EventCompaction support to EventActions and compaction filtering in contents_processor.go

1 participant