-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8c5b2d6
commit fec6b26
Showing
7 changed files
with
464 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import requests | ||
from typing import Optional | ||
|
||
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify | ||
|
||
from chat_gpt import ChatGPT | ||
|
||
app = Flask(__name__) | ||
app.secret_key = 'supersecretkey' | ||
chat_gpt: Optional[ChatGPT] = None | ||
|
||
|
||
@app.route('/') | ||
def setup(): | ||
return render_template('setup.html') | ||
|
||
|
||
@app.route('/setup', methods=['POST']) | ||
def setup_submit(): | ||
global chat_gpt | ||
api_key = request.form['api_key'] | ||
model_id = request.form['model_id'] | ||
context = request.form['context'] | ||
|
||
if not test_api(api_key): | ||
return jsonify({'status': 'error', 'message': 'Failed to connect to OpenAI API'}) | ||
|
||
chat_gpt = ChatGPT(model_id, api_key, context) | ||
chat_gpt.add_system_message(f"{context}. Respond precisely. Do not give more information than necessary.") | ||
|
||
try: | ||
chat_gpt.gpt_conversation() | ||
return jsonify({'status': 'success', 'message': 'Connection successful'}) | ||
except Exception as e: | ||
return jsonify({'status': 'error', 'message': str(e)}) | ||
|
||
|
||
def test_api(api_key: str) -> bool: | ||
headers = { | ||
'Authorization': f'Bearer {api_key}', | ||
} | ||
response = requests.get('https://api.openai.com/v1/models', headers=headers) | ||
return response.status_code == 200 | ||
|
||
|
||
@app.route('/chat') | ||
def chat(): | ||
if chat_gpt is None: | ||
return redirect(url_for('setup')) | ||
return render_template('chat.html') | ||
|
||
|
||
@app.route('/send_message', methods=['POST']) | ||
def send_message(): | ||
user_input = request.form['message'] | ||
chat_gpt.add_user_message(user_input) | ||
response = chat_gpt.gpt_conversation() | ||
|
||
if response['status'] == 'success': | ||
return jsonify({'status': 'success', 'message': response['message']}) | ||
else: | ||
return jsonify({'status': 'error', 'message': response['message']}) | ||
|
||
|
||
if __name__ == '__main__': | ||
app.run(debug=True, port=8801) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import openai | ||
from openai.error import AuthenticationError, InvalidRequestError, RateLimitError | ||
|
||
# Constants | ||
PRICE_MAP: dict = { | ||
"gpt-3.5-turbo": 0.002, | ||
"gpt-4": 0.06, | ||
} | ||
|
||
|
||
class ChatGPT: | ||
def __init__(self, model_id: str, api_key: str, context: str): | ||
self.model_id = model_id | ||
self.api_key = api_key | ||
self.context = context | ||
self.conversation = [] | ||
self.cost = 0.0 | ||
|
||
openai.api_key = api_key | ||
|
||
def add_system_message(self, message: str) -> None: | ||
self.conversation.append({'role': 'system', 'content': message}) | ||
|
||
def add_user_message(self, message: str) -> None: | ||
self.conversation.append({'role': 'user', 'content': message}) | ||
|
||
def gpt_conversation(self) -> dict: | ||
try: | ||
response = openai.ChatCompletion.create( | ||
model=self.model_id, | ||
messages=self.conversation | ||
) | ||
|
||
self.conversation.append( | ||
{ | ||
'role': response.choices[0].message.role, | ||
'content': response.choices[0].message.content, | ||
} | ||
) | ||
|
||
total_tokens = response.usage.total_tokens | ||
self.calculate_price_from_tokens(total_tokens) | ||
|
||
return {'status': 'success', 'message': response.choices[0].message.content.strip()} | ||
|
||
except InvalidRequestError as exc: | ||
return {'status': 'error', 'message': f"Invalid request. Error: {exc}"} | ||
except RateLimitError as exc: | ||
return {'status': 'error', 'message': f"Rate limit exceeded. Error: {exc}"} | ||
except Exception as exc: | ||
return {'status': 'error', 'message': f"Unexpected error. Error: {exc}"} | ||
|
||
def calculate_price_from_tokens(self, total_tokens: int) -> float: | ||
price_per_1000 = PRICE_MAP[self.model_id] | ||
price = (total_tokens / 1000) * price_per_1000 | ||
self.cost += price | ||
|
||
return round(price, 4) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ openai | |
rich | ||
cchardet | ||
keyboard | ||
Flask |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
$(document).ready(function () { | ||
$("#setup_form").on("submit", function (event) { | ||
event.preventDefault(); | ||
const formData = $(this).serialize(); // Serialize the form data before disabling the fields | ||
toggleFormFields(true); // Disable the form fields | ||
$.ajax({ | ||
url: "/setup", | ||
method: "POST", | ||
data: formData, | ||
success: function (response) { | ||
if (response.status === "success") { | ||
// Display success alert | ||
$("#message").html('<div class="alert alert-success" role="alert">' + response.message + '</div>'); | ||
// Redirect to chat page | ||
window.location.href = "/chat"; | ||
} else { | ||
$("#message").html('<div class="alert alert-danger" role="alert">' + response.message + '</div>'); | ||
toggleFormFields(false); // Enable the form fields | ||
} | ||
}, | ||
error: function () { | ||
$("#message").html('<div class="alert alert-danger" role="alert">An error occurred while processing the request.</div>'); | ||
toggleFormFields(false); // Enable the form fields | ||
} | ||
}); | ||
}); | ||
|
||
$("#send_button").on("click", function () { | ||
var userMessage = $("#user_message").val().trim(); | ||
if (userMessage !== "") { | ||
$("#user_message").val(""); | ||
$("#chatlog").append('<div class="user-message">You: ' + escapeHtml(userMessage) + '</div>'); | ||
scrollToBottom(); // Scroll to the bottom after appending user message | ||
$.ajax({ | ||
url: "/send_message", // Change this line to use the correct endpoint | ||
method: "POST", | ||
data: { message: userMessage }, | ||
success: function (response) { | ||
if (response.status === "success") { | ||
if (response.message) { | ||
$("#chatlog").append('<div class="assistant-message">ChatGPT: ' + escapeHtml(response.message) + '</div>'); | ||
} else { | ||
$("#chatlog").append('<div class="error-message">Error: Received an empty response.</div>'); | ||
} | ||
scrollToBottom(); // Scroll to the bottom after appending assistant message | ||
} else { | ||
if (response.message) { | ||
$("#chatlog").append('<div class="error-message">Error: ' + escapeHtml(response.message) + '</div>'); | ||
} else { | ||
$("#chatlog").append('<div class="error-message">Error: An unknown error occurred.</div>'); | ||
} | ||
scrollToBottom(); // Scroll to the bottom after appending error message | ||
} | ||
} | ||
}); | ||
} | ||
}); | ||
|
||
$("#user_message").on("keypress", function (event) { | ||
if (event.which == 13 && !event.shiftKey) { | ||
event.preventDefault(); | ||
$("#send_button").click(); | ||
} | ||
}); | ||
}); | ||
|
||
function toggleFormFields(disable) { | ||
$("#setup_form input, #setup_form select, #setup_form button").prop("disabled", disable); | ||
} | ||
|
||
function escapeHtml(text) { | ||
return text | ||
.replace(/</g, '<') | ||
.replace(/>/g, '>') | ||
.replace(/(?:\r\n|\r|\n)/g, '<br>') // Replace newline characters with <br> | ||
.replace(/\t/g, ' ') // Replace tab characters with four non-breaking spaces | ||
.replace(/ +/g, function (match) { | ||
return match.split('').map(() => ' ').join(''); | ||
}); // Replace consecutive spaces with non-breaking spaces | ||
} | ||
|
||
function scrollToBottom() { | ||
const chatlog = document.getElementById("chatlog"); | ||
chatlog.scrollTop = chatlog.scrollHeight; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
:root { | ||
--user-bg-color: #1e88e5; | ||
--assistant-bg-color: #f5f5f5; | ||
--error-bg-color: #e53935; | ||
--primary-text-color: #212121; | ||
--error-text-color: #fff; | ||
--message-margin: 20px; | ||
--border-radius: 8px; | ||
--font-family: 'Roboto', sans-serif; | ||
--container-bg: #e0e0e0; | ||
--gradient-start: #455a64; | ||
--gradient-end: #607d8b; | ||
--input-border-color: #bdbdbd; | ||
--button-color: #1976d2; | ||
--button-hover-color: #1565c0; | ||
} | ||
|
||
html, body { | ||
height: 100%; | ||
margin: 0; | ||
padding: 0; | ||
} | ||
|
||
body { | ||
font-family: var(--font-family); | ||
background-color: var(--container-bg); | ||
background-image: linear-gradient(120deg, var(--gradient-start), var(--gradient-end)); | ||
} | ||
|
||
.container { | ||
width: 100%; | ||
max-width: 100%; | ||
max-height: 100vh; | ||
padding: 20px; /* Add padding */ | ||
box-sizing: border-box; /* Add box-sizing to prevent the container from overflowing */ | ||
margin: 0; | ||
height: calc(100vh - 40px); /* Subtract the footer height */ | ||
display: flex; | ||
flex-direction: column; | ||
background-color: #ffffff; | ||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); | ||
} | ||
|
||
h1 { | ||
text-align: center; | ||
margin-bottom: 50px; | ||
color: var(--primary-text-color); | ||
} | ||
|
||
.form-group { | ||
margin-bottom: 20px; | ||
} | ||
|
||
label { | ||
display: block; | ||
color: var(--primary-text-color); | ||
} | ||
|
||
input[type="text"], textarea, select { | ||
width: 100%; | ||
padding: 8px 12px; | ||
box-sizing: border-box; | ||
border: 1px solid var(--input-border-color); | ||
border-radius: var(--border-radius); | ||
background-color: #ffffff; | ||
} | ||
|
||
button[type="submit"], .btn-primary { | ||
background-color: var(--button-color); | ||
color: white; | ||
padding: 8px 16px; | ||
border: none; | ||
border-radius: var(--border-radius); | ||
cursor: pointer; | ||
transition: background-color 0.3s; | ||
} | ||
|
||
button[type="submit"]:hover, | ||
.btn-primary:hover { | ||
background-color: var(--button-hover-color); | ||
} | ||
|
||
.chatbox { | ||
width: 100%; | ||
background-color: white; | ||
border: 1px solid var(--input-border-color); | ||
border-radius: var(--border-radius); | ||
} | ||
|
||
.chatlog { | ||
overflow-y: scroll; | ||
padding: 20px; | ||
border-bottom: 1px solid var(--input-border-color); | ||
height: calc(100% - 82px); | ||
} | ||
|
||
.input-group { | ||
border-top: 2px solid #f8f9fa; | ||
} | ||
|
||
.card { | ||
flex-grow: 1; | ||
display: flex; | ||
flex-direction: column; | ||
overflow: hidden; | ||
} | ||
|
||
.card-body { | ||
display: flex; | ||
flex-direction: column; | ||
height: calc(100% - 50px); | ||
overflow: hidden; | ||
} | ||
|
||
.chatlog { | ||
flex-grow: 1; | ||
overflow-y: scroll; | ||
} | ||
|
||
.user-message, | ||
.assistant-message, | ||
.error-message { | ||
border-radius: 5px; | ||
padding: 10px; | ||
margin-bottom: 10px; | ||
} | ||
|
||
.message { | ||
margin-right: var(--message-margin); | ||
margin-left: var(--message-margin); | ||
border-radius: var(--border-radius); | ||
padding: 12px 16px; | ||
line-height: 1.5; | ||
font-size: 16px; | ||
max-width: 80%; | ||
word-wrap: break-word; | ||
margin-bottom: 10px; | ||
} | ||
|
||
.user-message { | ||
background-color: var(--user-bg-color); | ||
color: var(--error-text-color); | ||
align-self: flex-end; | ||
} | ||
|
||
.assistant-message { | ||
background-color: var(--assistant-bg-color); | ||
color: var(--primary-text-color); | ||
align-self: flex-start; | ||
} | ||
|
||
.error-message { | ||
background-color: var(--error-bg-color); | ||
color: var(--error-text-color); | ||
align-self: center; | ||
} | ||
|
||
.footer { | ||
position: absolute; | ||
bottom: 0; | ||
width: 100%; | ||
text-align: center; | ||
padding: 10px 0; | ||
background-color: #455a64; /* Updated background color */ | ||
color: #fff; /* Updated text color */ | ||
height: 40px; /* Add a fixed height for the footer */ | ||
} |
Oops, something went wrong.