Skip to content

Commit 30a3ea9

Browse files
authored
Revert "fix(toolbox-core): Use typing.Annotated for reliable parameter descri…" (#377)
This reverts commit eb76680.
1 parent d915624 commit 30a3ea9

File tree

8 files changed

+207
-161
lines changed

8 files changed

+207
-161
lines changed

packages/toolbox-core/src/toolbox_core/protocol.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
from inspect import Parameter
16-
from typing import Annotated, Any, Optional, Type, Union
16+
from typing import Any, Optional, Type, Union
1717

1818
from pydantic import BaseModel
1919

@@ -60,12 +60,12 @@ class ParameterSchema(BaseModel):
6060
items: Optional["ParameterSchema"] = None
6161
additionalProperties: Optional[Union[bool, AdditionalPropertiesSchema]] = None
6262

63-
def __get_annotation(self) -> Any:
64-
base_type: Any
63+
def __get_type(self) -> Type:
64+
base_type: Type
6565
if self.type == "array":
6666
if self.items is None:
6767
raise ValueError("Unexpected value: type is 'array' but items is None")
68-
base_type = list[self.items.__get_annotation()] # type: ignore
68+
base_type = list[self.items.__get_type()] # type: ignore
6969
elif self.type == "object":
7070
if isinstance(self.additionalProperties, AdditionalPropertiesSchema):
7171
value_type = self.additionalProperties.get_value_type()
@@ -76,15 +76,15 @@ def __get_annotation(self) -> Any:
7676
base_type = _get_python_type(self.type)
7777

7878
if not self.required:
79-
base_type = Optional[base_type]
79+
return Optional[base_type] # type: ignore
8080

81-
return Annotated[base_type, self.description]
81+
return base_type
8282

8383
def to_param(self) -> Parameter:
8484
return Parameter(
8585
self.name,
8686
Parameter.POSITIONAL_OR_KEYWORD,
87-
annotation=self.__get_annotation(),
87+
annotation=self.__get_type(),
8888
default=Parameter.empty if self.required else None,
8989
)
9090

packages/toolbox-core/src/toolbox_core/tool.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
from .protocol import ParameterSchema
2626
from .utils import (
27+
create_func_docstring,
2728
identify_auth_requirements,
2829
params_to_pydantic_model,
2930
resolve_value,
@@ -100,7 +101,7 @@ def __init__(
100101

101102
# the following properties are set to help anyone that might inspect it determine usage
102103
self.__name__ = name
103-
self.__doc__ = self.__description
104+
self.__doc__ = create_func_docstring(self.__description, self.__params)
104105
self.__signature__ = Signature(
105106
parameters=inspect_type_params, return_annotation=str
106107
)

packages/toolbox-core/src/toolbox_core/utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@
3131
from toolbox_core.protocol import ParameterSchema
3232

3333

34+
def create_func_docstring(description: str, params: Sequence[ParameterSchema]) -> str:
35+
"""Convert tool description and params into its function docstring"""
36+
docstring = description
37+
if not params:
38+
return docstring
39+
docstring += "\n\nArgs:"
40+
for p in params:
41+
annotation = p.to_param().annotation
42+
docstring += f"\n {p.name} ({getattr(annotation, '__name__', str(annotation))}): {p.description}"
43+
return docstring
44+
45+
3446
def identify_auth_requirements(
3547
req_authn_params: Mapping[str, list[str]],
3648
req_authz_tokens: Sequence[str],

packages/toolbox-core/tests/test_client.py

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
import inspect
1717
import json
18-
from typing import Annotated, Any, Callable, Mapping, Optional, get_args, get_origin
18+
from typing import Any, Callable, Mapping, Optional
1919
from unittest.mock import AsyncMock, Mock
2020

2121
import pytest
@@ -192,30 +192,15 @@ async def test_load_tool_success(aioresponses, test_tool_str):
192192
assert callable(loaded_tool)
193193
# Assert introspection attributes are set correctly
194194
assert loaded_tool.__name__ == TOOL_NAME
195-
expected_description = test_tool_str.description
195+
expected_description = (
196+
test_tool_str.description
197+
+ f"\n\nArgs:\n param1 (str): Description of Param1"
198+
)
196199
assert loaded_tool.__doc__ == expected_description
197200

198-
# Assert signature inspection, including annotated descriptions
201+
# Assert signature inspection
199202
sig = inspect.signature(loaded_tool)
200-
actual_params_map = sig.parameters
201-
expected_params_list = test_tool_str.parameters
202-
203-
assert len(actual_params_map) == len(expected_params_list)
204-
205-
for expected_param_schema in expected_params_list:
206-
param_name = expected_param_schema.name
207-
assert param_name in actual_params_map
208-
actual_param = actual_params_map[param_name]
209-
annotation = actual_param.annotation
210-
assert get_origin(annotation) is Annotated
211-
annotation_args = get_args(annotation)
212-
actual_param_description = annotation_args[1]
213-
assert actual_param_description == expected_param_schema.description
214-
215-
if expected_param_schema.required:
216-
assert actual_param.default is inspect.Parameter.empty
217-
else:
218-
assert actual_param.default is None
203+
assert list(sig.parameters.keys()) == [p.name for p in test_tool_str.parameters]
219204

220205
assert await loaded_tool("some value") == "ok"
221206

packages/toolbox-core/tests/test_e2e.py

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
from inspect import Parameter, signature
16-
from typing import Annotated, Any, Optional
16+
from typing import Any, Optional
1717

1818
import pytest
1919
import pytest_asyncio
@@ -243,24 +243,15 @@ async def test_tool_signature_is_correct(self, toolbox: ToolboxClient):
243243

244244
# The required parameter should have no default
245245
assert sig.parameters["email"].default is Parameter.empty
246-
assert (
247-
sig.parameters["email"].annotation
248-
is Annotated[str, "The email to search for."]
249-
)
246+
assert sig.parameters["email"].annotation is str
250247

251248
# The optional parameter should have a default of None
252249
assert sig.parameters["data"].default is None
253-
assert (
254-
sig.parameters["data"].annotation
255-
is Annotated[Optional[str], "The row to narrow down the search."]
256-
)
250+
assert sig.parameters["data"].annotation is Optional[str]
257251

258252
# The optional parameter should have a default of None
259253
assert sig.parameters["id"].default is None
260-
assert (
261-
sig.parameters["id"].annotation
262-
is Annotated[Optional[int], "The id to narrow down the search."]
263-
)
254+
assert sig.parameters["id"].annotation is Optional[int]
264255

265256
async def test_run_tool_with_optional_params_omitted(self, toolbox: ToolboxClient):
266257
"""Invoke a tool providing only the required parameter."""
@@ -404,27 +395,15 @@ async def test_tool_signature_with_map_params(self, toolbox: ToolboxClient):
404395
sig = signature(tool)
405396

406397
assert "execution_context" in sig.parameters
407-
assert (
408-
sig.parameters["execution_context"].annotation
409-
== Annotated[
410-
dict[str, Any],
411-
"A flexible set of key-value pairs for the execution environment.",
412-
]
413-
)
398+
assert sig.parameters["execution_context"].annotation == dict[str, Any]
414399
assert sig.parameters["execution_context"].default is Parameter.empty
415400

416401
assert "user_scores" in sig.parameters
417-
assert (
418-
sig.parameters["user_scores"].annotation
419-
== Annotated[dict[str, int], "A map of user IDs to their scores."]
420-
)
402+
assert sig.parameters["user_scores"].annotation == dict[str, int]
421403
assert sig.parameters["user_scores"].default is Parameter.empty
422404

423405
assert "feature_flags" in sig.parameters
424-
assert (
425-
sig.parameters["feature_flags"].annotation
426-
== Annotated[Optional[dict[str, bool]], "Optional feature flags."]
427-
)
406+
assert sig.parameters["feature_flags"].annotation == Optional[dict[str, bool]]
428407
assert sig.parameters["feature_flags"].default is None
429408

430409
async def test_run_tool_with_map_params(self, toolbox: ToolboxClient):

0 commit comments

Comments
 (0)