131 lines
4.4 KiB
JavaScript
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'));
|
|
|
|
}
|