add message ids to frontend p

modified:   app.py
	modified:   templates/chat.html
This commit is contained in:
Russell Ballestrini 2023-10-29 18:00:48 -04:00
parent 19e6b7579e
commit 5abd5fa516
2 changed files with 137 additions and 60 deletions

111
app.py
View file

@ -65,11 +65,19 @@ def on_join(data):
for message in previous_messages:
emit(
"previous_messages",
{"username": message.username, "message": message.content},
{
"id": message.id,
"username": message.username,
"message": message.content,
},
room=request.sid,
)
emit("message", f"{data['username']} has joined the room.", room=room)
emit(
"message",
{"id": None, "content": f"{data['username']} has joined the room."},
room=room,
)
@socketio.on("message")
@ -81,30 +89,31 @@ def handle_message(data):
db.session.add(new_message)
db.session.commit()
emit("message", f"{data['username']}: {data['message']}", room=data["room"])
emit(
"message",
{"id": new_message.id, "content": f"{data['username']}: {data['message']}"},
room=data["room"],
)
if "claude" in data["message"] or "gpt" in data["message"]:
if "claude" in data["message"] or "gpt" in data["message"]:
# Emit a temporary message indicating that llm is processing
emit(
"message",
{"id": None, "content": f"<span id='processing'>Processing...</span>"},
room=data["room"],
)
if "claude" in data["message"]:
func = chat_claude
elif "gpt" in data["message"]:
func = chat_gpt
# Emit a temporary message indicating that llm is processing
emit("message", f"<span id='processing'>Processing...</span>", room=data["room"])
# Call the chat_claude function without blocking using eventlet.spawn
eventlet.spawn(func, data["username"], data["room"], data["message"])
eventlet.spawn(chat_claude, data["username"], data["room"], data["message"])
if "gpt" in data["message"]:
eventlet.spawn(chat_gpt, data["username"], data["room"], data["message"])
def chat_claude(username, room, message):
def chat_claude(username, room, message):
with app.app_context():
# claude has a 100,000 token context window for prompts.
all_messages = (
Message.query.filter_by(room=room)
.order_by(Message.id.desc())
.all()
Message.query.filter_by(room=room).order_by(Message.id.desc()).all()
)
chat_history = ""
@ -114,10 +123,9 @@ def chat_claude(username, room, message):
chat_history += f"Assistant: {msg.username}: {msg.content}\n\n"
else:
chat_history += f"Human: {msg.username}: {msg.content}\n\n"
# append the new message.
chat_history += f"Human: {username}: {message}\n\nAssistant:"
# Initialize the Bedrock client using boto3
client = boto3.client("bedrock-runtime", region_name="us-east-1")
@ -146,9 +154,15 @@ def chat_claude(username, room, message):
# Process the event stream
buffer = ""
# save empty message, we need the ID when we chunk the response.
with app.app_context():
new_message = Message(username="anthropic.claude-v2", content=buffer, room=room)
db.session.add(new_message)
db.session.commit()
msg_id = new_message.id
first_chunk = True
for event in response["body"]:
content = ""
if "chunk" in event:
@ -159,15 +173,27 @@ def chat_claude(username, room, message):
buffer += content # Accumulate content
if first_chunk:
socketio.emit("message_chunk", f"{username} (anthropic.claude-v2): {content}", room=room)
socketio.emit(
"message_chunk",
{
"id": msg_id,
"content": f"{username} (anthropic.claude-v2): {content}",
},
room=room,
)
first_chunk = False
else:
socketio.emit("message_chunk", content, room=room)
socketio.emit(
"message_chunk",
{"id": msg_id, "content": content},
room=room,
)
socketio.sleep(0) # Force immediate handling
# Save the entire completion to the database
with app.app_context():
new_message = Message(username="anthropic.claude-v2", content=buffer, room=room)
new_message = db.session.query(Message).filter(Message.id == msg_id).one_or_none()
new_message.content = buffer
db.session.add(new_message)
db.session.commit()
@ -175,7 +201,6 @@ def chat_claude(username, room, message):
def chat_gpt(username, room, message):
with app.app_context():
last_messages = (
Message.query.filter_by(room=room)
@ -185,7 +210,14 @@ def chat_gpt(username, room, message):
)
chat_history = [
{"role": "system" if (msg.username == "gpt-3.5-turbo" or msg.username == "anthropic.claude-v2") else "user", "content": f"{msg.username}: {msg.content}"}
{
"role": "system"
if (
msg.username == "gpt-3.5-turbo" or msg.username == "anthropic.claude-v2"
)
else "user",
"content": f"{msg.username}: {msg.content}",
}
for msg in reversed(last_messages)
]
@ -193,6 +225,13 @@ def chat_gpt(username, room, message):
buffer = "" # Content buffer for accumulating the chunks
# save empty message, we need the ID when we chunk the response.
with app.app_context():
new_message = Message(username="gpt-3.5-turbo", content=buffer, room=room)
db.session.add(new_message)
db.session.commit()
msg_id = new_message.id
first_chunk = True
for chunk in openai.ChatCompletion.create(
model="gpt-3.5-turbo",
@ -206,15 +245,27 @@ def chat_gpt(username, room, message):
buffer += content # Accumulate content
if first_chunk:
socketio.emit("message_chunk", f"{username} (gpt-3.5-turbo): {content}", room=room)
socketio.emit(
"message_chunk",
{
"id": msg_id,
"content": f"{username} (gpt-3.5-turbo): {content}",
},
room=room,
)
first_chunk = False
else:
socketio.emit("message_chunk", content, room=room)
socketio.emit(
"message_chunk",
{"id": msg_id, "content": content},
room=room,
)
socketio.sleep(0) # Force immediate handling
# Save the entire completion to the database
with app.app_context():
new_message = Message(username="gpt-3.5-turbo", content=buffer, room=room)
new_message = db.session.query(Message).filter(Message.id == msg_id).one_or_none()
new_message.content = buffer
db.session.add(new_message)
db.session.commit()

View file

@ -70,9 +70,6 @@ const urlParams = new URLSearchParams(window.location.search);
const username = urlParams.get('username');
const room = '{{ room }}';
let lastMessageElement = null;
let lastMessageContent = "";
// Function to handle sending the message
function sendMessage() {
const message = document.getElementById('message').value;
@ -100,63 +97,92 @@ socket.on('connect', () => {
socket.emit('join', {username: username, room: room});
});
socket.on('message', (message) => {
socket.on('message', (data) => {
const newMessage = document.createElement('p');
newMessage.innerHTML = marked.marked(message);
// Set the message ID as the id attribute
newMessage.id = "message-" + data.id; // Prefixing with "message-" to ensure the ID starts with a letter
// Convert the message content to markdown and set it as the innerHTML
newMessage.innerHTML = marked.marked(data.content);
// Append the new message to the chat container
document.getElementById('chat').appendChild(newMessage);
// Apply syntax highlighting to any code blocks within the message
newMessage.querySelectorAll('pre code').forEach((block) => {
hljs.highlightElement(block);
});
// Scroll to the bottom of the chat container
document.getElementById('chat').scrollTop = document.getElementById('chat').scrollHeight;
});
socket.on('previous_messages', (data) => {
const chat = document.getElementById('chat');
chat.innerHTML += '<p>' + data.username + ': ' + marked.marked(data.message) + '</p>';
chat.querySelectorAll('pre code').forEach((block) => {
hljs.highlightBlock(block);
// Create a new message element
const newMessage = document.createElement('p');
// Set the ID attribute for the message
newMessage.id = "message-" + data.id;
// Set the inner content for the message
newMessage.innerHTML = `${data.username}: ${marked.marked(data.message)}`;
// Append the new message to the chat container
chat.appendChild(newMessage);
// Apply syntax highlighting to any code blocks within the message
newMessage.querySelectorAll('pre code').forEach((block) => {
hljs.highlightElement(block);
});
});
socket.on('delete_processing_message', (data) => {
const tempMessage = document.getElementById("processing");
if (tempMessage) {
tempMessage.remove();
}
lastMessageElement = null;
lastMessageContent = "";
});
socket.on('message_chunk', (message_chunk) => {
if (lastMessageElement) {
// Accumulate the new chunk
lastMessageContent += message_chunk;
// Render the entire accumulated content as Markdown
lastMessageElement.innerHTML = marked.marked(lastMessageContent);
// A dictionary to hold buffers for each message ID
const messageBuffers = {};
// Apply syntax highlighting to code blocks within the content
lastMessageElement.querySelectorAll('pre code').forEach((block) => {
hljs.highlightElement(block);
});
} else {
lastMessageContent = message_chunk;
const newMessage = document.createElement('p');
newMessage.innerHTML = marked.marked(lastMessageContent);
document.getElementById('chat').appendChild(newMessage);
lastMessageElement = newMessage;
socket.on('message_chunk', (data) => {
const messageId = "message-" + data.id;
let targetMessageElement = document.getElementById(messageId);
// Apply syntax highlighting to code blocks within the content
newMessage.querySelectorAll('pre code').forEach((block) => {
hljs.highlightElement(block);
});
// If the target message element doesn't exist, it's an initial chunk
if (!targetMessageElement) {
targetMessageElement = document.createElement('p');
targetMessageElement.id = messageId;
document.getElementById('chat').appendChild(targetMessageElement);
}
// If the message buffer for this ID doesn't exist, create it
if (!messageBuffers[data.id]) {
messageBuffers[data.id] = "";
}
// Append the chunk to the buffer
messageBuffers[data.id] += data.content;
// Process the entire buffer with marked and set it as the content of the target element
targetMessageElement.innerHTML = marked.marked(messageBuffers[data.id]);
// Apply syntax highlighting to code blocks within the content
targetMessageElement.querySelectorAll('pre code').forEach((block) => {
hljs.highlightElement(block);
});
// Scroll to the bottom of the chat container
document.getElementById('chat').scrollTop = document.getElementById('chat').scrollHeight;
});
</script>
</body>