From 466c851743db8131a7206d9c98d4857180414c13 Mon Sep 17 00:00:00 2001 From: Rajan Date: Thu, 1 Aug 2024 20:36:04 -0400 Subject: [PATCH] [CAP] Added a factory for runtime (#3216) * Added Runtime Factory to support multiple implementations * Rename to ComponentEnsemble to ZMQRuntime * rename zmq_runtime * rename zmq_runtime * pre-commit fixes * pre-commit fix * pre-commit fixes and default runtime * pre-commit fixes * Rename constants * Rename Constants --------- Co-authored-by: Li Jiang --- .../apps/cap/py/autogencap/DirectorySvc.py | 2 +- .../apps/cap/py/autogencap/actor_runtime.py | 36 ++++++++++++++ .../cap/py/autogencap/ag_adapter/AG2CAP.py | 6 +-- .../cap/py/autogencap/ag_adapter/AGActor.py | 2 +- .../cap/py/autogencap/ag_adapter/CAP2AG.py | 8 ++-- .../py/autogencap/ag_adapter/CAPGroupChat.py | 7 +-- .../ag_adapter/CAPGroupChatManager.py | 7 +-- .../autogencap/{Constants.py => constants.py} | 1 + .../apps/cap/py/autogencap/runtime_factory.py | 47 +++++++++++++++++++ .../{ComponentEnsemble.py => zmq_runtime.py} | 5 +- samples/apps/cap/py/demo/AppAgents.py | 7 +-- .../apps/cap/py/demo/CAPAutGenGroupDemo.py | 4 +- .../apps/cap/py/demo/CAPAutoGenPairDemo.py | 4 +- samples/apps/cap/py/demo/ComplexActorDemo.py | 4 +- samples/apps/cap/py/demo/SimpleActorDemo.py | 12 ++--- samples/apps/cap/py/demo/list_agents.py | 4 +- samples/apps/cap/py/demo/single_threaded.py | 4 +- .../apps/cap/py/demo/standalone/UserProxy.py | 4 +- .../apps/cap/py/demo/standalone/assistant.py | 4 +- .../apps/cap/py/demo/standalone/user_proxy.py | 4 +- 20 files changed, 130 insertions(+), 42 deletions(-) create mode 100644 samples/apps/cap/py/autogencap/actor_runtime.py rename samples/apps/cap/py/autogencap/{Constants.py => constants.py} (76%) create mode 100644 samples/apps/cap/py/autogencap/runtime_factory.py rename samples/apps/cap/py/autogencap/{ComponentEnsemble.py => zmq_runtime.py} (96%) diff --git a/samples/apps/cap/py/autogencap/DirectorySvc.py b/samples/apps/cap/py/autogencap/DirectorySvc.py index acb3b6223df5..6057558c0b24 100644 --- a/samples/apps/cap/py/autogencap/DirectorySvc.py +++ b/samples/apps/cap/py/autogencap/DirectorySvc.py @@ -8,7 +8,7 @@ from autogencap.ActorConnector import ActorConnector, ActorSender from autogencap.Broker import Broker from autogencap.Config import router_url, xpub_url, xsub_url -from autogencap.Constants import Directory_Svc_Topic +from autogencap.constants import Directory_Svc_Topic from autogencap.DebugLog import Debug, Error, Info from autogencap.proto.CAP_pb2 import ( ActorInfo, diff --git a/samples/apps/cap/py/autogencap/actor_runtime.py b/samples/apps/cap/py/autogencap/actor_runtime.py new file mode 100644 index 000000000000..027b20905877 --- /dev/null +++ b/samples/apps/cap/py/autogencap/actor_runtime.py @@ -0,0 +1,36 @@ +from abc import ABC, abstractmethod +from typing import List + +from .Actor import Actor +from .ActorConnector import ActorConnector +from .proto.CAP_pb2 import ActorInfo + + +class IRuntime(ABC): + @abstractmethod + def register(self, actor: Actor): + pass + + @abstractmethod + def connect(self): + pass + + @abstractmethod + def disconnect(self): + pass + + @abstractmethod + def find_by_topic(self, topic: str) -> ActorConnector: + pass + + @abstractmethod + def find_by_name(self, name: str) -> ActorConnector: + pass + + @abstractmethod + def find_termination(self) -> ActorConnector: + pass + + @abstractmethod + def find_by_name_regex(self, name_regex) -> List[ActorInfo]: + pass diff --git a/samples/apps/cap/py/autogencap/ag_adapter/AG2CAP.py b/samples/apps/cap/py/autogencap/ag_adapter/AG2CAP.py index 1854d219e7d1..4b5f79aefd85 100644 --- a/samples/apps/cap/py/autogencap/ag_adapter/AG2CAP.py +++ b/samples/apps/cap/py/autogencap/ag_adapter/AG2CAP.py @@ -3,7 +3,7 @@ from autogen import Agent, ConversableAgent -from ..ComponentEnsemble import ComponentEnsemble +from ..actor_runtime import IRuntime from .AutoGenConnector import AutoGenConnector @@ -14,13 +14,13 @@ class AG2CAP(ConversableAgent): def __init__( self, - ensemble: ComponentEnsemble, + ensemble: IRuntime, agent_name: str, agent_description: Optional[str] = None, ): super().__init__(name=agent_name, description=agent_description, llm_config=False) self._agent_connector: AutoGenConnector = None - self._ensemble: ComponentEnsemble = ensemble + self._ensemble: IRuntime = ensemble self._recv_called = False def reset_receive_called(self): diff --git a/samples/apps/cap/py/autogencap/ag_adapter/AGActor.py b/samples/apps/cap/py/autogencap/ag_adapter/AGActor.py index cc301f38e7e7..6bd804e33e9e 100644 --- a/samples/apps/cap/py/autogencap/ag_adapter/AGActor.py +++ b/samples/apps/cap/py/autogencap/ag_adapter/AGActor.py @@ -1,7 +1,7 @@ import zmq from autogencap.Actor import Actor -from autogencap.Constants import Termination_Topic +from autogencap.constants import Termination_Topic from autogencap.DebugLog import Debug diff --git a/samples/apps/cap/py/autogencap/ag_adapter/CAP2AG.py b/samples/apps/cap/py/autogencap/ag_adapter/CAP2AG.py index 789ebd9bf4b8..0dd3b7be5ced 100644 --- a/samples/apps/cap/py/autogencap/ag_adapter/CAP2AG.py +++ b/samples/apps/cap/py/autogencap/ag_adapter/CAP2AG.py @@ -4,7 +4,7 @@ from autogen import ConversableAgent -from ..ComponentEnsemble import ComponentEnsemble +from ..actor_runtime import IRuntime from ..DebugLog import Debug, Error, Info, Warn, shorten from ..proto.Autogen_pb2 import GenReplyReq, GenReplyResp, PrepChat, ReceiveReq, Terminate from .AG2CAP import AG2CAP @@ -27,10 +27,10 @@ def __init__(self, ag_agent: ConversableAgent, the_other_name: str, init_chat: b self.STATE = self.States.INIT self._can2ag_name: str = self.actor_name + ".can2ag" self._self_recursive: bool = self_recursive - self._ensemble: ComponentEnsemble = None + self._ensemble: IRuntime = None self._connectors = {} - def on_connect(self, ensemble: ComponentEnsemble): + def on_connect(self, ensemble: IRuntime): """ Connect to the AutoGen system. """ @@ -38,7 +38,7 @@ def on_connect(self, ensemble: ComponentEnsemble): self._ag2can_other_agent = AG2CAP(self._ensemble, self._other_agent_name) Debug(self._can2ag_name, "connected to {ensemble}") - def disconnect_network(self, ensemble: ComponentEnsemble): + def disconnect_network(self, ensemble: IRuntime): """ Disconnect from the AutoGen system. """ diff --git a/samples/apps/cap/py/autogencap/ag_adapter/CAPGroupChat.py b/samples/apps/cap/py/autogencap/ag_adapter/CAPGroupChat.py index 5fad7b359e16..caf2a11a66f1 100644 --- a/samples/apps/cap/py/autogencap/ag_adapter/CAPGroupChat.py +++ b/samples/apps/cap/py/autogencap/ag_adapter/CAPGroupChat.py @@ -3,7 +3,8 @@ from autogen import Agent, AssistantAgent, GroupChat from autogencap.ag_adapter.AG2CAP import AG2CAP from autogencap.ag_adapter.CAP2AG import CAP2AG -from autogencap.ComponentEnsemble import ComponentEnsemble + +from ..actor_runtime import IRuntime class CAPGroupChat(GroupChat): @@ -13,10 +14,10 @@ def __init__( messages: List[str], max_round: int, chat_initiator: str, - ensemble: ComponentEnsemble, + ensemble: IRuntime, ): self.chat_initiator: str = chat_initiator - self._cap_network: ComponentEnsemble = ensemble + self._cap_network: IRuntime = ensemble self._cap_proxies: List[CAP2AG] = [] self._ag_proxies: List[AG2CAP] = [] self._ag_agents: List[Agent] = agents diff --git a/samples/apps/cap/py/autogencap/ag_adapter/CAPGroupChatManager.py b/samples/apps/cap/py/autogencap/ag_adapter/CAPGroupChatManager.py index 85a746d7c661..e71e6aecddf8 100644 --- a/samples/apps/cap/py/autogencap/ag_adapter/CAPGroupChatManager.py +++ b/samples/apps/cap/py/autogencap/ag_adapter/CAPGroupChatManager.py @@ -4,12 +4,13 @@ from autogencap.ActorConnector import ActorConnector from autogencap.ag_adapter.CAP2AG import CAP2AG from autogencap.ag_adapter.CAPGroupChat import CAPGroupChat -from autogencap.ComponentEnsemble import ComponentEnsemble + +from ..actor_runtime import IRuntime class CAPGroupChatManager: - def __init__(self, groupchat: CAPGroupChat, llm_config: dict, network: ComponentEnsemble): - self._ensemble: ComponentEnsemble = network + def __init__(self, groupchat: CAPGroupChat, llm_config: dict, network: IRuntime): + self._ensemble: IRuntime = network self._cap_group_chat: CAPGroupChat = groupchat self._ag_group_chat_manager: GroupChatManager = GroupChatManager( groupchat=self._cap_group_chat, llm_config=llm_config diff --git a/samples/apps/cap/py/autogencap/Constants.py b/samples/apps/cap/py/autogencap/constants.py similarity index 76% rename from samples/apps/cap/py/autogencap/Constants.py rename to samples/apps/cap/py/autogencap/constants.py index 8326d6753d35..217fcf45e619 100644 --- a/samples/apps/cap/py/autogencap/Constants.py +++ b/samples/apps/cap/py/autogencap/constants.py @@ -1,2 +1,3 @@ Termination_Topic: str = "Termination" Directory_Svc_Topic: str = "Directory_Svc" +ZMQ_Runtime: str = "ZMQ" diff --git a/samples/apps/cap/py/autogencap/runtime_factory.py b/samples/apps/cap/py/autogencap/runtime_factory.py new file mode 100644 index 000000000000..77fb091a248d --- /dev/null +++ b/samples/apps/cap/py/autogencap/runtime_factory.py @@ -0,0 +1,47 @@ +from autogencap.actor_runtime import IRuntime +from autogencap.constants import ZMQ_Runtime +from autogencap.DebugLog import Error +from autogencap.zmq_runtime import ZMQRuntime + + +class RuntimeFactory: + _supported_runtimes = {} + + """ + Factory class for creating a runtime instance. + """ + + @staticmethod + def get_runtime(runtime_type: str = ZMQ_Runtime) -> IRuntime: + """ + Creates a runtime instance based on the runtime type. + + :param runtime_type: The type of runtime to create. + :return: The runtime instance. + """ + if runtime_type in RuntimeFactory._supported_runtimes: + return RuntimeFactory._supported_runtimes[runtime_type] + else: + not_found = f"Runtime type not found: {runtime_type}" + Error("RuntimeFactory", not_found) + raise ValueError(not_found) + + @staticmethod + def register_runtime(runtime_type: str, runtime: IRuntime): + """ + Registers a runtime instance. + + :param runtime: The runtime instance. + """ + RuntimeFactory._supported_runtimes[runtime_type] = runtime + + @classmethod + def _initialize(cls): + """ + Static initialization method. + """ + cls.register_runtime(ZMQ_Runtime, ZMQRuntime()) + + +# Static initialization +RuntimeFactory._initialize() diff --git a/samples/apps/cap/py/autogencap/ComponentEnsemble.py b/samples/apps/cap/py/autogencap/zmq_runtime.py similarity index 96% rename from samples/apps/cap/py/autogencap/ComponentEnsemble.py rename to samples/apps/cap/py/autogencap/zmq_runtime.py index ebb31fb9aa7c..a8074fb48d3b 100644 --- a/samples/apps/cap/py/autogencap/ComponentEnsemble.py +++ b/samples/apps/cap/py/autogencap/zmq_runtime.py @@ -4,15 +4,16 @@ import zmq from .Actor import Actor +from .actor_runtime import IRuntime from .ActorConnector import ActorConnector from .Broker import Broker -from .Constants import Termination_Topic +from .constants import Termination_Topic from .DebugLog import Debug, Warn from .DirectorySvc import DirectorySvc from .proto.CAP_pb2 import ActorInfo, ActorInfoCollection -class ComponentEnsemble: +class ZMQRuntime(IRuntime): def __init__(self, name: str = "Local Actor Network", start_broker: bool = True): self.local_actors = {} self.name: str = name diff --git a/samples/apps/cap/py/demo/AppAgents.py b/samples/apps/cap/py/demo/AppAgents.py index bed163a293ee..f13e4b471fd6 100644 --- a/samples/apps/cap/py/demo/AppAgents.py +++ b/samples/apps/cap/py/demo/AppAgents.py @@ -5,9 +5,10 @@ """ from autogencap.Actor import Actor +from autogencap.actor_runtime import IRuntime from autogencap.ActorConnector import ActorConnector -from autogencap.ComponentEnsemble import ComponentEnsemble from autogencap.DebugLog import Debug, Info, shorten +from autogencap.runtime_factory import RuntimeFactory class GreeterAgent(Actor): @@ -136,7 +137,7 @@ def __init__( self.quant: ActorConnector = None self.risk_manager: ActorConnector = None - def on_connect(self, network: ComponentEnsemble): + def on_connect(self, network: IRuntime): """ Connects the personal assistant to the specified local actor network. @@ -150,7 +151,7 @@ def on_connect(self, network: ComponentEnsemble): self.risk_manager = network.find_by_name("Risk Manager") Debug(self.actor_name, "connected") - def disconnect_network(self, network: ComponentEnsemble): + def disconnect_network(self, network: IRuntime): """ Disconnects the personal assistant from the specified local actor network. diff --git a/samples/apps/cap/py/demo/CAPAutGenGroupDemo.py b/samples/apps/cap/py/demo/CAPAutGenGroupDemo.py index 4c3aa1b80305..93a28c753ca5 100644 --- a/samples/apps/cap/py/demo/CAPAutGenGroupDemo.py +++ b/samples/apps/cap/py/demo/CAPAutGenGroupDemo.py @@ -1,7 +1,7 @@ from autogencap.ag_adapter.CAPGroupChat import CAPGroupChat from autogencap.ag_adapter.CAPGroupChatManager import CAPGroupChatManager -from autogencap.ComponentEnsemble import ComponentEnsemble from autogencap.DebugLog import Info +from autogencap.runtime_factory import RuntimeFactory from autogen import AssistantAgent, UserProxyAgent, config_list_from_json @@ -31,7 +31,7 @@ def cap_ag_group_demo(): system_message="Creative in software product ideas.", llm_config=gpt4_config, ) - ensemble = ComponentEnsemble() + ensemble = RuntimeFactory.get_runtime("ZMQ") cap_groupchat = CAPGroupChat( agents=[user_proxy, coder, pm], messages=[], max_round=12, ensemble=ensemble, chat_initiator=user_proxy.name ) diff --git a/samples/apps/cap/py/demo/CAPAutoGenPairDemo.py b/samples/apps/cap/py/demo/CAPAutoGenPairDemo.py index 00ff7a892878..77323ddf8707 100644 --- a/samples/apps/cap/py/demo/CAPAutoGenPairDemo.py +++ b/samples/apps/cap/py/demo/CAPAutoGenPairDemo.py @@ -2,8 +2,8 @@ import autogencap.DebugLog as DebugLog from autogencap.ag_adapter.CAPPair import CAPPair -from autogencap.ComponentEnsemble import ComponentEnsemble from autogencap.DebugLog import ConsoleLogger, Info +from autogencap.runtime_factory import RuntimeFactory from autogen import AssistantAgent, UserProxyAgent, config_list_from_json @@ -20,7 +20,7 @@ def cap_ag_pair_demo(): ) # Composable Agent Platform AutoGen Pair adapter - ensemble = ComponentEnsemble() + ensemble = RuntimeFactory.get_runtime("ZMQ") pair = CAPPair(ensemble, user_proxy, assistant) user_cmd = "Plot a chart of MSFT daily closing prices for last 1 Month" diff --git a/samples/apps/cap/py/demo/ComplexActorDemo.py b/samples/apps/cap/py/demo/ComplexActorDemo.py index 6f6215daece8..b82d457cc67b 100644 --- a/samples/apps/cap/py/demo/ComplexActorDemo.py +++ b/samples/apps/cap/py/demo/ComplexActorDemo.py @@ -1,7 +1,7 @@ import time from AppAgents import FidelityAgent, FinancialPlannerAgent, PersonalAssistant, QuantAgent, RiskManager -from autogencap.ComponentEnsemble import ComponentEnsemble +from autogencap.runtime_factory import RuntimeFactory from termcolor import colored @@ -14,7 +14,7 @@ def complex_actor_demo(): sends them to the personal assistant agent, and terminates when the user enters "quit". """ - ensemble = ComponentEnsemble() + ensemble = RuntimeFactory.get_runtime("ZMQ") # Register agents ensemble.register(PersonalAssistant()) ensemble.register(FidelityAgent()) diff --git a/samples/apps/cap/py/demo/SimpleActorDemo.py b/samples/apps/cap/py/demo/SimpleActorDemo.py index f0f081a5d630..afc398297268 100644 --- a/samples/apps/cap/py/demo/SimpleActorDemo.py +++ b/samples/apps/cap/py/demo/SimpleActorDemo.py @@ -1,5 +1,5 @@ from AppAgents import GreeterAgent -from autogencap.ComponentEnsemble import ComponentEnsemble +from autogencap.runtime_factory import RuntimeFactory def simple_actor_demo(): @@ -8,10 +8,10 @@ def simple_actor_demo(): sending a message, and performing cleanup operations. """ # CAP Platform - ensemble = ComponentEnsemble() + runtime = RuntimeFactory.get_runtime("ZMQ") agent = GreeterAgent() - ensemble.register(agent) - ensemble.connect() - greeter_link = ensemble.find_by_name("Greeter") + runtime.register(agent) + runtime.connect() + greeter_link = runtime.find_by_name("Greeter") greeter_link.send_txt_msg("Hello World!") - ensemble.disconnect() + runtime.disconnect() diff --git a/samples/apps/cap/py/demo/list_agents.py b/samples/apps/cap/py/demo/list_agents.py index 3a93dda29422..ca02006df2b7 100644 --- a/samples/apps/cap/py/demo/list_agents.py +++ b/samples/apps/cap/py/demo/list_agents.py @@ -2,9 +2,9 @@ from typing import List from AppAgents import FidelityAgent, GreeterAgent -from autogencap.ComponentEnsemble import ComponentEnsemble from autogencap.DebugLog import Info from autogencap.proto.CAP_pb2 import ActorInfo +from autogencap.runtime_factory import RuntimeFactory def list_agents(): @@ -14,7 +14,7 @@ def list_agents(): """ # CAP Platform - ensemble = ComponentEnsemble() + ensemble = RuntimeFactory.get_runtime("ZMQ") # Register an actor ensemble.register(GreeterAgent()) # Register an actor diff --git a/samples/apps/cap/py/demo/single_threaded.py b/samples/apps/cap/py/demo/single_threaded.py index d95f67128e64..f4c1fdfb633f 100644 --- a/samples/apps/cap/py/demo/single_threaded.py +++ b/samples/apps/cap/py/demo/single_threaded.py @@ -1,8 +1,8 @@ import _paths from AppAgents import GreeterAgent -from autogencap.ComponentEnsemble import ComponentEnsemble from autogencap.DebugLog import Error from autogencap.proto.CAP_pb2 import Ping +from autogencap.runtime_factory import RuntimeFactory def single_threaded_demo(): @@ -11,7 +11,7 @@ def single_threaded_demo(): sending a message, and performing cleanup operations. """ # CAP Platform - ensemble = ComponentEnsemble() + ensemble = RuntimeFactory.get_runtime("ZMQ") agent = GreeterAgent(start_thread=False) ensemble.register(agent) ensemble.connect() diff --git a/samples/apps/cap/py/demo/standalone/UserProxy.py b/samples/apps/cap/py/demo/standalone/UserProxy.py index 981198072e6f..c2eb4bf42385 100644 --- a/samples/apps/cap/py/demo/standalone/UserProxy.py +++ b/samples/apps/cap/py/demo/standalone/UserProxy.py @@ -2,9 +2,9 @@ import _paths from autogencap.ag_adapter.CAP2AG import CAP2AG -from autogencap.ComponentEnsemble import ComponentEnsemble from autogencap.Config import IGNORED_LOG_CONTEXTS from autogencap.DebugLog import Info +from autogencap.runtime_factory import RuntimeFactory from autogen import UserProxyAgent, config_list_from_json @@ -23,7 +23,7 @@ def run(self): is_termination_msg=lambda x: "TERMINATE" in x.get("content"), ) # Composable Agent Network adapter - ensemble = ComponentEnsemble() + ensemble = RuntimeFactory.get_runtime("ZMQ") user_proxy_adptr = CAP2AG(ag_agent=user_proxy, the_other_name="assistant", init_chat=True, self_recursive=True) ensemble.register(user_proxy_adptr) ensemble.connect() diff --git a/samples/apps/cap/py/demo/standalone/assistant.py b/samples/apps/cap/py/demo/standalone/assistant.py index 789482e48881..162071e60898 100644 --- a/samples/apps/cap/py/demo/standalone/assistant.py +++ b/samples/apps/cap/py/demo/standalone/assistant.py @@ -2,8 +2,8 @@ import _paths from autogencap.ag_adapter.CAP2AG import CAP2AG -from autogencap.ComponentEnsemble import ComponentEnsemble from autogencap.DebugLog import Info +from autogencap.runtime_factory import RuntimeFactory from autogen import AssistantAgent, config_list_from_json @@ -18,7 +18,7 @@ def run(self): config_list = config_list_from_json(env_or_file="OAI_CONFIG_LIST") assistant = AssistantAgent("assistant", llm_config={"config_list": config_list}) # Composable Agent Network adapter - ensemble = ComponentEnsemble() + ensemble = RuntimeFactory.get_runtime("ZMQ") assistant_adptr = CAP2AG(ag_agent=assistant, the_other_name="user_proxy", init_chat=False, self_recursive=True) ensemble.register(assistant_adptr) ensemble.connect() diff --git a/samples/apps/cap/py/demo/standalone/user_proxy.py b/samples/apps/cap/py/demo/standalone/user_proxy.py index 4859361bb41b..d1183a7b7a6e 100644 --- a/samples/apps/cap/py/demo/standalone/user_proxy.py +++ b/samples/apps/cap/py/demo/standalone/user_proxy.py @@ -2,8 +2,8 @@ import _paths from autogencap.ag_adapter.agent import Agent -from autogencap.ComponentEnsemble import ComponentEnsemble from autogencap.Config import IGNORED_LOG_CONTEXTS +from autogencap.runtime_factory import RuntimeFactory from autogen import UserProxyAgent @@ -22,7 +22,7 @@ def main(): # Wrap AutoGen Agent in CAP cap_user_proxy = Agent(user_proxy, counter_party_name="assistant", init_chat=True) # Create the message bus - ensemble = ComponentEnsemble() + ensemble = RuntimeFactory.get_runtime("ZMQ") # Add the user_proxy to the message bus cap_user_proxy.register(ensemble) # Start message processing