Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions haystack/tools/from_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ def get_weather(
The function must include type hints for all parameters.
The function is expected to have basic python input types (str, int, float, bool, list, dict, tuple).
Other input types may work but are not guaranteed.
If a parameter is annotated using `typing.Annotated`, its metadata will be used as parameter description.
If a parameter is annotated using `typing.Annotated`, the first string in its metadata will be used as
parameter description. Other metadata, such as a Pydantic `Field`, is passed on to Pydantic.
:param name:
The name of the Tool. If not provided, the name of the function will be used.
:param description:
Expand Down Expand Up @@ -164,8 +165,11 @@ def get_weather(
default = param.default if param.default is not param.empty else ...
fields[param_name] = (annotation, default)

# Only a string in `Annotated` metadata is a description; other metadata like `Field(...)` is left to Pydantic
if hasattr(annotation, "__metadata__"):
descriptions[param_name] = annotation.__metadata__[0]
description = next((meta for meta in annotation.__metadata__ if isinstance(meta, str)), None)
if description is not None:
descriptions[param_name] = description

# create Pydantic model and generate JSON schema
try:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
fixes:
- |
``create_tool_from_function`` and the ``@tool`` decorator no longer fail with
"The provided parameters do not define a valid JSON schema" when a parameter is annotated with a Pydantic
``Field``, for example ``Annotated[int, Field(ge=1)]``. Previously, the first ``Annotated`` metadata item was
always written to the schema as the parameter description, even when it was not a string.
Now only a string metadata item is used as the description, and a ``Field`` is left to Pydantic, so its
constraints and its own ``description`` appear in the tool schema.
22 changes: 22 additions & 0 deletions test/tools/test_from_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,28 @@ def function_with_postponed_annotations(
}


def test_from_function_annotated_with_pydantic_field():
def function_with_fields(
city: Annotated[str, Field(min_length=1), "the city for which to get the weather"],
days: Annotated[int, Field(ge=1, description="the number of days to forecast")] = 1,
verbose: Annotated[bool, Field(strict=True)] = False,
) -> str:
"""A simple function to get the weather forecast for a location."""
return f"Weather report for {city}: 20°C, sunny"

tool = create_tool_from_function(function=function_with_fields)

assert tool.parameters == {
"type": "object",
"properties": {
"city": {"type": "string", "minLength": 1, "description": "the city for which to get the weather"},
"days": {"type": "integer", "minimum": 1, "description": "the number of days to forecast", "default": 1},
"verbose": {"type": "boolean", "default": False},
},
"required": ["city"],
}


def test_from_function_missing_type_hint():
def function_missing_type_hint(city) -> str: # type: ignore[no-untyped-def]
return f"Weather report for {city}: 20°C, sunny"
Expand Down
Loading