Files
hub/backend/app.py
T

214 lines
7.5 KiB
Python

from database import create_sql_engine, User, Agent, Chat, Message, Membership
from server import start_server
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import select
from quart import Quart, Response, request, send_from_directory, jsonify, websocket
from datetime import datetime
import asyncio
import json
import sys
class App:
def __init__(self, data_directory):
self.data_directory = data_directory
self.sql_engine = create_sql_engine(self.data_directory)
self.user_connections = {}
def sql_session(self):
return Session(self.sql_engine)
def add_user_connection(self, user_id, channel):
if user_id not in self.user_connections:
self.user_connections[user_id] = []
if channel not in self.user_connections[user_id]:
self.user_connections[user_id].append(channel)
def remove_user_connection(self, user_id, channel):
if user_id in self.user_connections:
if channel in self.user_connections[user_id]:
self.user_connections[user_id].remove(channel)
if not self.user_connections[user_id]: # Check if the list is empty
del self.user_connections[user_id]
def create_agent(self, user_id, name, description, picture, kind, data):
with self.sql_session() as session:
agent = Agent()
agent.user_id = user_id
agent.name = name
agent.description = description
agent.picture = picture
agent.kind = kind
agent.data = data
session.add(agent)
session.commit()
print(f"Created agent #{agent.id}: {agent.name}")
return agent.id
def create_chat(self, user_id, name, description, picture):
with self.sql_session() as session:
chat = Chat()
chat.user_id = user_id
chat.name = name
chat.description = description
chat.picture = picture
session.add(chat)
session.commit()
print(f"Created chat #{chat.id}: {chat.name}")
return chat.id
def get_chats(self, user_id):
with self.sql_session() as session:
chats = session.execute(select(Chat).where(Chat.user_id == user_id)).scalars().all()
return [{
"id": chat.id,
"name": chat.name,
"description": chat.description,
"picture": chat.picture
} for chat in chats]
def get_agents(self, user_id):
with self.sql_session() as session:
agents = session.execute(select(Agent).where(Agent.user_id == user_id)).scalars().all()
return [{
"id": agent.id,
"name": agent.name,
"description": agent.description,
"picture": agent.picture
} for agent in agents]
def get_chat_messages(self, chat_id, earliest_index=0):
with self.sql_session() as session:
messages = session.execute(
select(Message).where(Message.chat_id == chat_id).order_by(Message.id.desc())
).scalars().fetchmany(MESSAGE_PAGE_SIZE)
return [{
"id": message.id,
"time": message.time,
"sender": message.sender,
"kind": message.kind,
"content": message.content
} for message in messages]
def verify_user_credentials(self, username, password):
with self.sql_session() as session:
user = session.execute(
select(User).where(User.username == username)
).scalars().first()
if user and user.password == password:
return user.id
return None
def create_new_user(self, username, email, password):
with self.sql_session() as session:
result = session.execute(
select(User.id).where(User.email == email or User.username == username)
).first()
if result is None:
user = User()
user.email = email
user.username = username
user.password = password
session.add(user)
session.commit()
user_id = user.id
print(f"Created user #{user_id}: {username} ({email})")
else:
print(f"Signup error: '{email}' or '{username}' are already used")
return user_id
def add_agent_to_chat(self, chat_id, agent_id):
with self.sql_session() as session:
membership = Membership()
membership.chat_id = chat_id
membership.agent_id = agent_id
session.add(membership)
session.commit()
print(f"Added agent #{agent_id} to chat #{chat_id}")
# If an integer is passed in sender, it is interpreted as an agent id
def send_chat_message(self, chat_id, content, sender='user'):
with self.sql_session() as session:
message = Message()
message.chat_id = chat_id
message.time = int(datetime.now().timestamp())
message.sender = sender
message.kind = 'text' # default kind
message.content = content
session.add(message)
session.commit()
# Notify chat owner
if sender != 'user':
chat_owner = session.execute(select(Chat.user_id).where(Chat.id == chat_id)).scalar()
self.notify_message_to_user(chat_owner, message)
# Notify participant agents
agent_ids = session.execute(select(Membership.agent_id).where(Membership.chat_id == chat_id)).scalars().all()
for agent_id in agent_ids:
if agent_id != sender:
self.notify_message_to_agent(agent_id, message)
return {
'id': message.id,
'chatId': message.chat_id,
'time': message.time,
'sender': message.sender,
'kind': message.kind,
'content': message.content,
}
def notify_message_to_user(self, user_id, message):
if user_id in self.user_connections:
user_message = {
'action': 'chat-message',
'id': message.id,
'time': message.time,
'sender': message.sender,
'kind': message.kind,
'content': message.content
}
for channel in self.user_connections[user_id]:
asyncio.create_task(channel.put(user_message))
def notify_message_to_agent(self, agent_id, message):
pass # TODO
def generate_default_user_data(self, user_id):
agent_id = self.create_agent(
user_id=user_id,
name="Intro",
description="Introduces you to the application.",
picture=None,
kind="intro",
data={}
)
# Create a chat with that agent in it
chat_id = self.create_chat(
user_id=user_id,
name="Initial Chat",
description=None,
picture=None
)
# Add the agent to the chat
self.add_agent_to_chat(chat_id, agent_id)
if __name__ == "__main__":
if len(sys.argv) > 1:
data_directory = sys.argv[1]
else:
print("No directory specified. Using current directory as default.")
data_directory = "."
app = App(data_directory)
start_server(app)