1
- """This is an example of a chat room with AI agents. It demonstrates how to use the
2
- `TypeRoutedAgent` class to create custom agents that can use custom message types,
3
- and interact with other using event-based messaging without an orchestrator."""
4
-
5
1
import argparse
6
2
import asyncio
7
3
import json
8
- from dataclasses import dataclass
4
+ import logging
5
+ import os
6
+ import sys
7
+
8
+ sys .path .append (os .path .abspath (os .path .dirname (__file__ )))
9
9
10
10
from agnext .application import SingleThreadedAgentRuntime
11
11
from agnext .chat .memory import BufferedChatMemory , ChatMemory
14
14
from agnext .components import TypeRoutedAgent , message_handler
15
15
from agnext .components .models import ChatCompletionClient , OpenAI , SystemMessage
16
16
from agnext .core import AgentRuntime , CancellationToken
17
- from colorama import Fore , Style , init
18
-
19
-
20
- # Define a custom message type for chat room messages.
21
- @dataclass
22
- class ChatRoomMessage (TextMessage ): # type: ignore
23
- pass
24
-
25
-
26
- sep = "-" * 50
27
-
28
- init (autoreset = True )
17
+ from utils import TextualChatApp , TextualUserAgent , start_runtime
29
18
30
19
31
20
# Define a custom agent that can handle chat room messages.
@@ -38,7 +27,6 @@ def __init__( # type: ignore
38
27
background_story : str ,
39
28
memory : ChatMemory , # type: ignore
40
29
model_client : ChatCompletionClient , # type: ignore
41
- color : str = Style .RESET_ALL ,
42
30
) -> None : # type: ignore
43
31
super ().__init__ (name , description , runtime )
44
32
system_prompt = f"""Your name is { name } .
@@ -58,10 +46,9 @@ def __init__( # type: ignore
58
46
self ._system_messages = [SystemMessage (system_prompt )]
59
47
self ._memory = memory
60
48
self ._client = model_client
61
- self ._color = color
62
49
63
50
@message_handler () # type: ignore
64
- async def on_chat_room_message (self , message : ChatRoomMessage , cancellation_token : CancellationToken ) -> None : # type: ignore
51
+ async def on_chat_room_message (self , message : TextMessage , cancellation_token : CancellationToken ) -> None : # type: ignore
65
52
# Save the message to memory as structured JSON.
66
53
from_message = TextMessage (
67
54
content = json .dumps ({"sender" : message .source , "content" : message .content }), source = message .source
@@ -77,7 +64,7 @@ async def on_chat_room_message(self, message: ChatRoomMessage, cancellation_toke
77
64
assert isinstance (raw_response .content , str )
78
65
79
66
# Save the response to memory.
80
- await self ._memory .add_message (ChatRoomMessage (source = self .metadata ["name" ], content = raw_response .content ))
67
+ await self ._memory .add_message (TextMessage (source = self .metadata ["name" ], content = raw_response .content ))
81
68
82
69
# Parse the response.
83
70
data = json .loads (raw_response .content )
@@ -86,76 +73,75 @@ async def on_chat_room_message(self, message: ChatRoomMessage, cancellation_toke
86
73
87
74
# Publish the response if needed.
88
75
if respond is True or str (respond ).lower ().strip () == "true" :
89
- await self ._publish_message (ChatRoomMessage (source = self .metadata ["name" ], content = str (response )))
90
- print (f"{ sep } \n { self ._color } { self .metadata ['name' ]} :{ Style .RESET_ALL } \n { response } " )
76
+ await self ._publish_message (TextMessage (source = self .metadata ["name" ], content = str (response )))
77
+
78
+
79
+ class ChatRoomUserAgent (TextualUserAgent ): # type: ignore
80
+ """An agent that is used to receive messages from the runtime."""
81
+
82
+ @message_handler # type: ignore
83
+ async def on_chat_room_message (self , message : TextMessage , cancellation_token : CancellationToken ) -> None : # type: ignore
84
+ await self ._app .post_runtime_message (message )
91
85
92
86
93
87
# Define a chat room with participants -- the runtime is the chat room.
94
- def chat_room (runtime : AgentRuntime ) -> None : # type: ignore
95
- _ = ChatRoomAgent (
88
+ def chat_room (runtime : AgentRuntime , app : TextualChatApp ) -> None : # type: ignore
89
+ _ = ChatRoomUserAgent (
90
+ name = "User" ,
91
+ description = "The user in the chat room." ,
92
+ runtime = runtime ,
93
+ app = app ,
94
+ )
95
+ alice = ChatRoomAgent (
96
96
name = "Alice" ,
97
97
description = "Alice in the chat room." ,
98
98
runtime = runtime ,
99
99
background_story = "Alice is a software engineer who loves to code." ,
100
100
memory = BufferedChatMemory (buffer_size = 10 ),
101
101
model_client = OpenAI (model = "gpt-4-turbo" ), # type: ignore
102
- color = Fore .CYAN ,
103
102
)
104
- _ = ChatRoomAgent (
103
+ bob = ChatRoomAgent (
105
104
name = "Bob" ,
106
105
description = "Bob in the chat room." ,
107
106
runtime = runtime ,
108
107
background_story = "Bob is a data scientist who loves to analyze data." ,
109
108
memory = BufferedChatMemory (buffer_size = 10 ),
110
109
model_client = OpenAI (model = "gpt-4-turbo" ), # type: ignore
111
- color = Fore .GREEN ,
112
110
)
113
- _ = ChatRoomAgent (
111
+ charlie = ChatRoomAgent (
114
112
name = "Charlie" ,
115
113
description = "Charlie in the chat room." ,
116
114
runtime = runtime ,
117
115
background_story = "Charlie is a designer who loves to create art." ,
118
116
memory = BufferedChatMemory (buffer_size = 10 ),
119
117
model_client = OpenAI (model = "gpt-4-turbo" ), # type: ignore
120
- color = Fore .MAGENTA ,
121
118
)
119
+ app .welcoming_notice = f"""Welcome to the chat room demo with the following participants:
120
+ 1. 👧 { alice .metadata ['name' ]} : { alice .metadata ['description' ]}
121
+ 2. 👱🏼♂️ { bob .metadata ['name' ]} : { bob .metadata ['description' ]}
122
+ 3. 👨🏾🦳 { charlie .metadata ['name' ]} : { charlie .metadata ['description' ]}
122
123
124
+ Each participant decides on its own whether to respond to the latest message.
123
125
124
- async def get_user_input (prompt : str ) -> str :
125
- loop = asyncio .get_event_loop ()
126
- return await loop .run_in_executor (None , input , prompt )
126
+ You can greet the chat room by typing your first message below.
127
+ """
127
128
128
129
129
- async def main (user_name : str , wait_seconds : int ) -> None :
130
+ async def main () -> None :
130
131
runtime = SingleThreadedAgentRuntime ()
131
- chat_room (runtime )
132
- while True :
133
- # TODO: allow user to input at any time while runtime is running.
134
- # Get user input and send messages to the chat room.
135
- # TODO: use Textual to build the UI.
136
- user_input = await get_user_input (f"{ sep } \n You:\n " )
137
- if user_input .strip ():
138
- # Publish user message if it is not empty.
139
- await runtime .publish_message (ChatRoomMessage (source = user_name , content = user_input ))
140
- # Wait for agents to respond.
141
- while runtime .unprocessed_messages :
142
- await runtime .process_next ()
143
- await asyncio .sleep (wait_seconds )
132
+ app = TextualChatApp (runtime , user_name = "You" )
133
+ chat_room (runtime , app )
134
+ asyncio .create_task (start_runtime (runtime ))
135
+ await app .run_async ()
144
136
145
137
146
138
if __name__ == "__main__" :
147
- parser = argparse .ArgumentParser (description = "Run a chat room simulation." )
148
- parser .add_argument (
149
- "--user-name" ,
150
- type = str ,
151
- default = "Host" ,
152
- help = "The name of the user who is participating in the chat room." ,
153
- )
154
- parser .add_argument (
155
- "--wait-seconds" ,
156
- type = int ,
157
- default = 5 ,
158
- help = "The number of seconds to wait between processing messages." ,
159
- )
139
+ parser = argparse .ArgumentParser (description = "Chat room demo with self-driving AI agents." )
140
+ parser .add_argument ("--verbose" , action = "store_true" , help = "Enable verbose logging." )
160
141
args = parser .parse_args ()
161
- asyncio .run (main (args .user_name , args .wait_seconds ))
142
+ if args .verbose :
143
+ logging .basicConfig (level = logging .WARNING )
144
+ logging .getLogger ("agnext" ).setLevel (logging .DEBUG )
145
+ handler = logging .FileHandler ("chat_room.log" )
146
+ logging .getLogger ("agnext" ).addHandler (handler )
147
+ asyncio .run (main ())
0 commit comments