fix(tools): resolve Pydantic 2.12+ incompatibility with PEP 563 annotations#1449
fix(tools): resolve Pydantic 2.12+ incompatibility with PEP 563 annotations#1449cagataycali wants to merge 4 commits intostrands-agents:mainfrom
Conversation
…ations This fixes issue strands-agents#1208 where tools using Literal types fail with Pydantic 2.12+ when modules use `from __future__ import annotations` (PEP 563). Root cause: - `param.annotation` returns string literals when PEP 563 is active - Pydantic 2.12+ no longer auto-resolves these strings in `create_model()` Fix: - Use `get_type_hints(func, include_extras=True)` to resolve string annotations to actual types while preserving Annotated metadata - Use resolved type hints in `_create_input_model()` instead of raw `param.annotation` Tested with: - Pydantic 2.12.0+ - nova-act pattern (external Literal types with PEP 563) - Existing Annotated type handling (include_extras=True preserves metadata)
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
CI Status Update 🔍Failures Identified
Root CauseThe Options
Test Results Summary
The fix works correctly for PEP 563 cases. The test failure is about description ordering in Awaiting maintainer feedback on how to proceed. 🦆 |
Python 3.10 Fix Pushed! ✅Commit: Root CauseThe previous approach unconditionally used The FixDetect PEP 563 ( if isinstance(param.annotation, str):
# PEP 563 active - need to resolve string annotation to actual type
param_type = self.type_hints.get(name, param.annotation)
else:
# Normal case - annotation is already the actual type object
param_type = param.annotationWhy This Works
This preserves:
CI should re-run automatically. 🦆 |
🎉 Python 3.10 Tests PASS!
The fix works! The key insight was that PEP 563 turns annotations into strings, so we can detect it with Ready for maintainer review once Windows tests complete. 🦆 |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
✅ All Tests Passing!The Python 3.10 fix (commit
Summary of the FixThe approach is straightforward: detect PEP 563 by checking if if isinstance(param.annotation, str):
# PEP 563 active - resolve string annotation
param_type = self.type_hints.get(name, param.annotation)
else:
# Normal case - use annotation directly (preserves Annotated metadata)
param_type = param.annotationWhy This Works Better Than Before
About
|
✅ All Unit Tests Passing - Ready for Review!The PEP 563 detection fix (commit
Also passing: Lint, codecov/patch Expected Failures (No Action Needed)
The FixDetects PEP 563 ( if isinstance(param.annotation, str):
# PEP 563 active - resolve string annotation
param_type = self.type_hints.get(name, param.annotation)
else:
# Normal case - annotation is already resolved
param_type = param.annotationThis ensures:
Ready for maintainer review when you have a moment! 🦆 |
The previous approach of always using get_type_hints() broke extraction of Annotated metadata in Python 3.10 for modules that don't use 'from __future__ import annotations'. Fix: - Only use get_type_hints() when param.annotation is a string (PEP 563) - Use param.annotation directly when it's already a resolved type - This preserves Annotated metadata reliably across all Python versions Test coverage: - test_tool_decorator_annotated_optional_type now passes on Python 3.10 - All existing PEP 563 compatibility tests still pass
e0cf81f to
670bc9c
Compare
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as off-topic.
This comment was marked as off-topic.
The previous implementation used get_type_hints() unconditionally for all parameters, which caused Python 3.10 to behave inconsistently with Annotated[Optional[T], ...] types. Root cause: When __future__ annotations is NOT active (normal case), param.annotation already contains the full type with Annotated metadata. However, get_type_hints(include_extras=True) may return a different structure in Python 3.10, breaking the _extract_annotated_metadata() logic. Fix: Only use get_type_hints() when actually needed (PEP 563 active). - If param.annotation is a string → PEP 563 active → resolve via type_hints - Otherwise → use param.annotation directly → preserves Annotated wrapper This approach: 1. Fixes Python 3.10 compatibility (test_tool_decorator_annotated_optional_type) 2. Maintains PEP 563 support (all test_decorator_pep563.py tests pass) 3. Works consistently across Python 3.10-3.13 Fixes: test_tool_decorator_annotated_optional_type failure in Python 3.10
ecc9442 to
6925392
Compare
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
🔍 CI
|
|
closing due cluttered context ^^ |
Description
This PR fixes the incompatibility between strands-agents 1.16.0+ and Pydantic 2.12+ when tools use
Literaltypes in modules withfrom __future__ import annotations(PEP 563).Root Cause
In
src/strands/tools/decorator.py, the_create_input_model()method usedparam.annotationdirectly. When PEP 563 is active,param.annotationreturns string literals instead of resolved types. Pydantic 2.12+ changed its behavior and no longer auto-resolves these string annotations increate_model(), causing:Solution
get_type_hints(func, include_extras=True)to resolve string annotations to actual types while preservingAnnotatedmetadata_create_input_model()instead of rawparam.annotationThe
include_extras=Trueparameter is critical - without it,Annotatedwrappers would be stripped, breaking the existing description extraction logic.Verification
Annotatedtype handling preservedRelated Issues
Closes #1208
Documentation PR
No documentation changes required.
Type of Change
Testing
Added comprehensive test suite
tests/strands/tools/test_decorator_pep563.pycovering:__future__ annotationsChecklist
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
PR created by strands-coder 🦆