Repository navigation
fix(apps): stop orphaned function_call from permanently blocking compaction - #7237
Closed
chelsealong wants to merge 1 commit into
Closed
chelsealong wants to merge 1 commit into
chelsealong wants to merge 1 commit into
Conversation
…action _longest_self_contained_prefix walks a compaction window keeping a set of open obligations keyed by call id, and only records a cut point where that set is empty. Once the first compaction has happened, an orphaned function_call with no matching function_response anywhere in the session (e.g. the process handling the tool died) becomes the first candidate event of every following window, so the set can never empty again and compaction silently stops forever. When the strict pass finds no balanced point, retry once while ignoring call ids that are provably dead: no response exists anywhere in the session (not just the window, to avoid splitting a pair whose response lives past the window boundary), the call isn't long-running or a pending auth/tool-confirmation request, and it wasn't opened by the newest invocation in the session (which may still be in flight). Everything else keeps blocking exactly as before. Fixes google#7236
Collaborator
|
Thank you @chelsealong for your contribution! 🎉 Your changes have been successfully imported and merged via Copybara in commit 73ad447. Closing this PR as the changes are now in the main branch. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #7236
Problem
_longest_self_contained_prefix(insrc/google/adk/apps/compaction.py)walks a compaction window keeping a set of "open" obligations keyed by call
id (a
function_callopens an id, the matchingfunction_responseclosesit), and only records a cut point where that set is completely empty.
Once the first compaction has happened, an orphaned
function_call(atool call whose
function_responsenever arrives, e.g. the processhandling the tool died mid-call) becomes the first candidate event of
every subsequent compaction window. Its id can never close, so the "open"
set can never empty again,
safe_lengthfreezes at0forever, and both_events_to_compact_for_token_thresholdand the sliding-window path (whichshare this helper) silently stop compacting for the rest of the session,
even as the prompt keeps growing past the configured threshold. There is no
log or error.
Fix
When the strict left-to-right pass finds no balanced point at all, retry
once while ignoring call ids that
_provably_dead_call_idsproves cannever be fulfilled:
function_responsewith the same id exists anywhere in thesession (not just the compaction window -- a response that lives past
the window's end still protects its call, which is what the original
guard from Fix/5602 longrunningfunctiontool resume #5832 protects against splitting),
Event.long_running_tool_ids) or tiedto a pending tool-confirmation/auth request (these are expected to stay
open), and
still be in flight.
If the strict pass already finds a non-empty prefix, behavior is unchanged.
The retry only fires on the stall case this issue describes, so healthy
events after a dead orphaned call can be compacted again instead of the
block persisting for the lifetime of the session.
Testing plan
Added
test_sliding_window_orphaned_call_after_prior_compaction_does_not_block_foreverin
tests/unittests/apps/test_compaction.py, reproducing the reportedscenario: a prior compaction, followed by an orphaned
function_callwithno matching response anywhere in the session, followed by a further
invocation. Verified the test fails without the fix (reverted only the
source file) and passes with it:
Also ran the full
tests/unittests/apps/andtests/unittests/flows/llm_flows/context/suites (excluding two files thatfail to collect in this environment for unrelated reasons -- missing
optional
sqlalchemy/openaiextras, confirmed by importing them directlyon unmodified
main):Ran formatting/lint as configured in
pyproject.toml:AI assistance disclosure
This PR was written with the assistance of an AI coding agent (Claude Code),
following the approach the issue reporter proposed. All changes were
reviewed and tested before submission.