From e26f647c3db9ce81349e6c73d7a1e8c988433552 Mon Sep 17 00:00:00 2001 From: Eric Gustin <34000337+EricGustin@users.noreply.github.com> Date: Sat, 22 Mar 2025 08:25:56 -0800 Subject: [PATCH] Add 'Invalid Renamed Tool Parameter' Check (#324) Arcade tools can rename parameters like so, ```py @tool() def func_with_renamed_param( param1: Annotated[str, "MyRenamedParam", "The first parameter"], ): pass ``` but there is no check for whether the renamed parameter is a valid identifier. Anthropic models, for example, will fail if a renamed parameter is not a valid identifier (which was the cause for https://github.com/ArcadeAI/arcade-ai/pull/319). --- arcade/arcade/core/catalog.py | 9 ++++++++- .../tool/test_create_tool_definition_errors.py | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/arcade/arcade/core/catalog.py b/arcade/arcade/core/catalog.py index d0a1825f..6fe3296e 100644 --- a/arcade/arcade/core/catalog.py +++ b/arcade/arcade/core/catalog.py @@ -575,7 +575,14 @@ def extract_field_info(param: inspect.Parameter) -> ToolParamInfo: elif len(str_annotations) == 1: param_info.description = str_annotations[0] elif len(str_annotations) == 2: - param_info.name = str_annotations[0] + new_name = str_annotations[0] + if not new_name.isidentifier(): + raise ToolDefinitionError( + f"Invalid parameter name: '{new_name}' is not a valid identifier. " + "Identifiers must start with a letter or underscore, " + "and can only contain letters, digits, or underscores." + ) + param_info.name = new_name param_info.description = str_annotations[1] else: raise ToolDefinitionError( diff --git a/arcade/tests/tool/test_create_tool_definition_errors.py b/arcade/tests/tool/test_create_tool_definition_errors.py index fa40d712..5e6d10bf 100644 --- a/arcade/tests/tool/test_create_tool_definition_errors.py +++ b/arcade/tests/tool/test_create_tool_definition_errors.py @@ -1,3 +1,5 @@ +from typing import Annotated + import pytest from arcade.core.catalog import ToolCatalog @@ -41,6 +43,13 @@ def func_with_multiple_context_params(context: ToolContext, context2: ToolContex pass +@tool(desc="A function with an invalid renamed parameter") +def func_with_invalid_renamed_param( + param1: Annotated[str, "invalid-param-name", "The first parameter"], +): + pass + + @tool( desc="A function with a required secret with a missing key (illegal)", requires_secrets=[""], @@ -95,6 +104,11 @@ def func_with_secret_requirement_invalid_type(): ToolDefinitionError, id=func_with_multiple_context_params.__name__, ), + pytest.param( + func_with_invalid_renamed_param, + ToolDefinitionError, + id=func_with_invalid_renamed_param.__name__, + ), pytest.param( func_with_missing_secret_key, ToolDefinitionError,