mirror of
https://github.com/hwchase17/langchain.git
synced 2025-09-18 08:03:36 +00:00
Add ToolException that a tool can throw. (#5050)
# Add ToolException that a tool can throw This is an optional exception that tool throws when execution error occurs. When this exception is thrown, the agent will not stop working,but will handle the exception according to the handle_tool_error variable of the tool,and the processing result will be returned to the agent as observation,and printed in pink on the console.It can be used like this: ```python from langchain.schema import ToolException from langchain import LLMMathChain, SerpAPIWrapper, OpenAI from langchain.agents import AgentType, initialize_agent from langchain.chat_models import ChatOpenAI from langchain.tools import BaseTool, StructuredTool, Tool, tool from langchain.chat_models import ChatOpenAI llm = ChatOpenAI(temperature=0) llm_math_chain = LLMMathChain(llm=llm, verbose=True) class Error_tool: def run(self, s: str): raise ToolException('The current search tool is not available.') def handle_tool_error(error) -> str: return "The following errors occurred during tool execution:"+str(error) search_tool1 = Error_tool() search_tool2 = SerpAPIWrapper() tools = [ Tool.from_function( func=search_tool1.run, name="Search_tool1", description="useful for when you need to answer questions about current events.You should give priority to using it.", handle_tool_error=handle_tool_error, ), Tool.from_function( func=search_tool2.run, name="Search_tool2", description="useful for when you need to answer questions about current events", return_direct=True, ) ] agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True, handle_tool_errors=handle_tool_error) agent.run("Who is Leo DiCaprio's girlfriend? What is her current age raised to the 0.43 power?") ```  ## Who can review? - @vowelparrot --------- Co-authored-by: Dev 2049 <dev.dev2049@gmail.com>
This commit is contained in:
@@ -13,7 +13,12 @@ from langchain.callbacks.manager import (
|
||||
AsyncCallbackManagerForToolRun,
|
||||
CallbackManagerForToolRun,
|
||||
)
|
||||
from langchain.tools.base import BaseTool, SchemaAnnotationError, StructuredTool
|
||||
from langchain.tools.base import (
|
||||
BaseTool,
|
||||
SchemaAnnotationError,
|
||||
StructuredTool,
|
||||
ToolException,
|
||||
)
|
||||
|
||||
|
||||
def test_unnamed_decorator() -> None:
|
||||
@@ -479,3 +484,75 @@ async def test_create_async_tool() -> None:
|
||||
assert test_tool.description == "test_description"
|
||||
assert test_tool.coroutine is not None
|
||||
assert await test_tool.arun("foo") == "foo"
|
||||
|
||||
|
||||
class _FakeExceptionTool(BaseTool):
|
||||
name = "exception"
|
||||
description = "an exception-throwing tool"
|
||||
exception: Exception = ToolException()
|
||||
|
||||
def _run(self) -> str:
|
||||
raise self.exception
|
||||
|
||||
async def _arun(self) -> str:
|
||||
raise self.exception
|
||||
|
||||
|
||||
def test_exception_handling_bool() -> None:
|
||||
_tool = _FakeExceptionTool(handle_tool_error=True)
|
||||
expected = "Tool execution error"
|
||||
actual = _tool.run({})
|
||||
assert expected == actual
|
||||
|
||||
|
||||
def test_exception_handling_str() -> None:
|
||||
expected = "foo bar"
|
||||
_tool = _FakeExceptionTool(handle_tool_error=expected)
|
||||
actual = _tool.run({})
|
||||
assert expected == actual
|
||||
|
||||
|
||||
def test_exception_handling_callable() -> None:
|
||||
expected = "foo bar"
|
||||
handling = lambda _: expected # noqa: E731
|
||||
_tool = _FakeExceptionTool(handle_tool_error=handling)
|
||||
actual = _tool.run({})
|
||||
assert expected == actual
|
||||
|
||||
|
||||
def test_exception_handling_non_tool_exception() -> None:
|
||||
_tool = _FakeExceptionTool(exception=ValueError())
|
||||
with pytest.raises(ValueError):
|
||||
_tool.run({})
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_exception_handling_bool() -> None:
|
||||
_tool = _FakeExceptionTool(handle_tool_error=True)
|
||||
expected = "Tool execution error"
|
||||
actual = await _tool.arun({})
|
||||
assert expected == actual
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_exception_handling_str() -> None:
|
||||
expected = "foo bar"
|
||||
_tool = _FakeExceptionTool(handle_tool_error=expected)
|
||||
actual = await _tool.arun({})
|
||||
assert expected == actual
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_exception_handling_callable() -> None:
|
||||
expected = "foo bar"
|
||||
handling = lambda _: expected # noqa: E731
|
||||
_tool = _FakeExceptionTool(handle_tool_error=handling)
|
||||
actual = await _tool.arun({})
|
||||
assert expected == actual
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_exception_handling_non_tool_exception() -> None:
|
||||
_tool = _FakeExceptionTool(exception=ValueError())
|
||||
with pytest.raises(ValueError):
|
||||
await _tool.arun({})
|
||||
|
Reference in New Issue
Block a user