haystack/test/agents/test_tools_manager.py

101 lines
4.0 KiB
Python
Raw Normal View History

2023-05-17 15:19:09 +02:00
import unittest
from unittest import mock
import pytest
2023-05-17 15:19:09 +02:00
from haystack import Pipeline, Answer, Document
from haystack.agents.base import ToolsManager, Tool
@pytest.fixture
def tools_manager():
tools = [
Tool(name="ToolA", pipeline_or_node=mock.Mock(), description="Tool A Description"),
Tool(name="ToolB", pipeline_or_node=mock.Mock(), description="Tool B Description"),
]
return ToolsManager(tools=tools)
@pytest.mark.unit
def test_add_tool(tools_manager):
new_tool = Tool(name="ToolC", pipeline_or_node=mock.Mock(), description="Tool C Description")
tools_manager.add_tool(new_tool)
assert "ToolC" in tools_manager.tools
assert tools_manager.tools["ToolC"] == new_tool
@pytest.mark.unit
def test_get_tool_names(tools_manager):
assert tools_manager.get_tool_names() == "ToolA, ToolB"
@pytest.mark.unit
def test_get_tools(tools_manager):
tools = tools_manager.get_tools()
assert len(tools) == 2
assert tools[0].name == "ToolA"
assert tools[1].name == "ToolB"
@pytest.mark.unit
def test_get_tool_names_with_descriptions(tools_manager):
expected_output = "ToolA: Tool A Description\n" "ToolB: Tool B Description"
assert tools_manager.get_tool_names_with_descriptions() == expected_output
@pytest.mark.unit
def test_extract_tool_name_and_tool_input(tools_manager):
examples = [
"need to find out what city he was born.\nTool: Search\nTool Input: Where was Jeremy McKinnon born",
"need to find out what city he was born.\n\nTool: Search\n\nTool Input: Where was Jeremy McKinnon born",
"need to find out what city he was born. Tool: Search Tool Input: Where was Jeremy McKinnon born",
]
for example in examples:
tool_name, tool_input = tools_manager.extract_tool_name_and_tool_input(example)
assert tool_name == "Search" and tool_input == "Where was Jeremy McKinnon born"
negative_examples = [
"need to find out what city he was born.",
"Tool: Search",
"Tool Input: Where was Jeremy McKinnon born",
"need to find out what city he was born. Tool: Search",
"Tool Input: Where was Jeremy McKinnon born",
]
for example in negative_examples:
tool_name, tool_input = tools_manager.extract_tool_name_and_tool_input(example)
assert tool_name is None and tool_input is None
2023-05-17 15:19:09 +02:00
@pytest.mark.unit
def test_invalid_tool_creation():
with pytest.raises(ValueError, match="Invalid"):
Tool(name="Tool-A", pipeline_or_node=mock.Mock(), description="Tool A Description")
@pytest.mark.unit
def test_tool_invocation():
# by default for pipelines as tools we look for results key in the output
p = Pipeline()
tool = Tool(name="ToolA", pipeline_or_node=p, description="Tool A Description")
with unittest.mock.patch("haystack.pipelines.Pipeline.run", return_value={"results": "mock"}):
assert tool.run("input") == "mock"
# now fail if results key is not present
with unittest.mock.patch("haystack.pipelines.Pipeline.run", return_value={"no_results": "mock"}):
with pytest.raises(ValueError, match="Tool ToolA returned result"):
assert tool.run("input")
# now try tool with a correct output variable
tool = Tool(name="ToolA", pipeline_or_node=p, description="Tool A Description", output_variable="no_results")
with unittest.mock.patch("haystack.pipelines.Pipeline.run", return_value={"no_results": "mock_no_results"}):
assert tool.run("input") == "mock_no_results"
# try tool that internally returns an Answer object but we extract the string
tool = Tool(name="ToolA", pipeline_or_node=p, description="Tool A Description")
with unittest.mock.patch("haystack.pipelines.Pipeline.run", return_value=[Answer("mocked_answer")]):
assert tool.run("input") == "mocked_answer"
# same but for the document
with unittest.mock.patch("haystack.pipelines.Pipeline.run", return_value=[Document("mocked_document")]):
assert tool.run("input") == "mocked_document"