google / google/adk-python

Parallel ungated tool results are lost across a tool-confirmation pause, yielding an empty model reply after approval

Ouverte
#6,732 2 commentaires 0 réactions 1 personne assignée Réclamée par @sanketpatil06 Voir sur GitHub
agent engine request clarification
Langage dominant
Python
Étoiles
21.5k
Forks
4k
Merge moyen
1 j 14 h
PR mergées (30 j)
37

Description

**Describe the bug**

When a model turn contains a confirmation-gated function call (`FunctionTool(require_confirmation=True)`) **in parallel with** ungated function calls, the ungated siblings' results are lost across the confirmation pause. After the user approves, the resume processor (`flows/llm_flows/request_confirmation.py`) re-executes only the confirmed tool, leaving the sibling `function_call` in session history with no matching `function_response`. Gemini, given that dangling call, returns an **empty final message** — while the approved (destructive) tool HAS executed. The end user sees no answer and may retry an action that already succeeded.

**To Reproduce**

1. An `LlmAgent` with two tools: `delete_record` built with `require_confirmation=True`, and `list_records` (ungated).
2. Prompt so the model emits both calls in one turn, e.g. *"Delete record X, and after deleting, list the remaining records."* Gemini frequently parallelizes this into one response with both `function_call` parts.
3. The run pauses with `adk_request_confirmation`. Observations at this point:
- The ungated `list_records` **does execute** during the pause (its side effects are visible), but the merged function-response event — carrying the sibling's real result plus the gated tool's `"This tool call requires confirmation"` placeholder — is **never persisted to the session**. When the caller stops consuming the event stream at the confirmation event (the natural thing for a client that must wait for user input, e.g. any AG-UI/SSE bridge), the response event that `_postprocess_handle_function_calls_async` yields *after* `generate_request_confirmation_event` is never reached (`GeneratorExit`).
4. Resume with `ToolConfirmation(confirmed=True)`.
5. `_RequestConfirmationLlmRequestProcessor._resolve_confirmation_targets` re-executes only the confirmed `delete_record`. Session events now contain `function_call list_records` with no `function_response`.
6. The follow-up LLM call returns empty content (`{"text": ""}`).

Observed session event sequence (from a Postgres session service, ADK 2.7.0):

```
model: [text, function_call delete_record, thought_signature, function_call list_records, text]
model: [function_call adk_request_confirmation] (long_running_tool_ids set)
user: [function_response adk_request_confirmation {confirmed: true}]
model: [function_response delete_record {...success...}] <- only the confirmed tool
model: [text ""] <- empty final reply
```

**Expected behavior**

Either (a) the pause-time function-response event (sibling results + confirmation placeholder) is persisted before/with the confirmation-request event so history stays complete across the pause, or (b) the resume path re-executes (or synthesizes responses for) unresolved sibling calls from the same turn — so the post-approval continuation never sees a dangling `function_call`.

**Screenshots**

N/A (event sequence above).

**Desktop:**
- OS: macOS 26 / Linux (Docker python:3.12-slim)
- Python version: 3.12
- ADK version: 2.7.0 (also inspected 2.5.0 — same structure)

**Model Information:**
- Are you using LiteLLM: No
- Which model is being used: gemini via Vertex AI

**Additional context**

Single-call confirmation flows (pause → approve/reject → resume) work correctly in 2.7.0, including the 2.6.x fixes for consumed-confirmation re-validation. The failure is specific to the parallel gated+ungated combination. Downstream we mitigate with an `after_model_callback` that drops ungated siblings from mixed turns before execution (the model re-issues them after the decision), but a framework-level fix would let parallel calls keep their results.

Guide de contribution

Ouvrir le guide de contribution

Évaluation

Cette issue n'a pas encore été évaluée.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.