An Improved “Build Your Own ChatGPT” Application

Everything is perfect and there is always room for improvement.

Shunryu Suzuki

In my previous article, Build Your Own ChatGPT, I showed you how to use the OpenAI Chat Completion API to create a pseudo ChatGPT. My web application used a static system prompt to answer Beatle related questions. While it was useful in demonstrating the basics of a ChatGPT lookalike, it was too restricted. Today, I build on that code to allow you to create and manage your own system prompts for a far more complete and useful solution.

Before I present the code, take a look at this Cheapo-Cheapo Productions video of the running application. Notice how I use the System Prompt to dynamically change the personality of the virtual agent. Also, note how I can switch models from Turbo to GPT4 for more accurate and interesting answers. Finally, the last System Prompt shows how you can add data to model processing without resorting to Model Fine Tuning.

Here is the complete HTML and JavaScript (index.html) for the application. As before, to use the application you need to obtain your own Bearer token from the OpenAI developer website. You do not need a webserver to run the application. It can be run on your own PC by double-clicking on index.html.

<!DOCTYPE html>

<html>
<head>
<title>GPT Web Application</title>
<link rel="stylesheet" href="css/openai.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<div class="container-fluid">
<br>
<div class="col-sm-12 ">
<div style="width: 100%" class="form-group mb-3">
<textarea placeholder="System Prompt" class="form-control" id="systemPrompt" rows="1"></textarea>
</div>
<button style="padding: 0.8rem 8rem" id="setSystemPrompt" class="btn btn-primary " onclick="setSystemPrompt()" >Set Prompt</button>
</div>
<br>
<div class="col-sm-12 ">
<div style="width: 100%" class="form-group mb-3">
<textarea placeholder="Message" class="form-control" id="message" rows="1"></textarea>
</div>
<button style="padding: 0.8rem 8rem" id="sendMsg" class="btn btn-primary " onclick="sendMsg()" disabled>Send</button>
<button style="padding: 0.8rem 8rem" id="reset" class="btn btn-primary " onclick="reset()" disabled>GPT4</button>
</div>
<br>
<div class="col-sm-12">
<div id="console-log" class="console-div" readonly=""></div>
</div>
</div>
</div>
<script type="text/javascript">
const OPEN_AI_CHAT_URL = "https://api.openai.com/v1/chat/completions";

const OPENAI_AUTH = "xxxxxxxx"; // Replace with your OpenAI Bearer Token
var gptConversation = [];
var systemPrompt;
const GPT4 = "gpt-4-1106-preview";
const TURBO = "gpt-3.5-turbo";
var MODEL;
$(document).ready(init);

function init() {
MODEL = TURBO;
var input = document.getElementById("message");
input.addEventListener("keyup", function(event) {
if (event.key === "Enter") {
event.preventDefault();
document.getElementById("sendMsg").click();
}
});
}

async function setSystemPrompt() {
clearAllChats();
systemPrompt = document.getElementById('systemPrompt').value;
establishInitialResponse();
}

async function establishInitialResponse() {
gptConversation = [];
mytext = "";
var systemRole = `[{"role":"system", "content":"${systemPrompt}"}]`;
gptConversation = JSON.parse(systemRole);
var user = {
role: "user",
content: "Hello"
}
gptConversation.push(user);
initialPrompt = await callGPT(gptConversation);
$('#sendMsg').prop("disabled", false);
$('#reset').prop("disabled", false);
}

function reset() {
if (document.getElementById('reset').innerHTML == "GPT4") {
MODEL = GPT4;
document.getElementById('reset').innerHTML = "TURBO"
} else {
MODEL = TURBO;
document.getElementById('reset').innerHTML = "GPT4"
}
establishInitialResponse();
clearAllChats();
}

function clearAllChats() {
var consoleTxt = $('#console-log').val();
$('#console-log').val("");
$("#console-log span").remove();
}
async function callGPT(message) {
const openAI = {
model: MODEL,
messages: message,
max_tokens: 1200,
presence_penalty: 0.5,
frequency_penalty: 0.5,
temperature: 0
};
var settings = {
"url": OPEN_AI_CHAT_URL,
"method": "post",
"timeout": 0,
"headers": {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": `Bearer ${OPENAI_AUTH}`,
},
data: JSON.stringify(openAI)
};
await $.ajax(settings).done(function(response) {
console.log(response);
var gptMessage = {
role: "assistant",
content: response.choices[0].message.content
}
gptConversation.push(gptMessage);
addToConsole(`Agent: ${response.choices[0].message.content}`, "Agent");
return response.choices[0].message.content;
});
}

async function sendMsg() {
addToConsole(`User: ${document.getElementById('message').value}`, "User");
var gptMessage = {
role: "user",
content: document.getElementById('message').value
}
gptConversation.push(gptMessage);
response = await callGPT(gptConversation);
document.getElementById('message').value = "";
}

function writeToTrace(text, requestor) {
text = text.trim();
addToConsole(text, requestor);
}

function addToConsole(msg, requestor) {
var span = createLogElement(msg, requestor);
$('#console-log').append(span);
document.getElementById("console-log").scrollTop = document.getElementById("console-log").scrollHeight;
}

function createLogElement(msg, type) {
var span = document.createElement('span');
if (type == "User") {
$(span).addClass('log-element');
} else {
$(span).addClass('log-element-agent');
}
var msgArray = msg.split(':');
var sender = msgArray[0];
var restMsg = "";
for (var i = 1; i < msgArray.length; i++) {
if (i == 1) {
restMsg += " " + msgArray[i];
} else {
restMsg += ":" + msgArray[i];
}
}
var sendSpan = document.createElement('span');
$(sendSpan).addClass('log-element-sender');
sendSpan.innerHTML = sender + ":";
var msgSpan = document.createElement('span');
$(msgSpan).addClass('log-element-msg');
restMsg = restMsg.replaceAll(`\n`, `<BR>`)
$(msgSpan).append(messageWithLink(restMsg));
$(span).append(sendSpan);
$(span).append(msgSpan);
return span;
}

function messageWithLink(msg) {
var matches = msg.match(/[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g);
if (matches) {
for (var i = 0; i < matches.length; i++) {
if (matches[i].toLowerCase().indexOf("http") != -1) {
// The user or agent put a link into the chat window
msg = msg.replace(matches[i], ' <a href = "' + matches[i] + '"target = "_blank" > ' + matches[i] + ' </a>');
}
}
}
return msg;
}
</script>
</body>
</html>

Here again is the CSS (css/openai.css) to make things marginally pretty.

.log-element {

display: block;
margin-top: 5px;
margin-bottom: 5px;
margin-left: 1%;
margin-right: 1%;
background: #fce4c5;
border-radius: 5px;
padding: 10px;
font-family: "Noto Sans JP", sans-serif;
font-size: 13px;
max-width: 470px;
box-sizing: content-box;
}

.log-element-agent {
display: block;
margin-top: 5px;
margin-bottom: 5px;
margin-left: 1%;
margin-right: 1%;
background: #43c2dc;
border-radius: 5px;
padding: 10px;
font-family: "Noto Sans JP", sans-serif;
font-size: 13px;
max-width: 470px;
box-sizing: content-box;
}

.log-element-sender {
font-weight: bold;
font-style: italic;
font-size: 13px;
font-family: "Noto Sans JP", sans-serif;
}

.log-element-msg {
word-wrap: break-word;
}

#console-log {
background-color: white;
padding: 2%;
width: 100%;
overflow-y: scroll;
min-height: 100px;
}

.console-div{
display:block;
width:100%;
height:650px;
background-color:#475;
overflow:scroll;
}

The biggest change from the previous version is that I now allow the user to enter a system prompt. Clicking “Set Prompt” forces the software to flush the existing conversation array and reset its personality to whatever the system prompt defines. This allows the user to go from a cooking expert to a term paper writer. As you saw in the video, a system prompt of “You are a cloud communications and contact center as a service (CCaaS) expert.” replaces the need for someone like me.

Mischief Managed

I hope you appreciate the simple yet powerful enhancements I made to my pseudo ChatGPT software and are willing to make a few changes of your own. This is incredibly powerful technology and if you are like me, doing is understanding. Happy programming.

One comment

  1. We can dive deeper into the process described, including choosing an appropriate open-source framework, training your own language model, and setting up the application itself.

Leave a comment