mirror of
https://github.com/langchain-ai/langchain.git
synced 2026-10-05 09:25:14 +03:00
use _get_type_hints
This commit is contained in:
1 parent
39b4e0f9a8
commit
bbb105dfe1
2 files changed
+71
-2
No files matched your search
@@ -28,6 +28,7 @@ from langchain_core.tools.base import (
|
||||
ArgsSchema,
|
||||
BaseTool,
|
||||
_get_runnable_config_param,
|
||||
_get_type_hints,
|
||||
_is_injected_arg_type,
|
||||
create_schema_from_function,
|
||||
)
|
||||
@@ -256,10 +257,19 @@ class StructuredTool(BaseTool):
|
||||
fn = self.func or self.coroutine
|
||||
if fn is None:
|
||||
return _EMPTY_SET
|
||||
# Resolve annotations via `_get_type_hints` (rather than reading raw
|
||||
# `signature` annotations) so postponed annotations -- e.g. from
|
||||
# `from __future__ import annotations` or quoted forward references --
|
||||
# are recognized. `include_extras=True` preserves `Annotated` metadata
|
||||
# so `InjectedToolArg` markers survive resolution. Fall back to the raw
|
||||
# per-parameter annotation when a hint can't be resolved. Mirrors
|
||||
# `BaseTool._injected_args_keys` in `base.py`.
|
||||
params = signature(fn).parameters
|
||||
hints = _get_type_hints(fn, include_extras=True) or {}
|
||||
return frozenset(
|
||||
k
|
||||
for k, v in signature(fn).parameters.items()
|
||||
if _is_injected_arg_type(v.annotation)
|
||||
for k, v in params.items()
|
||||
if _is_injected_arg_type(hints.get(k, v.annotation))
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -3781,6 +3781,65 @@ def test_base_tool_subclass_injects_postponed_annotated_arg() -> None:
|
||||
assert captured["injected"] == "value"
|
||||
|
||||
|
||||
def test_structured_tool_injects_postponed_annotation_runtime() -> None:
|
||||
"""`StructuredTool` must resolve postponed / forward-ref injected args too.
|
||||
|
||||
`StructuredTool._injected_args_keys` inspects the wrapped `func`/`coroutine`
|
||||
instead of `_run`, so it needs the same hint resolution as
|
||||
`BaseTool._injected_args_keys`. A quoted forward reference exposes a raw
|
||||
string annotation at `signature()` time; without resolution the injected
|
||||
arg is omitted from `_injected_args_keys` and the call raises `TypeError`.
|
||||
Fixes #39568.
|
||||
"""
|
||||
|
||||
class MultiplyInput(BaseModel):
|
||||
a: int
|
||||
b: int
|
||||
|
||||
captured: dict[str, Any] = {}
|
||||
|
||||
# Quoted forward reference -> raw string annotation "_CustomRuntime".
|
||||
@tool(args_schema=MultiplyInput)
|
||||
def multiply(a: int, b: int, runtime: "_CustomRuntime") -> int:
|
||||
"""Multiply two numbers."""
|
||||
captured["runtime"] = runtime
|
||||
return a * b
|
||||
|
||||
assert "runtime" in multiply._injected_args_keys
|
||||
|
||||
runtime = _CustomRuntime(data={"scale": 10})
|
||||
result = multiply.invoke({"a": 2, "b": 3, "runtime": runtime})
|
||||
assert result == 6
|
||||
assert captured["runtime"] is runtime
|
||||
|
||||
|
||||
def test_structured_tool_injects_postponed_annotated_arg() -> None:
|
||||
"""`StructuredTool` must resolve postponed `Annotated[..., InjectedToolArg]`.
|
||||
|
||||
`include_extras=True` when resolving hints preserves the `InjectedToolArg`
|
||||
metadata, so annotated injected args on the wrapped function are still
|
||||
detected. Fixes #39568.
|
||||
"""
|
||||
|
||||
class QueryInput(BaseModel):
|
||||
query: str
|
||||
|
||||
captured: dict[str, Any] = {}
|
||||
|
||||
# Quoted forward reference to a postponed `Annotated` injected arg.
|
||||
@tool(args_schema=QueryInput)
|
||||
def echo(query: str, injected: "Annotated[str, InjectedToolArg]") -> str:
|
||||
"""Echo the query."""
|
||||
captured["injected"] = injected
|
||||
return query
|
||||
|
||||
assert "injected" in echo._injected_args_keys
|
||||
|
||||
result = echo.invoke({"query": "hi", "injected": "value"})
|
||||
assert result == "hi"
|
||||
assert captured["injected"] == "value"
|
||||
|
||||
|
||||
class CallbackHandlerWithToolCallIdCapture(FakeCallbackHandler):
|
||||
"""Callback handler that captures `tool_call_id` passed to `on_tool_start`.
|
||||
|
||||
|
||||
Reference in new issue
Block a user