diff --git a/camel/agents/chat_agent.py b/camel/agents/chat_agent.py index 2df0620f15..a0bd70076f 100644 --- a/camel/agents/chat_agent.py +++ b/camel/agents/chat_agent.py @@ -16,6 +16,7 @@ import json import logging import textwrap +import threading import uuid from collections import defaultdict from datetime import datetime @@ -151,6 +152,9 @@ class ChatAgent(BaseAgent): model calling at each step. (default: :obj:`False`) agent_id (str, optional): The ID of the agent. If not provided, a random UUID will be generated. (default: :obj:`None`) + stop_event (Optional[threading.Event], optional): Event to signal + termination of the agent's operation. When set, the agent will + terminate its execution. (default: :obj:`None`) """ def __init__( @@ -182,6 +186,7 @@ def __init__( scheduling_strategy: str = "round_robin", single_iteration: bool = False, agent_id: Optional[str] = None, + stop_event: Optional[threading.Event] = None, ) -> None: # Resolve model backends and set up model manager resolved_models = self._resolve_models(model) @@ -252,6 +257,7 @@ def __init__( self.terminated = False self.response_terminators = response_terminators or [] self.single_iteration = single_iteration + self.stop_event = stop_event def reset(self): r"""Resets the :obj:`ChatAgent` to its initial state.""" @@ -757,6 +763,13 @@ def step( self._get_full_tool_schemas(), ) + # Terminate Agent if stop_event is set + if self.stop_event and self.stop_event.is_set(): + # Use the _step_token_exceed to terminate the agent with reason + return self._step_token_exceed( + num_tokens, tool_call_records, "termination_triggered" + ) + if tool_call_requests := response.tool_call_requests: # Process all tool calls for tool_call_request in tool_call_requests: @@ -849,6 +862,13 @@ async def astep( self._get_full_tool_schemas(), ) + # Terminate Agent if stop_event is set + if self.stop_event and self.stop_event.is_set(): + # Use the _step_token_exceed to terminate the agent with reason + return self._step_token_exceed( + num_tokens, tool_call_records, "termination_triggered" + ) + if tool_call_requests := response.tool_call_requests: # Process all tool calls for tool_call_request in tool_call_requests: diff --git a/camel/responses/agent_responses.py b/camel/responses/agent_responses.py index 3fa960f0fa..0d7b1ec972 100644 --- a/camel/responses/agent_responses.py +++ b/camel/responses/agent_responses.py @@ -39,8 +39,5 @@ class ChatAgentResponse(BaseModel): @property def msg(self): if len(self.msgs) != 1: - raise RuntimeError( - "Property msg is only available " - "for a single message in msgs." - ) + return None return self.msgs[0] diff --git a/camel/societies/role_playing.py b/camel/societies/role_playing.py index be77e1f409..2c6c05cc45 100644 --- a/camel/societies/role_playing.py +++ b/camel/societies/role_playing.py @@ -12,6 +12,7 @@ # limitations under the License. # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. ========= import logging +import threading from typing import Dict, List, Optional, Sequence, Tuple, Union from camel.agents import ( @@ -77,6 +78,9 @@ class RolePlaying: task specify meta dict with. (default: :obj:`None`) output_language (str, optional): The language to be output by the agents. (default: :obj:`None`) + stop_event (Optional[threading.Event], optional): Event to signal + termination of the agent's operation. When set, the agent will + terminate its execution. (default: :obj:`None`) """ def __init__( @@ -101,6 +105,7 @@ def __init__( extend_sys_msg_meta_dicts: Optional[List[Dict]] = None, extend_task_specify_meta_dict: Optional[Dict] = None, output_language: Optional[str] = None, + stop_event: Optional[threading.Event] = None, ) -> None: if model is not None: logger.warning( @@ -156,6 +161,7 @@ def __init__( assistant_agent_kwargs=assistant_agent_kwargs, user_agent_kwargs=user_agent_kwargs, output_language=output_language, + stop_event=stop_event, ) self.critic: Optional[Union[CriticAgent, Human]] = None self.critic_sys_msg: Optional[BaseMessage] = None @@ -316,6 +322,7 @@ def _init_agents( assistant_agent_kwargs: Optional[Dict] = None, user_agent_kwargs: Optional[Dict] = None, output_language: Optional[str] = None, + stop_event: Optional[threading.Event] = None, ) -> None: r"""Initialize assistant and user agents with their system messages. @@ -330,6 +337,9 @@ def _init_agents( pass to the user agent. (default: :obj:`None`) output_language (str, optional): The language to be output by the agents. (default: :obj:`None`) + stop_event (Optional[threading.Event], optional): Event to signal + termination of the agent's operation. When set, the agent will + terminate its execution. (default: :obj:`None`) """ if self.model is not None: if assistant_agent_kwargs is None: @@ -344,6 +354,7 @@ def _init_agents( self.assistant_agent = ChatAgent( init_assistant_sys_msg, output_language=output_language, + stop_event=stop_event, **(assistant_agent_kwargs or {}), ) self.assistant_sys_msg = self.assistant_agent.system_message @@ -351,6 +362,7 @@ def _init_agents( self.user_agent = ChatAgent( init_user_sys_msg, output_language=output_language, + stop_event=stop_event, **(user_agent_kwargs or {}), ) self.user_sys_msg = self.user_agent.system_message