-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlib.py
More file actions
142 lines (118 loc) · 4.42 KB
/
Copy pathlib.py
File metadata and controls
142 lines (118 loc) · 4.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import asyncio
import json
import uuid
from dotenv import load_dotenv
from memory import ConversationMemory
from openai import OpenAI
from websocket import ConnectionManager
from ouro import Ouro
load_dotenv()
client = OpenAI()
async def send_typing_status(user_id, conversation, supabase):
channel = supabase.channel(f"{conversation.id}-presence")
def on_subscribe(status, err):
print("on_subscribe", status, err)
# if status == RealtimeSubscribeStates.SUBSCRIBED:
channel.send_broadcast(
"typing",
{
"user_id": user_id,
"active": True,
},
)
channel.subscribe(on_subscribe)
async def handle_message(message: str, ouro: Ouro, manager: ConnectionManager):
"""
Handle incoming messages from the backend (e.g. new messages from user conversations). Stateless and async.
"""
# The user id of the assistant
agent_user_id = ouro.user.id
try:
data = json.loads(message)
except:
print("Failed to parse message:", message)
return
if "event" in data and data["event"] == "new-message":
print("Parsed message:", data)
conversation_id = data["data"]["conversation_id"]
incoming_message = data["data"]["text"]
websocket = manager.active_connections.get(agent_user_id, None)
# Get the conversation
conversation = ouro.conversations.retrieve(conversation_id)
if conversation is None:
return
# If message is coming from self, ignore
if data["data"]["user_id"] == agent_user_id:
return
# Send typing status to the conversation channel
# asyncio.create_task(send_typing_status(user_id, conversation, ouro.supabase))
# TODO: Handle group conversations
# Get the recipient's user id
recipient_id = (
str(conversation.metadata.members[0])
if conversation.metadata.members[0] != agent_user_id
else str(conversation.metadata.members[1])
)
# Load the messages
messages = conversation.messages.list()
messages_formatted = [
{
"role": "user" if message["user_id"] != agent_user_id else "assistant",
"content": message["text"],
}
for message in messages
]
memory = ConversationMemory(short_term_size=5)
for m in messages_formatted:
memory.add_message(m)
context = memory.build_context(incoming_message)
# print(context)
# Get the response from the model
stream = client.chat.completions.create(
model="gpt-4o-mini",
messages=context,
stream=True,
)
message = ""
message_id = str(uuid.uuid4())
for chunk in stream:
if chunk.choices[0].delta.content is not None:
content = chunk.choices[0].delta.content
message += content
if websocket:
# Send each chunk back to the server
await websocket.send(
json.dumps(
{
"event": "llm-response",
"recipient_id": recipient_id,
"data": {
"content": content,
"id": message_id,
"user_id": agent_user_id,
},
}
)
)
# Artificially wait a few ms to simulate typing
await asyncio.sleep(0.001)
# Send a message to indicate the stream has ended
if websocket:
await websocket.send(
json.dumps(
{
"event": "llm-response-end",
"recipient_id": recipient_id,
"data": {"id": message_id, "user_id": agent_user_id},
}
)
)
# Write the response to the conversation
content = ouro.posts.Content(text=message)
created = conversation.messages.create(
id=message_id,
text=content.to_dict()["text"],
json=content.to_dict()["json"],
)
else:
print("Backend response:", data)