mirror of
https://github.com/hwchase17/langchain.git
synced 2026-06-09 10:17:00 +00:00
* use `override` instead of directly patching things on `ModelRequest` * rely on `ToolNode` for execution of tools related to said middleware, using `wrap_model_call` to inject the relevant claude tool specs + allowing tool node to forward them along to corresponding langchain tool implementations * making the same change for the native shell tool middleware * allowing shell tool middleware to specify a name for the shell tool (negative diff then for claude bash middleware) long term I think the solution might be to attach metadata to a tool to map the provider spec to a langchain implementation, which we could also take some lessons from on the MCP front.
62 lines
1.8 KiB
Python
62 lines
1.8 KiB
Python
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
|
|
pytest.importorskip(
|
|
"anthropic", reason="Anthropic SDK is required for Claude middleware tests"
|
|
)
|
|
|
|
from langchain_anthropic.middleware.bash import ClaudeBashToolMiddleware
|
|
|
|
|
|
def test_creates_bash_tool(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""Test that ClaudeBashToolMiddleware creates a tool named 'bash'."""
|
|
middleware = ClaudeBashToolMiddleware()
|
|
|
|
# Should have exactly one tool registered (from parent)
|
|
assert len(middleware.tools) == 1
|
|
|
|
# Tool is named "bash" (via tool_name parameter)
|
|
bash_tool = middleware.tools[0]
|
|
assert bash_tool.name == "bash"
|
|
|
|
|
|
def test_replaces_tool_with_claude_descriptor() -> None:
|
|
"""Test wrap_model_call replaces bash tool with Claude's bash descriptor."""
|
|
from langchain.agents.middleware.types import ModelRequest
|
|
|
|
middleware = ClaudeBashToolMiddleware()
|
|
|
|
# Create a mock request with the bash tool (inherited from parent)
|
|
bash_tool = middleware.tools[0]
|
|
request = ModelRequest(
|
|
model=MagicMock(),
|
|
system_prompt=None,
|
|
messages=[],
|
|
tool_choice=None,
|
|
tools=[bash_tool],
|
|
response_format=None,
|
|
state={"messages": []},
|
|
runtime=MagicMock(),
|
|
)
|
|
|
|
# Mock handler that captures the modified request
|
|
captured_request = None
|
|
|
|
def handler(req: ModelRequest) -> MagicMock:
|
|
nonlocal captured_request
|
|
captured_request = req
|
|
return MagicMock()
|
|
|
|
middleware.wrap_model_call(request, handler)
|
|
|
|
# The bash tool should be replaced with Claude's native bash descriptor
|
|
assert captured_request is not None
|
|
assert len(captured_request.tools) == 1
|
|
assert captured_request.tools[0] == {
|
|
"type": "bash_20250124",
|
|
"name": "bash",
|
|
}
|