feat: implement BaseAgentAdapter for agent integration

- Introduced BaseAgentAdapter as an abstract base class for agent adapters in CrewAI.
- Defined common interface and methods for configuring tools and structured output.
- Updated OpenAIAgentAdapter to inherit from BaseAgentAdapter, enhancing its structure and functionality.
This commit is contained in:
lorenzejay
2025-04-09 13:27:05 -07:00
parent c6ba1fd492
commit c9508821fa
2 changed files with 89 additions and 15 deletions

View File

@@ -0,0 +1,40 @@
from abc import ABC, abstractmethod
from typing import Any, List, Optional
from pydantic import BaseModel
from crewai.agent import BaseAgent
from crewai.tools import BaseTool
class BaseAgentAdapter(BaseAgent, ABC):
"""Base class for all agent adapters in CrewAI.
This abstract class defines the common interface and functionality that all
agent adapters must implement. It extends BaseAgent to maintain compatibility
with the CrewAI framework while adding adapter-specific requirements.
"""
adapted_structured_output: bool = False
model_config = {"arbitrary_types_allowed": True}
def __init__(self, **kwargs: Any):
super().__init__(**kwargs)
@abstractmethod
def configure_tools(self, tools: Optional[List[BaseTool]] = None) -> None:
"""Configure and adapt tools for the specific agent implementation.
Args:
tools: Optional list of BaseTool instances to be configured
"""
pass
def configure_structured_output(self, structured_output: Any) -> None:
"""Configure the structured output for the specific agent implementation.
Args:
structured_output: The structured output to be configured
"""
pass

View File

@@ -2,11 +2,11 @@ from typing import Any, List, Optional
from agents import Agent as OpenAIAgent from agents import Agent as OpenAIAgent
from agents import Runner, Tool, enable_verbose_stdout_logging from agents import Runner, Tool, enable_verbose_stdout_logging
from pydantic import BaseModel, Field, PrivateAttr from pydantic import Field, PrivateAttr
from crewai.agent import BaseAgent from crewai.agents.agent_adapters.base_agent_adapter import BaseAgentAdapter
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.tools import BaseTool from crewai.tools import BaseTool
from crewai.tools.agent_tools.agent_tools import AgentTools from crewai.tools.agent_tools.agent_tools import AgentTools
from crewai.utilities import Logger from crewai.utilities import Logger
from crewai.utilities.events import crewai_event_bus from crewai.utilities.events import crewai_event_bus
@@ -14,10 +14,11 @@ from crewai.utilities.events.agent_events import (
AgentExecutionErrorEvent, AgentExecutionErrorEvent,
AgentExecutionStartedEvent, AgentExecutionStartedEvent,
) )
from .openai_agent_tool_adapter import OpenAIAgentToolAdapter from .openai_agent_tool_adapter import OpenAIAgentToolAdapter
class OpenAIAgentAdapter(BaseAgent, BaseModel): class OpenAIAgentAdapter(BaseAgentAdapter):
"""Adapter for OpenAI Assistants""" """Adapter for OpenAI Assistants"""
model_config = {"arbitrary_types_allowed": True} model_config = {"arbitrary_types_allowed": True}
@@ -47,6 +48,7 @@ class OpenAIAgentAdapter(BaseAgent, BaseModel):
self._openai_agent.model = model self._openai_agent.model = model
self.tools = tools self.tools = tools
self._tool_adapter = OpenAIAgentToolAdapter(tools=tools) self._tool_adapter = OpenAIAgentToolAdapter(tools=tools)
self.llm = model
def execute_task( def execute_task(
self, self,
@@ -77,7 +79,7 @@ class OpenAIAgentAdapter(BaseAgent, BaseModel):
) )
# This is pretty much the agent_executor logic: # This is pretty much the agent_executor logic:
result = self.agent_executor.run_sync(self._openai_agent, task_prompt) result = self.agent_executor.run_sync(self._openai_agent, task_prompt)
return result.final_output return self.handle_execution_result(result)
except Exception as e: except Exception as e:
self._logger.log("error", f"Error executing OpenAI task: {str(e)}") self._logger.log("error", f"Error executing OpenAI task: {str(e)}")
@@ -100,18 +102,20 @@ class OpenAIAgentAdapter(BaseAgent, BaseModel):
all_tools = list(self.tools or []) + list(tools or []) all_tools = list(self.tools or []) + list(tools or [])
if all_tools: if all_tools:
self._tool_adapter.configure_tools(all_tools) self.configure_tools(all_tools)
if self._tool_adapter.converted_tools:
self._openai_agent.tools = self._tool_adapter.converted_tools
self.agent_executor = Runner self.agent_executor = Runner
def _prepare_task_input(self, task: Any, context: Optional[str]) -> str: def configure_tools(self, tools: Optional[List[BaseTool]] = None) -> None:
"""Prepare the task input with context if available""" """Configure tools for the OpenAI Assistant"""
task_input = task.description if hasattr(task, "description") else str(task) if tools:
if context: self._tool_adapter.configure_tools(tools)
task_input = f"Context:\n{context}\n\nTask:\n{task_input}" if self._tool_adapter.converted_tools:
return task_input self._openai_agent.tools = self._tool_adapter.converted_tools
def handle_execution_result(self, result: Any) -> str:
"""Process OpenAI Assistant execution result"""
return result.final_output
def get_delegation_tools(self, agents: List[BaseAgent]) -> List[BaseTool]: def get_delegation_tools(self, agents: List[BaseAgent]) -> List[BaseTool]:
"""Implement delegation tools support""" """Implement delegation tools support"""
@@ -129,4 +133,34 @@ class OpenAIAgentAdapter(BaseAgent, BaseModel):
def _parse_tools(self, tools: List[BaseTool]) -> List[BaseTool]: def _parse_tools(self, tools: List[BaseTool]) -> List[BaseTool]:
"""Parse and validate tools""" """Parse and validate tools"""
return tools tools_list = []
try:
# tentatively try to import from crewai_tools import BaseTool as CrewAITool
from crewai.tools import BaseTool as CrewAITool
for tool in tools:
if isinstance(tool, CrewAITool):
tools_list.append(tool.to_structured_tool())
else:
tools_list.append(tool)
except ModuleNotFoundError:
tools_list = []
for tool in tools:
tools_list.append(tool)
return tools_list
def configure_structured_output(self, task) -> None:
"""Configure the structured output for the specific agent implementation.
Args:
structured_output: The structured output to be configured
"""
if task.output_json or task.output_pydantic:
# Generate the schema based on the output format
if task.output_json:
# schema = json.dumps(task.output_json, indent=2)
self._openai_agent.output_type = task.output_json
elif task.output_pydantic:
self._openai_agent.output_type = task.output_pydantic