From be09b519c4fd94a55d3fafbbabcf178b3cda4a80 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 14:03:40 +0000 Subject: [PATCH] fix: Resolve type-checker errors for mixed tool types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Union types to handle both BaseTool and dict in tool parameters - Update tool utility functions to handle mixed types safely - Filter raw tool definitions in agent adapters - Fix experimental evaluation metrics to handle mixed tool types - Maintain backward compatibility while adding hosted tools support Co-Authored-By: João --- src/crewai/agents/agent_adapters/base_agent_adapter.py | 6 +++--- .../agent_adapters/langgraph/langgraph_adapter.py | 10 ++++++---- .../agent_adapters/openai_agents/openai_adapter.py | 8 +++++--- .../experimental/evaluation/metrics/tools_metrics.py | 4 +++- src/crewai/utilities/tool_utils.py | 9 +++++---- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/crewai/agents/agent_adapters/base_agent_adapter.py b/src/crewai/agents/agent_adapters/base_agent_adapter.py index 6b8a151d6..795ba70af 100644 --- a/src/crewai/agents/agent_adapters/base_agent_adapter.py +++ b/src/crewai/agents/agent_adapters/base_agent_adapter.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Union from pydantic import PrivateAttr @@ -25,11 +25,11 @@ class BaseAgentAdapter(BaseAgent, ABC): self._agent_config = agent_config @abstractmethod - def configure_tools(self, tools: Optional[List[BaseTool]] = None) -> None: + def configure_tools(self, tools: Optional[List[Union[BaseTool, dict]]] = None) -> None: """Configure and adapt tools for the specific agent implementation. Args: - tools: Optional list of BaseTool instances to be configured + tools: Optional list of BaseTool instances and raw tool definitions to be configured """ pass diff --git a/src/crewai/agents/agent_adapters/langgraph/langgraph_adapter.py b/src/crewai/agents/agent_adapters/langgraph/langgraph_adapter.py index ea2e373d2..afc16cf2d 100644 --- a/src/crewai/agents/agent_adapters/langgraph/langgraph_adapter.py +++ b/src/crewai/agents/agent_adapters/langgraph/langgraph_adapter.py @@ -1,4 +1,4 @@ -from typing import Any, AsyncIterable, Dict, List, Optional +from typing import Any, AsyncIterable, Dict, List, Optional, Union from pydantic import Field, PrivateAttr @@ -202,11 +202,13 @@ class LangGraphAgentAdapter(BaseAgentAdapter): """Configure the LangGraph agent for execution.""" self.configure_tools(tools) - def configure_tools(self, tools: Optional[List[BaseTool]] = None) -> None: + def configure_tools(self, tools: Optional[List[Union[BaseTool, dict]]] = None) -> None: """Configure tools for the LangGraph agent.""" if tools: - all_tools = list(self.tools or []) + list(tools or []) - self._tool_adapter.configure_tools(all_tools) + base_tools = [tool for tool in tools if isinstance(tool, BaseTool)] + all_tools = list(self.tools or []) + list(base_tools or []) + if all_tools: + self._tool_adapter.configure_tools(all_tools) available_tools = self._tool_adapter.tools() self._graph.tools = available_tools diff --git a/src/crewai/agents/agent_adapters/openai_agents/openai_adapter.py b/src/crewai/agents/agent_adapters/openai_agents/openai_adapter.py index ac368c1a3..853392f79 100644 --- a/src/crewai/agents/agent_adapters/openai_agents/openai_adapter.py +++ b/src/crewai/agents/agent_adapters/openai_agents/openai_adapter.py @@ -1,4 +1,4 @@ -from typing import Any, List, Optional +from typing import Any, List, Optional, Union from pydantic import Field, PrivateAttr @@ -152,10 +152,12 @@ class OpenAIAgentAdapter(BaseAgentAdapter): self.agent_executor = Runner - def configure_tools(self, tools: Optional[List[BaseTool]] = None) -> None: + def configure_tools(self, tools: Optional[List[Union[BaseTool, dict]]] = None) -> None: """Configure tools for the OpenAI Assistant""" if tools: - self._tool_adapter.configure_tools(tools) + base_tools = [tool for tool in tools if isinstance(tool, BaseTool)] + if base_tools: + self._tool_adapter.configure_tools(base_tools) if self._tool_adapter.converted_tools: self._openai_agent.tools = self._tool_adapter.converted_tools diff --git a/src/crewai/experimental/evaluation/metrics/tools_metrics.py b/src/crewai/experimental/evaluation/metrics/tools_metrics.py index 9ed857aa1..cccf0c06b 100644 --- a/src/crewai/experimental/evaluation/metrics/tools_metrics.py +++ b/src/crewai/experimental/evaluation/metrics/tools_metrics.py @@ -43,7 +43,9 @@ class ToolSelectionEvaluator(BaseEvaluator): available_tools_info = "" if agent.tools: for tool in agent.tools: - available_tools_info += f"- {tool.name}: {tool.description}\n" + tool_name = tool.name if hasattr(tool, 'name') else tool.get('name', 'unknown') + tool_desc = tool.description if hasattr(tool, 'description') else tool.get('description', 'No description') + available_tools_info += f"- {tool_name}: {tool_desc}\n" else: available_tools_info = "No tools available" diff --git a/src/crewai/utilities/tool_utils.py b/src/crewai/utilities/tool_utils.py index eaf065477..58418e346 100644 --- a/src/crewai/utilities/tool_utils.py +++ b/src/crewai/utilities/tool_utils.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Union from crewai.agents.parser import AgentAction from crewai.security import Fingerprint @@ -10,7 +10,7 @@ from crewai.utilities.i18n import I18N def execute_tool_and_check_finality( agent_action: AgentAction, - tools: List[CrewStructuredTool], + tools: List[Union[CrewStructuredTool, dict]], i18n: I18N, agent_key: Optional[str] = None, agent_role: Optional[str] = None, @@ -37,7 +37,8 @@ def execute_tool_and_check_finality( ToolResult containing the execution result and whether it should be treated as a final answer """ try: - tool_name_to_tool_map = {tool.name: tool for tool in tools} + executable_tools = [tool for tool in tools if hasattr(tool, 'name') and hasattr(tool, 'result_as_answer')] + tool_name_to_tool_map = {tool.name: tool for tool in executable_tools} if agent_key and agent_role and agent: fingerprint_context = fingerprint_context or {} @@ -82,7 +83,7 @@ def execute_tool_and_check_finality( # Handle invalid tool name tool_result = i18n.errors("wrong_tool_name").format( tool=tool_calling.tool_name, - tools=", ".join([tool.name.casefold() for tool in tools]), + tools=", ".join([tool.name.casefold() for tool in executable_tools]), ) return ToolResult(tool_result, False)