Skip to content

Commit dba2f79

Browse files
authored
Merge branch 'microsoft:main' into gemini-no-api-key
2 parents bb1263b + 47f9052 commit dba2f79

File tree

10 files changed

+138
-71
lines changed

10 files changed

+138
-71
lines changed

samples/apps/cap/py/README.md

+34-15
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
## I just want to run the remote AutoGen agents!
44
*Python Instructions (Windows, Linux, MacOS):*
55

6-
pip install autogencap
6+
pip install autogencap-rajan.jedi
77

88
1) AutoGen require OAI_CONFIG_LIST.
99
AutoGen python requirements: 3.8 <= python <= 3.11
@@ -14,26 +14,45 @@ pip install autogencap
1414
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.
1515
1616
Actors can register themselves with CAP, find other agents, construct arbitrary graphs, send and receive messages independently and many, many, many other things.
17+
1718
```python
18-
# CAP Platform
19-
network = LocalActorNetwork()
20-
# Register an agent
21-
network.register(GreeterAgent())
22-
# Tell agents to connect to other agents
23-
network.connect()
24-
# Get a channel to the agent
25-
greeter_link = network.lookup_agent("Greeter")
26-
# Send a message to the agent
27-
greeter_link.send_txt_msg("Hello World!")
28-
# Cleanup
29-
greeter_link.close()
30-
network.disconnect()
19+
# CAP Library
20+
from autogencap.ComponentEnsemble import ComponentEnsemble
21+
from autogencap.Actor import Actor
22+
23+
# A simple Agent
24+
class GreeterAgent(Actor):
25+
def __init__(self):
26+
super().__init__(
27+
agent_name="Greeter",
28+
description="This is the greeter agent, who knows how to greet people.")
29+
30+
# Prints out the message it receives
31+
def on_txt_msg(self, msg):
32+
print(f"Greeter received: {msg}")
33+
return True
34+
35+
ensemble = ComponentEnsemble()
36+
# Create an agent
37+
agent = GreeterAgent()
38+
# Register an agent
39+
ensemble.register(agent) # start message processing
40+
# call on_connect() on all Agents
41+
ensemble.connect()
42+
# Get a channel to the agent
43+
greeter_link = ensemble.find_by_name("Greeter")
44+
#Send a message to the agent
45+
greeter_link.send_txt_msg("Hello World!")
46+
# Cleanup
47+
greeter_link.close()
48+
ensemble.disconnect()
3149
```
50+
3251
### Check out other demos in the `py/demo` directory. We show the following: ###
3352
1) Hello World shown above
3453
2) Many CAP Actors interacting with each other
3554
3) A pair of interacting AutoGen Agents wrapped in CAP Actors
3655
4) CAP wrapped AutoGen Agents in a group chat
3756
5) Two AutoGen Agents running in different processes and communicating through CAP
3857
6) List all registered agents in CAP
39-
7) AutoGen integration to list all registered agents
58+
7) Run Agent in user supplied message loop
+53-36
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import threading
2-
import time
32
import traceback
43

54
import zmq
@@ -9,11 +8,12 @@
98

109

1110
class Actor:
12-
def __init__(self, agent_name: str, description: str):
11+
def __init__(self, agent_name: str, description: str, start_thread: bool = True):
1312
self.actor_name: str = agent_name
1413
self.agent_description: str = description
1514
self.run = False
1615
self._start_event = threading.Event()
16+
self._start_thread = start_thread
1717

1818
def on_connect(self, network):
1919
Debug(self.actor_name, f"is connecting to {network}")
@@ -27,37 +27,50 @@ def on_bin_msg(self, msg: bytes, msg_type: str, receiver: str, sender: str) -> b
2727
Info(self.actor_name, f"Msg: receiver=[{receiver}], msg_type=[{msg_type}]")
2828
return True
2929

30-
def _recv_thread(self):
30+
def _msg_loop_init(self):
31+
Debug(self.actor_name, "recv thread started")
32+
self._socket: zmq.Socket = self._context.socket(zmq.SUB)
33+
self._socket.setsockopt(zmq.RCVTIMEO, 500)
34+
self._socket.connect(xpub_url)
35+
str_topic = f"{self.actor_name}"
36+
Debug(self.actor_name, f"subscribe to: {str_topic}")
37+
self._socket.setsockopt_string(zmq.SUBSCRIBE, f"{str_topic}")
38+
self._start_event.set()
39+
40+
def get_message(self):
3141
try:
32-
Debug(self.actor_name, "recv thread started")
33-
self._socket: zmq.Socket = self._context.socket(zmq.SUB)
34-
self._socket.setsockopt(zmq.RCVTIMEO, 500)
35-
self._socket.connect(xpub_url)
36-
str_topic = f"{self.actor_name}"
37-
Debug(self.actor_name, f"subscribe to: {str_topic}")
38-
self._socket.setsockopt_string(zmq.SUBSCRIBE, f"{str_topic}")
39-
self._start_event.set()
42+
topic, msg_type, sender_topic, msg = self._socket.recv_multipart()
43+
topic = topic.decode("utf-8") # Convert bytes to string
44+
msg_type = msg_type.decode("utf-8") # Convert bytes to string
45+
sender_topic = sender_topic.decode("utf-8") # Convert bytes to string
46+
except zmq.Again:
47+
return None # No message received, continue to next iteration
48+
except Exception as e:
49+
Error(self.actor_name, f"recv thread encountered an error: {e}")
50+
traceback.print_exc()
51+
return None
52+
return topic, msg_type, sender_topic, msg
53+
54+
def dispatch_message(self, message):
55+
if message is None:
56+
return
57+
topic, msg_type, sender_topic, msg = message
58+
if msg_type == "text":
59+
msg = msg.decode("utf-8") # Convert bytes to string
60+
if not self.on_txt_msg(msg, msg_type, topic, sender_topic):
61+
msg = "quit"
62+
if msg.lower() == "quit":
63+
self.run = False
64+
else:
65+
if not self.on_bin_msg(msg, msg_type, topic, sender_topic):
66+
self.run = False
67+
68+
def _msg_loop(self):
69+
try:
70+
self._msg_loop_init()
4071
while self.run:
41-
try:
42-
topic, msg_type, sender_topic, msg = self._socket.recv_multipart()
43-
topic = topic.decode("utf-8") # Convert bytes to string
44-
msg_type = msg_type.decode("utf-8") # Convert bytes to string
45-
sender_topic = sender_topic.decode("utf-8") # Convert bytes to string
46-
except zmq.Again:
47-
continue # No message received, continue to next iteration
48-
except Exception as e:
49-
Error(self.actor_name, f"recv thread encountered an error: {e}")
50-
traceback.print_exc()
51-
continue
52-
if msg_type == "text":
53-
msg = msg.decode("utf-8") # Convert bytes to string
54-
if not self.on_txt_msg(msg, msg_type, topic, sender_topic):
55-
msg = "quit"
56-
if msg.lower() == "quit":
57-
break
58-
else:
59-
if not self.on_bin_msg(msg, msg_type, topic, sender_topic):
60-
break
72+
message = self.get_message()
73+
self.dispatch_message(message)
6174
except Exception as e:
6275
Debug(self.actor_name, f"recv thread encountered an error: {e}")
6376
traceback.print_exc()
@@ -68,12 +81,15 @@ def _recv_thread(self):
6881
self._start_event.set()
6982
Debug(self.actor_name, "recv thread ended")
7083

71-
def start(self, context: zmq.Context):
84+
def on_start(self, context: zmq.Context):
7285
self._context = context
7386
self.run: bool = True
74-
self._thread = threading.Thread(target=self._recv_thread)
75-
self._thread.start()
76-
self._start_event.wait()
87+
if self._start_thread:
88+
self._thread = threading.Thread(target=self._msg_loop)
89+
self._thread.start()
90+
self._start_event.wait()
91+
else:
92+
self._msg_loop_init()
7793

7894
def disconnect_network(self, network):
7995
Debug(self.actor_name, f"is disconnecting from {network}")
@@ -82,6 +98,7 @@ def disconnect_network(self, network):
8298

8399
def stop(self):
84100
self.run = False
85-
self._thread.join()
101+
if self._start_thread:
102+
self._thread.join()
86103
self._socket.setsockopt(zmq.LINGER, 0)
87104
self._socket.close()

samples/apps/cap/py/autogencap/ComponentEnsemble.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
from .DirectorySvc import DirectorySvc
1212
from .proto.CAP_pb2 import ActorInfo, ActorInfoCollection
1313

14-
# TODO: remove time import
15-
1614

1715
class ComponentEnsemble:
1816
def __init__(self, name: str = "Local Actor Network", start_broker: bool = True):
@@ -43,7 +41,7 @@ def register(self, actor: Actor):
4341
# that we can look up the actor by name
4442
self._directory_svc.register_actor_by_name(actor.actor_name)
4543
self.local_actors[actor.actor_name] = actor
46-
actor.start(self._context)
44+
actor.on_start(self._context)
4745
Debug("Local_Actor_Network", f"{actor.actor_name} registered in the network.")
4846

4947
def connect(self):

samples/apps/cap/py/autogencap/DirectorySvc.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def start(self):
121121
self._directory_connector = ActorConnector(self._context, Directory_Svc_Topic)
122122
if self._no_other_directory():
123123
self._directory_actor = DirectoryActor(Directory_Svc_Topic, "Directory Service")
124-
self._directory_actor.start(self._context)
124+
self._directory_actor.on_start(self._context)
125125
Info("DirectorySvc", "Directory service started.")
126126
else:
127127
Info("DirectorySvc", "Another directory service is running. This instance will not start.")

samples/apps/cap/py/autogencap/ag_adapter/AGActor.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77

88
class AGActor(Actor):
9-
def start(self, context: zmq.Context):
10-
super().start(context)
9+
def on_start(self, context: zmq.Context):
10+
super().on_start(context)
1111
str_topic = Termination_Topic
1212
Debug(self.actor_name, f"subscribe to: {str_topic}")
1313
self._socket.setsockopt_string(zmq.SUBSCRIBE, f"{str_topic}")

samples/apps/cap/py/demo/App.py

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from list_agents import list_agents
1616
from RemoteAGDemo import remote_ag_demo
1717
from SimpleActorDemo import simple_actor_demo
18+
from single_threaded import single_threaded_demo
1819

1920
####################################################################################################
2021

@@ -46,6 +47,7 @@ def main():
4647
print("4. AutoGen GroupChat")
4748
print("5. AutoGen Agents in different processes")
4849
print("6. List Actors in CAP (Registry)")
50+
print("7. Agent loop in main thread (no background thread for Agent)")
4951
choice = input("Enter your choice (1-6): ")
5052

5153
if choice == "1":
@@ -64,6 +66,8 @@ def main():
6466
remote_ag_demo()
6567
elif choice == "6":
6668
list_agents()
69+
elif choice == "7":
70+
single_threaded_demo()
6771
else:
6872
print("Quitting...")
6973
break

samples/apps/cap/py/demo/AppAgents.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ class GreeterAgent(Actor):
1717

1818
def __init__(
1919
self,
20+
start_thread=True,
2021
agent_name="Greeter",
2122
description="This is the greeter agent, who knows how to greet people.",
2223
):
23-
super().__init__(agent_name, description)
24+
super().__init__(agent_name, description, start_thread=start_thread)
2425

2526

2627
class FidelityAgent(Actor):
+1-12
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
import time
2-
31
from AppAgents import GreeterAgent
42
from autogencap.ComponentEnsemble import ComponentEnsemble
5-
from autogencap.DebugLog import Error
6-
from autogencap.proto.CAP_pb2 import Ping
73

84

95
def simple_actor_demo():
@@ -17,12 +13,5 @@ def simple_actor_demo():
1713
ensemble.register(agent)
1814
ensemble.connect()
1915
greeter_link = ensemble.find_by_name("Greeter")
20-
ensemble.disconnect()
21-
22-
ping = Ping()
23-
# Serialize and send the message
24-
msg_type_str = Ping.__name__
25-
msg_bytes = ping.SerializeToString()
2616
greeter_link.send_txt_msg("Hello World!")
27-
greeter_link.send_bin_msg(msg_type_str, msg_bytes)
28-
_, resp_type, resp_msg_bytes = greeter_link.send_recv_msg(msg_type_str, msg_bytes)
17+
ensemble.disconnect()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import _paths
2+
from AppAgents import GreeterAgent
3+
from autogencap.ComponentEnsemble import ComponentEnsemble
4+
from autogencap.DebugLog import Error
5+
from autogencap.proto.CAP_pb2 import Ping
6+
7+
8+
def single_threaded_demo():
9+
"""
10+
Demonstrates the usage of the CAP platform by registering an actor, connecting to the actor,
11+
sending a message, and performing cleanup operations.
12+
"""
13+
# CAP Platform
14+
ensemble = ComponentEnsemble()
15+
agent = GreeterAgent(start_thread=False)
16+
ensemble.register(agent)
17+
ensemble.connect()
18+
greeter_link = ensemble.find_by_name("Greeter")
19+
greeter_link.send_txt_msg("Hello World!")
20+
21+
no_msg = 0
22+
while no_msg < 5:
23+
message = agent.get_message()
24+
agent.dispatch_message(message)
25+
if message is None:
26+
no_msg += 1
27+
28+
message = agent.get_message()
29+
agent.dispatch_message(message)
30+
31+
ensemble.disconnect()
32+
33+
34+
def main():
35+
single_threaded_demo()
36+
37+
38+
if __name__ == "__main__":
39+
main()

samples/apps/cap/py/pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "autogencap_rajan.jedi"
7-
version = "0.0.9"
7+
version = "0.0.10"
88
authors = [
99
{ name="Rajan Chari", email="[email protected]" },
1010
]

0 commit comments

Comments
 (0)