Summary
activity_tool() in temporalio/contrib/google_adk_agents/workflow.py wraps an activity function for use as an ADK tool. The wrapper copies __name__, __doc__, and __signature__ from the original, but does not copy __annotations__ or __module__. This causes ADK's function declaration builder to crash with a KeyError when introspecting tool parameters.
Reproduction
from temporalio.contrib.google_adk_agents.workflow import activity_tool
from temporalio import activity
from google.adk.agents import LlmAgent
@activity.defn
async def add_to_cart(item_name: str, quantity: int = 1) -> dict:
"""Add an item to the cart."""
return {"item": item_name, "qty": quantity}
tool = activity_tool(add_to_cart, start_to_close_timeout=15)
# This crashes:
agent = LlmAgent(
model="gemini-2.0-flash",
name="test",
tools=[tool],
)
Error:
File ".../google/adk/tools/_function_parameter_parse_util.py", line 57, in _handle_params_as_deferred_annotations
param = param.replace(annotation=annotation_under_future[name])
KeyError: 'item_name'
Root Cause
The wrapper function is async def wrapper(*args: Any, **kw: Any), so:
| Attribute |
Original |
Wrapper |
__annotations__ |
{'item_name': str, 'quantity': int, 'return': dict} |
{'args': Any, 'kw': Any} ❌ |
__module__ |
my_tools |
temporalio.contrib.google_adk_agents.workflow ❌ |
ADK's _handle_params_as_deferred_annotations reads the wrapper's __annotations__ to resolve parameter types, but the wrapper's annotations only contain args and kw.
Proposed Fix
Replace manual metadata copying with functools.wraps:
+import functools
def activity_tool(activity_def: Callable, **kwargs: Any) -> Callable:
+ @functools.wraps(activity_def)
async def wrapper(*args: Any, **kw: Any):
# ... unchanged ...
- # Copy metadata
- wrapper.__name__ = activity_def.__name__
- wrapper.__doc__ = activity_def.__doc__
- setattr(wrapper, "__signature__", inspect.signature(activity_def))
+ # functools.wraps copies name/doc/module/annotations/qualname/dict.
+ # Signature must be set explicitly since the wrapper uses *args/**kw.
+ wrapper.__signature__ = inspect.signature(activity_def)
return wrapper
functools.wraps is the standard Python pattern for decorator metadata propagation and is future-proof against new introspectable attributes.
Environment
temporalio 1.27.0 (from PyPI, temporalio[google-adk])
google-adk 1.5.0
- Python 3.13
PR incoming.
Summary
activity_tool()intemporalio/contrib/google_adk_agents/workflow.pywraps an activity function for use as an ADK tool. The wrapper copies__name__,__doc__, and__signature__from the original, but does not copy__annotations__or__module__. This causes ADK's function declaration builder to crash with aKeyErrorwhen introspecting tool parameters.Reproduction
Error:
Root Cause
The wrapper function is
async def wrapper(*args: Any, **kw: Any), so:__annotations__{'item_name': str, 'quantity': int, 'return': dict}{'args': Any, 'kw': Any}❌__module__my_toolstemporalio.contrib.google_adk_agents.workflow❌ADK's
_handle_params_as_deferred_annotationsreads the wrapper's__annotations__to resolve parameter types, but the wrapper's annotations only containargsandkw.Proposed Fix
Replace manual metadata copying with
functools.wraps:functools.wrapsis the standard Python pattern for decorator metadata propagation and is future-proof against new introspectable attributes.Environment
temporalio1.27.0 (from PyPI,temporalio[google-adk])google-adk1.5.0PR incoming.