Skip to content

Commit aa26c8d

Browse files
fix: use functools.wraps in activity_tool to preserve metadata (#1636)
Replace manual metadata copying with @functools.wraps(activity_def) to preserve __annotations__, __module__, __qualname__, and __dict__ on the wrapper function. These are needed by ADK's tool schema generation (specifically _handle_params_as_deferred_annotations) to resolve type hints from the original activity function. Also adds test_activity_tool_preserves_metadata verifying that __name__, __doc__, __annotations__, __module__, and __signature__ are all correctly preserved on the wrapper. Fixes #1635 Co-authored-by: Brian Strauch <brian@brianstrauch.com>
1 parent 4ec9ab0 commit aa26c8d

2 files changed

Lines changed: 43 additions & 3 deletions

File tree

temporalio/contrib/google_adk_agents/workflow.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Workflow utilities for Google ADK agents integration with Temporal."""
22

3+
import functools
34
import inspect
45
from typing import Any, Callable
56

@@ -18,6 +19,7 @@ def activity_tool(activity_def: Callable, **kwargs: Any) -> Callable:
1819
while marking it as a tool that executes via 'workflow.execute_activity'.
1920
"""
2021

22+
@functools.wraps(activity_def)
2123
async def wrapper(*args: Any, **kw: Any):
2224
# Inspect signature to bind arguments
2325
sig = inspect.signature(activity_def)
@@ -48,9 +50,8 @@ async def wrapper(*args: Any, **kw: Any):
4850
activity_def, args=activity_args, **options
4951
)
5052

51-
# Copy metadata
52-
wrapper.__name__ = activity_def.__name__
53-
wrapper.__doc__ = activity_def.__doc__
53+
# functools.wraps copies name/doc/module/annotations/qualname/dict.
54+
# Signature must be set explicitly since the wrapper uses *args/**kw.
5455
setattr(wrapper, "__signature__", inspect.signature(activity_def))
5556

5657
return wrapper

tests/contrib/google_adk_agents/test_google_adk_agents.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
"""Integration tests for ADK Temporal support."""
1616

17+
import inspect
1718
import json
1819
import logging
1920
import os
@@ -1119,3 +1120,41 @@ def test_explicitly_set_none_preserved() -> None:
11191120

11201121
assert "cache_config" in serialized, "Explicitly-set None should be preserved"
11211122
assert serialized["cache_config"] is None
1123+
1124+
1125+
def test_activity_tool_preserves_metadata() -> None:
1126+
"""activity_tool wrapper preserves the original function's metadata.
1127+
1128+
This ensures ADK's tool schema generation can inspect __annotations__
1129+
and __module__ on the wrapper, which are needed by
1130+
``_handle_params_as_deferred_annotations`` to resolve type hints.
1131+
"""
1132+
1133+
@activity.defn
1134+
async def my_activity(city: str, count: int = 1) -> str:
1135+
"""Get info for a city."""
1136+
return f"{city}: {count}"
1137+
1138+
tool = temporalio.contrib.google_adk_agents.workflow.activity_tool(
1139+
my_activity, start_to_close_timeout=timedelta(seconds=30)
1140+
)
1141+
1142+
# __name__ and __doc__
1143+
assert tool.__name__ == "my_activity"
1144+
assert tool.__doc__ == "Get info for a city."
1145+
1146+
# __annotations__ — critical for ADK type introspection
1147+
assert "city" in tool.__annotations__
1148+
assert tool.__annotations__["city"] is str
1149+
assert tool.__annotations__["count"] is int
1150+
assert tool.__annotations__["return"] is str
1151+
1152+
# __module__ — needed by _handle_params_as_deferred_annotations
1153+
assert tool.__module__ == my_activity.__module__
1154+
1155+
# __signature__ — must match the original, not *args/**kw
1156+
sig = inspect.signature(tool)
1157+
params = list(sig.parameters.keys())
1158+
assert params == ["city", "count"]
1159+
assert sig.parameters["city"].annotation is str
1160+
assert sig.parameters["count"].default == 1

0 commit comments

Comments
 (0)