Skip to content

feat: add tool progress callback hook#5986

Open
ai-hustle-bro wants to merge 1 commit into
google:mainfrom
ai-hustle-bro:feat/tool-progress-callback
Open

feat: add tool progress callback hook#5986
ai-hustle-bro wants to merge 1 commit into
google:mainfrom
ai-hustle-bro:feat/tool-progress-callback

Conversation

@ai-hustle-bro
Copy link
Copy Markdown

Link to Issue or Description of Change

1. Link to an existing issue (if applicable):

Problem:
Long-running tools can currently only report intermediate status through model-facing results/yields. That is useful for context-relevant updates, but UI-only progress messages still consume model context.

Solution:
This PR adds an opt-in progress_callback injection path for FunctionTool:

  • hides progress_callback from model-facing function declarations
  • injects a bound callback in run_async() and live _call_live() when a tool declares the parameter
  • stores a runtime-only tool_progress_handler on InvocationContext
  • exposes Runner.on_tool_progress as the app-level registration point
  • makes the callback a no-op when no handler is registered
  • supports both sync and async handler functions

Testing Plan

Unit Tests:

  • I have added or updated unit tests for my change.
  • All targeted unit tests pass locally.

Passed locally:

uv run pytest tests/unittests/tools/test_function_tool.py tests/unittests/streaming/test_streaming.py::test_input_streaming_tool_registered_lazily_with_stream tests/unittests/streaming/test_streaming.py::test_input_streaming_tool_has_stream_set_at_registration -q
# 34 passed, warnings only

Additional local checks:

uv run --with pyink pyink --check src/google/adk/agents/invocation_context.py src/google/adk/tools/function_tool.py src/google/adk/runners.py tests/unittests/tools/test_function_tool.py
# 4 files would be left unchanged

git diff --check
# passed

Manual End-to-End (E2E) Tests:

Not run yet. I kept the first pass to isolated unit coverage plus two existing live streaming regressions. I can add an example runner snippet or run a manual live-mode check if maintainers prefer that evidence before review.

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and targeted existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules.

Additional context

The callback intentionally does not create model-facing content. Tool authors can still yield or return context-relevant updates when the model should reason about them, and use progress_callback(...) only for app/UI progress.

@google-cla
Copy link
Copy Markdown

google-cla Bot commented Jun 5, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@adk-bot adk-bot added the tools [Component] This issue is related to tools label Jun 5, 2026
@adk-bot
Copy link
Copy Markdown
Collaborator

adk-bot commented Jun 5, 2026

Response from ADK Triaging Agent

Hello @ai-hustle-bro, thank you for submitting this pull request!

We appreciate your work on adding the tool progress callback hook. However, it seems a couple of items from our contribution guidelines are still pending:

  1. Contributor License Agreement (CLA): The automated CLA check has reported a failure. Please make sure to sign the Google CLA if you haven't already so that our team can review your changes.
  2. Manual End-to-End (E2E) Tests: According to the guidelines, all feature changes must include verifiable manual E2E testing evidence (such as the runner setup/command used and console output/logs). Since this introduces a runner progress callback interface, could you please provide manual E2E testing logs or runner snippets showing the callback in action?

These details will help our reviewers understand and verify your changes more quickly. Thank you!

@ai-hustle-bro
Copy link
Copy Markdown
Author

I added a manual smoke check for the runner hook path requested by the triage bot. This avoids an external model call, but it exercises Runner-created InvocationContext plus FunctionTool execution with a declared progress_callback parameter.

Command run from the PR branch:

`powershell

Inline Python passed to: uv run python -

`

Smoke script:

`python
import asyncio
import json
from google.genai import types
from google.adk.agents.llm_agent import LlmAgent
from google.adk.agents.run_config import RunConfig
from google.adk.runners import Runner
from google.adk.sessions.in_memory_session_service import InMemorySessionService
from google.adk.tools.function_tool import FunctionTool
from google.adk.tools.tool_context import ToolContext

async def main():
events = []

async def on_progress(tool_name, function_call_id, data):
    events.append({"tool_name": tool_name, "function_call_id": function_call_id, "data": data})
    print("progress_event=" + json.dumps(events[-1], sort_keys=True))

async def slow_lookup(query: str, progress_callback):
    await progress_callback({"stage": "started", "query": query})
    await progress_callback({"stage": "finished", "rows": 2})
    return {"answer": "ok", "query": query}

session_service = InMemorySessionService()
session = await session_service.create_session(
    app_name="progress-e2e",
    user_id="user-1",
    session_id="session-1",
)
runner = Runner(
    app_name="progress-e2e",
    agent=LlmAgent(name="progress_agent"),
    session_service=session_service,
)
runner.on_tool_progress = on_progress

invocation_context = runner._new_invocation_context(
    session,
    new_message=types.UserContent(parts=[types.Part(text="run slow_lookup")]),
    run_config=RunConfig(),
)
tool_context = ToolContext(
    invocation_context=invocation_context,
    function_call_id="call-123",
)
result = await FunctionTool(slow_lookup).run_async(
    args={"query": "callback smoke"},
    tool_context=tool_context,
)

print("result=" + json.dumps(result, sort_keys=True))
print("handler_is_runner_hook=" + str(invocation_context.tool_progress_handler is runner.on_tool_progress))
print("event_count=" + str(len(events)))

asyncio.run(main())
`

Output:

ext progress_event={"data": {"query": "callback smoke", "stage": "started"}, "function_call_id": "call-123", "tool_name": "slow_lookup"} progress_event={"data": {"rows": 2, "stage": "finished"}, "function_call_id": "call-123", "tool_name": "slow_lookup"} result={"answer": "ok", "query": "callback smoke"} handler_is_runner_hook=True event_count=2

Other validation already run on this branch:

` ext
uv run pytest tests/unittests/tools/test_function_tool.py tests/unittests/streaming/test_streaming.py::test_input_streaming_tool_registered_lazily_with_stream tests/unittests/streaming/test_streaming.py::test_input_streaming_tool_has_stream_set_at_registration -q

34 passed, warnings only

uv run --with pyink pyink --check src/google/adk/agents/invocation_context.py src/google/adk/tools/function_tool.py src/google/adk/runners.py tests/unittests/tools/test_function_tool.py

4 files unchanged

git diff --check

no output

`

The remaining blocker I can see is the Google CLA check.

@rohityan rohityan self-assigned this Jun 5, 2026
@rohityan
Copy link
Copy Markdown
Collaborator

rohityan commented Jun 5, 2026

Hi @ai-hustle-bro , Thank you for your contribution! It appears you haven't yet signed the Contributor License Agreement (CLA). Please visit https://cla.developers.google.com/ to complete the signing process. Once the CLA is signed, we'll be able to proceed with the review of your PR. Thank you!

@rohityan rohityan added the request clarification [Status] The maintainer need clarification or more information from the author label Jun 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

request clarification [Status] The maintainer need clarification or more information from the author tools [Component] This issue is related to tools

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants