LLamaSharp/LLama.Web/wwwroot/js/sessionConnectionChat.js

226 lines
7.1 KiB
JavaScript

const createConnectionSessionChat = () => {
const outputErrorTemplate = $("#outputErrorTemplate").html();
const outputInfoTemplate = $("#outputInfoTemplate").html();
const outputUserTemplate = $("#outputUserTemplate").html();
const outputBotTemplate = $("#outputBotTemplate").html();
const signatureTemplate = $("#signatureTemplate").html();
let inferenceSession;
const connection = new signalR.HubConnectionBuilder().withUrl("/SessionConnectionHub").build();
const scrollContainer = $("#scroll-container");
const outputContainer = $("#output-container");
const chatInput = $("#input");
const onStatus = (connection, status) => {
if (status == Enums.SessionConnectionStatus.Connected) {
$("#socket").text("Connected").addClass("text-success");
}
else if (status == Enums.SessionConnectionStatus.Loaded) {
loaderHide();
enableControls();
$("#load").hide();
$("#unload").show();
onInfo(`New model session successfully started`)
}
}
const onError = (error) => {
enableControls();
outputContainer.append(Mustache.render(outputErrorTemplate, { text: error, date: getDateTime() }));
}
const onInfo = (message) => {
outputContainer.append(Mustache.render(outputInfoTemplate, { text: message, date: getDateTime() }));
}
let responseContent;
let responseContainer;
let responseFirstToken;
const onResponse = (response) => {
if (!response)
return;
if (response.tokenType == Enums.TokenType.Begin) {
let uniqueId = randomString();
outputContainer.append(Mustache.render(outputBotTemplate, { uniqueId: uniqueId, ...response }));
responseContainer = $(`#${uniqueId}`);
responseContent = responseContainer.find(".content");
responseFirstToken = true;
scrollToBottom(true);
return;
}
if (response.tokenType == Enums.TokenType.End || response.tokenType == Enums.TokenType.Cancel) {
enableControls();
responseContainer.find(".signature").append(Mustache.render(signatureTemplate, response));
scrollToBottom();
}
else {
if (responseFirstToken) {
responseContent.empty();
responseFirstToken = false;
responseContainer.find(".date").append(getDateTime());
}
responseContent.append(response.content);
scrollToBottom();
}
}
const sendPrompt = async () => {
const text = chatInput.val();
if (text) {
chatInput.val(null);
disableControls();
outputContainer.append(Mustache.render(outputUserTemplate, { text: text, date: getDateTime() }));
inferenceSession = await connection
.stream("SendPrompt", text, serializeFormToJson('SessionParameters'))
.subscribe({
next: onResponse,
complete: onResponse,
error: onError,
});
scrollToBottom(true);
}
}
const cancelPrompt = async () => {
if (inferenceSession)
inferenceSession.dispose();
}
const loadModel = async () => {
const sessionParams = serializeFormToJson('SessionParameters');
loaderShow();
disableControls();
disablePromptControls();
$("#load").attr("disabled", "disabled");
// TODO: Split parameters sets
await connection.invoke('LoadModel', sessionParams, sessionParams);
}
const unloadModel = async () => {
await cancelPrompt();
disableControls();
enablePromptControls();
$("#load").removeAttr("disabled");
}
const serializeFormToJson = (form) => {
const formDataJson = {};
const formData = new FormData(document.getElementById(form));
formData.forEach((value, key) => {
if (key.includes("."))
key = key.split(".")[1];
// Convert number strings to numbers
if (!isNaN(value) && value.trim() !== "") {
formDataJson[key] = parseFloat(value);
}
// Convert boolean strings to booleans
else if (value === "true" || value === "false") {
formDataJson[key] = (value === "true");
}
else {
formDataJson[key] = value;
}
});
return formDataJson;
}
const enableControls = () => {
$(".input-control").removeAttr("disabled");
}
const disableControls = () => {
$(".input-control").attr("disabled", "disabled");
}
const enablePromptControls = () => {
$("#load").show();
$("#unload").hide();
$(".prompt-control").removeAttr("disabled");
activatePromptTab();
}
const disablePromptControls = () => {
$(".prompt-control").attr("disabled", "disabled");
activateParamsTab();
}
const clearOutput = () => {
outputContainer.empty();
}
const updatePrompt = () => {
const customPrompt = $("#PromptText");
const selection = $("option:selected", "#Prompt");
const selectedValue = selection.data("prompt");
customPrompt.text(selectedValue);
}
const getDateTime = () => {
const dateTime = new Date();
return dateTime.toLocaleString();
}
const randomString = () => {
return Math.random().toString(36).slice(2);
}
const scrollToBottom = (force) => {
const scrollTop = scrollContainer.scrollTop();
const scrollHeight = scrollContainer[0].scrollHeight;
if (force) {
scrollContainer.scrollTop(scrollContainer[0].scrollHeight);
return;
}
if (scrollTop + 70 >= scrollHeight - scrollContainer.innerHeight()) {
scrollContainer.scrollTop(scrollContainer[0].scrollHeight)
}
}
const activatePromptTab = () => {
$("#nav-prompt-tab").trigger("click");
}
const activateParamsTab = () => {
$("#nav-params-tab").trigger("click");
}
const loaderShow = () => {
$(".spinner").show();
}
const loaderHide = () => {
$(".spinner").hide();
}
// Map UI functions
$("#load").on("click", loadModel);
$("#unload").on("click", unloadModel);
$("#send").on("click", sendPrompt);
$("#clear").on("click", clearOutput);
$("#cancel").on("click", cancelPrompt);
$("#Prompt").on("change", updatePrompt);
chatInput.on('keydown', function (event) {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
sendPrompt();
}
});
$(".slider").on("input", function (e) {
const slider = $(this);
slider.next().text(slider.val());
}).trigger("input");
// Map signalr functions
connection.on("OnStatus", onStatus);
connection.on("OnError", onError);
connection.on("OnResponse", onResponse);
connection.start();
}