diff --git a/package-lock.json b/package-lock.json index eb90c2063..101fa744d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "json5": "^2.2.3", "mime-types": "^2.1.35", "multer": "^1.4.5-lts.1", + "node-fetch": "^2.6.11", "node-rest-client": "^3.1.1", "open": "^8.4.0", "piexifjs": "^1.0.6", @@ -2055,8 +2056,9 @@ } }, "node_modules/node-fetch": { - "version": "2.6.9", - "license": "MIT", + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -3092,7 +3094,8 @@ }, "node_modules/tr46": { "version": "0.0.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", @@ -3180,7 +3183,8 @@ }, "node_modules/webidl-conversions": { "version": "3.0.1", - "license": "BSD-2-Clause" + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webp-converter": { "version": "2.3.2", @@ -3192,7 +3196,8 @@ }, "node_modules/whatwg-url": { "version": "5.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" diff --git a/package.json b/package.json index 3043057de..98dd786ed 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "json5": "^2.2.3", "mime-types": "^2.1.35", "multer": "^1.4.5-lts.1", + "node-fetch": "^2.6.11", "node-rest-client": "^3.1.1", "open": "^8.4.0", "piexifjs": "^1.0.6", diff --git a/public/index.html b/public/index.html index 9324e47e1..69c369ace 100644 --- a/public/index.html +++ b/public/index.html @@ -2494,9 +2494,6 @@
-
- -
@@ -2580,8 +2577,8 @@
-
-
+
+
diff --git a/public/script.js b/public/script.js index 38c16338e..496468ad8 100644 --- a/public/script.js +++ b/public/script.js @@ -587,6 +587,7 @@ var main_api;// = "kobold"; let novel_tier; let novelai_settings; let novelai_setting_names; +let abortController; //css var css_mes_bg = $('
').css("background"); @@ -1499,22 +1500,30 @@ function isStreamingEnabled() { && !isMultigenEnabled(); // Multigen has a quasi-streaming mode which breaks the real streaming } +function showStopButton() { + $('#mes_stop').css({ 'display': 'flex' }); +} + +function hideStopButton() { + $('#mes_stop').css({ 'display': 'none' }); +} + class StreamingProcessor { - showStopButton(messageId) { + showMessageButtons(messageId) { if (messageId == -1) { return; } - $(`#chat .mes[mesid="${messageId}"] .mes_stop`).css({ 'display': 'block' }); + showStopButton(); $(`#chat .mes[mesid="${messageId}"] .mes_buttons`).css({ 'display': 'none' }); } - hideStopButton(messageId) { + hideMessageButtons(messageId) { if (messageId == -1) { return; } - $(`#chat .mes[mesid="${messageId}"] .mes_stop`).css({ 'display': 'none' }); + hideStopButton(); $(`#chat .mes[mesid="${messageId}"] .mes_buttons`).css({ 'display': 'flex' }); } @@ -1527,7 +1536,7 @@ class StreamingProcessor { else { saveReply(this.type, text); messageId = count_view_mes - 1; - this.showStopButton(messageId); + this.showMessageButtons(messageId); } hideSwipeButtons(); @@ -1595,7 +1604,7 @@ class StreamingProcessor { } onFinishStreaming(messageId, text) { - this.hideStopButton(this.messageId); + this.hideMessageButtons(this.messageId); this.onProgressStreaming(messageId, text, true); addCopyToCodeBlocks($(`#chat .mes[mesid="${messageId}"]`)); saveChatConditional(); @@ -1641,7 +1650,7 @@ class StreamingProcessor { } onErrorStreaming() { - this.hideStopButton(this.messageId); + this.hideMessageButtons(this.messageId); $("#send_textarea").removeAttr('disabled'); is_send_press = false; activateSendButtons(); @@ -2203,12 +2212,15 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject, let generate_url = getGenerateUrl(); console.log('rungenerate calling API'); + abortController = new AbortController(); + showStopButton(); + if (main_api == 'openai') { if (isStreamingEnabled() && type !== 'quiet') { streamingProcessor.generator = await sendOpenAIRequest(type, generate_data.prompt, streamingProcessor.abortController.signal); } else { - sendOpenAIRequest(type, generate_data.prompt).then(onSuccess).catch(onError); + sendOpenAIRequest(type, generate_data.prompt, abortController.signal).then(onSuccess).catch(onError); } } else if (main_api == 'kobold' && horde_settings.use_horde) { @@ -2219,25 +2231,32 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject, streamingProcessor.generator = await generatePoe(type, finalPromt, streamingProcessor.abortController.signal); } else { - generatePoe(type, finalPromt).then(onSuccess).catch(onError); + generatePoe(type, finalPromt, abortController.signal).then(onSuccess).catch(onError); } } else if (main_api == 'textgenerationwebui' && isStreamingEnabled() && type !== 'quiet') { streamingProcessor.generator = await generateTextGenWithStreaming(generate_data, streamingProcessor.abortController.signal); } else { - jQuery.ajax({ - type: 'POST', // - url: generate_url, // - data: JSON.stringify(generate_data), - beforeSend: () => { }, - cache: false, - dataType: "json", - contentType: "application/json", - success: onSuccess, - error: onError - }); //end of "if not data error" - } + try { + const response = await fetch(generate_url, { + method: 'POST', + headers: getRequestHeaders(), + cache: 'no-cache', + body: JSON.stringify(generate_data), + signal: abortController.signal, + }); + + if (!response.ok) { + throw new Error(response.status); + } + + const data = await response.json(); + onSuccess(data); + } catch (error) { + onError(error); + } + } //set array object for prompt token itemization of this message let currentArrayEntry = Number(thisPromptBits.length - 1); @@ -2284,6 +2303,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject, } function onSuccess(data) { + hideStopButton(); is_send_press = false; if (!data.error) { //const getData = await response.json(); @@ -2386,14 +2406,14 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject, } }; - function onError(jqXHR, exception) { + function onError(exception) { + hideStopButton(); reject(exception); $("#send_textarea").removeAttr('disabled'); is_send_press = false; activateSendButtons(); setGenerationProgress(0); console.log(exception); - console.log(jqXHR); }; } //rungenerate ends @@ -3233,13 +3253,13 @@ export function isMultigenEnabled() { function activateSendButtons() { is_send_press = false; $("#send_but").css("display", "flex"); - $("#loading_mes").css("display", "none"); $("#send_textarea").attr("disabled", false); + hideStopButton(); } function deactivateSendButtons() { $("#send_but").css("display", "none"); - $("#loading_mes").css("display", "flex"); + showStopButton(); } function resetChatState() { @@ -6466,6 +6486,10 @@ $(document).ready(function () { streamingProcessor.onStopStreaming(); streamingProcessor = null; } + if (!isStreamingEnabled() && abortController) { + abortController.abort(); + abortController = null; + } }); $('.drawer-toggle').click(function () { diff --git a/public/style.css b/public/style.css index 4d2c203ef..185f89490 100644 --- a/public/style.css +++ b/public/style.css @@ -416,30 +416,34 @@ code { overflow: hidden; } -#send_but { + +#send_but_sheld>div { width: 40px; height: 40px; margin: 0; - display: none; outline: none; border: none; cursor: pointer; transition: 0.3s; opacity: 0.7; - order: 99999; align-items: center; justify-content: center; } -#loading_mes { +#options_button:hover, +#send_but_sheld>div:hover { + opacity: 1; + filter: brightness(1.2); +} + +#send_but { display: none; - width: 40px; - height: 40px; - margin: 0 auto; order: 99999; - align-items: center; - justify-content: center; - filter: brightness(0.7); +} + +.mes_stop { + display: none; + order: 99998; } #options_button { @@ -466,12 +470,6 @@ code { font-weight: 400; } -#options_button:hover, -#send_but:hover { - opacity: 1; - filter: brightness(1.2); -} - #options { opacity: 0.0; display: none; @@ -2269,18 +2267,6 @@ input[type="range"]::-webkit-slider-thumb { backdrop-filter: blur(var(--SmartThemeBlurStrength)); } -.mes_stop { - display: none; - cursor: pointer; - transition: opacity 0.3s ease-in-out; - height: 20px; - width: 20px; - opacity: 0.5; - position: absolute; - right: 15px; - bottom: 15px -} - .mes_buttons { height: 20px; grid-row-start: 1; diff --git a/server.js b/server.js index ccde712eb..a0df06405 100644 --- a/server.js +++ b/server.js @@ -39,13 +39,11 @@ const multer = require("multer"); const http = require("http"); const https = require('https'); const basicAuthMiddleware = require('./src/middleware/basicAuthMiddleware'); -//const PNG = require('pngjs').PNG; const extract = require('png-chunks-extract'); const encode = require('png-chunks-encode'); const PNGtext = require('png-chunk-text'); const jimp = require('jimp'); -//const path = require('path'); const sanitize = require('sanitize-filename'); const mime = require('mime-types'); @@ -54,13 +52,11 @@ const crypto = require('crypto'); const ipaddr = require('ipaddr.js'); const json5 = require('json5'); -const ExifReader = require('exifreader'); const exif = require('piexifjs'); const webp = require('webp-converter'); const DeviceDetector = require("device-detector-js"); const { TextEncoder, TextDecoder } = require('util'); const utf8Encode = new TextEncoder(); -const utf8Decode = new TextDecoder('utf-8', { ignoreBOM: true }); const commandExistsSync = require('command-exists').sync; const characterCardParser = require('./src/character-card-parser.js'); @@ -93,36 +89,25 @@ const ai_horde = new AIHorde({ }); const ipMatching = require('ip-matching'); -var Client = require('node-rest-client').Client; -var client = new Client(); +const Client = require('node-rest-client').Client; +const client = new Client(); client.on('error', (err) => { console.error('An error occurred:', err); }); -let poe = require('./poe-client'); +const poe = require('./poe-client'); -var api_server = "http://0.0.0.0:5000"; -var api_novelai = "https://api.novelai.net"; +let api_server = "http://0.0.0.0:5000"; +let api_novelai = "https://api.novelai.net"; let api_openai = "https://api.openai.com/v1"; -var main_api = "kobold"; +let main_api = "kobold"; -var response_get_story; -var response_generate; -var response_generate_novel; -var request_promt; -var response_promt; -var characters = {}; -var character_i = 0; -var response_create; -var response_edit; -var response_dw_bg; -var response_getstatus; -var response_getstatus_novel; -var response_getlastversion; +let response_generate_novel; +let characters = {}; +let response_dw_bg; +let response_getstatus; -let response_generate_openai; -let response_getstatus_openai; //RossAscends: Added function to format dates used in files and chat timestamps to a humanized format. //Mostly I wanted this to be for file names, but couldn't figure out exactly where the filename save code was as everything seemed to be connected. @@ -220,7 +205,7 @@ const doubleCsrf = require('csrf-csrf').doubleCsrf; const CSRF_SECRET = crypto.randomBytes(8).toString('hex'); const COOKIES_SECRET = crypto.randomBytes(8).toString('hex'); -const { invalidCsrfTokenError, generateToken, doubleCsrfProtection } = doubleCsrf({ +const { generateToken, doubleCsrfProtection } = doubleCsrf({ getSecret: () => CSRF_SECRET, cookieName: "X-CSRF-Token", cookieOptions: { @@ -342,27 +327,29 @@ app.get('/version', function (_, response) { //**************Kobold api app.post("/generate", jsonParser, async function (request, response_generate = response) { if (!request.body) return response_generate.sendStatus(400); - //console.log(request.body.prompt); - //const dataJson = JSON.parse(request.body); - request_promt = request.body.prompt; - //console.log(request.body); - var this_settings = { - prompt: request_promt, + const request_prompt = request.body.prompt; + const controller = new AbortController(); + request.socket.removeAllListeners('close'); + request.socket.on('close', function () { + console.log('Kobold aborted'); + controller.abort(); + }); + + let this_settings = { + prompt: request_prompt, use_story: false, use_memory: false, use_authors_note: false, use_world_info: false, max_context_length: request.body.max_context_length, singleline: !!request.body.singleline, - //temperature: request.body.temperature, - //max_length: request.body.max_length }; if (request.body.gui_settings == false) { - var sampler_order = [request.body.s1, request.body.s2, request.body.s3, request.body.s4, request.body.s5, request.body.s6, request.body.s7]; + const sampler_order = [request.body.s1, request.body.s2, request.body.s3, request.body.s4, request.body.s5, request.body.s6, request.body.s7]; this_settings = { - prompt: request_promt, + prompt: request_prompt, use_story: false, use_memory: false, use_authors_note: false, @@ -387,9 +374,10 @@ app.post("/generate", jsonParser, async function (request, response_generate = r } console.log(this_settings); - var args = { - data: this_settings, - headers: { "Content-Type": "application/json" } + const args = { + body: JSON.stringify(this_settings), + signal: controller.signal, + headers: { "Content-Type": "application/json" }, }; const MAX_RETRIES = 10; @@ -426,13 +414,15 @@ app.post("/generate_textgenerationwebui", jsonParser, async function (request, r console.log(request.body); - if (!!request.header('X-Response-Streaming')) { - let isStreamingStopped = false; - request.socket.removeAllListeners('close'); - request.socket.on('close', function () { - isStreamingStopped = true; - }); + const controller = new AbortController(); + let isGenerationStopped = false; + request.socket.removeAllListeners('close'); + request.socket.on('close', function () { + isGenerationStopped = true; + controller.abort(); + }); + if (request.header('X-Response-Streaming')) { response_generate.writeHead(200, { 'Content-Type': 'text/plain;charset=utf-8', 'Transfer-Encoding': 'chunked', @@ -459,7 +449,7 @@ app.post("/generate_textgenerationwebui", jsonParser, async function (request, r }); while (true) { - if (isStreamingStopped) { + if (isGenerationStopped) { console.error('Streaming stopped by user. Closing websocket...'); websocket.close(); return; @@ -504,29 +494,20 @@ app.post("/generate_textgenerationwebui", jsonParser, async function (request, r } } else { - var args = { - data: request.body, - headers: { "Content-Type": "application/json" } + const args = { + body: JSON.stringify(request.body), + headers: { "Content-Type": "application/json" }, + signal: controller.signal, }; - client.post(api_server + "/v1/generate", args, function (data, response) { - console.log("####", data); - if (response.statusCode == 200) { - console.log(data); - response_generate.send(data); - } - if (response.statusCode == 422) { - console.log('Validation error'); - response_generate.send({ error: true }); - } - if (response.statusCode == 501 || response.statusCode == 503 || response.statusCode == 507) { - console.log(data); - response_generate.send({ error: true }); - } - }).on('error', function (err) { - console.log(err); - //console.log('something went wrong on the request', err.request.options); - response_generate.send({ error: true }); - }); + + try { + const data = await postAsync(api_server + "/v1/generate", args); + console.log(data); + return response_generate.send(data); + } catch (error) { + console.log(error); + return response_generate.send({ error: true }); + } } }); @@ -610,7 +591,7 @@ app.post("/getstatus", jsonParser, async function (request, response_getstatus = data.result = "no_connection"; } response_getstatus.send(data); - }).on('error', function (err) { + }).on('error', function () { response_getstatus.send({ result: "no_connection" }); }); }); @@ -691,18 +672,6 @@ function tryParse(str) { } } -function checkServer() { - api_server = 'http://127.0.0.1:5000'; - var args = { - headers: { "Content-Type": "application/json" } - }; - client.get(api_server + "/v1/model", args, function (data, response) { - console.log(data.result); - console.log(data); - }).on('error', function (err) { - console.log(err); - }); -} //***************** Main functions function charaFormatData(data) { @@ -767,7 +736,6 @@ app.post("/renamecharacter", jsonParser, async function (request, response) { const newAvatarName = `${newInternalName}.png`; const oldAvatarPath = path.join(charactersPath, oldAvatarName); - const newAvatarPath = path.join(charactersPath, newAvatarName); const oldChatsPath = path.join(chatsPath, oldInternalName); const newChatsPath = path.join(chatsPath, newInternalName); @@ -1384,7 +1352,7 @@ app.post("/getstatus_novelai", jsonParser, function (request, response_getstatus console.log(data); response_getstatus_novel.send({ error: true }); } - }).on('error', function (err) { + }).on('error', function () { //console.log(''); //console.log('something went wrong on the request', err.request.options); response_getstatus_novel.send({ error: true }); @@ -1451,7 +1419,7 @@ app.post("/generate_novelai", jsonParser, function (request, response_generate_n console.log(data); response_generate_novel.send({ error: true }); } - }).on('error', function (err) { + }).on('error', function () { //console.log(''); //console.log('something went wrong on the request', err.request.options); response_getstatus.send({ error: true }); @@ -2111,6 +2079,15 @@ app.post('/generate_poe', jsonParser, async (request, response) => { return response.sendStatus(401); } + let isGenerationStopped = false; + request.socket.removeAllListeners('close'); + request.socket.on('close', function () { + isGenerationStopped = true; + + if (client) { + client.abortController.abort(); + } + }); const prompt = request.body.prompt; const bot = request.body.bot ?? POE_DEFAULT_BOT; const streaming = request.body.streaming ?? false; @@ -2126,13 +2103,6 @@ app.post('/generate_poe', jsonParser, async (request, response) => { } if (streaming) { - let isStreamingStopped = false; - request.socket.removeAllListeners('close'); - request.socket.on('close', function () { - isStreamingStopped = true; - client.abortController.abort(); - }); - try { response.writeHead(200, { 'Content-Type': 'text/plain;charset=utf-8', @@ -2142,7 +2112,7 @@ app.post('/generate_poe', jsonParser, async (request, response) => { let reply = ''; for await (const mes of client.send_message(bot, prompt)) { - if (isStreamingStopped) { + if (isGenerationStopped) { console.error('Streaming stopped by user. Closing websocket...'); break; } @@ -2158,7 +2128,7 @@ app.post('/generate_poe', jsonParser, async (request, response) => { } finally { client.disconnect_ws(); - return response.end(); + response.end(); } } else { @@ -2390,7 +2360,7 @@ app.post("/getstatus_openai", jsonParser, function (request, response_getstatus_ console.log(data); response_getstatus_openai.send({ error: true }); } - }).on('error', function (err) { + }).on('error', function () { response_getstatus_openai.send({ error: true }); }); }); @@ -2628,16 +2598,6 @@ app.post("/tokenize_llama", jsonParser, async function (request, response) { }); // ** REST CLIENT ASYNC WRAPPERS ** -function deleteAsync(url, args) { - return new Promise((resolve, reject) => { - client.delete(url, args, (data, response) => { - if (response.statusCode >= 400) { - reject(data); - } - resolve(data); - }).on('error', e => reject(e)); - }) -} function putAsync(url, args) { return new Promise((resolve, reject) => { @@ -2650,15 +2610,15 @@ function putAsync(url, args) { }) } -function postAsync(url, args) { - return new Promise((resolve, reject) => { - client.post(url, args, (data, response) => { - if (response.statusCode >= 400) { - reject([data, response]); - } - resolve(data); - }).on('error', e => reject(e)); - }) +async function postAsync(url, args) { + const response = await fetch(url, { method: 'POST', args }); + + if (response.ok) { + const data = response.json(); + return data; + } + + throw new Error(response.statusText); } function getAsync(url, args) { @@ -2876,7 +2836,7 @@ app.post('/generate_horde', jsonParser, async (request, response) => { const url = 'https://horde.koboldai.net/api/v2/generate/text/async'; const args = { - data: request.body, + body: JSON.stringify(request.body), headers: { "Content-Type": "application/json", "Client-Agent": request.header('Client-Agent'),