2023-04-30 12:16:48 +02:00
// Copyright (c) 2023 Julian Müller (ChaoticByte)
( ( ) => {
// Koala specific keywords
const conversationBeginning = "BEGINNING OF CONVERSATION:" ;
const userKeyword = " USER: " ;
const assistantKeyword = " GPT:" ;
const koalaStopSequence = "</s>" ;
// Get frontend config
let frontend _config = null ;
fetch ( "/config" )
. then ( r => {
return r . json ( ) ;
} )
. then ( j => {
frontend _config = j ;
} ) ;
// Message Context
let conversation = [ conversationBeginning ] ;
// Elements - Sidebar
const settingsMaxTokensElement = document . getElementById ( "settings-max-tokens" ) ;
const settingsTemperatureElement = document . getElementById ( "settings-temperature" ) ;
const settingsTopPElement = document . getElementById ( "settings-top-p" ) ;
2023-04-30 12:40:57 +02:00
const resetSettingsButtonElement = document . getElementById ( "reset-settings-btn" ) ;
2023-04-30 12:16:48 +02:00
const resetHistoryButtonElement = document . getElementById ( "reset-history-btn" ) ;
// Elements - Main
const messageHistoryContainer = document . getElementById ( "messages" ) ;
const textInputElement = document . getElementById ( "text-input" ) ;
const sendButtonElement = document . getElementById ( "send-btn" ) ;
// API requests
async function apiCompletion ( prompt , settings ) {
const bodyData = JSON . stringify ( {
"prompt" : prompt ,
"stop" : [ koalaStopSequence ] ,
"max_tokens" : settings . max _tokens ,
"temperature" : settings . temperature ,
"top_p" : settings . top _p
} ) ;
const response = await fetch ( frontend _config . api _url + "/v1/completions" , {
method : "post" ,
cache : "no-cache" ,
body : bodyData ,
headers : {
"content-type" : "application/json"
}
} ) ;
const responseData = await response . json ( ) ;
return responseData [ "choices" ] [ 0 ] [ "text" ] ;
}
// User-defined settings
2023-04-30 12:40:57 +02:00
const defaultSettings = {
max _tokens : 100 ,
temperature : 0.8 ,
top _p : 0.95
}
2023-04-30 12:16:48 +02:00
function getSettings ( ) {
return {
max _tokens : settingsMaxTokensElement . value ,
temperature : settingsTemperatureElement . value ,
top _p : settingsTopPElement . value
}
}
2023-04-30 12:40:57 +02:00
function resetSettings ( ) {
settingsMaxTokensElement . value = defaultSettings . max _tokens ;
settingsTemperatureElement . value = defaultSettings . temperature ;
settingsTopPElement . value = defaultSettings . top _p ;
}
2023-04-30 12:16:48 +02:00
// Chat
const MessageType = {
USER : {
name : "User" ,
class : "message-bg-user"
} ,
ASSISTANT : {
name : "Koala" ,
class : "message-bg-assistant"
}
}
function addMessage ( message , type ) {
if ( type == MessageType . USER ) {
conversation . push ( userKeyword + message + assistantKeyword ) ;
}
else { conversation . push ( message ) ; }
// UI
let messageTypeElem = document . createElement ( "div" ) ;
messageTypeElem . classList . add ( "message-type" ) ;
messageTypeElem . innerText = type . name ;
let messageTextElem = document . createElement ( "div" ) ;
messageTextElem . classList . add ( "message-text" ) ;
messageTextElem . innerText = message ;
let messageElem = document . createElement ( "div" ) ;
messageElem . classList . add ( "message" ) ;
messageElem . classList . add ( type . class ) ;
messageElem . appendChild ( messageTypeElem ) ;
messageElem . appendChild ( messageTextElem ) ;
messageHistoryContainer . appendChild ( messageElem ) ;
2023-04-30 12:25:04 +02:00
messageHistoryContainer . scrollTo ( 0 , messageHistoryContainer . scrollHeight ) ;
2023-04-30 12:16:48 +02:00
}
2023-04-30 19:34:11 +02:00
function resizeInputElement ( ) {
// Calculate Line height
textInputElement . style . removeProperty ( "height" ) ;
let newHeight = textInputElement . scrollHeight ;
textInputElement . style . height = newHeight . toString ( ) + "px" ;
}
2023-04-30 12:16:48 +02:00
function disableInput ( ) {
2023-04-30 12:40:57 +02:00
settingsMaxTokensElement . disabled = true ;
settingsTemperatureElement . disabled = true ;
settingsTopPElement . disabled = true ;
resetSettingsButtonElement . disabled = true ;
resetHistoryButtonElement . disabled = true ;
2023-04-30 12:16:48 +02:00
sendButtonElement . disabled = true ;
textInputElement . disabled = true ;
}
function enableInput ( ) {
2023-04-30 12:40:57 +02:00
settingsMaxTokensElement . disabled = false ;
settingsTemperatureElement . disabled = false ;
settingsTopPElement . disabled = false ;
resetSettingsButtonElement . disabled = false ;
resetHistoryButtonElement . disabled = false ;
sendButtonElement . disabled = false ;
2023-04-30 12:16:48 +02:00
textInputElement . disabled = false ;
// focus text input
textInputElement . focus ( ) ;
}
async function chat ( ) {
if ( frontend _config == null ) {
console . log ( "Couldn't fetch frontend configuration." ) ;
}
else {
disableInput ( ) ;
let input = textInputElement . value . trim ( ) ;
if ( input == "" ) {
enableInput ( ) ;
}
else {
textInputElement . value = "" ;
2023-04-30 19:34:11 +02:00
resizeInputElement ( ) ;
2023-04-30 12:16:48 +02:00
addMessage ( input , MessageType . USER ) ;
let prompt = conversation . join ( "" ) ;
let settings = getSettings ( ) ;
apiCompletion ( prompt , settings ) . then ( r => {
addMessage ( r , MessageType . ASSISTANT ) ;
enableInput ( ) ;
} ) ;
}
}
}
function resetHistory ( ) {
conversation = [ conversationBeginning ] ;
messageHistoryContainer . innerText = "" ;
}
// Event Listeners
2023-04-30 19:34:11 +02:00
2023-04-30 12:40:57 +02:00
resetSettingsButtonElement . addEventListener ( "click" , resetSettings ) ;
2023-04-30 12:16:48 +02:00
resetHistoryButtonElement . addEventListener ( "click" , resetHistory ) ;
sendButtonElement . addEventListener ( "click" , chat ) ;
2023-04-30 19:34:11 +02:00
2023-04-30 12:16:48 +02:00
textInputElement . addEventListener ( "keypress" , e => {
// Send via Ctrl+Enter
if ( e . key == "Enter" && e . ctrlKey ) {
chat ( ) ;
}
} ) ;
2023-04-30 19:34:11 +02:00
textInputElement . addEventListener ( "input" , resizeInputElement ) ;
2023-05-18 10:32:31 +02:00
resizeInputElement ( ) ;
2023-04-30 12:16:48 +02:00
} ) ( ) ;