chatlocal / static /js /chatHandler.js
MGLDZM's picture
dev step
253550d
class ChatGPT{
/* Eventos generados
**********************/
/* Eventos escuchados
- chat:enviar
- chat:eliminar
**********************/
definicion ={
conversacion: [],
usedTokens: 0
};
config = {
temperature: 0.5,
frequency_penalty: 0.0,
presence_penalty: 0.0,
useTool: true,
assistant: "clasico",
assistantPrompt: ""
}
tokens = {
month: 0,
total: 0
}
endpointChat = "/chat";
challenge = null;
windowHandlers = {}
constructor(secHand){
// Token JWT de ejecuci贸n
this.secHand = secHand;
this.obtenerToken();
this.cargarEventos();
}
obtenerToken(){
$.ajax({
method: "POST",
url: "/getToken",
headers: {
"Autorization": "Bearer " + this.token,
'Content-Type': 'application/json',
},
data: JSON.stringify({
fingerprint: this.secHand.fingerprint,
public_key: this.secHand.publicKey
}),
timeout: 5000,
dataType: "json"
}).done((data) => {
if(data.redirect){
$("#inserted")[0].showModal()
$("#inserted").html("<iframe src='"+data.redirect+"'>")
$("#inserted").on("close", (event) =>{
document.location.reload()
})
return
}
this.secHand.sign(data.challenge).then(
(result) => {
this.challenge = result
this.cargarConfigs(false, true);
})
this.token = data.token
}).fail((data => {
$("#inserted")[0].showModal()
$("#inserted").html("<iframe src='/login'>")
$("#inserted").on("close", (event) =>{
document.location.reload()
})
$("#inserted iframe").on("load", (event) =>{
let location = event.target.contentDocument.location.pathname
if(location == "/login"){
$("#inserted iframe")[0].contentWindow.data = {
fp: this.secHand.fingerprint,
pk: this.secHand.publicKey
}
}
if(location == "/"){
$("#inserted").html("")
$("#inserted")[0].close()
}
})
}))
}
cargarConfigs(abrirMenu, starting){
self = this
$.ajax({
method: "POST",
url: "/getConfigs",
headers: {
"Autorization": "Bearer " + this.token,
'Content-Type': 'application/json',
},
data:JSON.stringify({
challenge: this.challenge,
fingerprint: this.secHand.fingerprint
}),
timeout: 5000,
dataType: "json"
}).done((data) => {
let challenge = data["challenge"];
delete data["challenge"];
$("#assistant option[value='"+ data.assistant +"']")[0].selected=true
this.secHand.sign(challenge).then((result) => {this.challenge = result;})
this.config = data
for(let confEl in data){
if(typeof(data[confEl]) == "boolean"){
$("#"+confEl).prop("checked", data[confEl])
}else{
$("#"+confEl).val(data[confEl])
}
}
$(".range input").each(recalculateRanges)
$(".range input").on("input", recalculateRanges)
$(".item.parent input").on("change", function(){
self.config[this.id] = this.checked
if(!this.checked){
$(".item."+this.id+" input").prop("disabled", true)
}else{
$(".item."+this.id+" input").prop("disabled", false)
}
})
$(".range select").on("change", function(){
self.config[this.id] = this.value
})
$("#tokensUsedMonth").text(data.tokens.month)
$("#tokensUsedTotal").text(data.tokens.total)
if(abrirMenu) $("#menu")[0].showModal()
function recalculateRanges(){
let value = (Math.round(this.value * 100) / 100).toFixed(2)
$(this).parent().find("span").text(value);
self.config[this.id] = parseFloat(value)
}
$(".item.parent input").trigger("change")
if(starting){this.cargarChats()}
})
.fail(() => {
document.location.href = "/";
})
}
cargarEventos(){
$(document).on("chat:enviar", (event, params) => {
// Al enviar un mensaje, reintentos vuelve a 0
this.reintentos = 0;
this.enviar(params.ctx, params.ctx.conversacion);
});
$(document).on("chat:salvar", (event, params) => this.salvarChats(params.index, params.conversacion))
$(document).on("chat:eliminar", (event, params) => this.eliminarChat(params.ctx, params.index))
$(document).on("mostrar:opciones", () => this.mostrarOpciones())
$("#nuevoChat").on("click", () => this.crearChat())
$("#menu .saveButton").on("click", () => this.salvarConfigs())
}
cargarChats(){
let conversaciones = {}
try{conversaciones = JSON.parse(localStorage.getItem("conversaciones"))||{}}catch{}
if(!conversaciones || !Object.keys(conversaciones).length){
this.crearChat()
return
}
for(let key in conversaciones){
if(!conversaciones[key]){
continue
}
if(Object.keys(conversaciones[key]).indexOf('conversacion')!=-1){
this.crearChat(key, conversaciones[key].conversacion)
}else{
this.crearChat(key, conversaciones[key])
}
}
localStorage.setItem("conversaciones", JSON.stringify(conversaciones))
}
salvarConfigs(){
$.ajax({
method: "POST",
url: "/setConfigs",
headers: {
"Autorization": "Bearer " + this.token,
'Content-Type': 'application/json',
},
data: JSON.stringify(
{...this.config,
fingerprint: this.secHand.fingerprint,
challenge: this.challenge,
}),
timeout: 5000,
dataType: "json"
}).done((data) => {
this.secHand.sign(data.challenge).then((result) => {this.challenge = result})
this.token = data.token
this.config.assistantPrompt = data.assistantPrompt
}).fail(() => {
document.location.href = "/";
})
}
crearChat(index, conversacion){
// Se crea el nuevo manejador de ventana
let uuid = this.generateRandID()
this.windowHandlers[index||uuid] = new WindowHandler(
conversacion||[],
index||uuid,
this
);
}
eliminarChat(ctx, index){
// Elimina el elemento de la lista de conversas y handler de ventanas
delete this.windowHandlers[index]
let conversaciones = {}
try{conversaciones = JSON.parse(localStorage.getItem("conversaciones"))||{}}catch{}
if(index in conversaciones){delete conversaciones[index]}
localStorage.setItem("conversaciones", JSON.stringify(conversaciones))
// Renumera las etiquetas, y selecciona la primera
let labels = $(".tab-label")
$(labels[0]).click();
// Si no quedaron chats, crea uno vacio
if(Object.keys(this.windowHandlers).length==0){
this.crearChat()
}
}
enviar(ctx, conversacion){
// Envio de mensaje y manejo de comandos async
// Crea un espacio temporal para almacenar los mensajes con el formato correcto
let tempMensajes = [];
for(let actMsg of conversacion){
tempMensajes.push({role: actMsg.role, content: actMsg.content})
}
// Se anuncia la precarga
ctx.respuestaInicio()
// Se almacena el contexto this
let self = this;
// Consumo de mensajes asincronos
const consume = responseReader => {
// Se lee la respuesta
return responseReader.read().then(result => {
//si finaliz贸 el mensaje, se termina el proceso
if (result.done) { return; }
// Se obtiene y decodifica el segmento
const chunk = result.value;
let text = new TextDecoder("utf-8").decode(chunk)
// Se obtienen los mensajes y separan si son varios json juntos
let responses = JSON.parse('[' + text.replace(/\}\{/g, '},{') + ']')
// Por cada mensaje conseguido validamos el caso de accion
for(let response of responses){
switch(response.comando){
// Se carga el nuevo token
case "token":
self.token = response.token;
break;
// Se carga el nuevo challenge
case "challenge":
this.secHand.sign(response.challenge).then((result) => {self.challenge = result})
break;
// Status del comportamiento
case "status":
ctx.respuestaStatus(response.status.mensaje, response.status.modo)
break;
// Mensaje a mostrar
case "mensaje":
ctx.respuestaMensaje(response.mensaje)
break;
// // Es una funci贸n
// case "function":
// conversacion.push(response.function);
// localStorage.setItem("conversaciones", JSON.stringify(self.conversaciones))
// break;
// Algo fall贸
default:
console.log("???", response)
}
}
// Se consume la respuesta para continuar el proceso
return consume(responseReader);
}).catch(err =>{
// Error
console.log('algo paso', err)
});
}
// Se ejecuta el request
fetch(this.endpointChat, {
method: "POST",
body: JSON.stringify({
messages: tempMensajes,
challenge: this.challenge,
fingerprint: this.secHand.fingerprint
}),
headers: {
"Autorization": "Bearer " + this.token,
'Content-Type': 'application/json',
},
timeout: 60000,
dataType: "json"
}).then(response => {
if(response.status != 200){
ctx.respuestaError(response)
console.log("Error: ", response)
return
}
return consume(response.body.getReader());
}).catch(err =>{
// Error
console.log('Solicitud fallida', err)
ctx.respuestaError(response)
document.location.href = "/";
});
}
salvarChats(index, conversacion){
let conversaciones = {}
try{conversaciones = JSON.parse(localStorage.getItem("conversaciones"))||{}}catch{}
let temp = {}
for (let i in conversaciones){
temp[i]=conversaciones[i]
}
temp[index] = conversacion
localStorage.setItem("conversaciones", JSON.stringify(temp))
}
mostrarOpciones(){
this.cargarConfigs(true);
}
generateRandID(){
return btoa((Math.random()*100**8)).replaceAll("=","").split("").reverse().join("");
}
}