mirror of
https://github.com/hwchase17/langchain.git
synced 2026-07-11 09:26:29 +00:00
feat(core): wire subagent_name through @tool decorator
@tool now accepts subagent_name= and passes it to the underlying tool object. Tested with both static-string and callable values.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import inspect
|
||||
from collections.abc import Callable
|
||||
from typing import Any, Literal, cast, get_type_hints, overload
|
||||
from typing import TYPE_CHECKING, Any, Literal, cast, get_type_hints, overload
|
||||
|
||||
from pydantic import BaseModel, Field, create_model
|
||||
|
||||
@@ -12,6 +12,9 @@ from langchain_core.tools.base import ArgsSchema, BaseTool
|
||||
from langchain_core.tools.simple import Tool
|
||||
from langchain_core.tools.structured import StructuredTool
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langchain_core.messages.tool import ToolCall
|
||||
|
||||
|
||||
@overload
|
||||
def tool(
|
||||
@@ -24,6 +27,7 @@ def tool(
|
||||
parse_docstring: bool = False,
|
||||
error_on_invalid_docstring: bool = True,
|
||||
extras: dict[str, Any] | None = None,
|
||||
subagent_name: str | Callable[["ToolCall"], str] | None = None,
|
||||
) -> Callable[[Callable | Runnable], BaseTool]: ...
|
||||
|
||||
|
||||
@@ -40,6 +44,7 @@ def tool(
|
||||
parse_docstring: bool = False,
|
||||
error_on_invalid_docstring: bool = True,
|
||||
extras: dict[str, Any] | None = None,
|
||||
subagent_name: str | Callable[["ToolCall"], str] | None = None,
|
||||
) -> BaseTool: ...
|
||||
|
||||
|
||||
@@ -55,6 +60,7 @@ def tool(
|
||||
parse_docstring: bool = False,
|
||||
error_on_invalid_docstring: bool = True,
|
||||
extras: dict[str, Any] | None = None,
|
||||
subagent_name: str | Callable[["ToolCall"], str] | None = None,
|
||||
) -> BaseTool: ...
|
||||
|
||||
|
||||
@@ -70,6 +76,7 @@ def tool(
|
||||
parse_docstring: bool = False,
|
||||
error_on_invalid_docstring: bool = True,
|
||||
extras: dict[str, Any] | None = None,
|
||||
subagent_name: str | Callable[["ToolCall"], str] | None = None,
|
||||
) -> Callable[[Callable | Runnable], BaseTool]: ...
|
||||
|
||||
|
||||
@@ -85,6 +92,7 @@ def tool(
|
||||
parse_docstring: bool = False,
|
||||
error_on_invalid_docstring: bool = True,
|
||||
extras: dict[str, Any] | None = None,
|
||||
subagent_name: str | Callable[["ToolCall"], str] | None = None,
|
||||
) -> BaseTool | Callable[[Callable | Runnable], BaseTool]:
|
||||
"""Convert Python functions and `Runnables` to LangChain tools.
|
||||
|
||||
@@ -150,6 +158,9 @@ def tool(
|
||||
|
||||
For example, Anthropic-specific fields like `cache_control`,
|
||||
`defer_loading`, or `input_examples`.
|
||||
subagent_name: Optional name of the subagent that should handle this tool call
|
||||
(a static string), or a callable that resolves the subagent name from the
|
||||
parsed `ToolCall` at dispatch time.
|
||||
|
||||
Raises:
|
||||
ValueError: If too many positional arguments are provided (e.g. violating the
|
||||
@@ -313,6 +324,7 @@ def tool(
|
||||
parse_docstring=parse_docstring,
|
||||
error_on_invalid_docstring=error_on_invalid_docstring,
|
||||
extras=extras,
|
||||
subagent_name=subagent_name,
|
||||
)
|
||||
# If someone doesn't want a schema applied, we must treat it as
|
||||
# a simple string->string function
|
||||
@@ -322,7 +334,7 @@ def tool(
|
||||
"description not provided and infer_schema is False."
|
||||
)
|
||||
raise ValueError(msg)
|
||||
return Tool(
|
||||
result = Tool(
|
||||
name=tool_name,
|
||||
func=func,
|
||||
description=f"{tool_name} tool",
|
||||
@@ -331,6 +343,8 @@ def tool(
|
||||
response_format=response_format,
|
||||
extras=extras,
|
||||
)
|
||||
result.subagent_name = subagent_name
|
||||
return result
|
||||
|
||||
return _tool_factory
|
||||
|
||||
|
||||
@@ -3780,3 +3780,36 @@ def test_basetool_subagent_name_accepts_callable() -> None:
|
||||
}
|
||||
assert callable(tool.subagent_name)
|
||||
assert tool.subagent_name(fake_call) == "researcher"
|
||||
|
||||
|
||||
# --- @tool decorator subagent_name passthrough tests ---
|
||||
|
||||
|
||||
def test_tool_decorator_passes_subagent_name_static() -> None:
|
||||
@tool("call_weather", subagent_name="weather_agent")
|
||||
def call_weather(city: str) -> str:
|
||||
"""Call the weather agent."""
|
||||
return f"weather in {city}"
|
||||
|
||||
assert call_weather.subagent_name == "weather_agent"
|
||||
|
||||
|
||||
def test_tool_decorator_passes_subagent_name_callable() -> None:
|
||||
def resolver(call: ToolCall) -> str:
|
||||
return str(call["args"]["target"])
|
||||
|
||||
@tool("dispatch", subagent_name=resolver)
|
||||
def dispatch(target: str, query: str) -> str:
|
||||
"""Dispatch to a target."""
|
||||
return f"dispatched to {target}: {query}"
|
||||
|
||||
assert dispatch.subagent_name is resolver
|
||||
|
||||
|
||||
def test_tool_decorator_subagent_name_defaults_to_none() -> None:
|
||||
@tool("plain")
|
||||
def plain(x: int) -> str:
|
||||
"""A tool with no subagent_name declared."""
|
||||
return str(x)
|
||||
|
||||
assert plain.subagent_name is None
|
||||
|
||||
Reference in New Issue
Block a user