Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

Orphaned function_call permanently and silently disables context compaction (once triggered)

未关闭
#7,236 4 条评论 0 个 reaction 已指派 1 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
68/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
python
领域
ai, backend

调研方向

Start in src/google/adk/apps/compaction.py, especially _longest_self_contained_prefix and the two event-selection paths that call it. Run the focused tests in tests/unittests/apps/test_compaction.py and the linked deterministic reproduction before changing behavior. Done means orphaned calls no longer permanently block later compaction, while pending or cross-window calls retain the existing conservative behavior and all listed tests pass.

由索引模型根据 Issue 内容生成。

描述

core

🔴 Required Information

Describe the Bug:
Once the first context compaction happens in a session, if there is an
orphaned function_call (a tool call whose function_response never
arrives — e.g. the process handling the tool died, such as a Kubernetes pod
restart while the agent was waiting on a tool), every subsequent
compaction is silently and permanently blocked
, even as the prompt keeps
growing past the configured token_threshold. There is no log and no error.

Root cause: google.adk.apps.compaction._longest_self_contained_prefix()
walks the compaction window keeping a set of open obligations keyed by call
id (function_call opens an id, the matching function_response closes
it), and only records a cut point where that set is completely empty. Once
the first compaction has happened, an orphaned call becomes the first
candidate event of every subsequent round, so the set can never empty again
— safe_length freezes at 0 forever, and _events_to_compact_for_token_threshold
(and the sliding-window path, which shares the same helper) returns [].

This affects both compaction strategies since both call the same helper.

Steps to Reproduce:

  1. Install: pip install google-adk==2.9.2
  2. Persist a session (e.g. via DatabaseSessionService) that already had a
    successful 1st compaction, followed by one orphaned function_call
    (no matching function_response anywhere in the session), followed by
    several normal, balanced turns.
  3. Reload the session and call the internal
    google.adk.apps.compaction._events_to_compact_for_token_threshold(...)
    (or exercise the sliding-window path) on it.
  4. Observe the returned list is empty — no compaction — even though there
    are dozens of eligible events after the orphan.

A minimal, deterministic, runnable reproduction (no LLM call / API key
needed — the bug lives entirely in the pure event-selection logic) is here:
https://github.com/erickleal-azos/simulate-adk-fc-orphan

git clone https://github.com/erickleal-azos/simulate-adk-fc-orphan
cd simulate-adk-fc-orphan
poetry install
poetry run python repro_orphan_function_call.py

Expected Behavior:
An orphaned function_call from an already-finished invocation, with no
response anywhere in the session and not a long-running/pending
auth/tool-confirmation call, should eventually be treated as unanswerable
so that compaction can resume over the healthy events that follow it
(optionally with a warning log, since today the stall is silent).

Observed Behavior:
_events_to_compact_for_token_threshold (and the sliding-window variant)
returns [] forever after the orphan, so the session's prompt never
compacts again and grows without bound. Example from the linked repro:

Scenario A -- NO orphaned function_call (baseline)
Baseline: events_to_compact = 36 event(s)
  -> 2nd compaction would proceed normally.

Scenario B -- orphaned function_call right after the 1st compaction
Buggy: events_to_compact = 0 event(s)
  -> BLOCKED: no 2nd compaction, even with 42 eligible events in the session.

Scenario B (healed) -- removing only the orphaned call event
Healed: events_to_compact = 36 event(s)
  -> 2nd compaction would proceed normally.

Environment Details:

  • ADK Library Version (pip show google-adk): 2.9.2 (verified byte-identical on main, so also present on latest)
  • Desktop OS: Linux (also observed in a GKE/Kubernetes)
  • Python Version (python -V): 3.11

Model Information:

  • Are you using LiteLLM: No
  • Which model is being used: N/A — the bug is in pure event-selection logic (google.adk.apps.compaction), no LLM call is involved in triggering or reproducing it.

🟡 Optional Information

Regression:
Not a regression from a specific prior version as far as I can tell — the
guard this interacts with was added in PR #5832 (fixing #5602, "Compaction
breaks LongRunningFunctionTool resume") and is deliberate/tested for the
first stall. What wasn't accounted for is that the stall is unbounded:
once the window's start point is pinned at last_compacted_end_timestamp,
an orphaned call there blocks that window from ever balancing again.

Logs:
None — this is precisely the problem: the block is completely silent, no
log or error is emitted, which made the original incident hard to diagnose.

Additional Context:
This was found in a real staging incident: a Kubernetes pod running the
tool-execution side was restarted while an agent was waiting on a tool call.
The session came back with a function_call that would never be answered,
and from that point on it never compacted again.

I have a proposed fix with a 7-scenario validation matrix (including the
two existing upstream tests that encode the conservative window,
test_sliding_window_excludes_pending_function_call_events and
test_sliding_window_plain_orphaned_function_call_dropped_from_contents,
replayed to confirm no regression) here:
https://github.com/erickleal-azos/simulate-adk-fc-orphan#the-proposed-solution

Summary of the approach: keep the strict rule exactly as-is (if it yields a
non-empty prefix, return it untouched — behavior for the normal/healthy
case does not change). Only when it yields nothing (the stall) do we ask a
second question — is each open obligation still fulfillable? An id keeps
blocking if: (1) a matching function_response exists anywhere in the
session (not just the window, to avoid splitting a pair across the window
boundary when the response lives past the window's end — this is what the
original guard protects), (2) it is in Event.long_running_tool_ids or a
pending auth/tool-confirmation request, or (3) it was opened by the newest
invocation in the session (may still be in flight). Everything else is a
normal tool call from an already-finished invocation with no response
anywhere — provably dead, since a normal tool always emits a response event
(including on error), so its absence means the process died holding the
obligation. Event.long_running_tool_ids is persisted in its own DB column
and survives the kind of restart that causes this, which the fix relies on.

I'm open to opening a PR against src/google/adk/apps/compaction.py with
this fix, if the maintainers agree the approach makes sense — just let me
know and I'll follow up with tests in tests/unittests/apps/test_compaction.py.

Minimal Reproduction Code:
See https://github.com/erickleal-azos/simulate-adk-fc-orphan/blob/main/repro_orphan_function_call.py
for the full runnable script. Core of the buggy helper it exercises
(google/adk/apps/compaction.py):

open_ids: set[str] = set()
safe_length = 0
for index, event in enumerate(events):
  open_ids -= _event_function_response_ids(event)
  open_ids |= _event_function_call_ids(event)
  if event.actions:
    open_ids |= set(event.actions.requested_tool_confirmations)
    open_ids |= set(event.actions.requested_auth_configs)
  if not open_ids:
    safe_length = index + 1
return events[:safe_length]

How often has this issue occurred?:

  • Rare (<1%) — requires the process handling a tool call to die or be
    killed mid-call (pod restart, OOM kill, crash) after at least one
    compaction has already happened in the session. In practice an
    uncommon window, but when it does line up the block is permanent and
    silent.
主要语言
Python
星标
21.6k
派生
4k
平均合并
13 小时 49 分钟
30 天内合并 PR
10

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

google/adk-python 的其他 Issue

查看 google/adk-python 的全部 Issue

相似的 Issue

更多 Python Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。