Skip to content

activity_tool() wrapper breaks ADK tool declarations — missing __annotations__ and __module__ #1635

Description

@brucearctor

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ai-sdkRelated to AI integrations

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions