mirror of
https://github.com/microsoft/autogen.git
synced 2025-11-02 18:59:48 +00:00
Feature: Composable Actor Platform for AutoGen (#1655)
* Core CAP components + Autogen adapter + Demo * Cleanup Readme * C# folder * Cleanup readme * summary_method bug fix * CAN -> CAP * pre-commit fixes * pre-commit fixes * modification of sys path should ignore E402 * fix pre-commit check issues * Updated docs * Clean up docs * more refactoring * better packaging refactor * Refactoring for package changes * Run demo app without autogencap installed or in the path * Remove debug related sleep() * removed CAP in some class names * Investigate a logging framework that supports color in windows * added type hints * remove circular dependency * fixed pre-commit issues * pre-commit ruff issues * removed circular definition * pre-commit fixes * Fix pre-commit issues * pre-commit fixes * updated for _prepare_chat signature changes * Better instructions for demo and some minor refactoring * Added details that explain CAP * Reformat Readme * More ReadMe Formatting * Readme edits * Agent -> Actor * Broker can startup on it's own * Remote AutoGen Agents * Updated docs * 1) StandaloneBroker in demo 2) Removed Autogen only demo options * 1) Agent -> Actor refactor 2) init broker as early * rename user_proxy -> user_proxy_conn * Add DirectorySvc * Standalone demo refactor * Get ActorInfo from DirectorySvc when searching for Actor * Broker cleanup * Proper cleanup and remove debug sleep() * Run one directory service only. * fix paths to run demo apps from command line * Handle keyboard interrupt * Wait for Broker and Directory to start up * Move Terminate AGActor * Accept input from the user in UserProxy * Move sleeps close to operations that bind or connect * Comments * Created an encapsulated CAP Pair for AutoGen pair communication * pre-commit checks * fix pre-commit * Pair should not make assumptions about who is first and who is second * Use task passed into InitiateChat * Standalone directory svc * Fix broken LFS files * Long running DirectorySvc * DirectorySvc does not have a status * Exit DirectorySvc Loop * Debugging Remoting * Reduce frequency of status messages * Debugging remote Actor * roll back git-lfs updates * rollback git-lfs changes * Debug network connectivity * pre-commit fixes * Create a group chat interface familiar to AutoGen GroupChat users * pre-commit fixes
This commit is contained in:
parent
a120f0ed2b
commit
8f6590e231
54
samples/apps/cap/README.md
Normal file
54
samples/apps/cap/README.md
Normal file
@ -0,0 +1,54 @@
|
||||
# Composable Actor Platform (CAP) for AutoGen
|
||||
|
||||
## I just want to run the demo!
|
||||
*Python Instructions (Windows, Linux, MacOS):*
|
||||
|
||||
0) cd py
|
||||
1) pip install -r autogencap/requirements.txt
|
||||
2) python ./demo/App.py
|
||||
|
||||
*Demo Notes:*
|
||||
1) Options involving AutoGen require OAI_CONFIG_LIST.
|
||||
AutoGen python requirements: 3.8 <= python <= 3.11
|
||||
2) For option 2, type something in and see who receives the message. Quit to quit.
|
||||
3) To view any option that displays a chart (such as option 4), you will need to disable Docker code execution. You can do this by setting the environment variable `AUTOGEN_USE_DOCKER` to `False`.
|
||||
|
||||
*Demo Reference:*
|
||||
```
|
||||
Select the Composable Actor Platform (CAP) demo app to run:
|
||||
(enter anything else to quit)
|
||||
1. Hello World Actor
|
||||
2. Complex Actor Graph
|
||||
3. AutoGen Pair
|
||||
4. AutoGen GroupChat
|
||||
5. AutoGen Agents in different processes
|
||||
Enter your choice (1-5):
|
||||
```
|
||||
|
||||
## What is Composable Actor Platform (CAP)?
|
||||
AutoGen is about Agents and Agent Orchestration. CAP extends AutoGen to allows Agents to communicate via a message bus. CAP, therefore, deals with the space between these components. CAP is a message based, actor platform that allows actors to be composed into arbitrary graphs.
|
||||
|
||||
Actors can register themselves with CAP, find other agents, construct arbitrary graphs, send and receive messages independently and many, many, many other things.
|
||||
```python
|
||||
# CAP Platform
|
||||
network = LocalActorNetwork()
|
||||
# Register an agent
|
||||
network.register(GreeterAgent())
|
||||
# Tell agents to connect to other agents
|
||||
network.connect()
|
||||
# Get a channel to the agent
|
||||
greeter_link = network.lookup_agent("Greeter")
|
||||
# Send a message to the agent
|
||||
greeter_link.send_txt_msg("Hello World!")
|
||||
# Cleanup
|
||||
greeter_link.close()
|
||||
network.disconnect()
|
||||
```
|
||||
### Check out other demos in the `py/demo` directory. We show the following: ###
|
||||
1) Hello World shown above
|
||||
2) Many CAP Actors interacting with each other
|
||||
3) A pair of interacting AutoGen Agents wrapped in CAP Actors
|
||||
4) CAP wrapped AutoGen Agents in a group chat
|
||||
|
||||
### Coming soon. Stay tuned! ###
|
||||
1) Two AutoGen Agents running in different processes and communicating through CAP
|
||||
21
samples/apps/cap/TODO.md
Normal file
21
samples/apps/cap/TODO.md
Normal file
@ -0,0 +1,21 @@
|
||||
- ~~Pretty print debug_logs~~
|
||||
- ~~colors~~
|
||||
- ~~messages to oai should be condensed~~
|
||||
- ~~remove orchestrator in scenario 4 and have the two actors talk to each other~~
|
||||
- ~~pass a complex multi-part message~~
|
||||
- ~~protobuf for messages~~
|
||||
- ~~make changes to autogen to enable scenario 3 to work with CAN~~
|
||||
- ~~make groupchat work~~
|
||||
- ~~actors instead of agents~~
|
||||
- clean up for PR into autogen
|
||||
- ~~Create folder structure under Autogen examples~~
|
||||
- ~~CAN -> CAP (Composable Actor Protocol)~~
|
||||
- CAP actor lookup should use zmq
|
||||
- Add min C# actors & reorganize
|
||||
- Hybrid GroupChat with C# ProductManager
|
||||
- C++ Msg Layer
|
||||
- Rust Msg Layer
|
||||
- Node Msg Layer
|
||||
- Java Msg Layer
|
||||
- Investigate a standard logging framework that supports color in windows
|
||||
- structlog?
|
||||
1
samples/apps/cap/c#/Readme.md
Normal file
1
samples/apps/cap/c#/Readme.md
Normal file
@ -0,0 +1 @@
|
||||
Coming soon...
|
||||
1
samples/apps/cap/c++/Readme.md
Normal file
1
samples/apps/cap/c++/Readme.md
Normal file
@ -0,0 +1 @@
|
||||
Coming soon...
|
||||
1
samples/apps/cap/node/Readme.md
Normal file
1
samples/apps/cap/node/Readme.md
Normal file
@ -0,0 +1 @@
|
||||
Coming soon...
|
||||
78
samples/apps/cap/py/autogencap/Actor.py
Normal file
78
samples/apps/cap/py/autogencap/Actor.py
Normal file
@ -0,0 +1,78 @@
|
||||
import zmq
|
||||
import threading
|
||||
import traceback
|
||||
import time
|
||||
from .DebugLog import Debug, Info
|
||||
from .Config import xpub_url
|
||||
|
||||
|
||||
class Actor:
|
||||
def __init__(self, agent_name: str, description: str):
|
||||
self.actor_name: str = agent_name
|
||||
self.agent_description: str = description
|
||||
self.run = False
|
||||
|
||||
def connect_network(self, network):
|
||||
Debug(self.actor_name, f"is connecting to {network}")
|
||||
Debug(self.actor_name, "connected")
|
||||
|
||||
def _process_txt_msg(self, msg: str, msg_type: str, topic: str, sender: str) -> bool:
|
||||
Info(self.actor_name, f"InBox: {msg}")
|
||||
return True
|
||||
|
||||
def _process_bin_msg(self, msg: bytes, msg_type: str, topic: str, sender: str) -> bool:
|
||||
Info(self.actor_name, f"Msg: topic=[{topic}], msg_type=[{msg_type}]")
|
||||
return True
|
||||
|
||||
def _recv_thread(self):
|
||||
Debug(self.actor_name, "recv thread started")
|
||||
self._socket: zmq.Socket = self._context.socket(zmq.SUB)
|
||||
self._socket.setsockopt(zmq.RCVTIMEO, 500)
|
||||
self._socket.connect(xpub_url)
|
||||
str_topic = f"{self.actor_name}"
|
||||
Debug(self.actor_name, f"subscribe to: {str_topic}")
|
||||
self._socket.setsockopt_string(zmq.SUBSCRIBE, f"{str_topic}")
|
||||
try:
|
||||
while self.run:
|
||||
try:
|
||||
topic, msg_type, sender_topic, msg = self._socket.recv_multipart()
|
||||
topic = topic.decode("utf-8") # Convert bytes to string
|
||||
msg_type = msg_type.decode("utf-8") # Convert bytes to string
|
||||
sender_topic = sender_topic.decode("utf-8") # Convert bytes to string
|
||||
except zmq.Again:
|
||||
continue # No message received, continue to next iteration
|
||||
except Exception:
|
||||
continue
|
||||
if msg_type == "text":
|
||||
msg = msg.decode("utf-8") # Convert bytes to string
|
||||
if not self._process_txt_msg(msg, msg_type, topic, sender_topic):
|
||||
msg = "quit"
|
||||
if msg.lower() == "quit":
|
||||
break
|
||||
else:
|
||||
if not self._process_bin_msg(msg, msg_type, topic, sender_topic):
|
||||
break
|
||||
except Exception as e:
|
||||
Debug(self.actor_name, f"recv thread encountered an error: {e}")
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
self.run = False
|
||||
Debug(self.actor_name, "recv thread ended")
|
||||
|
||||
def start(self, context: zmq.Context):
|
||||
self._context = context
|
||||
self.run: bool = True
|
||||
self._thread = threading.Thread(target=self._recv_thread)
|
||||
self._thread.start()
|
||||
time.sleep(0.01)
|
||||
|
||||
def disconnect_network(self, network):
|
||||
Debug(self.actor_name, f"is disconnecting from {network}")
|
||||
Debug(self.actor_name, "disconnected")
|
||||
self.stop()
|
||||
|
||||
def stop(self):
|
||||
self.run = False
|
||||
self._thread.join()
|
||||
self._socket.setsockopt(zmq.LINGER, 0)
|
||||
self._socket.close()
|
||||
57
samples/apps/cap/py/autogencap/ActorConnector.py
Normal file
57
samples/apps/cap/py/autogencap/ActorConnector.py
Normal file
@ -0,0 +1,57 @@
|
||||
# Agent_Sender takes a zmq context, Topic and creates a
|
||||
# socket that can publish to that topic. It exposes this functionality
|
||||
# using send_msg method
|
||||
import zmq
|
||||
import time
|
||||
import uuid
|
||||
from .DebugLog import Debug, Error
|
||||
from .Config import xsub_url, xpub_url
|
||||
|
||||
|
||||
class ActorConnector:
|
||||
def __init__(self, context, topic):
|
||||
self._pub_socket = context.socket(zmq.PUB)
|
||||
self._pub_socket.setsockopt(zmq.LINGER, 0)
|
||||
self._pub_socket.connect(xsub_url)
|
||||
|
||||
self._resp_socket = context.socket(zmq.SUB)
|
||||
self._resp_socket.setsockopt(zmq.LINGER, 0)
|
||||
self._resp_socket.setsockopt(zmq.RCVTIMEO, 10000)
|
||||
self._resp_socket.connect(xpub_url)
|
||||
self._resp_topic = str(uuid.uuid4())
|
||||
Debug("AgentConnector", f"subscribe to: {self._resp_topic}")
|
||||
self._resp_socket.setsockopt_string(zmq.SUBSCRIBE, f"{self._resp_topic}")
|
||||
self._topic = topic
|
||||
time.sleep(0.01) # Let the network do things.
|
||||
|
||||
def send_txt_msg(self, msg):
|
||||
self._pub_socket.send_multipart(
|
||||
[self._topic.encode("utf8"), "text".encode("utf8"), self._resp_topic.encode("utf8"), msg.encode("utf8")]
|
||||
)
|
||||
|
||||
def send_bin_msg(self, msg_type: str, msg):
|
||||
self._pub_socket.send_multipart(
|
||||
[self._topic.encode("utf8"), msg_type.encode("utf8"), self._resp_topic.encode("utf8"), msg]
|
||||
)
|
||||
|
||||
def binary_request(self, msg_type: str, msg, retry=5):
|
||||
time.sleep(0.5) # Let the network do things.
|
||||
self._pub_socket.send_multipart(
|
||||
[self._topic.encode("utf8"), msg_type.encode("utf8"), self._resp_topic.encode("utf8"), msg]
|
||||
)
|
||||
time.sleep(0.5) # Let the network do things.
|
||||
for i in range(retry + 1):
|
||||
try:
|
||||
self._resp_socket.setsockopt(zmq.RCVTIMEO, 10000)
|
||||
resp_topic, resp_msg_type, resp_sender_topic, resp = self._resp_socket.recv_multipart()
|
||||
return resp_topic, resp_msg_type, resp_sender_topic, resp
|
||||
except zmq.Again:
|
||||
Debug("ActorConnector", f"binary_request: No response received. retry_count={i}, max_retry={retry}")
|
||||
time.sleep(0.01) # Wait a bit before retrying
|
||||
continue
|
||||
Error("ActorConnector", "binary_request: No response received. Giving up.")
|
||||
return None, None, None, None
|
||||
|
||||
def close(self):
|
||||
self._pub_socket.close()
|
||||
self._resp_socket.close()
|
||||
114
samples/apps/cap/py/autogencap/Broker.py
Normal file
114
samples/apps/cap/py/autogencap/Broker.py
Normal file
@ -0,0 +1,114 @@
|
||||
import time
|
||||
import zmq
|
||||
import threading
|
||||
from autogencap.DebugLog import Debug, Info, Warn
|
||||
from autogencap.Config import xsub_url, xpub_url
|
||||
|
||||
|
||||
class Broker:
|
||||
def __init__(self, context: zmq.Context = zmq.Context()):
|
||||
self._context: zmq.Context = context
|
||||
self._run: bool = False
|
||||
self._xpub: zmq.Socket = None
|
||||
self._xsub: zmq.Socket = None
|
||||
|
||||
def start(self) -> bool:
|
||||
try:
|
||||
# XPUB setup
|
||||
self._xpub = self._context.socket(zmq.XPUB)
|
||||
self._xpub.setsockopt(zmq.LINGER, 0)
|
||||
self._xpub.bind(xpub_url)
|
||||
|
||||
# XSUB setup
|
||||
self._xsub = self._context.socket(zmq.XSUB)
|
||||
self._xsub.setsockopt(zmq.LINGER, 0)
|
||||
self._xsub.bind(xsub_url)
|
||||
|
||||
except zmq.ZMQError as e:
|
||||
Debug("BROKER", f"Unable to start. Check details: {e}")
|
||||
# If binding fails, close the sockets and return False
|
||||
if self._xpub:
|
||||
self._xpub.close()
|
||||
if self._xsub:
|
||||
self._xsub.close()
|
||||
return False
|
||||
|
||||
self._run = True
|
||||
self._broker_thread: threading.Thread = threading.Thread(target=self.thread_fn)
|
||||
self._broker_thread.start()
|
||||
time.sleep(0.01)
|
||||
return True
|
||||
|
||||
def stop(self):
|
||||
# Error("BROKER_ERR", "fix cleanup self._context.term()")
|
||||
Debug("BROKER", "stopped")
|
||||
self._run = False
|
||||
self._broker_thread.join()
|
||||
if self._xpub:
|
||||
self._xpub.close()
|
||||
if self._xsub:
|
||||
self._xsub.close()
|
||||
# self._context.term()
|
||||
|
||||
def thread_fn(self):
|
||||
try:
|
||||
# Poll sockets for events
|
||||
self._poller: zmq.Poller = zmq.Poller()
|
||||
self._poller.register(self._xpub, zmq.POLLIN)
|
||||
self._poller.register(self._xsub, zmq.POLLIN)
|
||||
|
||||
# Receive msgs, forward and process
|
||||
while self._run:
|
||||
events = dict(self._poller.poll(500))
|
||||
if self._xpub in events:
|
||||
message = self._xpub.recv_multipart()
|
||||
Debug("BROKER", f"subscription message: {message[0]}")
|
||||
self._xsub.send_multipart(message)
|
||||
|
||||
if self._xsub in events:
|
||||
message = self._xsub.recv_multipart()
|
||||
Debug("BROKER", f"publishing message: {message}")
|
||||
self._xpub.send_multipart(message)
|
||||
|
||||
except Exception as e:
|
||||
Debug("BROKER", f"thread encountered an error: {e}")
|
||||
finally:
|
||||
self._run = False
|
||||
Debug("BROKER", "thread ended")
|
||||
return
|
||||
|
||||
|
||||
# Run a standalone broker that all other Actors can connect to.
|
||||
# This can also run inproc with the other actors.
|
||||
def main():
|
||||
broker = Broker()
|
||||
Info("BROKER", "Starting.")
|
||||
if broker.start():
|
||||
Info("BROKER", "Running.")
|
||||
else:
|
||||
Warn("BROKER", "Failed to start.")
|
||||
return
|
||||
|
||||
status_interval = 300 # seconds
|
||||
last_time = time.time()
|
||||
|
||||
# Broker is running in a separate thread. Here we are watching the
|
||||
# broker's status and printing status every few seconds. This is
|
||||
# a good place to print other statistics captured as the broker runs.
|
||||
# -- Exits when the user presses Ctrl+C --
|
||||
while broker._run:
|
||||
# print a message every n seconds
|
||||
current_time = time.time()
|
||||
elapsed_time = current_time - last_time
|
||||
if elapsed_time > status_interval:
|
||||
Info("BROKER", "Running.")
|
||||
last_time = current_time
|
||||
try:
|
||||
time.sleep(0.5)
|
||||
except KeyboardInterrupt:
|
||||
Info("BROKER", "KeyboardInterrupt. Stopping the broker.")
|
||||
broker.stop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
5
samples/apps/cap/py/autogencap/Config.py
Normal file
5
samples/apps/cap/py/autogencap/Config.py
Normal file
@ -0,0 +1,5 @@
|
||||
# Set the current log level
|
||||
LOG_LEVEL = 0
|
||||
IGNORED_LOG_CONTEXTS = []
|
||||
xpub_url: str = "tcp://127.0.0.1:5555"
|
||||
xsub_url: str = "tcp://127.0.0.1:5556"
|
||||
2
samples/apps/cap/py/autogencap/Constants.py
Normal file
2
samples/apps/cap/py/autogencap/Constants.py
Normal file
@ -0,0 +1,2 @@
|
||||
Termination_Topic: str = "Termination"
|
||||
Directory_Svc_Topic: str = "Directory_Svc"
|
||||
73
samples/apps/cap/py/autogencap/DebugLog.py
Normal file
73
samples/apps/cap/py/autogencap/DebugLog.py
Normal file
@ -0,0 +1,73 @@
|
||||
import threading
|
||||
import datetime
|
||||
from autogencap.Config import LOG_LEVEL, IGNORED_LOG_CONTEXTS
|
||||
from termcolor import colored
|
||||
|
||||
# Define log levels as constants
|
||||
DEBUG = 0
|
||||
INFO = 1
|
||||
WARN = 2
|
||||
ERROR = 3
|
||||
|
||||
# Map log levels to their names
|
||||
LEVEL_NAMES = ["DBG", "INF", "WRN", "ERR"]
|
||||
LEVEL_COLOR = ["dark_grey", "green", "yellow", "red"]
|
||||
|
||||
console_lock = threading.Lock()
|
||||
|
||||
|
||||
def Log(level, context, msg):
|
||||
# Check if the current level meets the threshold
|
||||
if level >= LOG_LEVEL: # Use the LOG_LEVEL from the Config module
|
||||
# Check if the context is in the list of ignored contexts
|
||||
if context in IGNORED_LOG_CONTEXTS:
|
||||
return
|
||||
with console_lock:
|
||||
timestamp = colored(datetime.datetime.now().strftime("%m/%d/%y %H:%M:%S"), "dark_grey")
|
||||
# Translate level number to name and color
|
||||
level_name = colored(LEVEL_NAMES[level], LEVEL_COLOR[level])
|
||||
# Center justify the context and color it blue
|
||||
context = colored(context.ljust(14), "blue")
|
||||
# color the msg based on the level
|
||||
msg = colored(msg, LEVEL_COLOR[level])
|
||||
print(f"{threading.get_ident()} {timestamp} {level_name}: [{context}] {msg}")
|
||||
|
||||
|
||||
def Debug(context, message):
|
||||
Log(DEBUG, context, message)
|
||||
|
||||
|
||||
def Info(context, message):
|
||||
Log(INFO, context, message)
|
||||
|
||||
|
||||
def Warn(context, message):
|
||||
Log(WARN, context, message)
|
||||
|
||||
|
||||
def Error(context, message):
|
||||
Log(ERROR, context, message)
|
||||
|
||||
|
||||
def shorten(msg, num_parts=5, max_len=100):
|
||||
# Remove new lines
|
||||
msg = msg.replace("\n", " ")
|
||||
|
||||
# If the message is shorter than or equal to max_len characters, return it as is
|
||||
if len(msg) <= max_len:
|
||||
return msg
|
||||
|
||||
# Calculate the length of each part
|
||||
part_len = max_len // num_parts
|
||||
|
||||
# Create a list to store the parts
|
||||
parts = []
|
||||
|
||||
# Get the parts from the message
|
||||
for i in range(num_parts):
|
||||
start = i * part_len
|
||||
end = start + part_len
|
||||
parts.append(msg[start:end])
|
||||
|
||||
# Join the parts with '...' and return the result
|
||||
return "...".join(parts)
|
||||
210
samples/apps/cap/py/autogencap/DirectorySvc.py
Normal file
210
samples/apps/cap/py/autogencap/DirectorySvc.py
Normal file
@ -0,0 +1,210 @@
|
||||
from autogencap.Constants import Directory_Svc_Topic
|
||||
from autogencap.Config import xpub_url, xsub_url
|
||||
from autogencap.DebugLog import Debug, Info, Error
|
||||
from autogencap.ActorConnector import ActorConnector
|
||||
from autogencap.Actor import Actor
|
||||
from autogencap.proto.CAP_pb2 import ActorRegistration, ActorInfo, ActorLookup, ActorLookupResponse, Ping, Pong
|
||||
import zmq
|
||||
import threading
|
||||
import time
|
||||
|
||||
# TODO (Future DirectorySv PR) use actor description, network_id, other properties to make directory
|
||||
# service more generic and powerful
|
||||
|
||||
|
||||
class DirectoryActor(Actor):
|
||||
def __init__(self, topic: str, name: str):
|
||||
super().__init__(topic, name)
|
||||
self._registered_actors = {}
|
||||
self._network_prefix = ""
|
||||
|
||||
def _process_bin_msg(self, msg: bytes, msg_type: str, topic: str, sender: str) -> bool:
|
||||
if msg_type == ActorRegistration.__name__:
|
||||
self._actor_registration_msg_handler(topic, msg_type, msg)
|
||||
elif msg_type == ActorLookup.__name__:
|
||||
self._actor_lookup_msg_handler(topic, msg_type, msg, sender)
|
||||
elif msg_type == Ping.__name__:
|
||||
self._ping_msg_handler(topic, msg_type, msg, sender)
|
||||
else:
|
||||
Error("DirectorySvc", f"Unknown message type: {msg_type}")
|
||||
return True
|
||||
|
||||
def _ping_msg_handler(self, topic: str, msg_type: str, msg: bytes, sender_topic: str):
|
||||
Info("DirectorySvc", f"Ping received: {sender_topic}")
|
||||
pong = Pong()
|
||||
serialized_msg = pong.SerializeToString()
|
||||
sender_connection = ActorConnector(self._context, sender_topic)
|
||||
sender_connection.send_bin_msg(Pong.__name__, serialized_msg)
|
||||
|
||||
def _actor_registration_msg_handler(self, topic: str, msg_type: str, msg: bytes):
|
||||
actor_reg = ActorRegistration()
|
||||
actor_reg.ParseFromString(msg)
|
||||
Info("DirectorySvc", f"Actor registration: {actor_reg.actor_info.name}")
|
||||
name = actor_reg.actor_info.name
|
||||
# TODO (Future DirectorySv PR) network_id should be namespace prefixed to support multiple networks
|
||||
actor_reg.actor_info.name + self._network_prefix
|
||||
if name in self._registered_actors:
|
||||
Error("DirectorySvc", f"Actor already registered: {name}")
|
||||
return
|
||||
self._registered_actors[name] = actor_reg.actor_info
|
||||
|
||||
def _actor_lookup_msg_handler(self, topic: str, msg_type: str, msg: bytes, sender_topic: str):
|
||||
actor_lookup = ActorLookup()
|
||||
actor_lookup.ParseFromString(msg)
|
||||
Debug("DirectorySvc", f"Actor lookup: {actor_lookup.actor_info.name}")
|
||||
actor: ActorInfo = None
|
||||
if actor_lookup.actor_info.name in self._registered_actors:
|
||||
Info("DirectorySvc", f"Actor found: {actor_lookup.actor_info.name}")
|
||||
actor = self._registered_actors[actor_lookup.actor_info.name]
|
||||
else:
|
||||
Error("DirectorySvc", f"Actor not found: {actor_lookup.actor_info.name}")
|
||||
actor_lookup_resp = ActorLookupResponse()
|
||||
if actor is not None:
|
||||
actor_lookup_resp.actor.info_coll.extend([actor])
|
||||
actor_lookup_resp.found = True
|
||||
else:
|
||||
actor_lookup_resp.found = False
|
||||
sender_connection = ActorConnector(self._context, sender_topic)
|
||||
serialized_msg = actor_lookup_resp.SerializeToString()
|
||||
sender_connection.send_bin_msg(ActorLookupResponse.__name__, serialized_msg)
|
||||
|
||||
|
||||
class DirectorySvc:
|
||||
def __init__(self, context: zmq.Context = zmq.Context()):
|
||||
self._context: zmq.Context = context
|
||||
self._directory_connector: ActorConnector = None
|
||||
self._directory_actor: DirectoryActor = None
|
||||
|
||||
def _no_other_directory(self) -> bool:
|
||||
ping = Ping()
|
||||
serialized_msg = ping.SerializeToString()
|
||||
_, _, _, resp = self._directory_connector.binary_request(Ping.__name__, serialized_msg, retry=0)
|
||||
if resp is None:
|
||||
return True
|
||||
return False
|
||||
|
||||
def start(self):
|
||||
self._directory_connector = ActorConnector(self._context, Directory_Svc_Topic)
|
||||
if self._no_other_directory():
|
||||
self._directory_actor = DirectoryActor(Directory_Svc_Topic, "Directory Service")
|
||||
self._directory_actor.start(self._context)
|
||||
Info("DirectorySvc", "Directory service started.")
|
||||
else:
|
||||
Info("DirectorySvc", "Another directory service is running. This instance will not start.")
|
||||
|
||||
def stop(self):
|
||||
if self._directory_actor:
|
||||
self._directory_actor.stop()
|
||||
if self._directory_connector:
|
||||
self._directory_connector.close()
|
||||
|
||||
def register_actor(self, actor_info: ActorInfo):
|
||||
# Send a message to the directory service
|
||||
# to register the actor
|
||||
actor_reg = ActorRegistration()
|
||||
actor_reg.actor_info.CopyFrom(actor_info)
|
||||
serialized_msg = actor_reg.SerializeToString()
|
||||
self._directory_connector.send_bin_msg(ActorRegistration.__name__, serialized_msg)
|
||||
|
||||
def register_actor_by_name(self, actor_name: str):
|
||||
actor_info = ActorInfo(name=actor_name)
|
||||
self.register_actor(actor_info)
|
||||
|
||||
def lookup_actor_by_name(self, actor_name: str) -> ActorInfo:
|
||||
actor_info = ActorInfo(name=actor_name)
|
||||
actor_lookup = ActorLookup(actor_info=actor_info)
|
||||
serialized_msg = actor_lookup.SerializeToString()
|
||||
_, _, _, resp = self._directory_connector.binary_request(ActorLookup.__name__, serialized_msg)
|
||||
actor_lookup_resp = ActorLookupResponse()
|
||||
actor_lookup_resp.ParseFromString(resp)
|
||||
if actor_lookup_resp.found:
|
||||
if len(actor_lookup_resp.actor.info_coll) > 0:
|
||||
return actor_lookup_resp.actor.info_coll[0]
|
||||
return None
|
||||
|
||||
|
||||
# Standalone min proxy for a standalone directory service
|
||||
class MinProxy:
|
||||
def __init__(self, context: zmq.Context):
|
||||
self._context: zmq.Context = context
|
||||
self._xpub: zmq.Socket = None
|
||||
self._xsub: zmq.Socket = None
|
||||
|
||||
def start(self):
|
||||
# Start the proxy thread
|
||||
proxy_thread = threading.Thread(target=self.proxy_thread_fn)
|
||||
proxy_thread.start()
|
||||
time.sleep(0.01)
|
||||
|
||||
def stop(self):
|
||||
self._xsub.setsockopt(zmq.LINGER, 0)
|
||||
self._xpub.setsockopt(zmq.LINGER, 0)
|
||||
self._xpub.close()
|
||||
self._xsub.close()
|
||||
time.sleep(0.01)
|
||||
|
||||
def proxy_thread_fn(self):
|
||||
self._xpub: zmq.Socket = self._context.socket(zmq.XPUB)
|
||||
self._xsub: zmq.Socket = self._context.socket(zmq.XSUB)
|
||||
try:
|
||||
self._xpub.bind(xpub_url)
|
||||
self._xsub.bind(xsub_url)
|
||||
zmq.proxy(self._xpub, self._xsub)
|
||||
except zmq.ContextTerminated:
|
||||
self._xpub.close()
|
||||
self._xsub.close()
|
||||
except Exception as e:
|
||||
Error("proxy_thread_fn", f"proxy_thread_fn encountered an error: {e}")
|
||||
self._xpub.setsockopt(zmq.LINGER, 0)
|
||||
self._xsub.setsockopt(zmq.LINGER, 0)
|
||||
self._xpub.close()
|
||||
self._xsub.close()
|
||||
finally:
|
||||
Info("proxy_thread_fn", "proxy_thread_fn terminated.")
|
||||
|
||||
|
||||
# Run a standalone directory service
|
||||
def main():
|
||||
context: zmq.Context = zmq.Context()
|
||||
# Start simple broker (will exit if real broker is running)
|
||||
proxy: MinProxy = MinProxy(context)
|
||||
proxy.start()
|
||||
# Start the directory service
|
||||
directory_svc = DirectorySvc(context)
|
||||
directory_svc.start()
|
||||
|
||||
# # How do you register an actor?
|
||||
# directory_svc.register_actor_by_name("my_actor")
|
||||
#
|
||||
# # How do you look up an actor?
|
||||
# actor: ActorInfo = directory_svc.lookup_actor_by_name("my_actor")
|
||||
# if actor is not None:
|
||||
# Info("main", f"Found actor: {actor.name}")
|
||||
|
||||
# DirectorySvc is running in a separate thread. Here we are watching the
|
||||
# status and printing status every few seconds. This is
|
||||
# a good place to print other statistics captured as the broker runs.
|
||||
# -- Exits when the user presses Ctrl+C --
|
||||
status_interval = 300 # seconds
|
||||
last_time = time.time()
|
||||
while True:
|
||||
# print a message every n seconds
|
||||
current_time = time.time()
|
||||
elapsed_time = current_time - last_time
|
||||
if elapsed_time > status_interval:
|
||||
Info("DirectorySvc", "Running.")
|
||||
last_time = current_time
|
||||
try:
|
||||
time.sleep(0.5)
|
||||
except KeyboardInterrupt:
|
||||
Info("DirectorySvc", "KeyboardInterrupt. Stopping the DirectorySvc.")
|
||||
break
|
||||
|
||||
directory_svc.stop()
|
||||
proxy.stop()
|
||||
context.term()
|
||||
Info("main", "Done.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
71
samples/apps/cap/py/autogencap/LocalActorNetwork.py
Normal file
71
samples/apps/cap/py/autogencap/LocalActorNetwork.py
Normal file
@ -0,0 +1,71 @@
|
||||
import zmq
|
||||
from .DebugLog import Debug, Warn
|
||||
from .ActorConnector import ActorConnector
|
||||
from .Broker import Broker
|
||||
from .DirectorySvc import DirectorySvc
|
||||
from .Constants import Termination_Topic
|
||||
from .Actor import Actor
|
||||
from .proto.CAP_pb2 import ActorInfo
|
||||
import time
|
||||
|
||||
# TODO: remove time import
|
||||
|
||||
|
||||
class LocalActorNetwork:
|
||||
def __init__(self, name: str = "Local Actor Network", start_broker: bool = True):
|
||||
self.local_actors = {}
|
||||
self.name: str = name
|
||||
self._context: zmq.Context = zmq.Context()
|
||||
self._start_broker: bool = start_broker
|
||||
self._broker: Broker = None
|
||||
self._directory_svc: DirectorySvc = None
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name}"
|
||||
|
||||
def _init_runtime(self):
|
||||
if self._start_broker and self._broker is None:
|
||||
self._broker = Broker(self._context)
|
||||
if not self._broker.start():
|
||||
self._start_broker = False # Don't try to start the broker again
|
||||
self._broker = None
|
||||
if self._directory_svc is None:
|
||||
self._directory_svc = DirectorySvc(self._context)
|
||||
self._directory_svc.start()
|
||||
|
||||
def register(self, actor: Actor):
|
||||
self._init_runtime()
|
||||
# Get actor's name and description and add to a dictionary so
|
||||
# that we can look up the actor by name
|
||||
self._directory_svc.register_actor_by_name(actor.actor_name)
|
||||
self.local_actors[actor.actor_name] = actor
|
||||
actor.start(self._context)
|
||||
Debug("Local_Actor_Network", f"{actor.actor_name} registered in the network.")
|
||||
|
||||
def connect(self):
|
||||
self._init_runtime()
|
||||
for actor in self.local_actors.values():
|
||||
actor.connect_network(self)
|
||||
|
||||
def disconnect(self):
|
||||
for actor in self.local_actors.values():
|
||||
actor.disconnect_network(self)
|
||||
if self._directory_svc:
|
||||
self._directory_svc.stop()
|
||||
if self._broker:
|
||||
self._broker.stop()
|
||||
|
||||
def actor_connector_by_topic(self, topic: str) -> ActorConnector:
|
||||
return ActorConnector(self._context, topic)
|
||||
|
||||
def lookup_actor(self, name: str) -> ActorConnector:
|
||||
actor_info: ActorInfo = self._directory_svc.lookup_actor_by_name(name)
|
||||
if actor_info is None:
|
||||
Warn("Local_Actor_Network", f"{name}, not found in the network.")
|
||||
return None
|
||||
Debug("Local_Actor_Network", f"[{name}] found in the network.")
|
||||
return self.actor_connector_by_topic(name)
|
||||
|
||||
def lookup_termination(self) -> ActorConnector:
|
||||
termination_topic: str = Termination_Topic
|
||||
return self.actor_connector_by_topic(termination_topic)
|
||||
0
samples/apps/cap/py/autogencap/__init__.py
Normal file
0
samples/apps/cap/py/autogencap/__init__.py
Normal file
81
samples/apps/cap/py/autogencap/ag_adapter/AG2CAP.py
Normal file
81
samples/apps/cap/py/autogencap/ag_adapter/AG2CAP.py
Normal file
@ -0,0 +1,81 @@
|
||||
import time
|
||||
from typing import Callable, Dict, List, Optional, Union
|
||||
from autogen import Agent, ConversableAgent
|
||||
from .AutoGenConnector import AutoGenConnector
|
||||
from ..LocalActorNetwork import LocalActorNetwork
|
||||
|
||||
|
||||
class AG2CAP(ConversableAgent):
|
||||
"""
|
||||
A conversable agent proxy that sends messages to CAN when called
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
network: LocalActorNetwork,
|
||||
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._network: LocalActorNetwork = network
|
||||
self._recv_called = False
|
||||
|
||||
def reset_receive_called(self):
|
||||
self._recv_called = False
|
||||
|
||||
def was_receive_called(self):
|
||||
return self._recv_called
|
||||
|
||||
def set_name(self, name: str):
|
||||
"""
|
||||
Set the name of the agent.
|
||||
Why? because we need it to look like different agents
|
||||
"""
|
||||
self._name = name
|
||||
|
||||
def _check_connection(self):
|
||||
if self._agent_connector is None:
|
||||
self._agent_connector = AutoGenConnector(self._network.lookup_actor(self.name))
|
||||
self._terminate_connector = AutoGenConnector(self._network.lookup_termination())
|
||||
|
||||
def receive(
|
||||
self,
|
||||
message: Union[Dict, str],
|
||||
sender: Agent,
|
||||
request_reply: Optional[bool] = None,
|
||||
silent: Optional[bool] = False,
|
||||
):
|
||||
"""
|
||||
Receive a message from the AutoGen system.
|
||||
"""
|
||||
self._recv_called = True
|
||||
self._check_connection()
|
||||
self._agent_connector.send_receive_req(message, sender, request_reply, silent)
|
||||
|
||||
def generate_reply(
|
||||
self,
|
||||
messages: Optional[List[Dict]] = None,
|
||||
sender: Optional[Agent] = None,
|
||||
exclude: Optional[List[Callable]] = None,
|
||||
) -> Union[str, Dict, None]:
|
||||
"""
|
||||
Generate a reply message for the AutoGen system.
|
||||
"""
|
||||
self._check_connection()
|
||||
return self._agent_connector.send_gen_reply_req()
|
||||
|
||||
def _prepare_chat(
|
||||
self,
|
||||
recipient: ConversableAgent,
|
||||
clear_history: bool,
|
||||
prepare_recipient: bool = True,
|
||||
reply_at_receive: bool = True,
|
||||
) -> None:
|
||||
self._check_connection()
|
||||
self._agent_connector.send_prep_chat(recipient, clear_history, prepare_recipient)
|
||||
|
||||
def send_terminate(self, recipient: ConversableAgent) -> None:
|
||||
self._check_connection()
|
||||
self._agent_connector.send_terminate(recipient)
|
||||
self._terminate_connector.send_terminate(self)
|
||||
12
samples/apps/cap/py/autogencap/ag_adapter/AGActor.py
Normal file
12
samples/apps/cap/py/autogencap/ag_adapter/AGActor.py
Normal file
@ -0,0 +1,12 @@
|
||||
import zmq
|
||||
from autogencap.Actor import Actor
|
||||
from autogencap.Constants import Termination_Topic
|
||||
from autogencap.DebugLog import Debug
|
||||
|
||||
|
||||
class AGActor(Actor):
|
||||
def start(self, context: zmq.Context):
|
||||
super().start(context)
|
||||
str_topic = Termination_Topic
|
||||
Debug(self.actor_name, f"subscribe to: {str_topic}")
|
||||
self._socket.setsockopt_string(zmq.SUBSCRIBE, f"{str_topic}")
|
||||
@ -0,0 +1,84 @@
|
||||
from typing import Dict, Optional, Union
|
||||
from autogen import Agent
|
||||
from ..ActorConnector import ActorConnector
|
||||
from ..proto.Autogen_pb2 import GenReplyReq, GenReplyResp, PrepChat, ReceiveReq, Terminate
|
||||
|
||||
|
||||
class AutoGenConnector:
|
||||
"""
|
||||
A specialized ActorConnector class for sending and receiving Autogen messages
|
||||
to/from the CAP system.
|
||||
"""
|
||||
|
||||
def __init__(self, cap_sender: ActorConnector):
|
||||
self._can_channel: ActorConnector = cap_sender
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close the connector.
|
||||
"""
|
||||
self._can_channel.close()
|
||||
|
||||
def _send_msg(self, msg):
|
||||
"""
|
||||
Send a message to CAP.
|
||||
"""
|
||||
serialized_msg = msg.SerializeToString()
|
||||
self._can_channel.send_bin_msg(type(msg).__name__, serialized_msg)
|
||||
|
||||
def send_gen_reply_req(self):
|
||||
"""
|
||||
Send a GenReplyReq message to CAP and receive the response.
|
||||
"""
|
||||
msg = GenReplyReq()
|
||||
serialized_msg = msg.SerializeToString()
|
||||
_, _, _, resp = self._can_channel.binary_request(type(msg).__name__, serialized_msg)
|
||||
gen_reply_resp = GenReplyResp()
|
||||
gen_reply_resp.ParseFromString(resp)
|
||||
return gen_reply_resp.data
|
||||
|
||||
def send_receive_req(
|
||||
self,
|
||||
message: Union[Dict, str],
|
||||
sender: Agent,
|
||||
request_reply: Optional[bool] = None,
|
||||
silent: Optional[bool] = False,
|
||||
):
|
||||
"""
|
||||
Send a ReceiveReq message to CAP.
|
||||
"""
|
||||
msg = ReceiveReq()
|
||||
if isinstance(message, dict):
|
||||
for key, value in message.items():
|
||||
msg.data_map.data[key] = value
|
||||
elif isinstance(message, str):
|
||||
msg.data = message
|
||||
msg.sender = sender.name
|
||||
if request_reply is not None:
|
||||
msg.request_reply = request_reply
|
||||
if silent is not None:
|
||||
msg.silent = silent
|
||||
self._send_msg(msg)
|
||||
|
||||
def send_terminate(self, sender: Agent):
|
||||
"""
|
||||
Send a Terminate message to CAP.
|
||||
"""
|
||||
msg = Terminate()
|
||||
msg.sender = sender.name
|
||||
self._send_msg(msg)
|
||||
|
||||
def send_prep_chat(self, recipient: "Agent", clear_history: bool, prepare_recipient: bool = True) -> None:
|
||||
"""
|
||||
Send a PrepChat message to CAP.
|
||||
|
||||
Args:
|
||||
recipient (Agent): _description_
|
||||
clear_history (bool): _description_
|
||||
prepare_recipient (bool, optional): _description_. Defaults to True.
|
||||
"""
|
||||
msg = PrepChat()
|
||||
msg.recipient = recipient.name
|
||||
msg.clear_history = clear_history
|
||||
msg.prepare_recipient = prepare_recipient
|
||||
self._send_msg(msg)
|
||||
155
samples/apps/cap/py/autogencap/ag_adapter/CAP2AG.py
Normal file
155
samples/apps/cap/py/autogencap/ag_adapter/CAP2AG.py
Normal file
@ -0,0 +1,155 @@
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
from ..DebugLog import Debug, Error, Info, Warn, shorten
|
||||
from ..LocalActorNetwork import LocalActorNetwork
|
||||
from ..proto.Autogen_pb2 import GenReplyReq, GenReplyResp, PrepChat, ReceiveReq, Terminate
|
||||
from .AGActor import AGActor
|
||||
from .AG2CAP import AG2CAP
|
||||
from autogen import ConversableAgent
|
||||
|
||||
|
||||
class CAP2AG(AGActor):
|
||||
"""
|
||||
A CAN actor that acts as an adapter for the AutoGen system.
|
||||
"""
|
||||
|
||||
States = Enum("States", ["INIT", "CONVERSING"])
|
||||
|
||||
def __init__(self, ag_agent: ConversableAgent, the_other_name: str, init_chat: bool, self_recursive: bool = True):
|
||||
super().__init__(ag_agent.name, ag_agent.description)
|
||||
self._the_ag_agent: ConversableAgent = ag_agent
|
||||
self._ag2can_other_agent: AG2CAP = None
|
||||
self._other_agent_name: str = the_other_name
|
||||
self._init_chat: bool = init_chat
|
||||
self.STATE = self.States.INIT
|
||||
self._can2ag_name: str = self.actor_name + ".can2ag"
|
||||
self._self_recursive: bool = self_recursive
|
||||
self._network: LocalActorNetwork = None
|
||||
self._connectors = {}
|
||||
|
||||
def connect_network(self, network: LocalActorNetwork):
|
||||
"""
|
||||
Connect to the AutoGen system.
|
||||
"""
|
||||
self._network = network
|
||||
self._ag2can_other_agent = AG2CAP(self._network, self._other_agent_name)
|
||||
Debug(self._can2ag_name, "connected to {network}")
|
||||
|
||||
def disconnect_network(self, network: LocalActorNetwork):
|
||||
"""
|
||||
Disconnect from the AutoGen system.
|
||||
"""
|
||||
super().disconnect_network(network)
|
||||
# self._the_other.close()
|
||||
Debug(self.actor_name, "disconnected")
|
||||
|
||||
def _process_txt_msg(self, msg: str, msg_type: str, topic: str, sender: str):
|
||||
"""
|
||||
Process a text message received from the AutoGen system.
|
||||
"""
|
||||
Info(self._can2ag_name, f"proc_txt_msg: [{topic}], [{msg_type}], {shorten(msg)}")
|
||||
if self.STATE == self.States.INIT:
|
||||
self.STATE = self.States.CONVERSING
|
||||
if self._init_chat:
|
||||
self._the_ag_agent.initiate_chat(self._ag2can_other_agent, message=msg, summary_method=None)
|
||||
else:
|
||||
self._the_ag_agent.receive(msg, self._ag2can_other_agent, True)
|
||||
else:
|
||||
self._the_ag_agent.receive(msg, self._ag2can_other_agent, True)
|
||||
return True
|
||||
|
||||
def _call_agent_receive(self, receive_params: ReceiveReq):
|
||||
request_reply: Optional[bool] = None
|
||||
silent: Optional[bool] = False
|
||||
|
||||
if receive_params.HasField("request_reply"):
|
||||
request_reply = receive_params.request_reply
|
||||
if receive_params.HasField("silent"):
|
||||
silent = receive_params.silent
|
||||
|
||||
save_name = self._ag2can_other_agent.name
|
||||
self._ag2can_other_agent.set_name(receive_params.sender)
|
||||
if receive_params.HasField("data_map"):
|
||||
data = dict(receive_params.data_map.data)
|
||||
else:
|
||||
data = receive_params.data
|
||||
self._the_ag_agent.receive(data, self._ag2can_other_agent, request_reply, silent)
|
||||
self._ag2can_other_agent.set_name(save_name)
|
||||
|
||||
def receive_msgproc(self, msg: bytes):
|
||||
"""
|
||||
Process a ReceiveReq message received from the AutoGen system.
|
||||
"""
|
||||
receive_params = ReceiveReq()
|
||||
receive_params.ParseFromString(msg)
|
||||
|
||||
self._ag2can_other_agent.reset_receive_called()
|
||||
|
||||
if self.STATE == self.States.INIT:
|
||||
self.STATE = self.States.CONVERSING
|
||||
|
||||
if self._init_chat:
|
||||
self._the_ag_agent.initiate_chat(
|
||||
self._ag2can_other_agent, message=receive_params.data, summary_method=None
|
||||
)
|
||||
else:
|
||||
self._call_agent_receive(receive_params)
|
||||
else:
|
||||
self._call_agent_receive(receive_params)
|
||||
|
||||
if not self._ag2can_other_agent.was_receive_called() and self._self_recursive:
|
||||
Warn(self._can2ag_name, "TERMINATE")
|
||||
self._ag2can_other_agent.send_terminate(self._the_ag_agent)
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_actor_connector(self, topic: str):
|
||||
"""
|
||||
Get the actor connector for the given topic.
|
||||
"""
|
||||
if topic in self._connectors:
|
||||
return self._connectors[topic]
|
||||
else:
|
||||
connector = self._network.actor_connector_by_topic(topic)
|
||||
self._connectors[topic] = connector
|
||||
return connector
|
||||
|
||||
def generate_reply_msgproc(self, msg: GenReplyReq, sender_topic: str):
|
||||
"""
|
||||
Process a GenReplyReq message received from the AutoGen system and generate a reply.
|
||||
"""
|
||||
generate_reply_params = GenReplyReq()
|
||||
generate_reply_params.ParseFromString(msg)
|
||||
reply = self._the_ag_agent.generate_reply(sender=self._ag2can_other_agent)
|
||||
connector = self.get_actor_connector(sender_topic)
|
||||
|
||||
reply_msg = GenReplyResp()
|
||||
if reply:
|
||||
reply_msg.data = reply.encode("utf8")
|
||||
serialized_msg = reply_msg.SerializeToString()
|
||||
connector.send_bin_msg(type(reply_msg).__name__, serialized_msg)
|
||||
return True
|
||||
|
||||
def prepchat_msgproc(self, msg, sender_topic):
|
||||
prep_chat = PrepChat()
|
||||
prep_chat.ParseFromString(msg)
|
||||
self._the_ag_agent._prepare_chat(self._ag2can_other_agent, prep_chat.clear_history, prep_chat.prepare_recipient)
|
||||
return True
|
||||
|
||||
def _process_bin_msg(self, msg: bytes, msg_type: str, topic: str, sender: str):
|
||||
"""
|
||||
Process a binary message received from the AutoGen system.
|
||||
"""
|
||||
Info(self._can2ag_name, f"proc_bin_msg: topic=[{topic}], msg_type=[{msg_type}]")
|
||||
if msg_type == ReceiveReq.__name__:
|
||||
return self.receive_msgproc(msg)
|
||||
elif msg_type == GenReplyReq.__name__:
|
||||
return self.generate_reply_msgproc(msg, sender)
|
||||
elif msg_type == PrepChat.__name__:
|
||||
return self.prepchat_msgproc(msg, sender)
|
||||
elif msg_type == Terminate.__name__:
|
||||
Warn(self._can2ag_name, f"TERMINATE received: topic=[{topic}], msg_type=[{msg_type}]")
|
||||
return False
|
||||
else:
|
||||
Error(self._can2ag_name, f"Unhandled message type: topic=[{topic}], msg_type=[{msg_type}]")
|
||||
return True
|
||||
39
samples/apps/cap/py/autogencap/ag_adapter/CAPGroupChat.py
Normal file
39
samples/apps/cap/py/autogencap/ag_adapter/CAPGroupChat.py
Normal file
@ -0,0 +1,39 @@
|
||||
from autogen import Agent, AssistantAgent, GroupChat
|
||||
from autogencap.ag_adapter.AG2CAP import AG2CAP
|
||||
from autogencap.ag_adapter.CAP2AG import CAP2AG
|
||||
from autogencap.LocalActorNetwork import LocalActorNetwork
|
||||
from typing import List
|
||||
|
||||
|
||||
class CAPGroupChat(GroupChat):
|
||||
def __init__(
|
||||
self,
|
||||
agents: List[AssistantAgent],
|
||||
messages: List[str],
|
||||
max_round: int,
|
||||
chat_initiator: str,
|
||||
network: LocalActorNetwork,
|
||||
):
|
||||
self.chat_initiator: str = chat_initiator
|
||||
self._cap_network: LocalActorNetwork = network
|
||||
self._cap_proxies: List[CAP2AG] = []
|
||||
self._ag_proxies: List[AG2CAP] = []
|
||||
self._ag_agents: List[Agent] = agents
|
||||
self._init_cap_proxies()
|
||||
self._init_ag_proxies()
|
||||
super().__init__(agents=self._ag_proxies, messages=messages, max_round=max_round)
|
||||
|
||||
def _init_cap_proxies(self):
|
||||
for agent in self._ag_agents:
|
||||
init_chat = agent.name == self.chat_initiator
|
||||
cap2ag = CAP2AG(ag_agent=agent, the_other_name="chat_manager", init_chat=init_chat, self_recursive=False)
|
||||
self._cap_network.register(cap2ag)
|
||||
self._cap_proxies.append(cap2ag)
|
||||
|
||||
def _init_ag_proxies(self):
|
||||
for agent in self._ag_agents:
|
||||
ag2cap = AG2CAP(self._cap_network, agent_name=agent.name, agent_description=agent.description)
|
||||
self._ag_proxies.append(ag2cap)
|
||||
|
||||
def is_running(self) -> bool:
|
||||
return all(proxy.run for proxy in self._cap_proxies)
|
||||
@ -0,0 +1,38 @@
|
||||
from autogen import GroupChatManager
|
||||
from autogencap.ActorConnector import ActorConnector
|
||||
from autogencap.LocalActorNetwork import LocalActorNetwork
|
||||
from autogencap.ag_adapter.CAP2AG import CAP2AG
|
||||
from autogencap.ag_adapter.CAPGroupChat import CAPGroupChat
|
||||
import time
|
||||
|
||||
|
||||
class CAPGroupChatManager:
|
||||
def __init__(self, groupchat: CAPGroupChat, llm_config: dict, network: LocalActorNetwork):
|
||||
self._network: LocalActorNetwork = network
|
||||
self._cap_group_chat: CAPGroupChat = groupchat
|
||||
self._ag_group_chat_manager: GroupChatManager = GroupChatManager(
|
||||
groupchat=self._cap_group_chat, llm_config=llm_config
|
||||
)
|
||||
self._cap_proxy: CAP2AG = CAP2AG(
|
||||
ag_agent=self._ag_group_chat_manager,
|
||||
the_other_name=self._cap_group_chat.chat_initiator,
|
||||
init_chat=False,
|
||||
self_recursive=True,
|
||||
)
|
||||
self._network.register(self._cap_proxy)
|
||||
|
||||
def initiate_chat(self, txt_msg: str) -> None:
|
||||
self._network.connect()
|
||||
user_proxy_conn: ActorConnector = self._network.lookup_actor(self._cap_group_chat.chat_initiator)
|
||||
user_proxy_conn.send_txt_msg(txt_msg)
|
||||
self._wait_for_user_exit()
|
||||
|
||||
def is_running(self) -> bool:
|
||||
return self._cap_group_chat.is_running()
|
||||
|
||||
def _wait_for_user_exit(self) -> None:
|
||||
try:
|
||||
while self.is_running():
|
||||
time.sleep(0.5)
|
||||
except KeyboardInterrupt:
|
||||
print("Interrupted by user, shutting down.")
|
||||
34
samples/apps/cap/py/autogencap/ag_adapter/CAPPair.py
Normal file
34
samples/apps/cap/py/autogencap/ag_adapter/CAPPair.py
Normal file
@ -0,0 +1,34 @@
|
||||
from autogencap.ag_adapter.CAP2AG import CAP2AG
|
||||
|
||||
|
||||
class CAPPair:
|
||||
def __init__(self, network, first, second):
|
||||
self._network = network
|
||||
self._first_ag_agent = first
|
||||
self._second_ag_agent = second
|
||||
self._first_adptr = None
|
||||
self._second_adptr = None
|
||||
|
||||
def initiate_chat(self, message: str):
|
||||
self._first_adptr = CAP2AG(
|
||||
ag_agent=self._first_ag_agent,
|
||||
the_other_name=self._second_ag_agent.name,
|
||||
init_chat=True,
|
||||
self_recursive=True,
|
||||
)
|
||||
self._second_adptr = CAP2AG(
|
||||
ag_agent=self._second_ag_agent,
|
||||
the_other_name=self._first_ag_agent.name,
|
||||
init_chat=False,
|
||||
self_recursive=True,
|
||||
)
|
||||
self._network.register(self._first_adptr)
|
||||
self._network.register(self._second_adptr)
|
||||
self._network.connect()
|
||||
|
||||
# Send a message to the user_proxy
|
||||
agent_connection = self._network.lookup_actor(self._first_ag_agent.name)
|
||||
agent_connection.send_txt_msg(message)
|
||||
|
||||
def running(self):
|
||||
return self._first_adptr.run and self._second_adptr.run
|
||||
35
samples/apps/cap/py/autogencap/proto/Autogen.proto
Normal file
35
samples/apps/cap/py/autogencap/proto/Autogen.proto
Normal file
@ -0,0 +1,35 @@
|
||||
syntax = "proto3";
|
||||
|
||||
// Get protoc here https://github.com/protocolbuffers/protobuf/releases
|
||||
// .\protoc --python_out=. --pyi_out=. Autogen.proto
|
||||
|
||||
message DataMap {
|
||||
map<string, string> data = 1;
|
||||
}
|
||||
|
||||
message ReceiveReq {
|
||||
oneof Type {
|
||||
DataMap data_map = 1;
|
||||
string data = 2;
|
||||
}
|
||||
optional string sender = 3;
|
||||
optional bool request_reply = 4;
|
||||
optional bool silent = 5;
|
||||
}
|
||||
|
||||
message Terminate {
|
||||
optional string sender = 1;
|
||||
}
|
||||
|
||||
message GenReplyReq {
|
||||
}
|
||||
|
||||
message GenReplyResp {
|
||||
optional string data = 1;
|
||||
}
|
||||
|
||||
message PrepChat {
|
||||
string recipient = 1;
|
||||
bool clear_history = 2;
|
||||
bool prepare_recipient = 3;
|
||||
}
|
||||
41
samples/apps/cap/py/autogencap/proto/Autogen_pb2.py
Normal file
41
samples/apps/cap/py/autogencap/proto/Autogen_pb2.py
Normal file
@ -0,0 +1,41 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: Autogen.proto
|
||||
# Protobuf Python Version: 4.25.2
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf.internal import builder as _builder
|
||||
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
|
||||
b'\n\rAutogen.proto"X\n\x07\x44\x61taMap\x12 \n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32\x12.DataMap.DataEntry\x1a+\n\tDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"\xb0\x01\n\nReceiveReq\x12\x1c\n\x08\x64\x61ta_map\x18\x01 \x01(\x0b\x32\x08.DataMapH\x00\x12\x0e\n\x04\x64\x61ta\x18\x02 \x01(\tH\x00\x12\x13\n\x06sender\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x1a\n\rrequest_reply\x18\x04 \x01(\x08H\x02\x88\x01\x01\x12\x13\n\x06silent\x18\x05 \x01(\x08H\x03\x88\x01\x01\x42\x06\n\x04TypeB\t\n\x07_senderB\x10\n\x0e_request_replyB\t\n\x07_silent"+\n\tTerminate\x12\x13\n\x06sender\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_sender"\r\n\x0bGenReplyReq"*\n\x0cGenReplyResp\x12\x11\n\x04\x64\x61ta\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x07\n\x05_data"O\n\x08PrepChat\x12\x11\n\trecipient\x18\x01 \x01(\t\x12\x15\n\rclear_history\x18\x02 \x01(\x08\x12\x19\n\x11prepare_recipient\x18\x03 \x01(\x08\x62\x06proto3'
|
||||
)
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "Autogen_pb2", _globals)
|
||||
if _descriptor._USE_C_DESCRIPTORS is False:
|
||||
DESCRIPTOR._options = None
|
||||
_globals["_DATAMAP_DATAENTRY"]._options = None
|
||||
_globals["_DATAMAP_DATAENTRY"]._serialized_options = b"8\001"
|
||||
_globals["_DATAMAP"]._serialized_start = 17
|
||||
_globals["_DATAMAP"]._serialized_end = 105
|
||||
_globals["_DATAMAP_DATAENTRY"]._serialized_start = 62
|
||||
_globals["_DATAMAP_DATAENTRY"]._serialized_end = 105
|
||||
_globals["_RECEIVEREQ"]._serialized_start = 108
|
||||
_globals["_RECEIVEREQ"]._serialized_end = 284
|
||||
_globals["_TERMINATE"]._serialized_start = 286
|
||||
_globals["_TERMINATE"]._serialized_end = 329
|
||||
_globals["_GENREPLYREQ"]._serialized_start = 331
|
||||
_globals["_GENREPLYREQ"]._serialized_end = 344
|
||||
_globals["_GENREPLYRESP"]._serialized_start = 346
|
||||
_globals["_GENREPLYRESP"]._serialized_end = 388
|
||||
_globals["_PREPCHAT"]._serialized_start = 390
|
||||
_globals["_PREPCHAT"]._serialized_end = 469
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
69
samples/apps/cap/py/autogencap/proto/Autogen_pb2.pyi
Normal file
69
samples/apps/cap/py/autogencap/proto/Autogen_pb2.pyi
Normal file
@ -0,0 +1,69 @@
|
||||
from google.protobuf.internal import containers as _containers
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from typing import ClassVar as _ClassVar, Mapping as _Mapping, Optional as _Optional, Union as _Union
|
||||
|
||||
DESCRIPTOR: _descriptor.FileDescriptor
|
||||
|
||||
class DataMap(_message.Message):
|
||||
__slots__ = ("data",)
|
||||
|
||||
class DataEntry(_message.Message):
|
||||
__slots__ = ("key", "value")
|
||||
KEY_FIELD_NUMBER: _ClassVar[int]
|
||||
VALUE_FIELD_NUMBER: _ClassVar[int]
|
||||
key: str
|
||||
value: str
|
||||
def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ...
|
||||
DATA_FIELD_NUMBER: _ClassVar[int]
|
||||
data: _containers.ScalarMap[str, str]
|
||||
def __init__(self, data: _Optional[_Mapping[str, str]] = ...) -> None: ...
|
||||
|
||||
class ReceiveReq(_message.Message):
|
||||
__slots__ = ("data_map", "data", "sender", "request_reply", "silent")
|
||||
DATA_MAP_FIELD_NUMBER: _ClassVar[int]
|
||||
DATA_FIELD_NUMBER: _ClassVar[int]
|
||||
SENDER_FIELD_NUMBER: _ClassVar[int]
|
||||
REQUEST_REPLY_FIELD_NUMBER: _ClassVar[int]
|
||||
SILENT_FIELD_NUMBER: _ClassVar[int]
|
||||
data_map: DataMap
|
||||
data: str
|
||||
sender: str
|
||||
request_reply: bool
|
||||
silent: bool
|
||||
def __init__(
|
||||
self,
|
||||
data_map: _Optional[_Union[DataMap, _Mapping]] = ...,
|
||||
data: _Optional[str] = ...,
|
||||
sender: _Optional[str] = ...,
|
||||
request_reply: bool = ...,
|
||||
silent: bool = ...,
|
||||
) -> None: ...
|
||||
|
||||
class Terminate(_message.Message):
|
||||
__slots__ = ("sender",)
|
||||
SENDER_FIELD_NUMBER: _ClassVar[int]
|
||||
sender: str
|
||||
def __init__(self, sender: _Optional[str] = ...) -> None: ...
|
||||
|
||||
class GenReplyReq(_message.Message):
|
||||
__slots__ = ()
|
||||
def __init__(self) -> None: ...
|
||||
|
||||
class GenReplyResp(_message.Message):
|
||||
__slots__ = ("data",)
|
||||
DATA_FIELD_NUMBER: _ClassVar[int]
|
||||
data: str
|
||||
def __init__(self, data: _Optional[str] = ...) -> None: ...
|
||||
|
||||
class PrepChat(_message.Message):
|
||||
__slots__ = ("recipient", "clear_history", "prepare_recipient")
|
||||
RECIPIENT_FIELD_NUMBER: _ClassVar[int]
|
||||
CLEAR_HISTORY_FIELD_NUMBER: _ClassVar[int]
|
||||
PREPARE_RECIPIENT_FIELD_NUMBER: _ClassVar[int]
|
||||
recipient: str
|
||||
clear_history: bool
|
||||
prepare_recipient: bool
|
||||
def __init__(
|
||||
self, recipient: _Optional[str] = ..., clear_history: bool = ..., prepare_recipient: bool = ...
|
||||
) -> None: ...
|
||||
35
samples/apps/cap/py/autogencap/proto/CAP.proto
Normal file
35
samples/apps/cap/py/autogencap/proto/CAP.proto
Normal file
@ -0,0 +1,35 @@
|
||||
syntax = "proto3";
|
||||
|
||||
// Get protoc here https://github.com/protocolbuffers/protobuf/releases
|
||||
// .\protoc --python_out=. --pyi_out=. CAP.proto
|
||||
|
||||
message ActorInfo {
|
||||
string name = 1;
|
||||
optional string namespace = 2;
|
||||
optional string description = 3;
|
||||
}
|
||||
|
||||
message ActorRegistration {
|
||||
ActorInfo actor_info = 1;
|
||||
}
|
||||
|
||||
message ActorLookup {
|
||||
optional ActorInfo actor_info = 1;
|
||||
// TODO: May need more structure here for semantic service discovery
|
||||
// optional string service_descriptor = 2;
|
||||
}
|
||||
|
||||
message ActorInfoCollection {
|
||||
repeated ActorInfo info_coll = 1;
|
||||
}
|
||||
|
||||
message ActorLookupResponse {
|
||||
bool found = 1;
|
||||
optional ActorInfoCollection actor = 2;
|
||||
}
|
||||
|
||||
message Ping {
|
||||
}
|
||||
|
||||
message Pong {
|
||||
}
|
||||
39
samples/apps/cap/py/autogencap/proto/CAP_pb2.py
Normal file
39
samples/apps/cap/py/autogencap/proto/CAP_pb2.py
Normal file
@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: CAP.proto
|
||||
# Protobuf Python Version: 4.25.2
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf.internal import builder as _builder
|
||||
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
|
||||
b'\n\tCAP.proto"i\n\tActorInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x16\n\tnamespace\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x0c\n\n_namespaceB\x0e\n\x0c_description"3\n\x11\x41\x63torRegistration\x12\x1e\n\nactor_info\x18\x01 \x01(\x0b\x32\n.ActorInfo"A\n\x0b\x41\x63torLookup\x12#\n\nactor_info\x18\x01 \x01(\x0b\x32\n.ActorInfoH\x00\x88\x01\x01\x42\r\n\x0b_actor_info"4\n\x13\x41\x63torInfoCollection\x12\x1d\n\tinfo_coll\x18\x01 \x03(\x0b\x32\n.ActorInfo"X\n\x13\x41\x63torLookupResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12(\n\x05\x61\x63tor\x18\x02 \x01(\x0b\x32\x14.ActorInfoCollectionH\x00\x88\x01\x01\x42\x08\n\x06_actor"\x06\n\x04Ping"\x06\n\x04Pongb\x06proto3'
|
||||
)
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "CAP_pb2", _globals)
|
||||
if _descriptor._USE_C_DESCRIPTORS is False:
|
||||
DESCRIPTOR._options = None
|
||||
_globals["_ACTORINFO"]._serialized_start = 13
|
||||
_globals["_ACTORINFO"]._serialized_end = 118
|
||||
_globals["_ACTORREGISTRATION"]._serialized_start = 120
|
||||
_globals["_ACTORREGISTRATION"]._serialized_end = 171
|
||||
_globals["_ACTORLOOKUP"]._serialized_start = 173
|
||||
_globals["_ACTORLOOKUP"]._serialized_end = 238
|
||||
_globals["_ACTORINFOCOLLECTION"]._serialized_start = 240
|
||||
_globals["_ACTORINFOCOLLECTION"]._serialized_end = 292
|
||||
_globals["_ACTORLOOKUPRESPONSE"]._serialized_start = 294
|
||||
_globals["_ACTORLOOKUPRESPONSE"]._serialized_end = 382
|
||||
_globals["_PING"]._serialized_start = 384
|
||||
_globals["_PING"]._serialized_end = 390
|
||||
_globals["_PONG"]._serialized_start = 392
|
||||
_globals["_PONG"]._serialized_end = 398
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
58
samples/apps/cap/py/autogencap/proto/CAP_pb2.pyi
Normal file
58
samples/apps/cap/py/autogencap/proto/CAP_pb2.pyi
Normal file
@ -0,0 +1,58 @@
|
||||
from google.protobuf.internal import containers as _containers
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from typing import (
|
||||
ClassVar as _ClassVar,
|
||||
Iterable as _Iterable,
|
||||
Mapping as _Mapping,
|
||||
Optional as _Optional,
|
||||
Union as _Union,
|
||||
)
|
||||
|
||||
DESCRIPTOR: _descriptor.FileDescriptor
|
||||
|
||||
class ActorInfo(_message.Message):
|
||||
__slots__ = ("name", "namespace", "description")
|
||||
NAME_FIELD_NUMBER: _ClassVar[int]
|
||||
NAMESPACE_FIELD_NUMBER: _ClassVar[int]
|
||||
DESCRIPTION_FIELD_NUMBER: _ClassVar[int]
|
||||
name: str
|
||||
namespace: str
|
||||
description: str
|
||||
def __init__(
|
||||
self, name: _Optional[str] = ..., namespace: _Optional[str] = ..., description: _Optional[str] = ...
|
||||
) -> None: ...
|
||||
|
||||
class ActorRegistration(_message.Message):
|
||||
__slots__ = ("actor_info",)
|
||||
ACTOR_INFO_FIELD_NUMBER: _ClassVar[int]
|
||||
actor_info: ActorInfo
|
||||
def __init__(self, actor_info: _Optional[_Union[ActorInfo, _Mapping]] = ...) -> None: ...
|
||||
|
||||
class ActorLookup(_message.Message):
|
||||
__slots__ = ("actor_info",)
|
||||
ACTOR_INFO_FIELD_NUMBER: _ClassVar[int]
|
||||
actor_info: ActorInfo
|
||||
def __init__(self, actor_info: _Optional[_Union[ActorInfo, _Mapping]] = ...) -> None: ...
|
||||
|
||||
class ActorInfoCollection(_message.Message):
|
||||
__slots__ = ("info_coll",)
|
||||
INFO_COLL_FIELD_NUMBER: _ClassVar[int]
|
||||
info_coll: _containers.RepeatedCompositeFieldContainer[ActorInfo]
|
||||
def __init__(self, info_coll: _Optional[_Iterable[_Union[ActorInfo, _Mapping]]] = ...) -> None: ...
|
||||
|
||||
class ActorLookupResponse(_message.Message):
|
||||
__slots__ = ("found", "actor")
|
||||
FOUND_FIELD_NUMBER: _ClassVar[int]
|
||||
ACTOR_FIELD_NUMBER: _ClassVar[int]
|
||||
found: bool
|
||||
actor: ActorInfoCollection
|
||||
def __init__(self, found: bool = ..., actor: _Optional[_Union[ActorInfoCollection, _Mapping]] = ...) -> None: ...
|
||||
|
||||
class Ping(_message.Message):
|
||||
__slots__ = ()
|
||||
def __init__(self) -> None: ...
|
||||
|
||||
class Pong(_message.Message):
|
||||
__slots__ = ()
|
||||
def __init__(self) -> None: ...
|
||||
0
samples/apps/cap/py/autogencap/proto/__init__.py
Normal file
0
samples/apps/cap/py/autogencap/proto/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
.\protoc --pyi_out=. --python_out=. CAP.proto Autogen.proto
|
||||
4
samples/apps/cap/py/autogencap/requirements.txt
Normal file
4
samples/apps/cap/py/autogencap/requirements.txt
Normal file
@ -0,0 +1,4 @@
|
||||
pyzmq
|
||||
protobuf
|
||||
termcolor
|
||||
pyautogen
|
||||
7
samples/apps/cap/py/autogencap/setup.py
Normal file
7
samples/apps/cap/py/autogencap/setup.py
Normal file
@ -0,0 +1,7 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="autogencap",
|
||||
version="0.1",
|
||||
packages=find_packages(),
|
||||
)
|
||||
12
samples/apps/cap/py/demo/AGDemo.py
Normal file
12
samples/apps/cap/py/demo/AGDemo.py
Normal file
@ -0,0 +1,12 @@
|
||||
from autogen import AssistantAgent, UserProxyAgent, config_list_from_json
|
||||
|
||||
|
||||
def ag_demo():
|
||||
config_list = config_list_from_json(env_or_file="OAI_CONFIG_LIST")
|
||||
assistant = AssistantAgent("assistant", llm_config={"config_list": config_list})
|
||||
user_proxy = UserProxyAgent(
|
||||
"user_proxy",
|
||||
code_execution_config={"work_dir": "coding"},
|
||||
is_termination_msg=lambda x: "TERMINATE" in x.get("content"),
|
||||
)
|
||||
user_proxy.initiate_chat(assistant, message="Plot a chart of MSFT daily closing prices for last 1 Month.")
|
||||
34
samples/apps/cap/py/demo/AGGroupChatDemo.py
Normal file
34
samples/apps/cap/py/demo/AGGroupChatDemo.py
Normal file
@ -0,0 +1,34 @@
|
||||
from autogen import AssistantAgent, GroupChat, GroupChatManager, UserProxyAgent, config_list_from_json
|
||||
|
||||
|
||||
def ag_groupchat_demo():
|
||||
config_list = config_list_from_json(env_or_file="OAI_CONFIG_LIST")
|
||||
gpt4_config = {
|
||||
"cache_seed": 72,
|
||||
"temperature": 0,
|
||||
"config_list": config_list,
|
||||
"timeout": 120,
|
||||
}
|
||||
user_proxy = UserProxyAgent(
|
||||
name="User_proxy",
|
||||
system_message="A human admin.",
|
||||
code_execution_config={
|
||||
"last_n_messages": 2,
|
||||
"work_dir": "groupchat",
|
||||
"use_docker": False,
|
||||
},
|
||||
human_input_mode="TERMINATE",
|
||||
is_termination_msg=lambda x: "TERMINATE" in x.get("content"),
|
||||
)
|
||||
coder = AssistantAgent(name="Coder", llm_config=gpt4_config)
|
||||
pm = AssistantAgent(
|
||||
name="Product_manager",
|
||||
system_message="Creative in software product ideas.",
|
||||
llm_config=gpt4_config,
|
||||
)
|
||||
groupchat = GroupChat(agents=[user_proxy, coder, pm], messages=[], max_round=12)
|
||||
manager = GroupChatManager(groupchat=groupchat, llm_config=gpt4_config)
|
||||
user_proxy.initiate_chat(
|
||||
manager,
|
||||
message="Find a latest paper about gpt-4 on arxiv and find its potential applications in software.",
|
||||
)
|
||||
66
samples/apps/cap/py/demo/App.py
Normal file
66
samples/apps/cap/py/demo/App.py
Normal file
@ -0,0 +1,66 @@
|
||||
"""
|
||||
Demo App
|
||||
"""
|
||||
import argparse
|
||||
import _paths
|
||||
from autogencap.Config import LOG_LEVEL, IGNORED_LOG_CONTEXTS
|
||||
import autogencap.DebugLog as DebugLog
|
||||
from SimpleActorDemo import simple_actor_demo
|
||||
from AGDemo import ag_demo
|
||||
from AGGroupChatDemo import ag_groupchat_demo
|
||||
from CAPAutGenGroupDemo import cap_ag_group_demo
|
||||
from CAPAutoGenPairDemo import cap_ag_pair_demo
|
||||
from ComplexActorDemo import complex_actor_demo
|
||||
from RemoteAGDemo import remote_ag_demo
|
||||
|
||||
####################################################################################################
|
||||
|
||||
|
||||
def parse_args():
|
||||
# Create a parser for the command line arguments
|
||||
parser = argparse.ArgumentParser(description="Demo App")
|
||||
parser.add_argument("--log_level", type=int, default=1, help="Set the log level (0-3)")
|
||||
# Parse the command line arguments
|
||||
args = parser.parse_args()
|
||||
# Set the log level
|
||||
# Print log level string based on names in debug_log.py
|
||||
print(f"Log level: {DebugLog.LEVEL_NAMES[args.log_level]}")
|
||||
# IGNORED_LOG_CONTEXTS.extend(["BROKER"])
|
||||
|
||||
|
||||
####################################################################################################
|
||||
|
||||
|
||||
def main():
|
||||
parse_args()
|
||||
while True:
|
||||
print("Select the Composable Actor Platform (CAP) demo app to run:")
|
||||
print("(enter anything else to quit)")
|
||||
print("1. Hello World")
|
||||
print("2. Complex Agent (e.g. Name or Quit)")
|
||||
print("3. AutoGen Pair")
|
||||
print("4. AutoGen GroupChat")
|
||||
print("5. AutoGen Agents in different processes")
|
||||
choice = input("Enter your choice (1-5): ")
|
||||
|
||||
if choice == "1":
|
||||
simple_actor_demo()
|
||||
elif choice == "2":
|
||||
complex_actor_demo()
|
||||
# elif choice == "3":
|
||||
# ag_demo()
|
||||
elif choice == "3":
|
||||
cap_ag_pair_demo()
|
||||
# elif choice == "5":
|
||||
# ag_groupchat_demo()
|
||||
elif choice == "4":
|
||||
cap_ag_group_demo()
|
||||
elif choice == "5":
|
||||
remote_ag_demo()
|
||||
else:
|
||||
print("Quitting...")
|
||||
break
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
189
samples/apps/cap/py/demo/AppAgents.py
Normal file
189
samples/apps/cap/py/demo/AppAgents.py
Normal file
@ -0,0 +1,189 @@
|
||||
"""
|
||||
This file contains the implementation of various agents used in the application.
|
||||
Each agent represents a different role and knows how to connect to external systems
|
||||
to retrieve information.
|
||||
"""
|
||||
|
||||
from autogencap.DebugLog import Debug, Info, shorten
|
||||
from autogencap.LocalActorNetwork import LocalActorNetwork
|
||||
from autogencap.ActorConnector import ActorConnector
|
||||
from autogencap.Actor import Actor
|
||||
|
||||
|
||||
class GreeterAgent(Actor):
|
||||
"""
|
||||
Prints message to screen
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
agent_name="Greeter",
|
||||
description="This is the greeter agent, who knows how to greet people.",
|
||||
):
|
||||
super().__init__(agent_name, description)
|
||||
|
||||
|
||||
class FidelityAgent(Actor):
|
||||
"""
|
||||
This class represents the fidelity agent, who knows how to connect to fidelity to get account,
|
||||
portfolio, and order information.
|
||||
|
||||
Args:
|
||||
agent_name (str, optional): The name of the agent. Defaults to "Fidelity".
|
||||
description (str, optional): A description of the agent. Defaults to "This is the
|
||||
fidelity agent who knows how to connect to fidelity to get account, portfolio, and
|
||||
order information."
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
agent_name="Fidelity",
|
||||
description=(
|
||||
"This is the fidelity agent, who knows"
|
||||
"how to connect to fidelity to get account, portfolio, and order information."
|
||||
),
|
||||
):
|
||||
super().__init__(agent_name, description)
|
||||
|
||||
|
||||
class FinancialPlannerAgent(Actor):
|
||||
"""
|
||||
This class represents the financial planner agent, who knows how to connect to a financial
|
||||
planner and get financial planning information.
|
||||
|
||||
Args:
|
||||
agent_name (str, optional): The name of the agent. Defaults to "Financial Planner".
|
||||
description (str, optional): A description of the agent. Defaults to "This is the
|
||||
financial planner agent, who knows how to connect to a financial planner and get
|
||||
financial planning information."
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
agent_name="Financial Planner",
|
||||
description=(
|
||||
"This is the financial planner"
|
||||
" agent, who knows how to connect to a financial planner and get financial"
|
||||
" planning information."
|
||||
),
|
||||
):
|
||||
super().__init__(agent_name, description)
|
||||
|
||||
|
||||
class QuantAgent(Actor):
|
||||
"""
|
||||
This class represents the quant agent, who knows how to connect to a quant and get
|
||||
quant information.
|
||||
|
||||
Args:
|
||||
agent_name (str, optional): The name of the agent. Defaults to "Quant".
|
||||
description (str, optional): A description of the agent. Defaults to "This is the
|
||||
quant agent, who knows how to connect to a quant and get quant information."
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
agent_name="Quant",
|
||||
description="This is the quant agent, who knows " "how to connect to a quant and get quant information.",
|
||||
):
|
||||
super().__init__(agent_name, description)
|
||||
|
||||
|
||||
class RiskManager(Actor):
|
||||
"""
|
||||
This class represents a risk manager, who will analyze portfolio risk.
|
||||
|
||||
Args:
|
||||
description (str, optional): A description of the agent. Defaults to "This is the user
|
||||
interface agent, who knows how to connect to a user interface and get
|
||||
user interface information."
|
||||
"""
|
||||
|
||||
cls_agent_name = "Risk Manager"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
description=(
|
||||
"This is the user interface agent, who knows how to connect"
|
||||
" to a user interface and get user interface information."
|
||||
),
|
||||
):
|
||||
super().__init__(RiskManager.cls_agent_name, description)
|
||||
|
||||
|
||||
class PersonalAssistant(Actor):
|
||||
"""
|
||||
This class represents the personal assistant, who knows how to connect to the other agents and
|
||||
get information from them.
|
||||
|
||||
Args:
|
||||
agent_name (str, optional): The name of the agent. Defaults to "PersonalAssistant".
|
||||
description (str, optional): A description of the agent. Defaults to "This is the personal assistant,
|
||||
who knows how to connect to the other agents and get information from them."
|
||||
"""
|
||||
|
||||
cls_agent_name = "PersonalAssistant"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
agent_name=cls_agent_name,
|
||||
description="This is the personal assistant, who knows how to connect to the other agents and get information from them.",
|
||||
):
|
||||
super().__init__(agent_name, description)
|
||||
self.fidelity: ActorConnector = None
|
||||
self.financial_planner: ActorConnector = None
|
||||
self.quant: ActorConnector = None
|
||||
self.risk_manager: ActorConnector = None
|
||||
|
||||
def connect_network(self, network: LocalActorNetwork):
|
||||
"""
|
||||
Connects the personal assistant to the specified local actor network.
|
||||
|
||||
Args:
|
||||
network (LocalActorNetwork): The local actor network to connect to.
|
||||
"""
|
||||
Debug(self.actor_name, f"is connecting to {network}")
|
||||
self.fidelity = network.lookup_actor("Fidelity")
|
||||
self.financial_planner = network.lookup_actor("Financial Planner")
|
||||
self.quant = network.lookup_actor("Quant")
|
||||
self.risk_manager = network.lookup_actor("Risk Manager")
|
||||
Debug(self.actor_name, "connected")
|
||||
|
||||
def disconnect_network(self, network: LocalActorNetwork):
|
||||
"""
|
||||
Disconnects the personal assistant from the specified local actor network.
|
||||
|
||||
Args:
|
||||
network (LocalActorNetwork): The local actor network to disconnect from.
|
||||
"""
|
||||
super().disconnect_network(network)
|
||||
self.fidelity.close()
|
||||
self.financial_planner.close()
|
||||
self.quant.close()
|
||||
self.risk_manager.close()
|
||||
Debug(self.actor_name, "disconnected")
|
||||
|
||||
def _process_txt_msg(self, msg, msg_type, topic, sender):
|
||||
"""
|
||||
Processes a text message received by the personal assistant.
|
||||
|
||||
Args:
|
||||
msg (str): The text message.
|
||||
msg_type (str): The type of the message.
|
||||
topic (str): The topic of the message.
|
||||
sender (str): The sender of the message.
|
||||
|
||||
Returns:
|
||||
bool: True if the message was processed successfully, False otherwise.
|
||||
"""
|
||||
if msg.strip().lower() != "quit" and msg.strip().lower() != "":
|
||||
Info(self.actor_name, f"Helping user: {shorten(msg)}")
|
||||
self.fidelity.send_txt_msg(f"I, {self.actor_name}, need your help to buy/sell assets for " + msg)
|
||||
self.financial_planner.send_txt_msg(
|
||||
f"I, {self.actor_name}, need your help in creating a financial plan for {msg}'s goals."
|
||||
)
|
||||
self.quant.send_txt_msg(
|
||||
f"I, {self.actor_name}, need your help with quantitative analysis of the interest rate for " + msg
|
||||
)
|
||||
self.risk_manager.send_txt_msg(f"I, {self.actor_name}, need your help in analyzing {msg}'s portfolio risk")
|
||||
return True
|
||||
40
samples/apps/cap/py/demo/CAPAutGenGroupDemo.py
Normal file
40
samples/apps/cap/py/demo/CAPAutGenGroupDemo.py
Normal file
@ -0,0 +1,40 @@
|
||||
from autogen import AssistantAgent, UserProxyAgent, config_list_from_json
|
||||
from autogencap.DebugLog import Info
|
||||
from autogencap.LocalActorNetwork import LocalActorNetwork
|
||||
from autogencap.ag_adapter.CAPGroupChat import CAPGroupChat
|
||||
from autogencap.ag_adapter.CAPGroupChatManager import CAPGroupChatManager
|
||||
|
||||
|
||||
def cap_ag_group_demo():
|
||||
config_list = config_list_from_json(env_or_file="OAI_CONFIG_LIST")
|
||||
gpt4_config = {
|
||||
"cache_seed": 73,
|
||||
"temperature": 0,
|
||||
"config_list": config_list,
|
||||
"timeout": 120,
|
||||
}
|
||||
user_proxy = UserProxyAgent(
|
||||
name="User_proxy",
|
||||
system_message="A human admin.",
|
||||
is_termination_msg=lambda x: "TERMINATE" in x.get("content"),
|
||||
code_execution_config={
|
||||
"last_n_messages": 2,
|
||||
"work_dir": "groupchat",
|
||||
"use_docker": False,
|
||||
},
|
||||
human_input_mode="TERMINATE",
|
||||
)
|
||||
coder = AssistantAgent(name="Coder", llm_config=gpt4_config)
|
||||
pm = AssistantAgent(
|
||||
name="Product_manager",
|
||||
system_message="Creative in software product ideas.",
|
||||
llm_config=gpt4_config,
|
||||
)
|
||||
network = LocalActorNetwork()
|
||||
cap_groupchat = CAPGroupChat(
|
||||
agents=[user_proxy, coder, pm], messages=[], max_round=12, network=network, chat_initiator=user_proxy.name
|
||||
)
|
||||
manager = CAPGroupChatManager(groupchat=cap_groupchat, llm_config=gpt4_config, network=network)
|
||||
manager.initiate_chat("Find a latest paper about gpt-4 on arxiv and find its potential applications in software.")
|
||||
network.disconnect()
|
||||
Info("App", "App Exit")
|
||||
31
samples/apps/cap/py/demo/CAPAutoGenPairDemo.py
Normal file
31
samples/apps/cap/py/demo/CAPAutoGenPairDemo.py
Normal file
@ -0,0 +1,31 @@
|
||||
import time
|
||||
from autogen import AssistantAgent, UserProxyAgent, config_list_from_json
|
||||
from autogencap.DebugLog import Info
|
||||
from autogencap.ag_adapter.CAPPair import CAPPair
|
||||
from autogencap.LocalActorNetwork import LocalActorNetwork
|
||||
|
||||
|
||||
def cap_ag_pair_demo():
|
||||
config_list = config_list_from_json(env_or_file="OAI_CONFIG_LIST")
|
||||
assistant = AssistantAgent("assistant", llm_config={"config_list": config_list})
|
||||
user_proxy = UserProxyAgent(
|
||||
"user_proxy",
|
||||
code_execution_config={"work_dir": "coding"},
|
||||
is_termination_msg=lambda x: "TERMINATE" in x.get("content"),
|
||||
)
|
||||
|
||||
# Composable Agent Platform AutoGen Pair adapter
|
||||
network = LocalActorNetwork()
|
||||
|
||||
pair = CAPPair(network, user_proxy, assistant)
|
||||
pair.initiate_chat("Plot a chart of MSFT daily closing prices for last 1 Month.")
|
||||
|
||||
# Wait for the pair to finish
|
||||
try:
|
||||
while pair.running():
|
||||
time.sleep(0.5)
|
||||
except KeyboardInterrupt:
|
||||
print("Interrupted by user, shutting down.")
|
||||
|
||||
network.disconnect()
|
||||
Info("App", "App Exit")
|
||||
50
samples/apps/cap/py/demo/ComplexActorDemo.py
Normal file
50
samples/apps/cap/py/demo/ComplexActorDemo.py
Normal file
@ -0,0 +1,50 @@
|
||||
import time
|
||||
from termcolor import colored
|
||||
from autogencap.LocalActorNetwork import LocalActorNetwork
|
||||
from AppAgents import FidelityAgent, FinancialPlannerAgent, PersonalAssistant, QuantAgent, RiskManager
|
||||
|
||||
|
||||
def complex_actor_demo():
|
||||
"""
|
||||
This function demonstrates the usage of a complex actor system.
|
||||
It creates a local actor graph, registers various agents,
|
||||
connects them, and interacts with a personal assistant agent.
|
||||
The function continuously prompts the user for input messages,
|
||||
sends them to the personal assistant agent, and terminates
|
||||
when the user enters "quit".
|
||||
"""
|
||||
network = LocalActorNetwork()
|
||||
# Register agents
|
||||
network.register(PersonalAssistant())
|
||||
network.register(FidelityAgent())
|
||||
network.register(FinancialPlannerAgent())
|
||||
network.register(RiskManager())
|
||||
network.register(QuantAgent())
|
||||
# Tell agents to connect to other agents
|
||||
network.connect()
|
||||
# Get a channel to the personal assistant agent
|
||||
pa = network.lookup_actor(PersonalAssistant.cls_agent_name)
|
||||
info_msg = """
|
||||
This is an imaginary personal assistant agent scenario.
|
||||
Five actors are connected in a self-determined graph. The user
|
||||
can interact with the personal assistant agent by entering
|
||||
their name. The personal assistant agent will then enlist
|
||||
the other four agents to create a financial plan.
|
||||
|
||||
Start by entering your name.
|
||||
"""
|
||||
print(colored(info_msg, "blue"))
|
||||
|
||||
while True:
|
||||
# For aesthetic reasons, better to let network messages complete
|
||||
time.sleep(0.1)
|
||||
# Get a message from the user
|
||||
msg = input(colored("Enter your name (or quit): ", "light_red"))
|
||||
# Send the message to the personal assistant agent
|
||||
pa.send_txt_msg(msg)
|
||||
if msg.lower() == "quit":
|
||||
break
|
||||
# Cleanup
|
||||
|
||||
pa.close()
|
||||
network.disconnect()
|
||||
18
samples/apps/cap/py/demo/RemoteAGDemo.py
Normal file
18
samples/apps/cap/py/demo/RemoteAGDemo.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Start Broker & Assistant
|
||||
# Start UserProxy - Let it run
|
||||
|
||||
|
||||
def remote_ag_demo():
|
||||
print("Remote Agent Demo")
|
||||
instructions = """
|
||||
In this demo, Broker, Assistant, and UserProxy are running in separate processes.
|
||||
demo/standalone/UserProxy.py will initiate a conversation by sending UserProxy a message.
|
||||
|
||||
Please do the following:
|
||||
1) Start Broker (python demo/standalone/Broker.py)
|
||||
2) Start Assistant (python demo/standalone/Assistant.py)
|
||||
3) Start UserProxy (python demo/standalone/UserProxy.py)
|
||||
"""
|
||||
print(instructions)
|
||||
input("Press Enter to return to demo menu...")
|
||||
pass
|
||||
26
samples/apps/cap/py/demo/SimpleActorDemo.py
Normal file
26
samples/apps/cap/py/demo/SimpleActorDemo.py
Normal file
@ -0,0 +1,26 @@
|
||||
import time
|
||||
from AppAgents import GreeterAgent
|
||||
from autogencap.LocalActorNetwork import LocalActorNetwork
|
||||
|
||||
|
||||
def simple_actor_demo():
|
||||
"""
|
||||
Demonstrates the usage of the CAP platform by registering an actor, connecting to the actor,
|
||||
sending a message, and performing cleanup operations.
|
||||
"""
|
||||
# CAP Platform
|
||||
|
||||
network = LocalActorNetwork()
|
||||
# Register an actor
|
||||
network.register(GreeterAgent())
|
||||
# Tell actor to connect to other actors
|
||||
network.connect()
|
||||
# Get a channel to the actor
|
||||
greeter_link = network.lookup_actor("Greeter")
|
||||
time.sleep(1)
|
||||
# Send a message to the actor
|
||||
greeter_link.send_txt_msg("Hello World!")
|
||||
time.sleep(1)
|
||||
# Cleanup
|
||||
greeter_link.close()
|
||||
network.disconnect()
|
||||
7
samples/apps/cap/py/demo/_paths.py
Normal file
7
samples/apps/cap/py/demo/_paths.py
Normal file
@ -0,0 +1,7 @@
|
||||
# Add autogencap to system path in case autogencap is not pip installed
|
||||
# Since this library has not been published to PyPi, it is not easy to install using pip
|
||||
import sys
|
||||
import os
|
||||
|
||||
absparent = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
sys.path.append(absparent)
|
||||
40
samples/apps/cap/py/demo/standalone/Assistant.py
Normal file
40
samples/apps/cap/py/demo/standalone/Assistant.py
Normal file
@ -0,0 +1,40 @@
|
||||
import time
|
||||
import _paths
|
||||
from autogen import AssistantAgent, config_list_from_json
|
||||
from autogencap.DebugLog import Info
|
||||
from autogencap.LocalActorNetwork import LocalActorNetwork
|
||||
from autogencap.ag_adapter.CAP2AG import CAP2AG
|
||||
|
||||
|
||||
# Starts the Broker and the Assistant. The UserProxy is started separately.
|
||||
class StandaloneAssistant:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
print("Running the StandaloneAssistant")
|
||||
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
|
||||
network = LocalActorNetwork()
|
||||
assistant_adptr = CAP2AG(ag_agent=assistant, the_other_name="user_proxy", init_chat=False, self_recursive=True)
|
||||
network.register(assistant_adptr)
|
||||
network.connect()
|
||||
|
||||
# Hang around for a while
|
||||
try:
|
||||
while assistant_adptr.run:
|
||||
time.sleep(0.5)
|
||||
except KeyboardInterrupt:
|
||||
print("Interrupted by user, shutting down.")
|
||||
network.disconnect()
|
||||
Info("StandaloneAssistant", "App Exit")
|
||||
|
||||
|
||||
def main():
|
||||
assistant = StandaloneAssistant()
|
||||
assistant.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
5
samples/apps/cap/py/demo/standalone/Broker.py
Normal file
5
samples/apps/cap/py/demo/standalone/Broker.py
Normal file
@ -0,0 +1,5 @@
|
||||
import _paths
|
||||
from autogencap.Broker import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
5
samples/apps/cap/py/demo/standalone/DirectorySvc.py
Normal file
5
samples/apps/cap/py/demo/standalone/DirectorySvc.py
Normal file
@ -0,0 +1,5 @@
|
||||
import _paths
|
||||
from autogencap.DirectorySvc import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
56
samples/apps/cap/py/demo/standalone/UserProxy.py
Normal file
56
samples/apps/cap/py/demo/standalone/UserProxy.py
Normal file
@ -0,0 +1,56 @@
|
||||
import time
|
||||
import _paths
|
||||
from autogen import UserProxyAgent, config_list_from_json
|
||||
from autogencap.DebugLog import Info
|
||||
from autogencap.LocalActorNetwork import LocalActorNetwork
|
||||
from autogencap.ag_adapter.CAP2AG import CAP2AG
|
||||
from autogencap.Config import IGNORED_LOG_CONTEXTS
|
||||
|
||||
|
||||
# Starts the Broker and the Assistant. The UserProxy is started separately.
|
||||
class StandaloneUserProxy:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
print("Running the StandaloneUserProxy")
|
||||
|
||||
user_proxy = UserProxyAgent(
|
||||
"user_proxy",
|
||||
code_execution_config={"work_dir": "coding"},
|
||||
is_termination_msg=lambda x: "TERMINATE" in x.get("content"),
|
||||
)
|
||||
# Composable Agent Network adapter
|
||||
network = LocalActorNetwork()
|
||||
user_proxy_adptr = CAP2AG(ag_agent=user_proxy, the_other_name="assistant", init_chat=True, self_recursive=True)
|
||||
network.register(user_proxy_adptr)
|
||||
network.connect()
|
||||
|
||||
# Send a message to the user_proxy
|
||||
user_proxy_conn = network.lookup_actor("user_proxy")
|
||||
example = "Plot a chart of MSFT daily closing prices for last 1 Month."
|
||||
print(f"Example: {example}")
|
||||
try:
|
||||
user_input = input("Please enter your command: ")
|
||||
if user_input == "":
|
||||
user_input = example
|
||||
print(f"Sending: {user_input}")
|
||||
user_proxy_conn.send_txt_msg(user_input)
|
||||
|
||||
# Hang around for a while
|
||||
while user_proxy_adptr.run:
|
||||
time.sleep(0.5)
|
||||
except KeyboardInterrupt:
|
||||
print("Interrupted by user, shutting down.")
|
||||
network.disconnect()
|
||||
Info("StandaloneUserProxy", "App Exit")
|
||||
|
||||
|
||||
def main():
|
||||
IGNORED_LOG_CONTEXTS.extend(["BROKER", "DirectorySvc"])
|
||||
assistant = StandaloneUserProxy()
|
||||
assistant.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
7
samples/apps/cap/py/demo/standalone/_paths.py
Normal file
7
samples/apps/cap/py/demo/standalone/_paths.py
Normal file
@ -0,0 +1,7 @@
|
||||
# Add autogencap to system path in case autogencap is not pip installed
|
||||
# Since this library has not been published to PyPi, it is not easy to install using pip
|
||||
import sys
|
||||
import os
|
||||
|
||||
absparent = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
|
||||
sys.path.append(absparent)
|
||||
Loading…
x
Reference in New Issue
Block a user