diff --git a/temporalio/contrib/google_adk_agents/workflow.py b/temporalio/contrib/google_adk_agents/workflow.py index 274dde807..b1d150391 100644 --- a/temporalio/contrib/google_adk_agents/workflow.py +++ b/temporalio/contrib/google_adk_agents/workflow.py @@ -1,5 +1,6 @@ """Workflow utilities for Google ADK agents integration with Temporal.""" +import functools import inspect from typing import Any, Callable @@ -18,6 +19,7 @@ def activity_tool(activity_def: Callable, **kwargs: Any) -> Callable: while marking it as a tool that executes via 'workflow.execute_activity'. """ + @functools.wraps(activity_def) async def wrapper(*args: Any, **kw: Any): # Inspect signature to bind arguments sig = inspect.signature(activity_def) @@ -48,9 +50,8 @@ async def wrapper(*args: Any, **kw: Any): activity_def, args=activity_args, **options ) - # Copy metadata - wrapper.__name__ = activity_def.__name__ - wrapper.__doc__ = activity_def.__doc__ + # functools.wraps copies name/doc/module/annotations/qualname/dict. + # Signature must be set explicitly since the wrapper uses *args/**kw. setattr(wrapper, "__signature__", inspect.signature(activity_def)) return wrapper diff --git a/tests/contrib/google_adk_agents/test_google_adk_agents.py b/tests/contrib/google_adk_agents/test_google_adk_agents.py index 64da07103..7be7ec8c6 100644 --- a/tests/contrib/google_adk_agents/test_google_adk_agents.py +++ b/tests/contrib/google_adk_agents/test_google_adk_agents.py @@ -14,6 +14,7 @@ """Integration tests for ADK Temporal support.""" +import inspect import json import logging import os @@ -1119,3 +1120,41 @@ def test_explicitly_set_none_preserved() -> None: assert "cache_config" in serialized, "Explicitly-set None should be preserved" assert serialized["cache_config"] is None + + +def test_activity_tool_preserves_metadata() -> None: + """activity_tool wrapper preserves the original function's metadata. + + This ensures ADK's tool schema generation can inspect __annotations__ + and __module__ on the wrapper, which are needed by + ``_handle_params_as_deferred_annotations`` to resolve type hints. + """ + + @activity.defn + async def my_activity(city: str, count: int = 1) -> str: + """Get info for a city.""" + return f"{city}: {count}" + + tool = temporalio.contrib.google_adk_agents.workflow.activity_tool( + my_activity, start_to_close_timeout=timedelta(seconds=30) + ) + + # __name__ and __doc__ + assert tool.__name__ == "my_activity" + assert tool.__doc__ == "Get info for a city." + + # __annotations__ — critical for ADK type introspection + assert "city" in tool.__annotations__ + assert tool.__annotations__["city"] is str + assert tool.__annotations__["count"] is int + assert tool.__annotations__["return"] is str + + # __module__ — needed by _handle_params_as_deferred_annotations + assert tool.__module__ == my_activity.__module__ + + # __signature__ — must match the original, not *args/**kw + sig = inspect.signature(tool) + params = list(sig.parameters.keys()) + assert params == ["city", "count"] + assert sig.parameters["city"].annotation is str + assert sig.parameters["count"].default == 1