mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-29 19:29:43 +02:00
Cherry-pick prebuilt (#5204)
fix: [prebuilt] Checks for pre-bound model with builtin tools (#5203)
This commit is contained in:
@@ -140,7 +140,9 @@ def _get_prompt_runnable(prompt: Optional[Prompt]) -> Runnable:
|
||||
return prompt_runnable
|
||||
|
||||
|
||||
def _should_bind_tools(model: LanguageModelLike, tools: Sequence[BaseTool]) -> bool:
|
||||
def _should_bind_tools(
|
||||
model: LanguageModelLike, tools: Sequence[BaseTool], num_builtin: int = 0
|
||||
) -> bool:
|
||||
if isinstance(model, RunnableSequence):
|
||||
model = next(
|
||||
(
|
||||
@@ -158,9 +160,10 @@ def _should_bind_tools(model: LanguageModelLike, tools: Sequence[BaseTool]) -> b
|
||||
return True
|
||||
|
||||
bound_tools = model.kwargs["tools"]
|
||||
if len(tools) != len(bound_tools):
|
||||
if len(tools) != len(bound_tools) - num_builtin:
|
||||
raise ValueError(
|
||||
"Number of tools in the model.bind_tools() and tools passed to create_react_agent must match"
|
||||
f" Got {len(tools)} tools, expected {len(bound_tools) - num_builtin}"
|
||||
)
|
||||
|
||||
tool_names = set(tool.name for tool in tools)
|
||||
@@ -444,9 +447,8 @@ def create_react_agent(
|
||||
tool_calling_enabled = len(tool_classes) > 0
|
||||
|
||||
if (
|
||||
_should_bind_tools(model, tool_classes)
|
||||
_should_bind_tools(model, tool_classes, num_builtin=len(llm_builtin_tools))
|
||||
and len(tool_classes) > 0
|
||||
or (len(llm_builtin_tools) > 0)
|
||||
):
|
||||
model = cast(BaseChatModel, model).bind_tools(tool_classes + llm_builtin_tools) # type: ignore[operator]
|
||||
|
||||
|
||||
@@ -73,9 +73,12 @@ class FakeToolCallingModel(BaseChatModel):
|
||||
|
||||
tool_dicts = []
|
||||
for tool in tools:
|
||||
if isinstance(tool, dict):
|
||||
tool_dicts.append(tool)
|
||||
continue
|
||||
if not isinstance(tool, BaseTool):
|
||||
raise TypeError(
|
||||
"Only BaseTool is supported by FakeToolCallingModel.bind_tools"
|
||||
"Only BaseTool and dict is supported by FakeToolCallingModel.bind_tools"
|
||||
)
|
||||
|
||||
# NOTE: this is a simplified tool spec for testing purposes only
|
||||
|
||||
@@ -275,7 +275,8 @@ async def test_prompt_with_store_async():
|
||||
|
||||
@pytest.mark.parametrize("tool_style", ["openai", "anthropic"])
|
||||
@pytest.mark.parametrize("version", REACT_TOOL_CALL_VERSIONS)
|
||||
def test_model_with_tools(tool_style: str, version: str):
|
||||
@pytest.mark.parametrize("include_builtin", [True, False])
|
||||
def test_model_with_tools(tool_style: str, version: str, include_builtin: bool):
|
||||
model = FakeToolCallingModel(tool_style=tool_style)
|
||||
|
||||
@dec_tool
|
||||
@@ -288,10 +289,27 @@ def test_model_with_tools(tool_style: str, version: str):
|
||||
"""Tool 2 docstring."""
|
||||
return f"Tool 2: {some_val}"
|
||||
|
||||
tools = [tool1, tool2]
|
||||
if include_builtin:
|
||||
tools.append(
|
||||
{
|
||||
"type": "mcp",
|
||||
"server_label": "atest_sever",
|
||||
"server_url": "https://some.mcp.somewhere.com/sse",
|
||||
"headers": {"foo": "bar"},
|
||||
"allowed_tools": [
|
||||
"mcp_tool_1",
|
||||
"set_active_account",
|
||||
"get_url_markdown",
|
||||
"get_url_screenshot",
|
||||
],
|
||||
"require_approval": "never",
|
||||
}
|
||||
)
|
||||
# check valid agent constructor
|
||||
agent = create_react_agent(
|
||||
model.bind_tools([tool1, tool2]),
|
||||
[tool1, tool2],
|
||||
model.bind_tools(tools),
|
||||
tools,
|
||||
version=version,
|
||||
)
|
||||
result = agent.nodes["tools"].invoke(
|
||||
|
||||
Reference in New Issue
Block a user