diff --git a/python/packages/autogen-core/src/autogen_core/_function_utils.py b/python/packages/autogen-core/src/autogen_core/_function_utils.py index 891027842794..40e30a0d398f 100644 --- a/python/packages/autogen-core/src/autogen_core/_function_utils.py +++ b/python/packages/autogen-core/src/autogen_core/_function_utils.py @@ -41,9 +41,8 @@ def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature: The signature of the function with type annotations """ signature = inspect.signature(call) - globalns = getattr(call, "__globals__", {}) func_call = call.func if isinstance(call, partial) else call - type_hints = typing.get_type_hints(func_call, globalns, include_extras=True) + type_hints = typing.get_type_hints(func_call, include_extras=True) typed_params = [ inspect.Parameter( name=param.name, @@ -73,8 +72,7 @@ def get_typed_return_annotation(call: Callable[..., Any]) -> Any: if annotation is inspect.Signature.empty: return None - globalns = getattr(call, "__globals__", {}) - type_hints = typing.get_type_hints(call, globalns, include_extras=True) + type_hints = typing.get_type_hints(call, include_extras=True) return type_hints.get("return", inspect.Signature.empty) diff --git a/python/packages/autogen-core/tests/test_wrapped_annotations.py b/python/packages/autogen-core/tests/test_wrapped_annotations.py new file mode 100644 index 000000000000..a0e284ac14b5 --- /dev/null +++ b/python/packages/autogen-core/tests/test_wrapped_annotations.py @@ -0,0 +1,57 @@ +from datetime import datetime +from functools import wraps +from types import ModuleType +from typing import Any, Callable + +import pytest +from autogen_core import CancellationToken +from autogen_core._function_utils import get_function_schema, get_typed_return_annotation, get_typed_signature +from autogen_core.tools import FunctionTool + + +@pytest.fixture(params=[0, 1, 2]) +def wrapped_function(request: pytest.FixtureRequest) -> Callable[..., Any]: + module = ModuleType("wrapped_tool_module") + exec( + "from datetime import datetime as Timestamp\n" + "def echo(value: 'Timestamp') -> 'Timestamp':\n" + " return value\n", + module.__dict__, + ) + + def decorate(original: Callable[..., Any]) -> Callable[..., Any]: + @wraps(original) + def wrapper(*args: Any, **kwargs: Any) -> Any: + return original(*args, **kwargs) + + return wrapper + + function: Callable[..., Any] = module.echo + for _ in range(request.param): + function = decorate(function) + return function + + +def test_wrapped_function_signature(wrapped_function: Callable[..., Any]) -> None: + signature = get_typed_signature(wrapped_function) + assert signature.parameters["value"].annotation is datetime + assert signature.return_annotation is datetime + + +def test_wrapped_function_return_annotation(wrapped_function: Callable[..., Any]) -> None: + assert get_typed_return_annotation(wrapped_function) is datetime + + +def test_wrapped_function_schema(wrapped_function: Callable[..., Any]) -> None: + schema = get_function_schema(wrapped_function, description="Echo a timestamp.") + assert schema["function"]["parameters"]["properties"]["value"]["format"] == "date-time" + + +@pytest.mark.asyncio +async def test_wrapped_function_tool(wrapped_function: Callable[..., Any]) -> None: + tool = FunctionTool(wrapped_function, description="Echo a timestamp.") + schema = tool.schema + assert "parameters" in schema + assert schema["parameters"]["properties"]["value"]["format"] == "date-time" + assert tool.return_type() is datetime + assert await tool.run_json({"value": "2026-10-05T12:00:00"}, CancellationToken()) == datetime(2026, 10, 5, 12)