Merge https://github.com/google/adk-python/pull/3875 # Problem The example in `contributing/samples/human_in_loop/README.md` shows: ```python await runner.run_async(...) ``` However, `run_async` returns an **async generator**, so awaiting it raises: ``` TypeError: object async_generator can't be used in 'await' expression ``` Additionally, the example payload uses `"ticket-id"` while ADK tools and other examples use `"ticketId"`, creating a mismatch that breaks copy/paste usage. # Solution - Updated the snippet to consume the async generator correctly: ```python async for event in runner.run_async(...): ... ``` - Aligned the payload key from `"ticket-id"` → `"ticketId"` for consistency with ADK schema and other examples. These changes make the example runnable and consistent with the API’s actual behavior. # Testing Plan This PR is a **small documentation correction**, so no unit tests are required per contribution guidelines. - Verified the corrected snippet manually to ensure it no longer raises `TypeError`. # Checklist - [x] I have read the CONTRIBUTING.md document. - [x] I have performed a self-review of my own code. - [ ] I have commented my code, particularly in hard-to-understand areas. *(N/A – docs only)* - [ ] I have added tests that prove my fix is effective or that my feature works. *(N/A – docs only)* - [ ] New and existing unit tests pass locally with my changes. *(N/A – docs only)* - [x] I have manually tested my changes end-to-end. - [ ] Any dependent changes have been merged and published in downstream modules. *(N/A)* COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3875 from krishna-dhulipalla:docs/fix-adk-run_async-example 83fc5b430690b63b8b7bf1025ef03b0761264751 PiperOrigin-RevId: 842952362
Agent with Long-Running Tools
This example demonstrates an agent using a long-running tool (ask_for_approval).
Key Flow for Long-Running Tools
-
Initial Call: The agent calls the long-running tool (e.g.,
ask_for_approval). -
Initial Tool Response: The tool immediately returns an initial response, typically indicating a "pending" status and a way to track the request (e.g., a
ticket-id). This is sent back to the agent as atypes.FunctionResponse(usually processed internally by the runner and then influencing the agent's next turn). -
Agent Acknowledges: The agent processes this initial response and usually informs the user about the pending status.
-
External Process/Update: The long-running task progresses externally (e.g., a human approves the request).
-
❗️Crucial Step: Provide Updated Tool Response❗️:
- Once the external process completes or updates, your application must construct a new
types.FunctionResponse. - This response should use the same
idandnameas the originalFunctionCallto the long-running tool. - The
responsefield within thistypes.FunctionResponseshould contain the updated data (e.g.,{'status': 'approved', ...}). - Send this
types.FunctionResponseback to the agent as a part within a new message usingrole="user".
# Example: After external approval updated_tool_output_data = { "status": "approved", "ticketId": ticket_id, # from original call # ... other relevant updated data } updated_function_response_part = types.Part( function_response=types.FunctionResponse( id=long_running_function_call.id, # Original call ID name=long_running_function_call.name, # Original call name response=updated_tool_output_data, ) ) # Send this back to the agent async for _ in runner.run_async( # ... session_id, user_id ... new_message=types.Content( parts=[updated_function_response_part], role="user" ), ): pass # exhaust generator (or handle events) - Once the external process completes or updates, your application must construct a new
-
Agent Acts on Update: The agent receives this message containing the
types.FunctionResponseand, based on its instructions, proceeds with the next steps (e.g., calling another tool likereimburse).
Why is this important? The agent relies on receiving this subsequent types.FunctionResponse (provided in a message with role="user" containing the specific Part) to understand that the long-running task has concluded or its state has changed. Without it, the agent will remain unaware of the outcome of the pending task.