From 5f1bed1e7012d3182f8d3af1bf23223ad64cac06 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Mon, 4 Dec 2023 12:32:41 -0500 Subject: [PATCH 1/5] Enable object-curly-spacing lint --- .eslintrc.js | 1 + public/script.js | 28 ++++++++++---------- public/scripts/bulk-edit.js | 2 +- public/scripts/extensions/translate/index.js | 2 +- public/scripts/extensions/tts/index.js | 2 +- public/scripts/extensions/tts/novel.js | 2 +- public/scripts/power-user.js | 8 +++--- 7 files changed, 23 insertions(+), 22 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index faafd14c6..a7d0800a7 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -58,6 +58,7 @@ module.exports = { 'comma-dangle': ['error', 'always-multiline'], 'eol-last': ['error', 'always'], 'no-trailing-spaces': 'error', + 'object-curly-spacing': ['error', 'always'], // These rules should eventually be enabled. 'no-async-promise-executor': 'off', diff --git a/public/script.js b/public/script.js index 713300982..5cc778d9a 100644 --- a/public/script.js +++ b/public/script.js @@ -4157,7 +4157,7 @@ async function DupeChar() { const confirm = await callPopup(`

Are you sure you want to duplicate this character?

If you just want to start a new chat with the same character, use "Start new chat" option in the bottom-left options menu.

`, - 'confirm', + 'confirm', ); if (!confirm) { @@ -7635,22 +7635,22 @@ function addDebugFunctions() { `Recalculates token counts of all messages in the current chat to refresh the counters. Useful when you switch between models that have different tokenizers. This is a visual change only. Your chat will be reloaded.`, async () => { - for (const message of chat) { + for (const message of chat) { // System messages are not counted - if (message.is_system) { - continue; + if (message.is_system) { + continue; + } + + if (!message.extra) { + message.extra = {}; + } + + message.extra.token_count = getTokenCount(message.mes, 0); } - if (!message.extra) { - message.extra = {}; - } - - message.extra.token_count = getTokenCount(message.mes, 0); - } - - await saveChatConditional(); - await reloadCurrentChat(); - }); + await saveChatConditional(); + await reloadCurrentChat(); + }); registerDebugFunction('generationTest', 'Send a generation request', 'Generates text using the currently selected API.', async () => { const text = prompt('Input text:', 'Hello'); diff --git a/public/scripts/bulk-edit.js b/public/scripts/bulk-edit.js index 59124ff2b..7cb0d17b9 100644 --- a/public/scripts/bulk-edit.js +++ b/public/scripts/bulk-edit.js @@ -1,5 +1,5 @@ import { characters, getCharacters, handleDeleteCharacter, callPopup } from '../script.js'; -import {BulkEditOverlay, BulkEditOverlayState} from './BulkEditOverlay.js'; +import { BulkEditOverlay, BulkEditOverlayState } from './BulkEditOverlay.js'; let is_bulk_edit = false; diff --git a/public/scripts/extensions/translate/index.js b/public/scripts/extensions/translate/index.js index efd2ff5e0..d8e14b3d7 100644 --- a/public/scripts/extensions/translate/index.js +++ b/public/scripts/extensions/translate/index.js @@ -1,4 +1,4 @@ -export {translate}; +export { translate }; import { callPopup, diff --git a/public/scripts/extensions/tts/index.js b/public/scripts/extensions/tts/index.js index 68561d3b7..9ae47750b 100644 --- a/public/scripts/extensions/tts/index.js +++ b/public/scripts/extensions/tts/index.js @@ -10,7 +10,7 @@ import { NovelTtsProvider } from './novel.js'; import { power_user } from '../../power-user.js'; import { registerSlashCommand } from '../../slash-commands.js'; import { OpenAITtsProvider } from './openai.js'; -import {XTTSTtsProvider} from './xtts.js'; +import { XTTSTtsProvider } from './xtts.js'; export { talkingAnimation }; const UPDATE_INTERVAL = 1000; diff --git a/public/scripts/extensions/tts/novel.js b/public/scripts/extensions/tts/novel.js index 48db4aee2..62a6dd9ad 100644 --- a/public/scripts/extensions/tts/novel.js +++ b/public/scripts/extensions/tts/novel.js @@ -125,7 +125,7 @@ class NovelTtsProvider { throw 'TTS Voice name not provided'; } - return { name: voiceName, voice_id: voiceName, lang: 'en-US', preview_url: false}; + return { name: voiceName, voice_id: voiceName, lang: 'en-US', preview_url: false }; } async generateTts(text, voiceId) { diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index 8c9b9dbec..ad45ab7ce 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -666,7 +666,7 @@ async function CreateZenSliders(elmnt) { min: sliderMin, max: sliderMax, create: async function () { - await delay(100) + await delay(100); var handle = $(this).find('.ui-slider-handle'); var handleText, stepNumber, leftMargin; @@ -711,7 +711,7 @@ async function CreateZenSliders(elmnt) { stepNumber = ((sliderValue - sliderMin) / stepScale); leftMargin = (stepNumber / numSteps) * 50 * -1; originalSlider.val(numVal) - .data('newSlider', newSlider) + .data('newSlider', newSlider); //console.log(`${newSlider.attr('id')} sliderValue = ${sliderValue}, handleText:${handleText, numVal}, stepNum:${stepNumber}, numSteps:${numSteps}, left-margin:${leftMargin}`) var isManualInput = false; var valueBeforeManualInput; @@ -737,8 +737,8 @@ async function CreateZenSliders(elmnt) { isManualInput = true; //allow enter to trigger slider update if (e.key === 'Enter') { - e.preventDefault - handle.trigger('blur') + e.preventDefault; + handle.trigger('blur'); } }) //trigger slider changes when user clicks away From d24c1dde10355000ef84dd5a23ee782941823927 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Mon, 4 Dec 2023 12:40:53 -0500 Subject: [PATCH 2/5] Use Express router for assets + "files" endpoints I've split out the "file/upload" endpoint into its own module, and renamed it to "files" to be consistent with the existing naming scheme. --- public/scripts/chats.js | 2 +- server.js | 5 +- src/endpoints/assets.js | 497 ++++++++++++++++++++-------------------- src/endpoints/files.js | 35 +++ 4 files changed, 286 insertions(+), 253 deletions(-) create mode 100644 src/endpoints/files.js diff --git a/public/scripts/chats.js b/public/scripts/chats.js index 26a40d90c..7ccfd1195 100644 --- a/public/scripts/chats.js +++ b/public/scripts/chats.js @@ -152,7 +152,7 @@ export async function populateFileAttachment(message, inputId = 'file_form_input */ export async function uploadFileAttachment(fileName, base64Data) { try { - const result = await fetch('/api/file/upload', { + const result = await fetch('/api/files/upload', { method: 'POST', headers: getRequestHeaders(), body: JSON.stringify({ diff --git a/server.js b/server.js index a0b24bb5b..6e41cb32f 100644 --- a/server.js +++ b/server.js @@ -3597,7 +3597,10 @@ require('./src/endpoints/novelai').registerEndpoints(app, jsonParser); require('./src/endpoints/extensions').registerEndpoints(app, jsonParser); // Asset management -require('./src/endpoints/assets').registerEndpoints(app, jsonParser); +app.use('/api/assets', require('./src/endpoints/assets').router); + +// File management +app.use('/api/files', require('./src/endpoints/files').router); // Character sprite management require('./src/endpoints/sprites').registerEndpoints(app, jsonParser, urlencodedParser); diff --git a/src/endpoints/assets.js b/src/endpoints/assets.js index 5264e3576..55a939816 100644 --- a/src/endpoints/assets.js +++ b/src/endpoints/assets.js @@ -1,10 +1,12 @@ const path = require('path'); const fs = require('fs'); +const express = require('express'); const sanitize = require('sanitize-filename'); const fetch = require('node-fetch').default; const { finished } = require('stream/promises'); const writeFileSyncAtomic = require('write-file-atomic').sync; const { DIRECTORIES, UNSAFE_EXTENSIONS } = require('../constants'); +const { jsonParser } = require('../express-common'); const VALID_CATEGORIES = ['bgm', 'ambient', 'blip', 'live2d']; @@ -57,273 +59,266 @@ function getFiles(dir, files = []) { return files; } +const router = express.Router(); + /** - * Registers the endpoints for the asset management. - * @param {import('express').Express} app Express app - * @param {any} jsonParser JSON parser middleware + * HTTP POST handler function to retrieve name of all files of a given folder path. + * + * @param {Object} request - HTTP Request object. Require folder path in query + * @param {Object} response - HTTP Response object will contain a list of file path. + * + * @returns {void} */ -function registerEndpoints(app, jsonParser) { - /** - * HTTP POST handler function to retrieve name of all files of a given folder path. - * - * @param {Object} request - HTTP Request object. Require folder path in query - * @param {Object} response - HTTP Response object will contain a list of file path. - * - * @returns {void} - */ - app.post('/api/assets/get', jsonParser, async (_, response) => { - const folderPath = path.join(DIRECTORIES.assets); - let output = {}; - //console.info("Checking files into",folderPath); +router.post('/get', jsonParser, async (_, response) => { + const folderPath = path.join(DIRECTORIES.assets); + let output = {}; + //console.info("Checking files into",folderPath); - try { - if (fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory()) { - const folders = fs.readdirSync(folderPath) - .filter(filename => { - return fs.statSync(path.join(folderPath, filename)).isDirectory(); - }); - - for (const folder of folders) { - if (folder == 'temp') - continue; - - // Live2d assets - if (folder == 'live2d') { - output[folder] = []; - const live2d_folder = path.normalize(path.join(folderPath, folder)); - const files = getFiles(live2d_folder); - //console.debug("FILE FOUND:",files) - for (let file of files) { - file = path.normalize(file.replace('public' + path.sep, '')); - if (file.includes('model') && file.endsWith('.json')) { - //console.debug("Asset live2d model found:",file) - output[folder].push(path.normalize(path.join(file))); - } - } - continue; - } - - // Other assets (bgm/ambient/blip) - const files = fs.readdirSync(path.join(folderPath, folder)) - .filter(filename => { - return filename != '.placeholder'; - }); - output[folder] = []; - for (const file of files) { - output[folder].push(path.join('assets', folder, file)); - } - } - } - } - catch (err) { - console.log(err); - } - return response.send(output); - }); - - /** - * HTTP POST handler function to download the requested asset. - * - * @param {Object} request - HTTP Request object, expects a url, a category and a filename. - * @param {Object} response - HTTP Response only gives status. - * - * @returns {void} - */ - app.post('/api/assets/download', jsonParser, async (request, response) => { - const url = request.body.url; - const inputCategory = request.body.category; - const inputFilename = sanitize(request.body.filename); - - // Check category - let category = null; - for (let i of VALID_CATEGORIES) - if (i == inputCategory) - category = i; - - if (category === null) { - console.debug('Bad request: unsuported asset category.'); - return response.sendStatus(400); - } - - // Sanitize filename - const safe_input = checkAssetFileName(inputFilename); - if (safe_input == '') - return response.sendStatus(400); - - const temp_path = path.join(DIRECTORIES.assets, 'temp', safe_input); - const file_path = path.join(DIRECTORIES.assets, category, safe_input); - console.debug('Request received to download', url, 'to', file_path); - - try { - // Download to temp - const res = await fetch(url); - if (!res.ok || res.body === null) { - throw new Error(`Unexpected response ${res.statusText}`); - } - const destination = path.resolve(temp_path); - // Delete if previous download failed - if (fs.existsSync(temp_path)) { - fs.unlink(temp_path, (err) => { - if (err) throw err; + try { + if (fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory()) { + const folders = fs.readdirSync(folderPath) + .filter(filename => { + return fs.statSync(path.join(folderPath, filename)).isDirectory(); }); - } - const fileStream = fs.createWriteStream(destination, { flags: 'wx' }); - await finished(res.body.pipe(fileStream)); - // Move into asset place - console.debug('Download finished, moving file from', temp_path, 'to', file_path); - fs.renameSync(temp_path, file_path); - response.sendStatus(200); - } - catch (error) { - console.log(error); - response.sendStatus(500); - } - }); - - /** - * HTTP POST handler function to delete the requested asset. - * - * @param {Object} request - HTTP Request object, expects a category and a filename - * @param {Object} response - HTTP Response only gives stats. - * - * @returns {void} - */ - app.post('/api/assets/delete', jsonParser, async (request, response) => { - const inputCategory = request.body.category; - const inputFilename = sanitize(request.body.filename); - - // Check category - let category = null; - for (let i of VALID_CATEGORIES) - if (i == inputCategory) - category = i; - - if (category === null) { - console.debug('Bad request: unsuported asset category.'); - return response.sendStatus(400); - } - - // Sanitize filename - const safe_input = checkAssetFileName(inputFilename); - if (safe_input == '') - return response.sendStatus(400); - - const file_path = path.join(DIRECTORIES.assets, category, safe_input); - console.debug('Request received to delete', category, file_path); - - try { - // Delete if previous download failed - if (fs.existsSync(file_path)) { - fs.unlink(file_path, (err) => { - if (err) throw err; - }); - console.debug('Asset deleted.'); - } - else { - console.debug('Asset not found.'); - response.sendStatus(400); - } - // Move into asset place - response.sendStatus(200); - } - catch (error) { - console.log(error); - response.sendStatus(500); - } - }); - - /////////////////////////////// - /** - * HTTP POST handler function to retrieve a character background music list. - * - * @param {Object} request - HTTP Request object, expects a character name in the query. - * @param {Object} response - HTTP Response object will contain a list of audio file path. - * - * @returns {void} - */ - app.post('/api/assets/character', jsonParser, async (request, response) => { - if (request.query.name === undefined) return response.sendStatus(400); - const name = sanitize(request.query.name.toString()); - const inputCategory = request.query.category; - - // Check category - let category = null; - for (let i of VALID_CATEGORIES) - if (i == inputCategory) - category = i; - - if (category === null) { - console.debug('Bad request: unsuported asset category.'); - return response.sendStatus(400); - } - - const folderPath = path.join(DIRECTORIES.characters, name, category); - - let output = []; - try { - if (fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory()) { + for (const folder of folders) { + if (folder == 'temp') + continue; // Live2d assets - if (category == 'live2d') { - const folders = fs.readdirSync(folderPath); - for (let modelFolder of folders) { - const live2dModelPath = path.join(folderPath, modelFolder); - if (fs.statSync(live2dModelPath).isDirectory()) { - for (let file of fs.readdirSync(live2dModelPath)) { - //console.debug("Character live2d model found:", file) - if (file.includes('model') && file.endsWith('.json')) - output.push(path.join('characters', name, category, modelFolder, file)); - } + if (folder == 'live2d') { + output[folder] = []; + const live2d_folder = path.normalize(path.join(folderPath, folder)); + const files = getFiles(live2d_folder); + //console.debug("FILE FOUND:",files) + for (let file of files) { + file = path.normalize(file.replace('public' + path.sep, '')); + if (file.includes('model') && file.endsWith('.json')) { + //console.debug("Asset live2d model found:",file) + output[folder].push(path.normalize(path.join(file))); } } - return response.send(output); + continue; } - // Other assets - const files = fs.readdirSync(folderPath) + // Other assets (bgm/ambient/blip) + const files = fs.readdirSync(path.join(folderPath, folder)) .filter(filename => { return filename != '.placeholder'; }); - - for (let i of files) - output.push(`/characters/${name}/${category}/${i}`); + output[folder] = []; + for (const file of files) { + output[folder].push(path.join('assets', folder, file)); + } } - return response.send(output); } - catch (err) { - console.log(err); - return response.sendStatus(500); + } + catch (err) { + console.log(err); + } + return response.send(output); +}); + +/** + * HTTP POST handler function to download the requested asset. + * + * @param {Object} request - HTTP Request object, expects a url, a category and a filename. + * @param {Object} response - HTTP Response only gives status. + * + * @returns {void} + */ +router.post('/download', jsonParser, async (request, response) => { + const url = request.body.url; + const inputCategory = request.body.category; + const inputFilename = sanitize(request.body.filename); + + // Check category + let category = null; + for (let i of VALID_CATEGORIES) + if (i == inputCategory) + category = i; + + if (category === null) { + console.debug('Bad request: unsuported asset category.'); + return response.sendStatus(400); + } + + // Sanitize filename + const safe_input = checkAssetFileName(inputFilename); + if (safe_input == '') + return response.sendStatus(400); + + const temp_path = path.join(DIRECTORIES.assets, 'temp', safe_input); + const file_path = path.join(DIRECTORIES.assets, category, safe_input); + console.debug('Request received to download', url, 'to', file_path); + + try { + // Download to temp + const res = await fetch(url); + if (!res.ok || res.body === null) { + throw new Error(`Unexpected response ${res.statusText}`); } - }); - - app.post('/api/file/upload', jsonParser, async (request, response) => { - try { - if (!request.body.name) { - return response.status(400).send('No upload name specified'); - } - - if (!request.body.data) { - return response.status(400).send('No upload data specified'); - } - - const safeInput = checkAssetFileName(request.body.name); - - if (!safeInput) { - return response.status(400).send('Invalid upload name'); - } - - const pathToUpload = path.join(DIRECTORIES.files, safeInput); - writeFileSyncAtomic(pathToUpload, request.body.data, 'base64'); - const url = path.normalize(pathToUpload.replace('public' + path.sep, '')); - return response.send({ path: url }); - } catch (error) { - console.log(error); - return response.sendStatus(500); + const destination = path.resolve(temp_path); + // Delete if previous download failed + if (fs.existsSync(temp_path)) { + fs.unlink(temp_path, (err) => { + if (err) throw err; + }); } - }); -} + const fileStream = fs.createWriteStream(destination, { flags: 'wx' }); + await finished(res.body.pipe(fileStream)); -module.exports = { - registerEndpoints, -}; + // Move into asset place + console.debug('Download finished, moving file from', temp_path, 'to', file_path); + fs.renameSync(temp_path, file_path); + response.sendStatus(200); + } + catch (error) { + console.log(error); + response.sendStatus(500); + } +}); + +/** + * HTTP POST handler function to delete the requested asset. + * + * @param {Object} request - HTTP Request object, expects a category and a filename + * @param {Object} response - HTTP Response only gives stats. + * + * @returns {void} + */ +router.post('/delete', jsonParser, async (request, response) => { + const inputCategory = request.body.category; + const inputFilename = sanitize(request.body.filename); + + // Check category + let category = null; + for (let i of VALID_CATEGORIES) + if (i == inputCategory) + category = i; + + if (category === null) { + console.debug('Bad request: unsuported asset category.'); + return response.sendStatus(400); + } + + // Sanitize filename + const safe_input = checkAssetFileName(inputFilename); + if (safe_input == '') + return response.sendStatus(400); + + const file_path = path.join(DIRECTORIES.assets, category, safe_input); + console.debug('Request received to delete', category, file_path); + + try { + // Delete if previous download failed + if (fs.existsSync(file_path)) { + fs.unlink(file_path, (err) => { + if (err) throw err; + }); + console.debug('Asset deleted.'); + } + else { + console.debug('Asset not found.'); + response.sendStatus(400); + } + // Move into asset place + response.sendStatus(200); + } + catch (error) { + console.log(error); + response.sendStatus(500); + } +}); + +/////////////////////////////// +/** + * HTTP POST handler function to retrieve a character background music list. + * + * @param {Object} request - HTTP Request object, expects a character name in the query. + * @param {Object} response - HTTP Response object will contain a list of audio file path. + * + * @returns {void} + */ +router.post('/character', jsonParser, async (request, response) => { + if (request.query.name === undefined) return response.sendStatus(400); + const name = sanitize(request.query.name.toString()); + const inputCategory = request.query.category; + + // Check category + let category = null; + for (let i of VALID_CATEGORIES) + if (i == inputCategory) + category = i; + + if (category === null) { + console.debug('Bad request: unsuported asset category.'); + return response.sendStatus(400); + } + + const folderPath = path.join(DIRECTORIES.characters, name, category); + + let output = []; + try { + if (fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory()) { + + // Live2d assets + if (category == 'live2d') { + const folders = fs.readdirSync(folderPath); + for (let modelFolder of folders) { + const live2dModelPath = path.join(folderPath, modelFolder); + if (fs.statSync(live2dModelPath).isDirectory()) { + for (let file of fs.readdirSync(live2dModelPath)) { + //console.debug("Character live2d model found:", file) + if (file.includes('model') && file.endsWith('.json')) + output.push(path.join('characters', name, category, modelFolder, file)); + } + } + } + return response.send(output); + } + + // Other assets + const files = fs.readdirSync(folderPath) + .filter(filename => { + return filename != '.placeholder'; + }); + + for (let i of files) + output.push(`/characters/${name}/${category}/${i}`); + } + return response.send(output); + } + catch (err) { + console.log(err); + return response.sendStatus(500); + } +}); + +router.post('/upload', jsonParser, async (request, response) => { + try { + if (!request.body.name) { + return response.status(400).send('No upload name specified'); + } + + if (!request.body.data) { + return response.status(400).send('No upload data specified'); + } + + const safeInput = checkAssetFileName(request.body.name); + + if (!safeInput) { + return response.status(400).send('Invalid upload name'); + } + + const pathToUpload = path.join(DIRECTORIES.files, safeInput); + writeFileSyncAtomic(pathToUpload, request.body.data, 'base64'); + const url = path.normalize(pathToUpload.replace('public' + path.sep, '')); + return response.send({ path: url }); + } catch (error) { + console.log(error); + return response.sendStatus(500); + } +}); + +module.exports = { router, checkAssetFileName }; diff --git a/src/endpoints/files.js b/src/endpoints/files.js new file mode 100644 index 000000000..191b0f081 --- /dev/null +++ b/src/endpoints/files.js @@ -0,0 +1,35 @@ +const path = require('path'); +const writeFileSyncAtomic = require('write-file-atomic').sync; +const express = require('express'); +const router = express.Router(); +const { checkAssetFileName } = require('./assets'); +const { jsonParser } = require('../express-common'); +const { DIRECTORIES } = require('../constants'); + +router.post('/upload', jsonParser, async (request, response) => { + try { + if (!request.body.name) { + return response.status(400).send('No upload name specified'); + } + + if (!request.body.data) { + return response.status(400).send('No upload data specified'); + } + + const safeInput = checkAssetFileName(request.body.name); + + if (!safeInput) { + return response.status(400).send('Invalid upload name'); + } + + const pathToUpload = path.join(DIRECTORIES.files, safeInput); + writeFileSyncAtomic(pathToUpload, request.body.data, 'base64'); + const url = path.normalize(pathToUpload.replace('public' + path.sep, '')); + return response.send({ path: url }); + } catch (error) { + console.log(error); + return response.sendStatus(500); + } +}); + +module.exports = { router }; From d2e1577acba70a2a74d8be867b326803da7edc73 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Mon, 4 Dec 2023 12:43:37 -0500 Subject: [PATCH 3/5] Use Express router for caption endpoint --- server.js | 2 +- src/endpoints/caption.js | 53 +++++++++++++++++++--------------------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/server.js b/server.js index a0b24bb5b..f3c208f17 100644 --- a/server.js +++ b/server.js @@ -3621,7 +3621,7 @@ require('./src/endpoints/translate').registerEndpoints(app, jsonParser); require('./src/endpoints/classify').registerEndpoints(app, jsonParser); // Image captioning -require('./src/endpoints/caption').registerEndpoints(app, jsonParser); +app.use('/api/extra/caption', require('./src/endpoints/caption').router); // Web search extension require('./src/endpoints/serpapi').registerEndpoints(app, jsonParser); diff --git a/src/endpoints/caption.js b/src/endpoints/caption.js index 81a8d029d..ac6e2b896 100644 --- a/src/endpoints/caption.js +++ b/src/endpoints/caption.js @@ -1,35 +1,32 @@ +const express = require('express'); +const { jsonParser } = require('../express-common'); + const TASK = 'image-to-text'; -/** - * @param {import("express").Express} app - * @param {any} jsonParser - */ -function registerEndpoints(app, jsonParser) { - app.post('/api/extra/caption', jsonParser, async (req, res) => { - try { - const { image } = req.body; +const router = express.Router(); - const module = await import('../transformers.mjs'); - const rawImage = await module.default.getRawImage(image); +router.post('/', jsonParser, async (req, res) => { + try { + const { image } = req.body; - if (!rawImage) { - console.log('Failed to parse captioned image'); - return res.sendStatus(400); - } + const module = await import('../transformers.mjs'); + const rawImage = await module.default.getRawImage(image); - const pipe = await module.default.getPipeline(TASK); - const result = await pipe(rawImage); - const text = result[0].generated_text; - console.log('Image caption:', text); - - return res.json({ caption: text }); - } catch (error) { - console.error(error); - return res.sendStatus(500); + if (!rawImage) { + console.log('Failed to parse captioned image'); + return res.sendStatus(400); } - }); -} -module.exports = { - registerEndpoints, -}; + const pipe = await module.default.getPipeline(TASK); + const result = await pipe(rawImage); + const text = result[0].generated_text; + console.log('Image caption:', text); + + return res.json({ caption: text }); + } catch (error) { + console.error(error); + return res.sendStatus(500); + } +}); + +module.exports = { router }; From 3ad7d5d5207e3bd25ffcafbf94998d29dfa6f769 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Mon, 4 Dec 2023 20:59:11 +0200 Subject: [PATCH 4/5] Negotiate formatting with VS Code autoformat --- public/script.js | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/public/script.js b/public/script.js index 5cc778d9a..a3b5512e2 100644 --- a/public/script.js +++ b/public/script.js @@ -887,7 +887,7 @@ async function getStatus() { api_type: textgen_settings.type, legacy_api: main_api == 'textgenerationwebui' ? textgen_settings.legacy_api && - textgen_settings.type !== MANCER : + textgen_settings.type !== MANCER : false, }), signal: abortStatusCheck.signal, @@ -4154,11 +4154,11 @@ async function DupeChar() { return; } - const confirm = await callPopup(` -

Are you sure you want to duplicate this character?

- If you just want to start a new chat with the same character, use "Start new chat" option in the bottom-left options menu.

`, - 'confirm', - ); + const confirmMessage = ` +

Are you sure you want to duplicate this character?

+ If you just want to start a new chat with the same character, use "Start new chat" option in the bottom-left options menu.

`; + + const confirm = await callPopup(confirmMessage, 'confirm'); if (!confirm) { console.log('User cancelled duplication'); @@ -7631,26 +7631,28 @@ function doTogglePanels() { } function addDebugFunctions() { + const doBackfill = async () => { + for (const message of chat) { + // System messages are not counted + if (message.is_system) { + continue; + } + + if (!message.extra) { + message.extra = {}; + } + + message.extra.token_count = getTokenCount(message.mes, 0); + } + + await saveChatConditional(); + await reloadCurrentChat(); + }; + registerDebugFunction('backfillTokenCounts', 'Backfill token counters', `Recalculates token counts of all messages in the current chat to refresh the counters. Useful when you switch between models that have different tokenizers. - This is a visual change only. Your chat will be reloaded.`, async () => { - for (const message of chat) { - // System messages are not counted - if (message.is_system) { - continue; - } - - if (!message.extra) { - message.extra = {}; - } - - message.extra.token_count = getTokenCount(message.mes, 0); - } - - await saveChatConditional(); - await reloadCurrentChat(); - }); + This is a visual change only. Your chat will be reloaded.`, doBackfill); registerDebugFunction('generationTest', 'Send a generation request', 'Generates text using the currently selected API.', async () => { const text = prompt('Input text:', 'Hello'); From 1ac494d4680aaa49e54e9e4a018fdfd07abf872c Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Mon, 4 Dec 2023 21:28:36 +0200 Subject: [PATCH 5/5] Don't attempt to send files on dry runs. --- public/script.js | 6 +++--- public/scripts/chats.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/script.js b/public/script.js index 713300982..3d0977a2b 100644 --- a/public/script.js +++ b/public/script.js @@ -2914,7 +2914,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject, let textareaText; if (type !== 'regenerate' && type !== 'swipe' && type !== 'quiet' && !isImpersonate && !dryRun) { is_send_press = true; - textareaText = $('#send_textarea').val(); + textareaText = String($('#send_textarea').val()); $('#send_textarea').val('').trigger('input'); } else { textareaText = ''; @@ -2960,7 +2960,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject, //********************************* //for normal messages sent from user.. - if ((textareaText != '' || hasPendingFileAttachment()) && !automatic_trigger && type !== 'quiet') { + if ((textareaText != '' || hasPendingFileAttachment()) && !automatic_trigger && type !== 'quiet' && !dryRun) { // If user message contains no text other than bias - send as a system message if (messageBias && replaceBiasMarkup(textareaText).trim().length === 0) { sendSystemMessage(system_message_types.GENERIC, ' ', { bias: messageBias }); @@ -2969,7 +2969,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject, await sendMessageAsUser(textareaText, messageBias); } } - else if (textareaText == '' && !automatic_trigger && type === undefined && main_api == 'openai' && oai_settings.send_if_empty.trim().length > 0) { + else if (textareaText == '' && !automatic_trigger && !dryRun && type === undefined && main_api == 'openai' && oai_settings.send_if_empty.trim().length > 0) { // Use send_if_empty if set and the user message is empty. Only when sending messages normally await sendMessageAsUser(oai_settings.send_if_empty.trim(), messageBias); } diff --git a/public/scripts/chats.js b/public/scripts/chats.js index 7ccfd1195..a53b8c712 100644 --- a/public/scripts/chats.js +++ b/public/scripts/chats.js @@ -302,7 +302,7 @@ async function viewMessageFile(messageId) { modalTemplate.addClass('file_modal'); addCopyToCodeBlocks(modalTemplate); - callPopup(modalTemplate, 'text'); + callPopup(modalTemplate, 'text', '', { wide: true, large: true }); } /**