meshagent-agents 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of meshagent-agents might be problematic. Click here for more details.
- meshagent/agents/__init__.py +5 -0
- meshagent/agents/adapter.py +39 -0
- meshagent/agents/agent.py +427 -0
- meshagent/agents/chat.py +316 -0
- meshagent/agents/context.py +90 -0
- meshagent/agents/development.py +32 -0
- meshagent/agents/hosting.py +117 -0
- meshagent/agents/indexer.py +593 -0
- meshagent/agents/listener.py +155 -0
- meshagent/agents/planning.py +603 -0
- meshagent/agents/prompt.py +49 -0
- meshagent/agents/pydantic.py +137 -0
- meshagent/agents/schema.py +50 -0
- meshagent/agents/single_shot_writer.py +92 -0
- meshagent/agents/version.py +1 -0
- meshagent/agents/worker.py +126 -0
- meshagent/agents/writer.py +82 -0
- meshagent_agents-0.0.1.dist-info/LICENSE +201 -0
- meshagent_agents-0.0.1.dist-info/METADATA +29 -0
- meshagent_agents-0.0.1.dist-info/RECORD +22 -0
- meshagent_agents-0.0.1.dist-info/WHEEL +5 -0
- meshagent_agents-0.0.1.dist-info/top_level.txt +1 -0
meshagent/agents/chat.py
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
from .agent import SingleRoomAgent, AgentChatContext
|
|
2
|
+
from meshagent.api.chan import Chan
|
|
3
|
+
from meshagent.api import RoomMessage, RoomException, RoomClient, RemoteParticipant
|
|
4
|
+
from meshagent.tools import Toolkit
|
|
5
|
+
from .adapter import LLMAdapter, ToolResponseAdapter
|
|
6
|
+
import asyncio
|
|
7
|
+
from typing import Optional
|
|
8
|
+
import logging
|
|
9
|
+
from meshagent.tools import MultiToolkit
|
|
10
|
+
import urllib
|
|
11
|
+
|
|
12
|
+
logging.basicConfig()
|
|
13
|
+
logger = logging.getLogger("chat")
|
|
14
|
+
logger.setLevel(logging.INFO)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# todo: thread should stop when participant stops?
|
|
18
|
+
|
|
19
|
+
class ChatBot(SingleRoomAgent):
|
|
20
|
+
def __init__(self, *, name, title = None, description = None, requires = None, llm_adapter: LLMAdapter, tool_adapter: Optional[ToolResponseAdapter] = None, toolkits: Optional[list[Toolkit]] = None, rules : Optional[list[str]] = None, auto_greet_prompt : Optional[str] = None, auto_greet_message : Optional[str] = None, empty_state_title : Optional[str] = None, labels: Optional[str] = None):
|
|
21
|
+
super().__init__(
|
|
22
|
+
name=name,
|
|
23
|
+
title=title,
|
|
24
|
+
description=description,
|
|
25
|
+
requires=requires,
|
|
26
|
+
labels=labels
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
if toolkits == None:
|
|
30
|
+
toolkits = []
|
|
31
|
+
|
|
32
|
+
self._llm_adapter = llm_adapter
|
|
33
|
+
self._tool_adapter = tool_adapter
|
|
34
|
+
|
|
35
|
+
self._message_channels = dict[str, Chan[RoomMessage]]()
|
|
36
|
+
|
|
37
|
+
self._room : RoomClient | None = None
|
|
38
|
+
self._toolkits = toolkits
|
|
39
|
+
|
|
40
|
+
if rules == None:
|
|
41
|
+
rules = []
|
|
42
|
+
|
|
43
|
+
self._rules = rules
|
|
44
|
+
self._is_typing = dict[str,asyncio.Task]()
|
|
45
|
+
self._auto_greet_prompt = auto_greet_prompt
|
|
46
|
+
self._auto_greet_message = auto_greet_message
|
|
47
|
+
|
|
48
|
+
if empty_state_title == None:
|
|
49
|
+
empty_state_title = "How can I help you?"
|
|
50
|
+
self._empty_state_title = empty_state_title
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async def greet(self, *, chat_context: AgentChatContext, messages: Chan[RoomMessage], participant: RemoteParticipant):
|
|
54
|
+
|
|
55
|
+
if self._auto_greet_prompt != None:
|
|
56
|
+
messages.send_nowait(RoomMessage(from_participant_id=participant.id, type="chat", message={"text": self._auto_greet_prompt }))
|
|
57
|
+
|
|
58
|
+
if self._auto_greet_message != None:
|
|
59
|
+
chat_context.append_user_message(self._auto_greet_message)
|
|
60
|
+
|
|
61
|
+
await self.room.messaging.send_message(to=RemoteParticipant(id=participant.id), type="chat", message={ "text" : self._auto_greet_message })
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
async def finalize_toolkits(self, toolkits) -> list[Toolkit]:
|
|
65
|
+
|
|
66
|
+
toaster = None
|
|
67
|
+
|
|
68
|
+
for toolkit in toolkits:
|
|
69
|
+
|
|
70
|
+
if toolkit.name == "meshagent.ui":
|
|
71
|
+
|
|
72
|
+
for tool in toolkit.tools:
|
|
73
|
+
|
|
74
|
+
if tool.name == "show_toast":
|
|
75
|
+
|
|
76
|
+
toaster = tool
|
|
77
|
+
|
|
78
|
+
if toaster != None:
|
|
79
|
+
|
|
80
|
+
def multi_tool(toolkit: Toolkit):
|
|
81
|
+
if toaster in toolkit.tools:
|
|
82
|
+
return toolkit
|
|
83
|
+
|
|
84
|
+
return MultiToolkit(required=[ toaster ], base_toolkit=toolkit )
|
|
85
|
+
|
|
86
|
+
toolkits = list(map(multi_tool, toolkits))
|
|
87
|
+
|
|
88
|
+
return toolkits
|
|
89
|
+
|
|
90
|
+
async def _spawn_thread(self, participant_id: str, messages: Chan[RoomMessage]):
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
chat_context = await self.init_chat_context()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
chat_context.append_rules(
|
|
97
|
+
rules=[
|
|
98
|
+
*self._rules,
|
|
99
|
+
"think step by step",
|
|
100
|
+
]
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
opened = False
|
|
104
|
+
chat_with_participant = None
|
|
105
|
+
|
|
106
|
+
for participant in self._room.messaging.get_participants():
|
|
107
|
+
if participant.id == participant_id:
|
|
108
|
+
chat_with_participant = participant
|
|
109
|
+
break
|
|
110
|
+
|
|
111
|
+
if chat_with_participant == None:
|
|
112
|
+
raise RoomException(f"caller did not have messaging turned on")
|
|
113
|
+
|
|
114
|
+
messaging = self._room.messaging
|
|
115
|
+
|
|
116
|
+
current_file = None
|
|
117
|
+
|
|
118
|
+
step_schema = {
|
|
119
|
+
"type" : "object",
|
|
120
|
+
"required" : ["text","finished"],
|
|
121
|
+
"additionalProperties" : False,
|
|
122
|
+
"description" : "execute a step",
|
|
123
|
+
"properties" : {
|
|
124
|
+
"text" : {
|
|
125
|
+
"description" : "a reply to the user or status to display during an intermediate step",
|
|
126
|
+
"type" : "string"
|
|
127
|
+
},
|
|
128
|
+
"finished" : {
|
|
129
|
+
"description" : "whether the agent has finished answering the user's last message. you MUST set this to true if there are no more tool calls to be made or you are stuck in a loop.",
|
|
130
|
+
"type" : "boolean"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
installed = False
|
|
136
|
+
|
|
137
|
+
while True:
|
|
138
|
+
|
|
139
|
+
while True:
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
received = await messages.recv()
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
if current_file != chat_with_participant.get_attribute("current_file"):
|
|
148
|
+
logger.info(f"participant is now looking at {chat_with_participant.get_attribute("current_file")}")
|
|
149
|
+
current_file = chat_with_participant.get_attribute("current_file")
|
|
150
|
+
|
|
151
|
+
if current_file != None:
|
|
152
|
+
chat_context.append_assistant_message(message=f"the user is currently viewing the file at the path: {current_file}")
|
|
153
|
+
|
|
154
|
+
elif current_file != None:
|
|
155
|
+
chat_context.append_assistant_message(message=f"the user is not current viewing any files")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
if installed == False:
|
|
160
|
+
installed = True
|
|
161
|
+
await self.install_requirements(participant_id=participant_id)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
if received.type == "opened":
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
if opened == False:
|
|
168
|
+
|
|
169
|
+
opened = True
|
|
170
|
+
|
|
171
|
+
await self.greet(chat_context=chat_context, participant=chat_with_participant, messages=messages)
|
|
172
|
+
|
|
173
|
+
if received.type == "chat":
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
await self._room.messaging.send_message(to=chat_with_participant, type="thinking", message={"thinking":True})
|
|
177
|
+
|
|
178
|
+
if chat_with_participant.id == received.from_participant_id:
|
|
179
|
+
self.room.developer.log_nowait(type="llm.message", data={ "context" : chat_context.id, "participant_id" : self.room.local_participant.id, "participant_name" : self.room.local_participant.get_attribute("name"), "message" : { "content" : { "role" : "user", "text" : received.message["text"] } } })
|
|
180
|
+
|
|
181
|
+
text = received.message["text"]
|
|
182
|
+
attachments = received.message.get("attachments", [])
|
|
183
|
+
|
|
184
|
+
for attachment in attachments:
|
|
185
|
+
|
|
186
|
+
chat_context.append_assistant_message(message=f"the user attached a file '{attachment["filename"]}' with the content: '{attachment["content"]}'")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
chat_context.append_user_message(message=text)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# if user is typing, wait for typing to stop
|
|
193
|
+
while True:
|
|
194
|
+
|
|
195
|
+
if chat_with_participant.id not in self._is_typing:
|
|
196
|
+
break
|
|
197
|
+
|
|
198
|
+
await asyncio.sleep(.5)
|
|
199
|
+
|
|
200
|
+
if messages.empty() == True:
|
|
201
|
+
break
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
while True:
|
|
206
|
+
|
|
207
|
+
toolkits = [
|
|
208
|
+
*self._toolkits,
|
|
209
|
+
*await self.get_required_tools(participant_id=chat_with_participant.id)
|
|
210
|
+
]
|
|
211
|
+
|
|
212
|
+
toolkits = await self.finalize_toolkits(toolkits)
|
|
213
|
+
|
|
214
|
+
response = await self._llm_adapter.next(
|
|
215
|
+
context=chat_context,
|
|
216
|
+
room=self._room,
|
|
217
|
+
toolkits=toolkits,
|
|
218
|
+
tool_adapter=self._tool_adapter,
|
|
219
|
+
output_schema=step_schema,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
text = response["text"]
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
if response["finished"] or len(toolkits) == 0:
|
|
226
|
+
await self._room.messaging.send_message(
|
|
227
|
+
to=chat_with_participant,
|
|
228
|
+
type="chat",
|
|
229
|
+
message={
|
|
230
|
+
"text": text
|
|
231
|
+
}
|
|
232
|
+
)
|
|
233
|
+
break
|
|
234
|
+
else:
|
|
235
|
+
await self._room.messaging.send_message(
|
|
236
|
+
to=chat_with_participant,
|
|
237
|
+
type="status",
|
|
238
|
+
message={
|
|
239
|
+
"text": text
|
|
240
|
+
}
|
|
241
|
+
)
|
|
242
|
+
chat_context.append_user_message(message="proceed to the next step if you are ready")
|
|
243
|
+
|
|
244
|
+
finally:
|
|
245
|
+
|
|
246
|
+
await self._room.messaging.send_message(to=chat_with_participant, type="thinking", message={"thinking":False})
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _get_message_channel(self, participant_id: str) -> Chan[RoomMessage]:
|
|
251
|
+
if participant_id not in self._message_channels:
|
|
252
|
+
chan = Chan[RoomMessage]()
|
|
253
|
+
self._message_channels[participant_id] = chan
|
|
254
|
+
|
|
255
|
+
def thread_done(task: asyncio.Task):
|
|
256
|
+
|
|
257
|
+
self._message_channels.pop(participant_id)
|
|
258
|
+
try:
|
|
259
|
+
task.result()
|
|
260
|
+
logger.info("ending chat thread")
|
|
261
|
+
except Exception as e:
|
|
262
|
+
logger.error("chat thread error", exc_info=e)
|
|
263
|
+
|
|
264
|
+
task = asyncio.create_task(self._spawn_thread(participant_id=participant_id, messages=chan))
|
|
265
|
+
task.add_done_callback(thread_done)
|
|
266
|
+
|
|
267
|
+
chan = self._message_channels[participant_id]
|
|
268
|
+
|
|
269
|
+
return chan
|
|
270
|
+
|
|
271
|
+
async def start(self, *, room):
|
|
272
|
+
|
|
273
|
+
await super().start(room=room)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
await self.room.local_participant.set_attribute("empty_state_title", self._empty_state_title)
|
|
277
|
+
|
|
278
|
+
def on_message(message: RoomMessage):
|
|
279
|
+
messages = self._get_message_channel(participant_id=message.from_participant_id)
|
|
280
|
+
if message.type == "chat" or message.type == "opened":
|
|
281
|
+
messages.send_nowait(message)
|
|
282
|
+
|
|
283
|
+
elif message.type == "typing":
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def callback(task: asyncio.Task):
|
|
287
|
+
try:
|
|
288
|
+
task.result()
|
|
289
|
+
except:
|
|
290
|
+
pass
|
|
291
|
+
|
|
292
|
+
async def remove_timeout(id: str):
|
|
293
|
+
await asyncio.sleep(1)
|
|
294
|
+
self._is_typing.pop(id)
|
|
295
|
+
|
|
296
|
+
if message.from_participant_id in self._is_typing:
|
|
297
|
+
self._is_typing[message.from_participant_id].cancel()
|
|
298
|
+
|
|
299
|
+
timeout = asyncio.create_task(remove_timeout(id=message.from_participant_id))
|
|
300
|
+
timeout.add_done_callback(callback)
|
|
301
|
+
|
|
302
|
+
self._is_typing[message.from_participant_id] = timeout
|
|
303
|
+
|
|
304
|
+
room.messaging.on("message", on_message)
|
|
305
|
+
|
|
306
|
+
if self._auto_greet_prompt != None or self._auto_greet_message != None:
|
|
307
|
+
def on_participant_added(participant:RemoteParticipant):
|
|
308
|
+
|
|
309
|
+
# will spawn the initial thread
|
|
310
|
+
self._get_message_channel(participant_id=participant.id)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
room.messaging.on("participant_added", on_participant_added)
|
|
314
|
+
|
|
315
|
+
await room.messaging.enable()
|
|
316
|
+
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
from meshagent.api import RoomClient
|
|
5
|
+
from meshagent.tools.toolkit import Toolkit
|
|
6
|
+
from meshagent.api.participant import Participant
|
|
7
|
+
|
|
8
|
+
import uuid
|
|
9
|
+
|
|
10
|
+
class AgentChatContext:
|
|
11
|
+
def __init__(self, *, messages: Optional[list[dict]] = None, system_role: Optional[str] = None):
|
|
12
|
+
self.id = str(uuid.uuid4())
|
|
13
|
+
if messages == None:
|
|
14
|
+
messages = list[dict]()
|
|
15
|
+
self._messages = messages.copy()
|
|
16
|
+
if system_role == None:
|
|
17
|
+
system_role = "system"
|
|
18
|
+
self._system_role = system_role
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def messages(self):
|
|
22
|
+
return self._messages
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def system_role(self):
|
|
26
|
+
return self._system_role
|
|
27
|
+
|
|
28
|
+
def append_rules(self, rules: list[str]):
|
|
29
|
+
|
|
30
|
+
system_message = None
|
|
31
|
+
|
|
32
|
+
for m in self.messages:
|
|
33
|
+
if m["role"] == self.system_role:
|
|
34
|
+
system_message = m
|
|
35
|
+
break
|
|
36
|
+
|
|
37
|
+
if system_message == None:
|
|
38
|
+
system_message = {"role": self.system_role, "content" : ""}
|
|
39
|
+
self.messages.insert(0, system_message)
|
|
40
|
+
|
|
41
|
+
plan = f"Rules:\n-{'\n-'.join(rules)}\n"
|
|
42
|
+
system_message["content"] = system_message["content"] + plan
|
|
43
|
+
|
|
44
|
+
def append_assistant_message(self, message: str) -> None:
|
|
45
|
+
self.messages.append({ "role" : "assistant", "content" : message })
|
|
46
|
+
|
|
47
|
+
def append_user_message(self, message: str) -> None:
|
|
48
|
+
self.messages.append({ "role" : "user", "content" : message })
|
|
49
|
+
|
|
50
|
+
def append_user_image(self, url: str) -> None:
|
|
51
|
+
self.messages.append({ "role" : "user", "content" : [ { "type" : "image_url", "image_url": {"url": url, "detail": "auto"} } ] })
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def copy(self) -> 'AgentChatContext':
|
|
55
|
+
return AgentChatContext(messages=deepcopy(self.messages), system_role=self._system_role)
|
|
56
|
+
|
|
57
|
+
def to_json(self) -> dict:
|
|
58
|
+
return {
|
|
59
|
+
"messages" : self.messages,
|
|
60
|
+
"system_role" : self.system_role
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def from_json(json: dict):
|
|
65
|
+
return AgentChatContext(messages=json["messages"], system_role=json.get("system_role", None))
|
|
66
|
+
|
|
67
|
+
class AgentCallContext:
|
|
68
|
+
def __init__(self, *, chat: AgentChatContext, room: RoomClient, toolkits: Optional[list[Toolkit]] = None, caller: Optional[Participant] = None):
|
|
69
|
+
self._room = room
|
|
70
|
+
if toolkits == None:
|
|
71
|
+
toolkits = list[Toolkit]()
|
|
72
|
+
self._toolkits = toolkits
|
|
73
|
+
self._chat = chat
|
|
74
|
+
self._caller = caller
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def toolkits(self):
|
|
78
|
+
return self._toolkits
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def chat(self):
|
|
82
|
+
return self._chat
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def caller(self):
|
|
86
|
+
return self._caller
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def room(self):
|
|
90
|
+
return self._room
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from .agent import SingleRoomAgent
|
|
2
|
+
from meshagent.api import websocket_protocol, RoomClient, MeshSchema, RoomException
|
|
3
|
+
import asyncio
|
|
4
|
+
import signal
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
from aiohttp import web
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def connect_development_agent(*, room_name:str, agent: SingleRoomAgent):
|
|
11
|
+
|
|
12
|
+
async with RoomClient(
|
|
13
|
+
protocol = websocket_protocol(participant_name=agent.name, room_name=room_name, role="agent")) as room:
|
|
14
|
+
|
|
15
|
+
await agent.start(room=room)
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
|
|
19
|
+
term = asyncio.Future()
|
|
20
|
+
|
|
21
|
+
def clean_termination(signal, frame):
|
|
22
|
+
term.set_result(True)
|
|
23
|
+
|
|
24
|
+
signal.signal(signal.SIGTERM, clean_termination)
|
|
25
|
+
signal.signal(signal.SIGABRT, clean_termination)
|
|
26
|
+
|
|
27
|
+
await asyncio.wait([asyncio.ensure_future(room.protocol.wait_for_close()), term], return_when=asyncio.FIRST_COMPLETED)
|
|
28
|
+
|
|
29
|
+
finally:
|
|
30
|
+
|
|
31
|
+
await agent.stop()
|
|
32
|
+
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from meshagent.api import websocket_protocol, RoomMessage
|
|
4
|
+
from meshagent.api.webhooks import WebhookServer, RoomStartedEvent, RoomEndedEvent, WebSocketClientProtocol, CallEvent
|
|
5
|
+
from meshagent.api.room_server_client import RoomClient
|
|
6
|
+
from meshagent.agents import SingleRoomAgent
|
|
7
|
+
from aiohttp import web
|
|
8
|
+
import asyncio
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from typing import Callable, Optional, TypeVar
|
|
12
|
+
|
|
13
|
+
from .agent import TaskRunner
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger("hosting")
|
|
16
|
+
logger.setLevel(logging.INFO)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RemoteTaskRunnerServer[T:TaskRunner](WebhookServer):
|
|
20
|
+
def __init__(self, *, cls: Optional[T] = None, path: Optional[str] = None, app: Optional[web.Application] = None, host = None, port = None, webhook_secret = None, create_agent: Optional[Callable[[dict],TaskRunner]] = None, validate_webhook_secret: Optional[bool] = None):
|
|
21
|
+
super().__init__(path=path, app=app, host=host, port=port, webhook_secret=webhook_secret, validate_webhook_secret=validate_webhook_secret)
|
|
22
|
+
|
|
23
|
+
if create_agent == None:
|
|
24
|
+
def default_create_agent(arguments: dict) -> TaskRunner:
|
|
25
|
+
return cls(**arguments)
|
|
26
|
+
|
|
27
|
+
create_agent = default_create_agent
|
|
28
|
+
|
|
29
|
+
self._create_agent = create_agent
|
|
30
|
+
|
|
31
|
+
async def _spawn(self, *, room_name: str, room_url: str, token: str, arguments: Optional[dict] = None):
|
|
32
|
+
agent = self._create_agent(arguments=arguments)
|
|
33
|
+
|
|
34
|
+
async def run():
|
|
35
|
+
async with RoomClient(protocol=WebSocketClientProtocol(url= room_url, token=token)) as room:
|
|
36
|
+
|
|
37
|
+
dismissed = asyncio.Future()
|
|
38
|
+
|
|
39
|
+
def on_message(message: RoomMessage):
|
|
40
|
+
if message.type == "dismiss":
|
|
41
|
+
logger.info(f"dismissed by {message.from_participant_id}")
|
|
42
|
+
dismissed.set_result(True)
|
|
43
|
+
|
|
44
|
+
room.messaging.on("message", on_message)
|
|
45
|
+
|
|
46
|
+
await agent.start(room=room)
|
|
47
|
+
|
|
48
|
+
done, pending = await asyncio.wait([
|
|
49
|
+
dismissed,
|
|
50
|
+
asyncio.ensure_future(room.protocol.wait_for_close())
|
|
51
|
+
], return_when=asyncio.FIRST_COMPLETED)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
await agent.stop()
|
|
55
|
+
|
|
56
|
+
def on_done(task: asyncio.Task):
|
|
57
|
+
try:
|
|
58
|
+
result = task.result()
|
|
59
|
+
except Exception as e:
|
|
60
|
+
logger.error("agent encountered an error", exc_info=e)
|
|
61
|
+
|
|
62
|
+
task = asyncio.create_task(run())
|
|
63
|
+
task.add_done_callback(on_done)
|
|
64
|
+
|
|
65
|
+
async def on_call(self, event: CallEvent):
|
|
66
|
+
await self._spawn(room_name=event.room_name, room_url=event.room_url, token=event.token, arguments=event.arguments)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class RemoteAgentServer[T:SingleRoomAgent](WebhookServer):
|
|
70
|
+
def __init__(self, *, cls: Optional[T] = None, path: Optional[str] = None, app: Optional[web.Application] = None, host = None, port = None, webhook_secret = None, create_agent: Optional[Callable[[dict], SingleRoomAgent]] = None, validate_webhook_secret: Optional[bool] = None):
|
|
71
|
+
super().__init__(path=path, app=app, host=host, port=port, webhook_secret=webhook_secret, validate_webhook_secret=validate_webhook_secret)
|
|
72
|
+
|
|
73
|
+
if create_agent == None:
|
|
74
|
+
def default_create_agent(arguments: dict) -> SingleRoomAgent:
|
|
75
|
+
return cls(**arguments)
|
|
76
|
+
|
|
77
|
+
create_agent = default_create_agent
|
|
78
|
+
|
|
79
|
+
self._create_agent = create_agent
|
|
80
|
+
|
|
81
|
+
async def _spawn(self, *, room_name: str, room_url: str, token: str, arguments: Optional[dict] = None):
|
|
82
|
+
|
|
83
|
+
logger.info(f"room: {room_name} url: {room_url} token: {token} arguments: {arguments}")
|
|
84
|
+
agent = self._create_agent(arguments=arguments)
|
|
85
|
+
|
|
86
|
+
async def run():
|
|
87
|
+
async with RoomClient(protocol=WebSocketClientProtocol(url=room_url, token=token)) as room:
|
|
88
|
+
|
|
89
|
+
dismissed = asyncio.Future()
|
|
90
|
+
|
|
91
|
+
def on_message(message: RoomMessage):
|
|
92
|
+
if message.type == "dismiss":
|
|
93
|
+
logger.info(f"dismissed by {message.from_participant_id}")
|
|
94
|
+
dismissed.set_result(True)
|
|
95
|
+
|
|
96
|
+
room.messaging.on("message", on_message)
|
|
97
|
+
|
|
98
|
+
await agent.start(room=room)
|
|
99
|
+
|
|
100
|
+
done, pending = await asyncio.wait([
|
|
101
|
+
dismissed,
|
|
102
|
+
asyncio.ensure_future(room.protocol.wait_for_close())
|
|
103
|
+
], return_when=asyncio.FIRST_COMPLETED)
|
|
104
|
+
|
|
105
|
+
await agent.stop()
|
|
106
|
+
|
|
107
|
+
def on_done(task: asyncio.Task):
|
|
108
|
+
try:
|
|
109
|
+
result = task.result()
|
|
110
|
+
except Exception as e:
|
|
111
|
+
logger.error("agent encountered an error", exc_info=e)
|
|
112
|
+
|
|
113
|
+
task = asyncio.create_task(run())
|
|
114
|
+
task.add_done_callback(on_done)
|
|
115
|
+
|
|
116
|
+
async def on_call(self, event: CallEvent):
|
|
117
|
+
await self._spawn(room_name=event.room_name, room_url=event.room_url, token=event.token, arguments=event.arguments)
|