rkllm-server/static/v2.js

131 lines
4.4 KiB
JavaScript

//var stmd = require('stmd');
const messagesContainer = document.getElementById('messages');
const systemInput = document.getElementById('systemMessage');
const messageInput = document.getElementById('messageInput');
var parser = new stmd.DocParser();
var renderer = new stmd.HtmlRenderer();
var cur_idx = 0;
const questions = [];
const answers = [];
var selected_msg = [];
messageInput.addEventListener('keydown', (event) => {
if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
sendMessage();
} else if (event.key === "Escape" && (event.metaKey || event.ctrlKey)) {
sendAbort();
}
});
function messageListener(elem, idx, type) {
return () => {
elem.classList.toggle('selected');
const e = { id: idx, type: type };
const i = selected_msg.findIndex(o => e.id === o.id && o.type === e.type);
if (i === -1)
selected_msg.push(e);
else selected_msg.splice(i, 1);
}
}
async function sendAbort() {
fetch('/rkllm_abort', {
method: 'POST',
body: true
}).catch(error => alert(error))
}
function _try_highlightAll() {
try {
hljs.highlightAll();
} catch { /*e*/ }
}
async function sendMessage() {
const messageText = messageInput.value.trim();
if (!messageText) return;
messageHTML = renderer.render(parser.parse(`**You:**\n\n${messageText}`))
mess = document.createElement('div');
mess.classList.add('message', 'clearfix');
mess.style.float = "right"
mess.innerHTML = messageHTML
mess.addEventListener('click', messageListener(mess, cur_idx, 'q'));
questions.push(messageText);
messagesContainer.appendChild(mess);
Array.from(mess.getElementsByTagName('code')).forEach(e => hljs.highlightElement(e));
messageInput.value = '';
messagesContainer.scrollTop = messagesContainer.scrollHeight; // Auto-scroll to bottom
const messagesToSend = selected_msg.sort((a,b) => {
const sub = a.id-b.id;
if (sub === 0) return b.type === 'q' ? 1 : -1;
return sub;
}).map(m => {
const q = m.type === 'q';
return {
role: q ? 'user' : 'assistant',
content: (q ? questions : answers)[m.id]
}});
messagesToSend.push({role:'user', content: messageText})
const sysText = systemInput.value.trim();
const withSysMsg = (sysText ? [{role: 'system', 'content': sysText}] : []).concat(messagesToSend);
const response = await fetch('/rkllm_chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: "some model",
messages: withSysMsg,
stream: true
}),
});
if (!response.ok) {
throw new Error('Failed to connect to chat server');
}
const reader = response.body.getReader();
let decoder = new TextDecoder('utf-8');
//const name = (Math.random() + 1).toString(36).substring(7);
const message = document.createElement('div');
message.classList.add('message', 'clearfix');
message.style.float = 'left';
messagesContainer.appendChild(message);
let chunks = '';
function double_try() {
function display(n) {
const ccs = chunks.split(/\n/).filter(e => e !== "");
const json = JSON.parse(ccs[ccs.length - n]);
const text = `${json.choices.map((c) => c.delta.content).join('')}`;
while (cur_idx >= answers.length) answers.push("");
answers[cur_idx] = text;
message.innerHTML = renderer.render(parser.parse(`**RKLLM**:\n\n${text}`));
Array.from(message.getElementsByTagName('code')).forEach(e => hljs.highlightElement(e));
}
const at_bottom = Math.abs(messagesContainer.scrollHeight - messagesContainer.clientHeight - messagesContainer.scrollTop) <= 1;
try {
display(1);
} catch {
try {
display(2);
} catch (e) { console.error(e, chunks); }
} finally {
if (at_bottom) messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
}
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
chunks += chunk.replace('\r', '');
double_try();
}
double_try();
message.addEventListener('click', messageListener(message, cur_idx++, 'a'));
}