Skip to content

Commit 516d74b

Browse files
authored
fix(core): use get_type_hints for Python 3.14 TypedDict compatibility (#34390)
Replace direct `__annotations__` access with `get_type_hints()` in `_convert_any_typed_dicts_to_pydantic` to handle [PEP 649](https://peps.python.org/pep-0649/) deferred annotations in Python 3.14: > [`Changed in version 3.14: Annotations are now lazily evaluated by default`](https://docs.python.org/3/reference/compound_stmts.html#annotations) Before: ```python class MyTool(TypedDict): name: str MyTool.__annotations__ # {'name': 'str'} - string, not type issubclass('str', ...) # TypeError: arg 1 must be a class ``` After: ```python get_type_hints(MyTool) # {'name': <class 'str'>} - actual type ``` Fixes #34291
1 parent c85f7b6 commit 516d74b

File tree

2 files changed

+13
-3
lines changed

2 files changed

+13
-3
lines changed

libs/core/langchain_core/tools/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
get_type_hints,
2323
)
2424

25+
import typing_extensions
2526
from pydantic import (
2627
BaseModel,
2728
ConfigDict,
@@ -94,7 +95,7 @@ def _is_annotated_type(typ: type[Any]) -> bool:
9495
Returns:
9596
`True` if the type is an Annotated type, `False` otherwise.
9697
"""
97-
return get_origin(typ) is typing.Annotated
98+
return get_origin(typ) in (typing.Annotated, typing_extensions.Annotated)
9899

99100

100101
def _get_annotation_description(arg_type: type) -> str | None:

libs/core/langchain_core/utils/function_calling.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
cast,
1919
get_args,
2020
get_origin,
21+
get_type_hints,
2122
)
2223

24+
import typing_extensions
2325
from pydantic import BaseModel
2426
from pydantic.v1 import BaseModel as BaseModelV1
2527
from pydantic.v1 import Field as Field_v1
@@ -232,13 +234,20 @@ def _convert_any_typed_dicts_to_pydantic(
232234
if is_typeddict(type_):
233235
typed_dict = type_
234236
docstring = inspect.getdoc(typed_dict)
235-
annotations_ = typed_dict.__annotations__
237+
# Use get_type_hints to properly resolve forward references and
238+
# string annotations in Python 3.14+ (PEP 649 deferred annotations).
239+
# include_extras=True preserves Annotated metadata.
240+
try:
241+
annotations_ = get_type_hints(typed_dict, include_extras=True)
242+
except Exception:
243+
# Fallback for edge cases where get_type_hints might fail
244+
annotations_ = typed_dict.__annotations__
236245
description, arg_descriptions = _parse_google_docstring(
237246
docstring, list(annotations_)
238247
)
239248
fields: dict = {}
240249
for arg, arg_type in annotations_.items():
241-
if get_origin(arg_type) is Annotated: # type: ignore[comparison-overlap]
250+
if get_origin(arg_type) in (Annotated, typing_extensions.Annotated):
242251
annotated_args = get_args(arg_type)
243252
new_arg_type = _convert_any_typed_dicts_to_pydantic(
244253
annotated_args[0], depth=depth + 1, visited=visited

0 commit comments

Comments
 (0)