Streaming results to frontend realtime, remove jquery.

modified:   app.py
	modified:   templates/chat.html
This commit is contained in:
Russell Ballestrini 2023-10-22 12:28:08 -04:00
parent 546518c604
commit 180dec6318
2 changed files with 114 additions and 60 deletions

41
app.py
View file

@ -9,6 +9,8 @@ import openai
import os
import time
app = Flask(__name__)
app.config["SECRET_KEY"] = "your_secret_key"
@ -84,7 +86,6 @@ def handle_message(data):
# Call the chat_gpt function without blocking using eventlet.spawn
eventlet.spawn(chat_gpt, data["username"], data["room"], data["message"])
def chat_gpt(username, room, message):
with app.app_context():
@ -95,39 +96,41 @@ def chat_gpt(username, room, message):
.all()
)
# Format these messages as a chat history, with each message being a dict with 'role' and 'content'.
chat_history = [
{"role": "system" if msg.username == "GPT-3.5" else "user", "content": msg.content}
for msg in reversed(last_messages)
]
# Append the new message
chat_history.append({"role": "user", "content": message})
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=chat_history
)
buffer = "" # Content buffer for accumulating the chunks
# Extract response from ChatGPT API
response_text = response["choices"][0]["message"]["content"]
first_chunk = True
for chunk in openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=chat_history,
stream=True,
):
content = chunk["choices"][0].get("delta", {}).get("content")
# Convert response_text to Markdown
response_md = markdown.markdown(response_text, extensions=["fenced_code"])
if content:
buffer += content # Accumulate content
# Save ChatGPT's response in the database
if first_chunk:
socketio.emit("message_chunk", f"{username} (GPT-3.5): {content}", room=room)
first_chunk = False
else:
socketio.emit("message_chunk", content, room=room)
socketio.sleep(0) # Force immediate handling
# Save the entire completion to the database
with app.app_context():
chatgpt_response_message = Message(
username="GPT-3.5", content=response_md, room=room
)
db.session.add(chatgpt_response_message)
new_message = Message(username="GPT-3.5", content=buffer, room=room)
db.session.add(new_message)
db.session.commit()
socketio.emit("delete_processing_message", "", room=room)
# Emit the response to the room
socketio.emit("message", f"{username} (GPT-3.5): {response_md}", room=room)
if __name__ == "__main__":
socketio.run(app, host="0.0.0.0", port=5001)

View file

@ -11,7 +11,8 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/default.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.2/marked.min.js" integrity="sha512-rfX4p3RNnxdwLT3wWP1K0NR3ztTobn+sISlT9WhxDDK00zNYbQ6MCHA5OHm0hqKAzEMXYCgFrp8iY/ER5MkXqA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<style>
html, body {
height: 100%;
@ -57,56 +58,106 @@
<div id="chat-container">
<div id="chat"></div>
<form id="message-form">
<input id="message" type="text" placeholder="Type your message...">
<textarea id="message" rows="4" placeholder="Type your message..."></textarea>
</form>
</div>
<script>
const socket = io.connect('http://' + document.domain + ':' + location.port);
const urlParams = new URLSearchParams(window.location.search);
const username = urlParams.get('username');
const room = '{{ room }}';
const socket = io.connect('http://' + document.domain + ':' + location.port);
socket.on('connect', () => {
socket.emit('join', {username: username, room: room});
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;
if (message.trim() !== "") { // Ensure we're not sending empty messages
socket.emit('message', {'username': username, 'message': message, 'room': room});
document.getElementById('message').value = '';
}
}
// Event listener for form submission
document.getElementById('message-form').addEventListener('submit', (e) => {
e.preventDefault();
sendMessage();
});
// Event listener for the Enter key press in the textarea
document.getElementById('message').addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
socket.on('connect', () => {
socket.emit('join', {username: username, room: room});
});
socket.on('message', (message) => {
const newMessage = document.createElement('p');
newMessage.innerHTML = marked.marked(message);
document.getElementById('chat').appendChild(newMessage);
newMessage.querySelectorAll('pre code').forEach((block) => {
hljs.highlightElement(block);
});
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);
});
});
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);
// 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;
$('#message-form').submit((e) => {
e.preventDefault();
const message = $('#message').val();
socket.emit('message', {'username': username, 'message': message, room: room});
$('#message').val('');
});
socket.on('message', (message) => {
var newMessage = document.createElement('p');
newMessage.innerHTML = message;
$('#chat').append(newMessage);
// Apply syntax highlighting to the new message
newMessage.querySelectorAll('pre code').forEach((block) => {
hljs.highlightElement(block);
});
// Scroll to the bottom of the chat container
$('#chat').scrollTop($('#chat')[0].scrollHeight);
});
socket.on('previous_messages', (data) => {
$('#chat').append('<p>' + data.username + ': ' + data.message + '</p>');
$('pre code').each((i, block) => {
hljs.highlightBlock(block);
});
});
socket.on('delete_processing_message', (data) => {
const tempMessage = document.getElementById("processing");
if (tempMessage) {
tempMessage.remove();
}
// Apply syntax highlighting to code blocks within the content
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;
});
</script>
</body>