Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Composable Actor Platform for AutoGen #1655

Merged
merged 101 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
101 commits
Select commit Hold shift + click to select a range
3d8f9d9
Core CAP components + Autogen adapter + Demo
rajan-chari Feb 12, 2024
dea9cdc
Cleanup Readme
rajan-chari Feb 12, 2024
5063503
C# folder
rajan-chari Feb 12, 2024
7f793e8
Cleanup readme
rajan-chari Feb 12, 2024
fdf4f54
summary_method bug fix
rajan-chari Feb 13, 2024
a802ff4
CAN -> CAP
rajan-chari Feb 13, 2024
2663379
Merge branch 'main' into rajan/cap
rajan-chari Feb 13, 2024
521876a
pre-commit fixes
rajan-chari Feb 13, 2024
b8d15c0
pre-commit fixes
rajan-chari Feb 13, 2024
ffc35d3
modification of sys path should ignore E402
rajan-chari Feb 13, 2024
c74132e
fix pre-commit check issues
rajan-chari Feb 13, 2024
adfde5b
Updated docs
rajan-chari Feb 13, 2024
4f6c920
Clean up docs
rajan-chari Feb 13, 2024
698ccd7
Merge branch 'main' into rajan/cap
rajan-chari Feb 13, 2024
9275807
more refactoring
rajan-chari Feb 14, 2024
136e4bf
better packaging refactor
rajan-chari Feb 16, 2024
c7b09ce
Merge branch 'main' into rajan/cap
rajan-chari Feb 16, 2024
4d4cb8e
Refactoring for package changes
rajan-chari Feb 18, 2024
f4858ff
Run demo app without autogencap installed or in the path
rajan-chari Feb 18, 2024
0aac04c
Remove debug related sleep()
rajan-chari Feb 19, 2024
605f21d
removed CAP in some class names
rajan-chari Feb 19, 2024
fd8d990
Investigate a logging framework that supports color in windows
rajan-chari Feb 19, 2024
bd44d26
added type hints
rajan-chari Feb 20, 2024
cf860d1
remove circular dependency
rajan-chari Feb 20, 2024
3dc522b
fixed pre-commit issues
rajan-chari Feb 20, 2024
524263e
pre-commit ruff issues
rajan-chari Feb 20, 2024
c85eabd
removed circular definition
rajan-chari Feb 20, 2024
70c5a14
pre-commit fixes
rajan-chari Feb 20, 2024
aa41e17
Fix pre-commit issues
rajan-chari Feb 20, 2024
b3201bf
pre-commit fixes
rajan-chari Feb 20, 2024
2b867b5
Merge branch 'main' into rajan/cap
rajan-chari Feb 20, 2024
34f4724
updated for _prepare_chat signature changes
rajan-chari Feb 20, 2024
f9e9d6c
Better instructions for demo and some minor refactoring
rajan-chari Feb 21, 2024
58defba
Merge branch 'main' into rajan/cap
rajan-chari Feb 21, 2024
3b9a15b
Added details that explain CAP
rajan-chari Feb 21, 2024
85a768e
Reformat Readme
rajan-chari Feb 21, 2024
879ab24
More ReadMe Formatting
rajan-chari Feb 21, 2024
b9fd73b
Readme edits
rajan-chari Feb 21, 2024
85d62b9
Agent -> Actor
rajan-chari Feb 21, 2024
1aed1b4
Merge branch 'main' into rajan/cap
rajan-chari Feb 21, 2024
5a20dd2
Merge branch 'main' into rajan/cap
rajan-chari Feb 23, 2024
d806376
Broker can startup on it's own
rajan-chari Feb 26, 2024
2124516
Remote AutoGen Agents
rajan-chari Feb 26, 2024
375685c
Updated docs
rajan-chari Feb 26, 2024
9a44555
Merge branch 'rajan/cap' of https://github.com/rajan-chari/autogen in…
rajan-chari Feb 26, 2024
19f23f3
Merge branch 'main' into rajan/cap
rajan-chari Feb 26, 2024
ba737ae
1) StandaloneBroker in demo
rajan-chari Feb 26, 2024
ad39b05
1) Agent -> Actor refactor
rajan-chari Feb 26, 2024
644f6e5
rename user_proxy -> user_proxy_conn
rajan-chari Feb 26, 2024
d8bc66d
Merge branch 'main' into rajan/cap
rajan-chari Feb 27, 2024
86498e2
Add DirectorySvc
rajan-chari Feb 28, 2024
59d2f2b
Standalone demo refactor
rajan-chari Feb 28, 2024
676259b
Get ActorInfo from DirectorySvc when searching for Actor
rajan-chari Feb 28, 2024
dd60be5
Broker cleanup
rajan-chari Feb 28, 2024
54a598e
Merge branch 'main' into rajan/cap
rajan-chari Feb 29, 2024
6344f99
Proper cleanup and remove debug sleep()
rajan-chari Feb 29, 2024
6c5326b
Run one directory service only.
rajan-chari Feb 29, 2024
7ed4d7a
fix paths to run demo apps from command line
rajan-chari Feb 29, 2024
1e0cf44
Handle keyboard interrupt
rajan-chari Mar 1, 2024
a40e3f8
Wait for Broker and Directory to start up
rajan-chari Mar 1, 2024
4e960c3
Move Terminate AGActor
rajan-chari Mar 1, 2024
672efe4
Accept input from the user in UserProxy
rajan-chari Mar 1, 2024
5beeb4d
Move sleeps close to operations that bind or connect
rajan-chari Mar 1, 2024
7f96f23
Comments
rajan-chari Mar 1, 2024
f43a5b9
Created an encapsulated CAP Pair for AutoGen pair communication
rajan-chari Mar 1, 2024
4ed77dd
Merge branch 'rajan/cap' of https://github.com/rajan-chari/autogen in…
rajan-chari Mar 1, 2024
fe121b6
pre-commit checks
rajan-chari Mar 1, 2024
9e8e2a9
fix pre-commit
rajan-chari Mar 1, 2024
23a833d
Merge branch 'main' into rajan/cap
rajan-chari Mar 1, 2024
cf92f4c
Merge branch 'main' into rajan/cap
rajan-chari Mar 5, 2024
868168b
Merge branch 'main' into rajan/cap
rajan-chari Mar 6, 2024
fb1569e
Pair should not make assumptions about who is first and who is second
rajan-chari Mar 6, 2024
4f2aae7
Merge branch 'rajan/cap' of https://github.com/rajan-chari/autogen in…
rajan-chari Mar 6, 2024
3ac880f
Use task passed into InitiateChat
rajan-chari Mar 6, 2024
c43249a
Merge branch 'main' into rajan/cap
rajan-chari Mar 7, 2024
8b378c9
Standalone directory svc
rajan-chari Mar 7, 2024
4756c43
Fix broken LFS files
rajan-chari Mar 7, 2024
f3d50fc
Merge branch 'rajan/cap' of https://github.com/rajan-chari/autogen in…
rajan-chari Mar 7, 2024
07264a1
Long running DirectorySvc
rajan-chari Mar 7, 2024
7882eca
DirectorySvc does not have a status
rajan-chari Mar 7, 2024
808e5b6
Exit DirectorySvc Loop
rajan-chari Mar 7, 2024
21b8939
Debugging Remoting
rajan-chari Mar 7, 2024
c7243ec
Reduce frequency of status messages
rajan-chari Mar 7, 2024
881d15a
Debugging remote Actor
rajan-chari Mar 7, 2024
592a508
Merge branch 'main' into rajan/cap
rajan-chari Mar 7, 2024
8aed651
Merge branch 'main' into rajan/cap
rajan-chari Mar 8, 2024
97c5bbe
roll back git-lfs updates
rajan-chari Mar 8, 2024
b86abbc
Merge branch 'rajan/cap' of https://github.com/rajan-chari/autogen in…
rajan-chari Mar 8, 2024
7799f47
rollback git-lfs changes
rajan-chari Mar 8, 2024
6f75f08
Debug network connectivity
rajan-chari Mar 8, 2024
7cbb646
Merge branch 'rajan/cap' of https://github.com/rajan-chari/autogen in…
rajan-chari Mar 8, 2024
8569a7e
pre-commit fixes
rajan-chari Mar 8, 2024
3dd11a8
Merge branch 'main' into rajan/cap
rajan-chari Mar 11, 2024
6f60687
Create a group chat interface familiar to AutoGen GroupChat users
rajan-chari Mar 12, 2024
c0cd5c5
Merge branch 'rajan/cap' of https://github.com/rajan-chari/autogen in…
rajan-chari Mar 12, 2024
d1fae5a
Merge branch 'main' into rajan/cap
rajan-chari Mar 12, 2024
f232e88
pre-commit fixes
rajan-chari Mar 12, 2024
9c2688f
Merge branch 'rajan/cap' of https://github.com/rajan-chari/autogen in…
rajan-chari Mar 12, 2024
4cb8d48
Merge branch 'main' into rajan/cap
rajan-chari Mar 12, 2024
8e972ae
Merge branch 'main' into rajan/cap
rajan-chari Mar 12, 2024
d7bbbf1
Merge branch 'main' into rajan/cap
rajan-chari Mar 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions samples/apps/cap/README.md
Original file line number Diff line number Diff line change
@@ -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 changes: 21 additions & 0 deletions samples/apps/cap/TODO.md
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions samples/apps/cap/c#/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Coming soon...
1 change: 1 addition & 0 deletions samples/apps/cap/c++/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Coming soon...
1 change: 1 addition & 0 deletions samples/apps/cap/node/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Coming soon...
78 changes: 78 additions & 0 deletions samples/apps/cap/py/autogencap/Actor.py
Original file line number Diff line number Diff line change
@@ -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 changes: 57 additions & 0 deletions samples/apps/cap/py/autogencap/ActorConnector.py
Original file line number Diff line number Diff line change
@@ -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 changes: 114 additions & 0 deletions samples/apps/cap/py/autogencap/Broker.py
Original file line number Diff line number Diff line change
@@ -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 changes: 5 additions & 0 deletions samples/apps/cap/py/autogencap/Config.py
Original file line number Diff line number Diff line change
@@ -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 changes: 2 additions & 0 deletions samples/apps/cap/py/autogencap/Constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Termination_Topic: str = "Termination"
Directory_Svc_Topic: str = "Directory_Svc"
Loading
Loading