mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-04-26 12:52:39 +00:00
Compare commits
6 Commits
devin/1744
...
devin/1744
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84a16abb4a | ||
|
|
77ed11898b | ||
|
|
c73c795993 | ||
|
|
4bd7d6968a | ||
|
|
c9f47e6a37 | ||
|
|
5780c3147a |
@@ -60,7 +60,7 @@ def test():
|
||||
"current_year": str(datetime.now().year)
|
||||
}
|
||||
try:
|
||||
{{crew_name}}().crew().test(n_iterations=int(sys.argv[1]), openai_model_name=sys.argv[2], inputs=inputs)
|
||||
{{crew_name}}().crew().test(n_iterations=int(sys.argv[1]), eval_llm=sys.argv[2], inputs=inputs)
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"An error occurred while testing the crew: {e}")
|
||||
|
||||
@@ -244,9 +244,13 @@ def to_langchain(
|
||||
return [t.to_structured_tool() if isinstance(t, BaseTool) else t for t in tools]
|
||||
|
||||
|
||||
def tool(*args):
|
||||
def tool(*args, result_as_answer=False):
|
||||
"""
|
||||
Decorator to create a tool from a function.
|
||||
|
||||
Args:
|
||||
*args: Positional arguments, either the function to decorate or the tool name.
|
||||
result_as_answer: Flag to indicate if the tool result should be used as the final agent answer.
|
||||
"""
|
||||
|
||||
def _make_with_name(tool_name: str) -> Callable:
|
||||
@@ -272,6 +276,7 @@ def tool(*args):
|
||||
description=f.__doc__,
|
||||
func=f,
|
||||
args_schema=args_schema,
|
||||
result_as_answer=result_as_answer,
|
||||
)
|
||||
|
||||
return _make_tool
|
||||
|
||||
@@ -141,7 +141,8 @@ class ToolUsage:
|
||||
self._printer.print(content=f"\n\n{error}\n", color="red")
|
||||
return error
|
||||
|
||||
return f"{self._use(tool_string=tool_string, tool=tool, calling=calling)}"
|
||||
result = self._use(tool_string=tool_string, tool=tool, calling=calling)
|
||||
return f"{result}"
|
||||
|
||||
def _use(
|
||||
self,
|
||||
@@ -149,9 +150,9 @@ class ToolUsage:
|
||||
tool: CrewStructuredTool,
|
||||
calling: Union[ToolCalling, InstructorToolCalling],
|
||||
) -> str:
|
||||
if self._check_tool_repeated_usage(calling=calling): # type: ignore # _check_tool_repeated_usage of "ToolUsage" does not return a value (it only ever returns None)
|
||||
if self._check_tool_repeated_usage(calling=calling):
|
||||
try:
|
||||
result = self._i18n.errors("task_repeated_usage").format(
|
||||
repeated_result = self._i18n.errors("task_repeated_usage").format(
|
||||
tool_names=self.tools_names
|
||||
)
|
||||
self._telemetry.tool_repeated_usage(
|
||||
@@ -159,8 +160,8 @@ class ToolUsage:
|
||||
tool_name=tool.name,
|
||||
attempts=self._run_attempts,
|
||||
)
|
||||
result = self._format_result(result=result) # type: ignore # "_format_result" of "ToolUsage" does not return a value (it only ever returns None)
|
||||
return result # type: ignore # Fix the return type of this function
|
||||
repeated_result = self._format_result(result=repeated_result)
|
||||
return repeated_result
|
||||
|
||||
except Exception:
|
||||
if self.task:
|
||||
@@ -168,13 +169,15 @@ class ToolUsage:
|
||||
|
||||
started_at = time.time()
|
||||
from_cache = False
|
||||
result = None # type: ignore
|
||||
result: Optional[str] = None
|
||||
|
||||
if self.tools_handler and self.tools_handler.cache:
|
||||
result = self.tools_handler.cache.read(
|
||||
cache_result = self.tools_handler.cache.read(
|
||||
tool=calling.tool_name, input=calling.arguments
|
||||
) # type: ignore
|
||||
from_cache = result is not None
|
||||
)
|
||||
if cache_result is not None:
|
||||
result = cache_result
|
||||
from_cache = True
|
||||
|
||||
available_tool = next(
|
||||
(
|
||||
@@ -236,31 +239,32 @@ class ToolUsage:
|
||||
self._printer.print(
|
||||
content=f"\n\n{error_message}\n", color="red"
|
||||
)
|
||||
return error # type: ignore # No return value expected
|
||||
return error
|
||||
|
||||
if self.task:
|
||||
self.task.increment_tools_errors()
|
||||
return self.use(calling=calling, tool_string=tool_string) # type: ignore # No return value expected
|
||||
return self.use(calling=calling, tool_string=tool_string)
|
||||
|
||||
if self.tools_handler:
|
||||
should_cache = True
|
||||
if (
|
||||
if available_tool is not None and (
|
||||
hasattr(available_tool, "cache_function")
|
||||
and available_tool.cache_function # type: ignore # Item "None" of "Any | None" has no attribute "cache_function"
|
||||
and available_tool.cache_function
|
||||
):
|
||||
should_cache = available_tool.cache_function( # type: ignore # Item "None" of "Any | None" has no attribute "cache_function"
|
||||
should_cache = available_tool.cache_function(
|
||||
calling.arguments, result
|
||||
)
|
||||
|
||||
output_result = "" if result is None else result
|
||||
self.tools_handler.on_tool_use(
|
||||
calling=calling, output=result, should_cache=should_cache
|
||||
calling=calling, output=output_result, should_cache=should_cache
|
||||
)
|
||||
self._telemetry.tool_usage(
|
||||
llm=self.function_calling_llm,
|
||||
tool_name=tool.name,
|
||||
attempts=self._run_attempts,
|
||||
)
|
||||
result = self._format_result(result=result) # type: ignore # "_format_result" of "ToolUsage" does not return a value (it only ever returns None)
|
||||
result = self._format_result(result=result)
|
||||
data = {
|
||||
"result": result,
|
||||
"tool_name": tool.name,
|
||||
@@ -275,12 +279,13 @@ class ToolUsage:
|
||||
result=result,
|
||||
)
|
||||
|
||||
if (
|
||||
if available_tool is not None and (
|
||||
hasattr(available_tool, "result_as_answer")
|
||||
and available_tool.result_as_answer # type: ignore # Item "None" of "Any | None" has no attribute "cache_function"
|
||||
and available_tool.result_as_answer
|
||||
):
|
||||
result_as_answer = available_tool.result_as_answer # type: ignore # Item "None" of "Any | None" has no attribute "result_as_answer"
|
||||
data["result_as_answer"] = result_as_answer # type: ignore
|
||||
result_as_answer = available_tool.result_as_answer
|
||||
if isinstance(result_as_answer, bool):
|
||||
data["result_as_answer"] = result_as_answer
|
||||
|
||||
if self.agent and hasattr(self.agent, "tools_results"):
|
||||
self.agent.tools_results.append(data)
|
||||
@@ -448,7 +453,7 @@ class ToolUsage:
|
||||
self.task.increment_tools_errors()
|
||||
if self.agent and self.agent.verbose:
|
||||
self._printer.print(content=f"\n\n{e}\n", color="red")
|
||||
return ToolUsageErrorException( # type: ignore # Incompatible return value type (got "ToolUsageErrorException", expected "ToolCalling | InstructorToolCalling")
|
||||
return ToolUsageErrorException(
|
||||
f"{self._i18n.errors('tool_usage_error').format(error=e)}\nMoving on then. {self._i18n.slice('format').format(tool_names=self.tools_names)}"
|
||||
)
|
||||
return self._tool_calling(tool_string)
|
||||
|
||||
71
tests/test_code_interpreter_tool.py
Normal file
71
tests/test_code_interpreter_tool.py
Normal file
@@ -0,0 +1,71 @@
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from crewai import Agent, Crew, Task
|
||||
from crewai.agents.tools_handler import ToolsHandler
|
||||
from crewai.tasks.task_output import TaskOutput
|
||||
from crewai.tools import BaseTool
|
||||
from crewai.tools.structured_tool import CrewStructuredTool
|
||||
from crewai.tools.tool_calling import InstructorToolCalling, ToolCalling
|
||||
from crewai.tools.tool_usage import ToolUsage
|
||||
|
||||
|
||||
class TestCodeInterpreterTool(BaseTool):
|
||||
name: str = "Test Code Interpreter"
|
||||
description: str = "A test tool that simulates code execution."
|
||||
result_as_answer: bool = False
|
||||
execution_called: bool = False
|
||||
|
||||
def _run(self, code: str = "", libraries_used: List[str] = []) -> str:
|
||||
self.execution_called = True
|
||||
return f"Code executed: {code}"
|
||||
|
||||
|
||||
def test_direct_tool_execution():
|
||||
"""Test that the tool can be executed directly."""
|
||||
test_tool = TestCodeInterpreterTool()
|
||||
result = test_tool.run("print('Hello World')")
|
||||
assert "Code executed" in result
|
||||
assert test_tool.execution_called
|
||||
|
||||
|
||||
|
||||
|
||||
def test_tool_usage_return_types():
|
||||
"""Test that the ToolUsage methods return the correct types."""
|
||||
agent_mock = MagicMock()
|
||||
task_mock = MagicMock()
|
||||
task_mock.used_tools = 0
|
||||
tools_handler_mock = MagicMock()
|
||||
|
||||
tool_usage = ToolUsage(
|
||||
tools_handler=tools_handler_mock,
|
||||
tools=[],
|
||||
task=task_mock,
|
||||
function_calling_llm=None,
|
||||
agent=agent_mock,
|
||||
action=None
|
||||
)
|
||||
|
||||
i18n_mock = MagicMock()
|
||||
i18n_mock.slice.return_value.format.return_value = "Tool description"
|
||||
tool_usage._i18n = i18n_mock
|
||||
|
||||
result = "test result"
|
||||
formatted_result = tool_usage._format_result(result)
|
||||
assert formatted_result is not None, "_format_result should return the result"
|
||||
assert isinstance(formatted_result, str), "_format_result should return a string"
|
||||
|
||||
result = tool_usage._should_remember_format()
|
||||
assert isinstance(result, bool), "_should_remember_format should return a boolean"
|
||||
|
||||
result = "test result"
|
||||
remembered_result = tool_usage._remember_format(result)
|
||||
assert remembered_result is not None, "_remember_format should return the result"
|
||||
assert isinstance(remembered_result, str), "_remember_format should return a string"
|
||||
|
||||
tool_calling_mock = MagicMock()
|
||||
result = tool_usage._check_tool_repeated_usage(tool_calling_mock)
|
||||
assert isinstance(result, bool), "_check_tool_repeated_usage should return a boolean"
|
||||
@@ -100,3 +100,25 @@ def test_default_cache_function_is_true():
|
||||
my_tool = MyCustomTool()
|
||||
# Assert all the right attributes were defined
|
||||
assert my_tool.cache_function()
|
||||
|
||||
|
||||
def test_result_as_answer_in_tool_decorator():
|
||||
@tool("Tool with result as answer", result_as_answer=True)
|
||||
def my_tool_with_result_as_answer(question: str) -> str:
|
||||
"""This tool will return its result as the final answer."""
|
||||
return question
|
||||
|
||||
assert my_tool_with_result_as_answer.result_as_answer is True
|
||||
|
||||
converted_tool = my_tool_with_result_as_answer.to_structured_tool()
|
||||
assert converted_tool.result_as_answer is True
|
||||
|
||||
@tool("Tool with default result_as_answer")
|
||||
def my_tool_with_default(question: str) -> str:
|
||||
"""This tool uses the default result_as_answer value."""
|
||||
return question
|
||||
|
||||
assert my_tool_with_default.result_as_answer is False
|
||||
|
||||
converted_tool = my_tool_with_default.to_structured_tool()
|
||||
assert converted_tool.result_as_answer is False
|
||||
|
||||
Reference in New Issue
Block a user