Skip to content

Conversation

@Ratish1
Copy link
Contributor

@Ratish1 Ratish1 commented Oct 24, 2025

Description

This PR adds support for using typing.Annotated to define argument descriptions directly in the type hints of @tool decorated functions. This provides a cleaner and more maintainable way to document tool parameters.

To ensure a robust and maintainable implementation, this feature is currently focused on string-based descriptions. Support for pydantic.Field constraints within Annotated is deferred to a future enhancement, and a NotImplementedError is raised to guide developers.

Key changes

  • The tool decorator now correctly parses string metadata from Annotated type hints and uses it as the parameter's description, prioritizing it over docstrings.
  • Uses param.annotation for type inspection to ensure consistent behavior across all supported Python versions, including Python 3.10.
  • Adds a check to raise a NotImplementedError if pydantic.Field is used within an Annotated type.value handling, and correct priority.
  • Includes unit tests to validate the new description parsing and the NotImplementedError case.

Related Issues

#511

Documentation PR

N/A

Type of Change

New feature

Testing

How have you tested the change? Verify that the changes do not break functionality or introduce warnings in consuming repositories: agents-docs, agents-tools, agents-cli

  • I ran hatch run prepare
  • I ran formatters and linters
  • I ran the new unit tests:
  • pytest -q tests/strands/tools/test_decorator.py → All 61 tests passed

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@Ratish1
Copy link
Contributor Author

Ratish1 commented Oct 31, 2025

The unit tests seems to be failing but they pass on my end, is it due to the logic written in decorator file init method @dbschmigelski

@dbschmigelski
Copy link
Member

The unit tests seems to be failing but they pass on my end, is it due to the logic written in decorator file init method @dbschmigelski

They are failing when I run locally. I think there are some small bugs related to truthiness. I also am not seeing proper handling of the required case.

I think there is an issue when it comes to defaulting when we have Field within Annotated

with

        score: Annotated[int, Field(description="Score between 0-100", ge=0, le=100)] = 50,

Field does not have a default so it is incorrectly still in required,

If we did

        score: Annotated[int, Field(description="Score between 0-100", ge=0, le=100, default=20)] = 50,

it would pass though

So we have to address the failing tests and probably add a test with this inner-default

@Ratish1
Copy link
Contributor Author

Ratish1 commented Oct 31, 2025

It seems to be failing again @dbschmigelski , I added a new commit again to fix the default handling. For Annotated params with Field, I copy the Field (preserving constraints) then override the default from function signature. For non-Annotated params, I pass the default directly to Field constructor.

@dbschmigelski
Copy link
Member

dbschmigelski commented Oct 31, 2025

I can check again regarding the branch protection.

I think there are some interesting things going on for the test failures

FAILED tests/strands/tools/test_decorator.py::test_tool_decorator_annotated_pydantic_field_constraints - AssertionError: assert 'score' not in ['email', 'score']
FAILED tests/strands/tools/test_decorator.py::test_tool_decorator_annotated_no_description_fallback - KeyError: 'description'

For

FAILED tests/strands/tools/test_decorator.py::test_tool_decorator_annotated_pydantic_field_constraints - AssertionError: assert 'score' not in ['email', 'score']

this is the default issue I mentioned, where when default is present INSIDE the Field it is respected. But, if we have an Annotation, with a Field, and the Annotation has the default then we unexpectedly are not respecting the default.

For

FAILED tests/strands/tools/test_decorator.py::test_tool_decorator_annotated_no_description_fallback - KeyError: 'description'

It looks like when the Field does not have any description, even when we do field.description = ... it is not getting set properly which is strange

@Ratish1
Copy link
Contributor Author

Ratish1 commented Oct 31, 2025

I can check again regarding the branch protection.

I think there are some interesting things going on for the test failures

FAILED tests/strands/tools/test_decorator.py::test_tool_decorator_annotated_pydantic_field_constraints - AssertionError: assert 'score' not in ['email', 'score']
FAILED tests/strands/tools/test_decorator.py::test_tool_decorator_annotated_no_description_fallback - KeyError: 'description'

For

FAILED tests/strands/tools/test_decorator.py::test_tool_decorator_annotated_pydantic_field_constraints - AssertionError: assert 'score' not in ['email', 'score']

this is the default issue I mentioned, where when default is present INSIDE the Field it is respected. But, if we have an Annotation, with a Field, and the Annotation has the default then we unexpectedly are not respecting the default.

For

FAILED tests/strands/tools/test_decorator.py::test_tool_decorator_annotated_no_description_fallback - KeyError: 'description'

It looks like when the Field does not have any description, even when we do field.description = ... it is not getting set properly which is strange

No I figured out the branch protection happens cause I opened the PR from my fork, I should be opening it from the main repo itself. So its my fault, if you want I can create a new PR immediately with the same changes.

@dbschmigelski
Copy link
Member

Actually we only allow PRs from forks, so you're definitely doing it right, may just be a problem with my git locally

@github-actions github-actions bot removed the size/m label Oct 31, 2025
@codecov
Copy link

codecov bot commented Nov 4, 2025

Codecov Report

❌ Patch coverage is 95.65217% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/strands/tools/decorator.py 95.65% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@dbschmigelski
Copy link
Member

Looks like we have one failing test

FAILED tests/strands/tools/test_decorator.py::test_tool_decorator_annotated_optional_type - AssertionError: assert 'Parameter optional' == 'Optional parameter'

@Ratish1, are you unable to run hatch test locally?

@Ratish1
Copy link
Contributor Author

Ratish1 commented Nov 4, 2025

Looks like we have one failing test

FAILED tests/strands/tools/test_decorator.py::test_tool_decorator_annotated_optional_type - AssertionError: assert 'Parameter optional' == 'Optional parameter'

@Ratish1, are you unable to run hatch test locally?

No i can run it, it passes for me locally @dbschmigelski . Idk why this is failing, I think I found a fix, pushing the changes now.

@dbschmigelski
Copy link
Member

dbschmigelski commented Nov 4, 2025

it passes for me locally

Strange, I had thought hatch would prevent issues

@Ratish1
Copy link
Contributor Author

Ratish1 commented Nov 4, 2025

it passes for me locally

Strange, I had thought hatch would prevent issues

I normally just do pre commit check all command. I tried hatch and it passes for me, dont know if this is a me problem. Anyway hopefully it works now.

@Ratish1 Ratish1 requested a review from dbschmigelski November 4, 2025 21:09
@Ratish1
Copy link
Contributor Author

Ratish1 commented Nov 5, 2025

It seems to still fail @dbschmigelski . I think get_type_hints() behaves differently for complex Annotated types in older Python versions so I removed it and switched to use param.annotation directly.

@github-actions github-actions bot added size/m and removed size/m labels Nov 5, 2025
@Ratish1 Ratish1 changed the title feat(tools): Support Annotated for argument descriptions and Pydantic Field constraints feat(tools): Support string descriptions in Annotated parameters Nov 7, 2025
@Ratish1
Copy link
Contributor Author

Ratish1 commented Nov 7, 2025

The tests pass now @dbschmigelski, I also updated the description of the PR. Lmk if you need more changes, thanks.

@dbschmigelski
Copy link
Member

The tests pass now @dbschmigelski, I also updated the description of the PR. Lmk if you need more changes, thanks.

Made some minor changes to avoid making breaking changes to the FunctionToolMetadata class. It is highly unlikely, but technically someone could have depended on self.type_hints and the old behavior of self.param_descriptions

@github-actions github-actions bot added size/m and removed size/m labels Nov 10, 2025
@dbschmigelski dbschmigelski deployed to manual-approval November 10, 2025 16:35 — with GitHub Actions Active
@Ratish1
Copy link
Contributor Author

Ratish1 commented Nov 10, 2025

The tests pass now @dbschmigelski, I also updated the description of the PR. Lmk if you need more changes, thanks.

Made some minor changes to avoid making breaking changes to the FunctionToolMetadata class. It is highly unlikely, but technically someone could have depended on self.type_hints and the old behavior of self.param_descriptions

Oh ok got it, thanks for the help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants