google / google/adk-python

Parallel calls to the same streaming tool leave an orphan task after live teardown

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

Description

## Required Information

**Describe the Bug:**

When one live model response contains multiple calls to the same async-generator tool, ADK starts each call concurrently but stores only one task under `InvocationContext.active_streaming_tools[tool.name]`. Each registration overwrites the previous `task` reference. As a result, run teardown and `stop_streaming(function_name)` can only cancel the last registered call; earlier calls continue writing to the live request queue after their agent run has ended.

The violated lifecycle invariant is that every background tool task started by a live agent run must remain tracked until it completes or the run tears it down.

**Steps to Reproduce:**

1. Create an async-generator `FunctionTool` that yields once and then waits.
2. Build one live function-call event containing two calls to that tool with different call IDs/arguments.
3. Pass the event through `handle_function_calls_live()`.
4. Wait until both tool tasks have started.
5. Call `SingleFlow()._stop_background_tool_tasks(invocation_context)`.
6. Inspect both tasks.

**Expected Behavior:**

Both calls are tracked and cancelled during live-run teardown.

**Observed Behavior:**

Only the task currently stored in `active_streaming_tools[tool.name].task` is cancelled. The other task remains pending. This reproduces consistently on current `main` (`b0180620f4c2f4f4467a89c37a30f75bf849700b`).

**Environment Details:**

- ADK Library Version: current `main` at `b0180620`
- Desktop OS: macOS
- Python Version: 3.10.20

**Model Information:**

- Are you using LiteLLM: No
- Which model is being used: N/A (unit-level live function-call event)

## Additional Context

The overwrite occurs in `_process_function_live_helper()` when it assigns a newly created task to `active_streaming_tools[tool.name].task`. `stop_background_tool_tasks()` later enumerates one `task` per registry entry, so it has no reference to the earlier call. Non-blocking live tools avoid the same collision by including the function-call ID in their task key.

I intend to keep all concurrent tasks for a streaming tool tracked while preserving the existing name-based `stop_streaming` behavior, and add a regression test through the real live function-call path. I can send the focused fix and test within 24 hours.

**Regression:**

Not confirmed.

**Logs:**

```text
assert all(task.done() for task in tasks)
E assert False
```

**Minimal Reproduction Code:**

```python
await handle_function_calls_live(
invocation_context,
event_with_two_calls_to_monitor,
{monitor_tool.name: monitor_tool},
)
await both_started.wait()
await SingleFlow()._stop_background_tool_tasks(invocation_context)
assert all(task.done() for task in started_tasks) # fails: one is still pending
```

**How often has this issue occurred?:**

- Always (100%)

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.