fix(flows): terminate invocation at tool-level EUC#5638
Closed
doughayden wants to merge 2 commits into
Closed
Conversation
- Set end_invocation after auth event in postprocess - Mirrors _resolve_toolset_auth's interrupt signal - Tests assert single LLM call when auth requested - events[-3] -> events[-2] in resume tests (tail trimmed)
Collaborator
|
Hi @doughayden , Thank you for your contribution! We appreciate you taking the time to submit this pull request. Your PR has been received by the team and is currently under review. We will provide feedback as soon as we have an update to share. |
Collaborator
|
Hi @Jacksunwei , can you please review this. |
Contributor
Author
|
Gentle bump on this one. CI is green with one pending check. Happy to rebase or address any feedback. |
copybara-service Bot
pushed a commit
that referenced
this pull request
Jun 15, 2026
Merge #5638 ### Link to Issue or Description of Change **1. Link to an existing issue (if applicable):** - Closes: #5637 This change adds `invocation_context.end_invocation = True` after the auth event yield in `_postprocess_handle_function_calls_async`, mirroring the existing termination signal in `_resolve_toolset_auth`. Tool-level auth now terminates symmetrically with toolset-level auth at the EUC, instead of continuing for one more LLM call. ### Testing Plan **Unit Tests:** - [x] I have added or updated unit tests for my change. - [x] All unit tests pass locally. Three existing tests in `test_functions_request_euc.py` had assertions tied to the trailing post-EUC LLM call: - `test_function_request_euc`: adds `assert len(mock_model.requests) == 1` to anchor the new termination behavior. - `test_function_get_auth_response`: `events[-3]` → `events[-2]` for the auth event lookup, since the auth event is now second-to-last. - `test_function_get_auth_response_partial`: same `events[-3]` → `events[-2]` change, plus the two `len(mock_model.requests)` assertions drop by 1 (3 → 2 and 4 → 3). ``` $ pytest tests/unittests/flows/llm_flows/test_functions_request_euc.py ======================== 3 passed, 17 warnings in 1.31s ======================== $ pytest tests/unittests/ =============== 5695 passed, 2308 warnings in 122.89s (0:02:02) ================ ``` **Manual End-to-End (E2E) Tests:** A self-contained Runner-based reproduction is at https://github.com/doughayden/adk-issue-examples/tree/main/04-tool_level_auth_continuation. The agent definition (`agent.py`) wires up an `OpenAPIToolset` against a local OAuth2 test server. `main.py` constructs an `InMemoryRunner`, applies the workaround for #5327 (`get_auth_config = lambda: None`) at runtime to land on the tool-level auth path, and sends a tool-triggering prompt. The `--apply-fix` flag monkey-patches the proposed fix to demonstrate the resolution end-to-end. Without the fix: ``` 👤 User: What's the weather in San Francisco? 🌤️ Weather Assistant event stream: [function_call] get_weather by WeatherAssistant [auth_event] adk_request_credential by WeatherAssistant [function_response] get_weather by WeatherAssistant [post_euc_text] WeatherAssistant: "I'm sorry, I cannot retrieve the weather for San Francisco at the moment. It ..." Event counts: function_calls: 1 auth_events: 1 function_responses: 1 text_events: 1 post_euc_text_events: 1 ✅ Bug reproduced: 1 text event(s) after the EUC (agent loop continued past adk_request_credential). ``` With the fix: ``` 👤 User: What's the weather in San Francisco? 🌤️ Weather Assistant event stream: [function_call] get_weather by WeatherAssistant [auth_event] adk_request_credential by WeatherAssistant [function_response] get_weather by WeatherAssistant Event counts: function_calls: 1 auth_events: 1 function_responses: 1 text_events: 0 post_euc_text_events: 0 ✅ Fix verified: no LLM events after the EUC. ``` ### Checklist - [x] I have read the [CONTRIBUTING.md](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) document. - [x] I have performed a self-review of my own code. - [x] I have commented my code, particularly in hard-to-understand areas. - [x] I have added tests that prove my fix is effective or that my feature works. - [x] New and existing unit tests pass locally with my changes. - [x] I have manually tested my changes end-to-end. - [ ] Any dependent changes have been merged and published in downstream modules. ### Additional context **Alternative considered:** A reorder of the yields (yield `auth_event` last so `last_event.is_final_response()` returns True) would also fix the loop termination in a single iteration without needing the flag. I went with `end_invocation = True` to preserve the observable event order and to match the existing pattern in `_resolve_toolset_auth`. Happy to switch if maintainers prefer the reorder. **Related:** The same yield site at lines 1126-1130 also produces `tool_confirmation_event` for HITL with the same `long_running_tool_ids` shape and the same termination gap. This PR scopes to `auth_event` only. Happy to open a follow-up PR with the same fix for `tool_confirmation_event` if the team agrees with the approach here. Co-authored-by: George Weale <gweale@google.com> COPYBARA_INTEGRATE_REVIEW=#5638 from doughayden:fix/tool-level-auth-terminates-at-euc 0a04d30 PiperOrigin-RevId: 932731604
Collaborator
|
Thank you @doughayden for your contribution! 🎉 Your changes have been successfully imported and merged via Copybara in commit 883ff98. 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.
Link to Issue or Description of Change
1. Link to an existing issue (if applicable):
This change adds
invocation_context.end_invocation = Trueafter the auth event yield in_postprocess_handle_function_calls_async, mirroring the existing termination signal in_resolve_toolset_auth. Tool-level auth now terminates symmetrically with toolset-level auth at the EUC, instead of continuing for one more LLM call.Testing Plan
Unit Tests:
Three existing tests in
test_functions_request_euc.pyhad assertions tied to the trailing post-EUC LLM call:test_function_request_euc: addsassert len(mock_model.requests) == 1to anchor the new termination behavior.test_function_get_auth_response:events[-3]→events[-2]for the auth event lookup, since the auth event is now second-to-last.test_function_get_auth_response_partial: sameevents[-3]→events[-2]change, plus the twolen(mock_model.requests)assertions drop by 1 (3 → 2 and 4 → 3).Manual End-to-End (E2E) Tests:
A self-contained Runner-based reproduction is at https://github.com/doughayden/adk-issue-examples/tree/main/04-tool_level_auth_continuation. The agent definition (
agent.py) wires up anOpenAPIToolsetagainst a local OAuth2 test server.main.pyconstructs anInMemoryRunner, applies the workaround for #5327 (get_auth_config = lambda: None) at runtime to land on the tool-level auth path, and sends a tool-triggering prompt. The--apply-fixflag monkey-patches the proposed fix to demonstrate the resolution end-to-end.Without the fix:
With the fix:
Checklist
Additional context
Alternative considered:
A reorder of the yields (yield
auth_eventlast solast_event.is_final_response()returns True) would also fix the loop termination in a single iteration without needing the flag. I went withend_invocation = Trueto preserve the observable event order and to match the existing pattern in_resolve_toolset_auth. Happy to switch if maintainers prefer the reorder.Related:
The same yield site at lines 1126-1130 also produces
tool_confirmation_eventfor HITL with the samelong_running_tool_idsshape and the same termination gap. This PR scopes toauth_eventonly. Happy to open a follow-up PR with the same fix fortool_confirmation_eventif the team agrees with the approach here.