From 1e56c66f86033049f320d7e1cd793c36e597cab7 Mon Sep 17 00:00:00 2001 From: CtrlMj <43945314+CtrlMj@users.noreply.github.com> Date: Mon, 12 May 2025 11:04:13 -0400 Subject: [PATCH] core: Fix issue 31035 alias fields in base tool langchain core (#31112) **Description**: The 'inspect' package in python skips over the aliases set in the schema of a pydantic model. This is a workound to include the aliases from the original input. **issue**: #31035 Cc: @ccurme @eyurtsev --------- Co-authored-by: Chester Curme --- libs/core/langchain_core/tools/base.py | 12 +++++++----- libs/core/tests/unit_tests/test_tools.py | 9 +++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/libs/core/langchain_core/tools/base.py b/libs/core/langchain_core/tools/base.py index daee8604307..52123ec6b4f 100644 --- a/libs/core/langchain_core/tools/base.py +++ b/libs/core/langchain_core/tools/base.py @@ -1077,16 +1077,18 @@ def get_all_basemodel_annotations( """ # cls has no subscript: cls = FooBar if isinstance(cls, type): + # Gather pydantic field objects (v2: model_fields / v1: __fields__) + fields = getattr(cls, "model_fields", {}) or getattr(cls, "__fields__", {}) + alias_map = {field.alias: name for name, field in fields.items() if field.alias} + annotations: dict[str, type] = {} for name, param in inspect.signature(cls).parameters.items(): # Exclude hidden init args added by pydantic Config. For example if # BaseModel(extra="allow") then "extra_data" will part of init sig. - if ( - fields := getattr(cls, "model_fields", {}) # pydantic v2+ - or getattr(cls, "__fields__", {}) # pydantic v1 - ) and name not in fields: + if fields and name not in fields and name not in alias_map: continue - annotations[name] = param.annotation + field_name = alias_map.get(name, name) + annotations[field_name] = param.annotation orig_bases: tuple = getattr(cls, "__orig_bases__", ()) # cls has subscript: cls = FooBar[int] else: diff --git a/libs/core/tests/unit_tests/test_tools.py b/libs/core/tests/unit_tests/test_tools.py index 3ffc15ad0fd..025be61b461 100644 --- a/libs/core/tests/unit_tests/test_tools.py +++ b/libs/core/tests/unit_tests/test_tools.py @@ -2146,6 +2146,15 @@ def test__get_all_basemodel_annotations_v1() -> None: assert actual == expected +def test_get_all_basemodel_annotations_aliases() -> None: + class CalculatorInput(BaseModel): + a: int = Field(description="first number", alias="A") + b: int = Field(description="second number") + + actual = get_all_basemodel_annotations(CalculatorInput) + assert actual == {"a": int, "b": int} + + def test_tool_annotations_preserved() -> None: """Test that annotations are preserved when creating a tool."""