Spaces:
Build error
Build error
File size: 6,267 Bytes
51ff9e5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from openhands.controller.state.state import State
from openhands.core.config import AgentConfig
from openhands.events.action import Action
from openhands.events.action.message import SystemMessageAction
from openhands.utils.prompt import PromptManager
from litellm import ChatCompletionToolParam
from openhands.core.exceptions import (
AgentAlreadyRegisteredError,
AgentNotRegisteredError,
)
from openhands.core.logger import openhands_logger as logger
from openhands.events.event import EventSource
from openhands.llm.llm import LLM
from openhands.runtime.plugins import PluginRequirement
class Agent(ABC):
DEPRECATED = False
"""
This abstract base class is an general interface for an agent dedicated to
executing a specific instruction and allowing human interaction with the
agent during execution.
It tracks the execution status and maintains a history of interactions.
"""
_registry: dict[str, type['Agent']] = {}
sandbox_plugins: list[PluginRequirement] = []
def __init__(
self,
llm: LLM,
config: 'AgentConfig',
):
self.llm = llm
self.config = config
self._complete = False
self._prompt_manager: 'PromptManager' | None = None
self.mcp_tools: dict[str, ChatCompletionToolParam] = {}
self.tools: list = []
@property
def prompt_manager(self) -> 'PromptManager':
if self._prompt_manager is None:
raise ValueError(f'Prompt manager not initialized for agent {self.name}')
return self._prompt_manager
def get_system_message(self) -> 'SystemMessageAction | None':
"""
Returns a SystemMessageAction containing the system message and tools.
This will be added to the event stream as the first message.
Returns:
SystemMessageAction: The system message action with content and tools
None: If there was an error generating the system message
"""
# Import here to avoid circular imports
from openhands.events.action.message import SystemMessageAction
try:
if not self.prompt_manager:
logger.warning(
f'[{self.name}] Prompt manager not initialized before getting system message'
)
return None
system_message = self.prompt_manager.get_system_message()
# Get tools if available
tools = getattr(self, 'tools', None)
system_message_action = SystemMessageAction(
content=system_message, tools=tools, agent_class=self.name
)
# Set the source attribute
system_message_action._source = EventSource.AGENT # type: ignore
return system_message_action
except Exception as e:
logger.warning(f'[{self.name}] Failed to generate system message: {e}')
return None
@property
def complete(self) -> bool:
"""Indicates whether the current instruction execution is complete.
Returns:
- complete (bool): True if execution is complete; False otherwise.
"""
return self._complete
@abstractmethod
def step(self, state: 'State') -> 'Action':
"""Starts the execution of the assigned instruction. This method should
be implemented by subclasses to define the specific execution logic.
"""
pass
def reset(self) -> None:
"""Resets the agent's execution status and clears the history. This method can be used
to prepare the agent for restarting the instruction or cleaning up before destruction.
"""
# TODO clear history
self._complete = False
if self.llm:
self.llm.reset()
@property
def name(self) -> str:
return self.__class__.__name__
@classmethod
def register(cls, name: str, agent_cls: type['Agent']) -> None:
"""Registers an agent class in the registry.
Parameters:
- name (str): The name to register the class under.
- agent_cls (Type['Agent']): The class to register.
Raises:
- AgentAlreadyRegisteredError: If name already registered
"""
if name in cls._registry:
raise AgentAlreadyRegisteredError(name)
cls._registry[name] = agent_cls
@classmethod
def get_cls(cls, name: str) -> type['Agent']:
"""Retrieves an agent class from the registry.
Parameters:
- name (str): The name of the class to retrieve
Returns:
- agent_cls (Type['Agent']): The class registered under the specified name.
Raises:
- AgentNotRegisteredError: If name not registered
"""
if name not in cls._registry:
raise AgentNotRegisteredError(name)
return cls._registry[name]
@classmethod
def list_agents(cls) -> list[str]:
"""Retrieves the list of all agent names from the registry.
Raises:
- AgentNotRegisteredError: If no agent is registered
"""
if not bool(cls._registry):
raise AgentNotRegisteredError()
return list(cls._registry.keys())
def set_mcp_tools(self, mcp_tools: list[dict]) -> None:
"""Sets the list of MCP tools for the agent.
Args:
- mcp_tools (list[dict]): The list of MCP tools.
"""
logger.info(
f'Setting {len(mcp_tools)} MCP tools for agent {self.name}: {[tool["function"]["name"] for tool in mcp_tools]}'
)
for tool in mcp_tools:
_tool = ChatCompletionToolParam(**tool)
if _tool['function']['name'] in self.mcp_tools:
logger.warning(
f'Tool {_tool["function"]["name"]} already exists, skipping'
)
continue
self.mcp_tools[_tool['function']['name']] = _tool
self.tools.append(_tool)
logger.info(
f'Tools updated for agent {self.name}, total {len(self.tools)}: {[tool["function"]["name"] for tool in self.tools]}'
)
|