Merge pull request #3307 from Eradev/staging-3071-v2 Added option to disable the chat logs in terminal v2

76d166176877779dfba11090d2849604e5fd242b

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
46 files changed, +566 -570Ignore whitespace
default/config.yaml+2 -0
@@ -65,6 +65,8 @@ autheliaAuth: false
65# the username and passwords for basic auth are the same as those65# the username and passwords for basic auth are the same as those
66# for the individual accounts66# for the individual accounts
67perUserBasicAuth: false67perUserBasicAuth: false
68# Minimum log level to display in the terminal (DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3)
69minLogLevel: 0
6870
69# User session timeout *in seconds* (defaults to 24 hours).71# User session timeout *in seconds* (defaults to 24 hours).
70## Set to a positive number to expire session after a certain time of inactivity72## Set to a positive number to expire session after a certain time of inactivity
public/scripts/chat-templates.js+2 -1
@@ -69,6 +69,7 @@ const hash_derivations = {
69 // DeepSeek R169 // DeepSeek R1
70 'b6835114b7303ddd78919a82e4d9f7d8c26ed0d7dfc36beeb12d524f6144eab1':70 'b6835114b7303ddd78919a82e4d9f7d8c26ed0d7dfc36beeb12d524f6144eab1':
71 'DeepSeek-V2.5'71 'DeepSeek-V2.5'
72 ,
72};73};
7374
74const substr_derivations = {75const substr_derivations = {
@@ -97,6 +98,6 @@ export async function deriveTemplatesFromChatTemplate(chat_template, hash) {
97 }98 }
98 }99 }
99100
100 console.log(`Unknown chat template hash: ${hash} for [${chat_template}]`);101 console.warn(`Unknown chat template hash: ${hash} for [${chat_template}]`);
101 return null;102 return null;
102}103}
public/scripts/extensions/quick-reply/src/QuickReplySet.js+6 -28
@@ -1,15 +1,14 @@
1import { getRequestHeaders, substituteParams } from '../../../../script.js';1import { getRequestHeaders, substituteParams } from '../../../../script.js';
2import { Popup, POPUP_RESULT, POPUP_TYPE } from '../../../popup.js';2import { Popup, POPUP_RESULT, POPUP_TYPE } from '../../../popup.js';
3import { executeSlashCommands, executeSlashCommandsOnChatInput, executeSlashCommandsWithOptions } from '../../../slash-commands.js';3import { executeSlashCommandsOnChatInput, executeSlashCommandsWithOptions } from '../../../slash-commands.js';
4import { SlashCommandParser } from '../../../slash-commands/SlashCommandParser.js';
5import { SlashCommandScope } from '../../../slash-commands/SlashCommandScope.js';4import { SlashCommandScope } from '../../../slash-commands/SlashCommandScope.js';
6import { debounceAsync, log, warn } from '../index.js';5import { SlashCommandParser } from '../../../slash-commands/SlashCommandParser.js';
6import { debounceAsync, warn } from '../index.js';
7import { QuickReply } from './QuickReply.js';7import { QuickReply } from './QuickReply.js';
88
9export class QuickReplySet {9export class QuickReplySet {
10 /**@type {QuickReplySet[]}*/ static list = [];10 /**@type {QuickReplySet[]}*/ static list = [];
1111
12
13 static from(props) {12 static from(props) {
14 props.qrList = []; //props.qrList?.map(it=>QuickReply.from(it));13 props.qrList = []; //props.qrList?.map(it=>QuickReply.from(it));
15 const instance = Object.assign(new this(), props);14 const instance = Object.assign(new this(), props);
@@ -24,9 +23,6 @@ export class QuickReplySet {
24 return this.list.find(it=>it.name == name);23 return this.list.find(it=>it.name == name);
25 }24 }
2625
27
28
29
30 /**@type {string}*/ name;26 /**@type {string}*/ name;
31 /**@type {boolean}*/ disableSend = false;27 /**@type {boolean}*/ disableSend = false;
32 /**@type {boolean}*/ placeBeforeInput = false;28 /**@type {boolean}*/ placeBeforeInput = false;
@@ -34,19 +30,12 @@ export class QuickReplySet {
34 /**@type {string}*/ color = 'transparent';30 /**@type {string}*/ color = 'transparent';
35 /**@type {boolean}*/ onlyBorderColor = false;31 /**@type {boolean}*/ onlyBorderColor = false;
36 /**@type {QuickReply[]}*/ qrList = [];32 /**@type {QuickReply[]}*/ qrList = [];
37
38 /**@type {number}*/ idIndex = 0;33 /**@type {number}*/ idIndex = 0;
39
40 /**@type {boolean}*/ isDeleted = false;34 /**@type {boolean}*/ isDeleted = false;
41
42 /**@type {function}*/ save;35 /**@type {function}*/ save;
43
44 /**@type {HTMLElement}*/ dom;36 /**@type {HTMLElement}*/ dom;
45 /**@type {HTMLElement}*/ settingsDom;37 /**@type {HTMLElement}*/ settingsDom;
4638
47
48
49
50 constructor() {39 constructor() {
51 this.save = debounceAsync(()=>this.performSave(), 200);40 this.save = debounceAsync(()=>this.performSave(), 200);
52 }41 }
@@ -55,9 +44,6 @@ export class QuickReplySet {
55 this.qrList.forEach(qr=>this.hookQuickReply(qr));44 this.qrList.forEach(qr=>this.hookQuickReply(qr));
56 }45 }
5746
58
59
60
61 unrender() {47 unrender() {
62 this.dom?.remove();48 this.dom?.remove();
63 this.dom = null;49 this.dom = null;
@@ -100,9 +86,6 @@ export class QuickReplySet {
100 }86 }
101 }87 }
10288
103
104
105
106 renderSettings() {89 renderSettings() {
107 if (!this.settingsDom) {90 if (!this.settingsDom) {
108 this.settingsDom = document.createElement('div'); {91 this.settingsDom = document.createElement('div'); {
@@ -123,9 +106,6 @@ export class QuickReplySet {
123 this.settingsDom.append(qr.renderSettings(idx));106 this.settingsDom.append(qr.renderSettings(idx));
124 }107 }
125108
126
127
128
129 /**109 /**
130 *110 *
131 * @param {QuickReply} qr111 * @param {QuickReply} qr
@@ -138,6 +118,7 @@ export class QuickReplySet {
138 closure.scope.setMacro('arg::*', '');118 closure.scope.setMacro('arg::*', '');
139 return (await closure.execute())?.pipe;119 return (await closure.execute())?.pipe;
140 }120 }
121
141 /**122 /**
142 *123 *
143 * @param {QuickReply} qr The QR to execute.124 * @param {QuickReply} qr The QR to execute.
@@ -207,6 +188,7 @@ export class QuickReplySet {
207 document.querySelector('#send_but').click();188 document.querySelector('#send_but').click();
208 }189 }
209 }190 }
191
210 /**192 /**
211 * @param {QuickReply} qr193 * @param {QuickReply} qr
212 * @param {string} [message] - optional altered message to be used194 * @param {string} [message] - optional altered message to be used
@@ -220,9 +202,6 @@ export class QuickReplySet {
220 });202 });
221 }203 }
222204
223
224
225
226 addQuickReply(data = {}) {205 addQuickReply(data = {}) {
227 const id = Math.max(this.idIndex, this.qrList.reduce((max,qr)=>Math.max(max,qr.id),0)) + 1;206 const id = Math.max(this.idIndex, this.qrList.reduce((max,qr)=>Math.max(max,qr.id),0)) + 1;
228 data.id =207 data.id =
@@ -239,6 +218,7 @@ export class QuickReplySet {
239 this.save();218 this.save();
240 return qr;219 return qr;
241 }220 }
221
242 addQuickReplyFromText(qrJson) {222 addQuickReplyFromText(qrJson) {
243 let data;223 let data;
244 if (qrJson) {224 if (qrJson) {
@@ -371,7 +351,6 @@ export class QuickReplySet {
371 this.save();351 this.save();
372 }352 }
373353
374
375 toJSON() {354 toJSON() {
376 return {355 return {
377 version: 2,356 version: 2,
@@ -386,7 +365,6 @@ export class QuickReplySet {
386 };365 };
387 }366 }
388367
389
390 async performSave() {368 async performSave() {
391 const response = await fetch('/api/quick-replies/save', {369 const response = await fetch('/api/quick-replies/save', {
392 method: 'POST',370 method: 'POST',
public/scripts/extensions/vectors/index.js+4 -6
@@ -1621,14 +1621,14 @@ jQuery(async () => {
1621 const attachments = source ? getDataBankAttachmentsForSource(source, false) : getDataBankAttachments(false);1621 const attachments = source ? getDataBankAttachmentsForSource(source, false) : getDataBankAttachments(false);
1622 const collectionIds = await ingestDataBankAttachments(String(source));1622 const collectionIds = await ingestDataBankAttachments(String(source));
1623 const queryResults = await queryMultipleCollections(collectionIds, String(query), count, threshold);1623 const queryResults = await queryMultipleCollections(collectionIds, String(query), count, threshold);
1624 1624
1625 // Get URLs1625 // Get URLs
1626 const urls = Object1626 const urls = Object
1627 .keys(queryResults)1627 .keys(queryResults)
1628 .map(x => attachments.find(y => getFileCollectionId(y.url) === x))1628 .map(x => attachments.find(y => getFileCollectionId(y.url) === x))
1629 .filter(x => x)1629 .filter(x => x)
1630 .map(x => x.url);1630 .map(x => x.url);
1631 1631
1632 // Gets the actual text content of chunks1632 // Gets the actual text content of chunks
1633 const getChunksText = () => {1633 const getChunksText = () => {
1634 let textResult = '';1634 let textResult = '';
@@ -1638,14 +1638,12 @@ jQuery(async () => {
1638 }1638 }
1639 return textResult;1639 return textResult;
1640 };1640 };
1641
1642 if (args.return === 'chunks') {1641 if (args.return === 'chunks') {
1643 return getChunksText();1642 return getChunksText();
1644 }1643 }
16451644
1646 // @ts-ignore1645 // @ts-ignore
1647 return slashCommandReturnHelper.doReturn(args.return ?? 'object', urls, { objectToStringFunc: list => list.join('\n') });1646 return slashCommandReturnHelper.doReturn(args.return ?? 'object', urls, { objectToStringFunc: list => list.join('\n') });
1648
1649 },1647 },
1650 aliases: ['databank-search', 'data-bank-search'],1648 aliases: ['databank-search', 'data-bank-search'],
1651 helpString: 'Search the Data Bank for a specific query using vector similarity. Returns a list of file URLs with the most relevant content.',1649 helpString: 'Search the Data Bank for a specific query using vector similarity. Returns a list of file URLs with the most relevant content.',
@@ -1660,10 +1658,10 @@ jQuery(async () => {
1660 defaultValue: 'object',1658 defaultValue: 'object',
1661 enumList: [1659 enumList: [
1662 new SlashCommandEnumValue('chunks', 'Return the actual content chunks', enumTypes.enum, '{}'),1660 new SlashCommandEnumValue('chunks', 'Return the actual content chunks', enumTypes.enum, '{}'),
1663 ...slashCommandReturnHelper.enumList({ allowObject: true })1661 ...slashCommandReturnHelper.enumList({ allowObject: true }),
1664 ],1662 ],
1665 forceEnum: true,1663 forceEnum: true,
1666 })1664 }),
1667 ],1665 ],
1668 unnamedArgumentList: [1666 unnamedArgumentList: [
1669 new SlashCommandArgument('Query to search by.', ARGUMENT_TYPE.STRING, true, false),1667 new SlashCommandArgument('Query to search by.', ARGUMENT_TYPE.STRING, true, false),
public/scripts/openai.js+1 -3
@@ -1443,9 +1443,7 @@ async function sendWindowAIRequest(messages, signal, stream) {
1443 }1443 }
14441444
1445 const onStreamResult = (res, err) => {1445 const onStreamResult = (res, err) => {
1446 if (err) {1446 if (err) return;
1447 return;
1448 }
14491447
1450 const thisContent = res?.message?.content;1448 const thisContent = res?.message?.content;
14511449
server.js+6 -2
@@ -66,6 +66,7 @@ import {
66 removeColorFormatting,66 removeColorFormatting,
67 getSeparator,67 getSeparator,
68 safeReadFileSync,68 safeReadFileSync,
69 setupLogLevel,
69} from './src/util.js';70} from './src/util.js';
70import { UPLOADS_DIRECTORY } from './src/constants.js';71import { UPLOADS_DIRECTORY } from './src/constants.js';
71import { ensureThumbnailCache } from './src/endpoints/thumbnails.js';72import { ensureThumbnailCache } from './src/endpoints/thumbnails.js';
@@ -698,8 +699,9 @@ const preSetupTasks = async function () {
698function getAutorunHostname() {699function getAutorunHostname() {
699 if (autorunHostname === 'auto') {700 if (autorunHostname === 'auto') {
700 if (enableIPv6 && enableIPv4) {701 if (enableIPv6 && enableIPv4) {
701 if (avoidLocalhost) return '[::1]';702 return avoidLocalhost
702 return 'localhost';703 ? '[::1]'
704 : 'localhost';
703 }705 }
704706
705 if (enableIPv6) {707 if (enableIPv6) {
@@ -765,6 +767,8 @@ const postSetupTasks = async function (v6Failed, v4Failed) {
765 }767 }
766 }768 }
767 }769 }
770
771 setupLogLevel();
768};772};
769773
770/**774/**
src/constants.js+7 -0
@@ -414,3 +414,10 @@ export const VLLM_KEYS = [
414 'guided_decoding_backend',414 'guided_decoding_backend',
415 'guided_whitespace_pattern',415 'guided_whitespace_pattern',
416];416];
417
418export const LOG_LEVELS = {
419 DEBUG: 0,
420 INFO: 1,
421 WARN: 2,
422 ERROR: 3,
423};
src/endpoints/anthropic.js+3 -3
@@ -32,7 +32,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
32 max_tokens: 4096,32 max_tokens: 4096,
33 };33 };
3434
35 console.log('Multimodal captioning request', body);35 console.debug('Multimodal captioning request', body);
3636
37 const result = await fetch(url, {37 const result = await fetch(url, {
38 body: JSON.stringify(body),38 body: JSON.stringify(body),
@@ -46,14 +46,14 @@ router.post('/caption-image', jsonParser, async (request, response) => {
4646
47 if (!result.ok) {47 if (!result.ok) {
48 const text = await result.text();48 const text = await result.text();
49 console.log(`Claude API returned error: ${result.status} ${result.statusText}`, text);49 console.warn(`Claude API returned error: ${result.status} ${result.statusText}`, text);
50 return response.status(result.status).send({ error: true });50 return response.status(result.status).send({ error: true });
51 }51 }
5252
53 /** @type {any} */53 /** @type {any} */
54 const generateResponseJson = await result.json();54 const generateResponseJson = await result.json();
55 const caption = generateResponseJson.content[0].text;55 const caption = generateResponseJson.content[0].text;
56 console.log('Claude response:', generateResponseJson);56 console.debug('Claude response:', generateResponseJson);
5757
58 if (!caption) {58 if (!caption) {
59 return response.status(500).send('No caption found');59 return response.status(500).send('No caption found');
src/endpoints/assets.js+13 -12
@@ -176,7 +176,7 @@ router.post('/get', jsonParser, async (request, response) => {
176 }176 }
177 }177 }
178 catch (err) {178 catch (err) {
179 console.log(err);179 console.error(err);
180 }180 }
181 return response.send(output);181 return response.send(output);
182});182});
@@ -200,7 +200,7 @@ router.post('/download', jsonParser, async (request, response) => {
200 category = i;200 category = i;
201201
202 if (category === null) {202 if (category === null) {
203 console.debug('Bad request: unsupported asset category.');203 console.error('Bad request: unsupported asset category.');
204 return response.sendStatus(400);204 return response.sendStatus(400);
205 }205 }
206206
@@ -212,7 +212,7 @@ router.post('/download', jsonParser, async (request, response) => {
212212
213 const temp_path = path.join(request.user.directories.assets, 'temp', request.body.filename);213 const temp_path = path.join(request.user.directories.assets, 'temp', request.body.filename);
214 const file_path = path.join(request.user.directories.assets, category, request.body.filename);214 const file_path = path.join(request.user.directories.assets, category, request.body.filename);
215 console.debug('Request received to download', url, 'to', file_path);215 console.info('Request received to download', url, 'to', file_path);
216216
217 try {217 try {
218 // Download to temp218 // Download to temp
@@ -241,13 +241,13 @@ router.post('/download', jsonParser, async (request, response) => {
241 }241 }
242242
243 // Move into asset place243 // Move into asset place
244 console.debug('Download finished, moving file from', temp_path, 'to', file_path);244 console.info('Download finished, moving file from', temp_path, 'to', file_path);
245 fs.copyFileSync(temp_path, file_path);245 fs.copyFileSync(temp_path, file_path);
246 fs.rmSync(temp_path);246 fs.rmSync(temp_path);
247 response.sendStatus(200);247 response.sendStatus(200);
248 }248 }
249 catch (error) {249 catch (error) {
250 console.log(error);250 console.error(error);
251 response.sendStatus(500);251 response.sendStatus(500);
252 }252 }
253});253});
@@ -270,7 +270,7 @@ router.post('/delete', jsonParser, async (request, response) => {
270 category = i;270 category = i;
271271
272 if (category === null) {272 if (category === null) {
273 console.debug('Bad request: unsupported asset category.');273 console.error('Bad request: unsupported asset category.');
274 return response.sendStatus(400);274 return response.sendStatus(400);
275 }275 }
276276
@@ -280,7 +280,7 @@ router.post('/delete', jsonParser, async (request, response) => {
280 return response.status(400).send(validation.message);280 return response.status(400).send(validation.message);
281281
282 const file_path = path.join(request.user.directories.assets, category, request.body.filename);282 const file_path = path.join(request.user.directories.assets, category, request.body.filename);
283 console.debug('Request received to delete', category, file_path);283 console.info('Request received to delete', category, file_path);
284284
285 try {285 try {
286 // Delete if previous download failed286 // Delete if previous download failed
@@ -288,17 +288,17 @@ router.post('/delete', jsonParser, async (request, response) => {
288 fs.unlink(file_path, (err) => {288 fs.unlink(file_path, (err) => {
289 if (err) throw err;289 if (err) throw err;
290 });290 });
291 console.debug('Asset deleted.');291 console.info('Asset deleted.');
292 }292 }
293 else {293 else {
294 console.debug('Asset not found.');294 console.error('Asset not found.');
295 response.sendStatus(400);295 response.sendStatus(400);
296 }296 }
297 // Move into asset place297 // Move into asset place
298 response.sendStatus(200);298 response.sendStatus(200);
299 }299 }
300 catch (error) {300 catch (error) {
301 console.log(error);301 console.error(error);
302 response.sendStatus(500);302 response.sendStatus(500);
303 }303 }
304});304});
@@ -314,6 +314,7 @@ router.post('/delete', jsonParser, async (request, response) => {
314 */314 */
315router.post('/character', jsonParser, async (request, response) => {315router.post('/character', jsonParser, async (request, response) => {
316 if (request.query.name === undefined) return response.sendStatus(400);316 if (request.query.name === undefined) return response.sendStatus(400);
317
317 // For backwards compatibility, don't reject invalid character names, just sanitize them318 // For backwards compatibility, don't reject invalid character names, just sanitize them
318 const name = sanitize(request.query.name.toString());319 const name = sanitize(request.query.name.toString());
319 const inputCategory = request.query.category;320 const inputCategory = request.query.category;
@@ -325,7 +326,7 @@ router.post('/character', jsonParser, async (request, response) => {
325 category = i;326 category = i;
326327
327 if (category === null) {328 if (category === null) {
328 console.debug('Bad request: unsupported asset category.');329 console.error('Bad request: unsupported asset category.');
329 return response.sendStatus(400);330 return response.sendStatus(400);
330 }331 }
331332
@@ -364,7 +365,7 @@ router.post('/character', jsonParser, async (request, response) => {
364 return response.send(output);365 return response.send(output);
365 }366 }
366 catch (err) {367 catch (err) {
367 console.log(err);368 console.error(err);
368 return response.sendStatus(500);369 return response.sendStatus(500);
369 }370 }
370});371});
src/endpoints/azure.js+6 -6
@@ -11,14 +11,14 @@ router.post('/list', jsonParser, async (req, res) => {
11 const key = readSecret(req.user.directories, SECRET_KEYS.AZURE_TTS);11 const key = readSecret(req.user.directories, SECRET_KEYS.AZURE_TTS);
1212
13 if (!key) {13 if (!key) {
14 console.error('Azure TTS API Key not set');14 console.warn('Azure TTS API Key not set');
15 return res.sendStatus(403);15 return res.sendStatus(403);
16 }16 }
1717
18 const region = req.body.region;18 const region = req.body.region;
1919
20 if (!region) {20 if (!region) {
21 console.error('Azure TTS region not set');21 console.warn('Azure TTS region not set');
22 return res.sendStatus(400);22 return res.sendStatus(400);
23 }23 }
2424
@@ -32,7 +32,7 @@ router.post('/list', jsonParser, async (req, res) => {
32 });32 });
3333
34 if (!response.ok) {34 if (!response.ok) {
35 console.error('Azure Request failed', response.status, response.statusText);35 console.warn('Azure Request failed', response.status, response.statusText);
36 return res.sendStatus(500);36 return res.sendStatus(500);
37 }37 }
3838
@@ -49,13 +49,13 @@ router.post('/generate', jsonParser, async (req, res) => {
49 const key = readSecret(req.user.directories, SECRET_KEYS.AZURE_TTS);49 const key = readSecret(req.user.directories, SECRET_KEYS.AZURE_TTS);
5050
51 if (!key) {51 if (!key) {
52 console.error('Azure TTS API Key not set');52 console.warn('Azure TTS API Key not set');
53 return res.sendStatus(403);53 return res.sendStatus(403);
54 }54 }
5555
56 const { text, voice, region } = req.body;56 const { text, voice, region } = req.body;
57 if (!text || !voice || !region) {57 if (!text || !voice || !region) {
58 console.error('Missing required parameters');58 console.warn('Missing required parameters');
59 return res.sendStatus(400);59 return res.sendStatus(400);
60 }60 }
6161
@@ -75,7 +75,7 @@ router.post('/generate', jsonParser, async (req, res) => {
75 });75 });
7676
77 if (!response.ok) {77 if (!response.ok) {
78 console.error('Azure Request failed', response.status, response.statusText);78 console.warn('Azure Request failed', response.status, response.statusText);
79 return res.sendStatus(500);79 return res.sendStatus(500);
80 }80 }
8181
src/endpoints/backends/chat-completions.js+48 -47
@@ -114,7 +114,7 @@ async function sendClaudeRequest(request, response) {
114 }114 }
115115
116 if (!apiKey) {116 if (!apiKey) {
117 console.log(color.red(`Claude API key is missing.\n${divider}`));117 console.warn(color.red(`Claude API key is missing.\n${divider}`));
118 return response.status(400).send({ error: true });118 return response.status(400).send({ error: true });
119 }119 }
120120
@@ -230,12 +230,12 @@ async function sendScaleRequest(request, response) {
230 const apiKey = readSecret(request.user.directories, SECRET_KEYS.SCALE);230 const apiKey = readSecret(request.user.directories, SECRET_KEYS.SCALE);
231231
232 if (!apiKey) {232 if (!apiKey) {
233 console.log('Scale API key is missing.');233 console.warn('Scale API key is missing.');
234 return response.status(400).send({ error: true });234 return response.status(400).send({ error: true });
235 }235 }
236236
237 const requestPrompt = convertTextCompletionPrompt(request.body.messages);237 const requestPrompt = convertTextCompletionPrompt(request.body.messages);
238 console.log('Scale request:', requestPrompt);238 console.debug('Scale request:', requestPrompt);
239239
240 try {240 try {
241 const controller = new AbortController();241 const controller = new AbortController();
@@ -254,18 +254,18 @@ async function sendScaleRequest(request, response) {
254 });254 });
255255
256 if (!generateResponse.ok) {256 if (!generateResponse.ok) {
257 console.log(`Scale API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);257 console.warn(`Scale API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
258 return response.status(500).send({ error: true });258 return response.status(500).send({ error: true });
259 }259 }
260260
261 /** @type {any} */261 /** @type {any} */
262 const generateResponseJson = await generateResponse.json();262 const generateResponseJson = await generateResponse.json();
263 console.log('Scale response:', generateResponseJson);263 console.debug('Scale response:', generateResponseJson);
264264
265 const reply = { choices: [{ 'message': { 'content': generateResponseJson.output } }] };265 const reply = { choices: [{ 'message': { 'content': generateResponseJson.output } }] };
266 return response.send(reply);266 return response.send(reply);
267 } catch (error) {267 } catch (error) {
268 console.log(error);268 console.error(error);
269 if (!response.headersSent) {269 if (!response.headersSent) {
270 return response.status(500).send({ error: true });270 return response.status(500).send({ error: true });
271 }271 }
@@ -282,7 +282,7 @@ async function sendMakerSuiteRequest(request, response) {
282 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);282 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);
283283
284 if (!request.body.reverse_proxy && !apiKey) {284 if (!request.body.reverse_proxy && !apiKey) {
285 console.log('Google AI Studio API key is missing.');285 console.warn('Google AI Studio API key is missing.');
286 return response.status(400).send({ error: true });286 return response.status(400).send({ error: true });
287 }287 }
288288
@@ -340,7 +340,7 @@ async function sendMakerSuiteRequest(request, response) {
340 }340 }
341341
342 const body = getGeminiBody();342 const body = getGeminiBody();
343 console.log('Google AI Studio request:', body);343 console.debug('Google AI Studio request:', body);
344344
345 try {345 try {
346 const controller = new AbortController();346 const controller = new AbortController();
@@ -366,14 +366,14 @@ async function sendMakerSuiteRequest(request, response) {
366 // Pipe remote SSE stream to Express response366 // Pipe remote SSE stream to Express response
367 forwardFetchResponse(generateResponse, response);367 forwardFetchResponse(generateResponse, response);
368 } catch (error) {368 } catch (error) {
369 console.log('Error forwarding streaming response:', error);369 console.error('Error forwarding streaming response:', error);
370 if (!response.headersSent) {370 if (!response.headersSent) {
371 return response.status(500).send({ error: true });371 return response.status(500).send({ error: true });
372 }372 }
373 }373 }
374 } else {374 } else {
375 if (!generateResponse.ok) {375 if (!generateResponse.ok) {
376 console.log(`Google AI Studio API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);376 console.warn(`Google AI Studio API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
377 return response.status(generateResponse.status).send({ error: true });377 return response.status(generateResponse.status).send({ error: true });
378 }378 }
379379
@@ -391,12 +391,12 @@ async function sendMakerSuiteRequest(request, response) {
391 }391 }
392392
393 const responseContent = candidates[0].content ?? candidates[0].output;393 const responseContent = candidates[0].content ?? candidates[0].output;
394 console.log('Google AI Studio response:', responseContent);394 console.warn('Google AI Studio response:', responseContent);
395395
396 const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.filter(part => !part.thought)?.map(part => part.text)?.join('\n\n');396 const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.filter(part => !part.thought)?.map(part => part.text)?.join('\n\n');
397 if (!responseText) {397 if (!responseText) {
398 let message = 'Google AI Studio Candidate text empty';398 let message = 'Google AI Studio Candidate text empty';
399 console.log(message, generateResponseJson);399 console.warn(message, generateResponseJson);
400 return response.send({ error: { message } });400 return response.send({ error: { message } });
401 }401 }
402402
@@ -405,7 +405,7 @@ async function sendMakerSuiteRequest(request, response) {
405 return response.send(reply);405 return response.send(reply);
406 }406 }
407 } catch (error) {407 } catch (error) {
408 console.log('Error communicating with Google AI Studio API: ', error);408 console.error('Error communicating with Google AI Studio API: ', error);
409 if (!response.headersSent) {409 if (!response.headersSent) {
410 return response.status(500).send({ error: true });410 return response.status(500).send({ error: true });
411 }411 }
@@ -419,8 +419,9 @@ async function sendMakerSuiteRequest(request, response) {
419 */419 */
420async function sendAI21Request(request, response) {420async function sendAI21Request(request, response) {
421 if (!request.body) return response.sendStatus(400);421 if (!request.body) return response.sendStatus(400);
422
422 const controller = new AbortController();423 const controller = new AbortController();
423 console.log(request.body.messages);424 console.debug(request.body.messages);
424 request.socket.removeAllListeners('close');425 request.socket.removeAllListeners('close');
425 request.socket.on('close', function () {426 request.socket.on('close', function () {
426 controller.abort();427 controller.abort();
@@ -446,7 +447,7 @@ async function sendAI21Request(request, response) {
446 signal: controller.signal,447 signal: controller.signal,
447 };448 };
448449
449 console.log('AI21 request:', body);450 console.debug('AI21 request:', body);
450451
451 try {452 try {
452 const generateResponse = await fetch(API_AI21 + '/chat/completions', options);453 const generateResponse = await fetch(API_AI21 + '/chat/completions', options);
@@ -455,16 +456,16 @@ async function sendAI21Request(request, response) {
455 } else {456 } else {
456 if (!generateResponse.ok) {457 if (!generateResponse.ok) {
457 const errorText = await generateResponse.text();458 const errorText = await generateResponse.text();
458 console.log(`AI21 API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);459 console.warn(`AI21 API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
459 const errorJson = tryParse(errorText) ?? { error: true };460 const errorJson = tryParse(errorText) ?? { error: true };
460 return response.status(500).send(errorJson);461 return response.status(500).send(errorJson);
461 }462 }
462 const generateResponseJson = await generateResponse.json();463 const generateResponseJson = await generateResponse.json();
463 console.log('AI21 response:', generateResponseJson);464 console.debug('AI21 response:', generateResponseJson);
464 return response.send(generateResponseJson);465 return response.send(generateResponseJson);
465 }466 }
466 } catch (error) {467 } catch (error) {
467 console.log('Error communicating with AI21 API: ', error);468 console.error('Error communicating with AI21 API: ', error);
468 if (!response.headersSent) {469 if (!response.headersSent) {
469 response.send({ error: true });470 response.send({ error: true });
470 } else {471 } else {
@@ -483,7 +484,7 @@ async function sendMistralAIRequest(request, response) {
483 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MISTRALAI);484 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MISTRALAI);
484485
485 if (!apiKey) {486 if (!apiKey) {
486 console.log('MistralAI API key is missing.');487 console.warn('MistralAI API key is missing.');
487 return response.status(400).send({ error: true });488 return response.status(400).send({ error: true });
488 }489 }
489490
@@ -524,7 +525,7 @@ async function sendMistralAIRequest(request, response) {
524 timeout: 0,525 timeout: 0,
525 };526 };
526527
527 console.log('MisralAI request:', requestBody);528 console.debug('MisralAI request:', requestBody);
528529
529 const generateResponse = await fetch(apiUrl + '/chat/completions', config);530 const generateResponse = await fetch(apiUrl + '/chat/completions', config);
530 if (request.body.stream) {531 if (request.body.stream) {
@@ -532,16 +533,16 @@ async function sendMistralAIRequest(request, response) {
532 } else {533 } else {
533 if (!generateResponse.ok) {534 if (!generateResponse.ok) {
534 const errorText = await generateResponse.text();535 const errorText = await generateResponse.text();
535 console.log(`MistralAI API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);536 console.warn(`MistralAI API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
536 const errorJson = tryParse(errorText) ?? { error: true };537 const errorJson = tryParse(errorText) ?? { error: true };
537 return response.status(500).send(errorJson);538 return response.status(500).send(errorJson);
538 }539 }
539 const generateResponseJson = await generateResponse.json();540 const generateResponseJson = await generateResponse.json();
540 console.log('MistralAI response:', generateResponseJson);541 console.debug('MistralAI response:', generateResponseJson);
541 return response.send(generateResponseJson);542 return response.send(generateResponseJson);
542 }543 }
543 } catch (error) {544 } catch (error) {
544 console.log('Error communicating with MistralAI API: ', error);545 console.error('Error communicating with MistralAI API: ', error);
545 if (!response.headersSent) {546 if (!response.headersSent) {
546 response.send({ error: true });547 response.send({ error: true });
547 } else {548 } else {
@@ -564,7 +565,7 @@ async function sendCohereRequest(request, response) {
564 });565 });
565566
566 if (!apiKey) {567 if (!apiKey) {
567 console.log('Cohere API key is missing.');568 console.warn('Cohere API key is missing.');
568 return response.status(400).send({ error: true });569 return response.status(400).send({ error: true });
569 }570 }
570571
@@ -603,7 +604,7 @@ async function sendCohereRequest(request, response) {
603 requestBody.safety_mode = 'OFF';604 requestBody.safety_mode = 'OFF';
604 }605 }
605606
606 console.log('Cohere request:', requestBody);607 console.debug('Cohere request:', requestBody);
607608
608 const config = {609 const config = {
609 method: 'POST',610 method: 'POST',
@@ -625,16 +626,16 @@ async function sendCohereRequest(request, response) {
625 const generateResponse = await fetch(apiUrl, config);626 const generateResponse = await fetch(apiUrl, config);
626 if (!generateResponse.ok) {627 if (!generateResponse.ok) {
627 const errorText = await generateResponse.text();628 const errorText = await generateResponse.text();
628 console.log(`Cohere API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);629 console.warn(`Cohere API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
629 const errorJson = tryParse(errorText) ?? { error: true };630 const errorJson = tryParse(errorText) ?? { error: true };
630 return response.status(500).send(errorJson);631 return response.status(500).send(errorJson);
631 }632 }
632 const generateResponseJson = await generateResponse.json();633 const generateResponseJson = await generateResponse.json();
633 console.log('Cohere response:', generateResponseJson);634 console.debug('Cohere response:', generateResponseJson);
634 return response.send(generateResponseJson);635 return response.send(generateResponseJson);
635 }636 }
636 } catch (error) {637 } catch (error) {
637 console.log('Error communicating with Cohere API: ', error);638 console.error('Error communicating with Cohere API: ', error);
638 if (!response.headersSent) {639 if (!response.headersSent) {
639 response.send({ error: true });640 response.send({ error: true });
640 } else {641 } else {
@@ -653,7 +654,7 @@ async function sendDeepSeekRequest(request, response) {
653 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK);654 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK);
654655
655 if (!apiKey && !request.body.reverse_proxy) {656 if (!apiKey && !request.body.reverse_proxy) {
656 console.log('DeepSeek API key is missing.');657 console.warn('DeepSeek API key is missing.');
657 return response.status(400).send({ error: true });658 return response.status(400).send({ error: true });
658 }659 }
659660
@@ -774,12 +775,12 @@ router.post('/status', jsonParser, async function (request, response_getstatus_o
774 api_key_openai = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK);775 api_key_openai = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK);
775 headers = {};776 headers = {};
776 } else {777 } else {
777 console.log('This chat completion source is not supported yet.');778 console.warn('This chat completion source is not supported yet.');
778 return response_getstatus_openai.status(400).send({ error: true });779 return response_getstatus_openai.status(400).send({ error: true });
779 }780 }
780781
781 if (!api_key_openai && !request.body.reverse_proxy && request.body.chat_completion_source !== CHAT_COMPLETION_SOURCES.CUSTOM) {782 if (!api_key_openai && !request.body.reverse_proxy && request.body.chat_completion_source !== CHAT_COMPLETION_SOURCES.CUSTOM) {
782 console.log('Chat Completion API key is missing.');783 console.warn('Chat Completion API key is missing.');
783 return response_getstatus_openai.status(400).send({ error: true });784 return response_getstatus_openai.status(400).send({ error: true });
784 }785 }
785786
@@ -814,23 +815,23 @@ router.post('/status', jsonParser, async function (request, response_getstatus_o
814 };815 };
815 });816 });
816817
817 console.log('Available OpenRouter models:', models);818 console.info('Available OpenRouter models:', models);
818 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MISTRALAI) {819 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MISTRALAI) {
819 const models = data?.data;820 const models = data?.data;
820 console.log(models);821 console.info(models);
821 } else {822 } else {
822 const models = data?.data;823 const models = data?.data;
823824
824 if (Array.isArray(models)) {825 if (Array.isArray(models)) {
825 const modelIds = models.filter(x => x && typeof x === 'object').map(x => x.id).sort();826 const modelIds = models.filter(x => x && typeof x === 'object').map(x => x.id).sort();
826 console.log('Available models:', modelIds);827 console.info('Available models:', modelIds);
827 } else {828 } else {
828 console.log('Chat Completion endpoint did not return a list of models.');829 console.warn('Chat Completion endpoint did not return a list of models.');
829 }830 }
830 }831 }
831 }832 }
832 else {833 else {
833 console.log('Chat Completion status check failed. Either Access Token is incorrect or API endpoint is down.');834 console.error('Chat Completion status check failed. Either Access Token is incorrect or API endpoint is down.');
834 response_getstatus_openai.send({ error: true, can_bypass: true, data: { data: [] } });835 response_getstatus_openai.send({ error: true, can_bypass: true, data: { data: [] } });
835 }836 }
836 } catch (e) {837 } catch (e) {
@@ -863,7 +864,7 @@ router.post('/bias', jsonParser, async function (request, response) {
863 const tokenizer = getSentencepiceTokenizer(model);864 const tokenizer = getSentencepiceTokenizer(model);
864 const instance = await tokenizer?.get();865 const instance = await tokenizer?.get();
865 if (!instance) {866 if (!instance) {
866 console.warn('Tokenizer not initialized:', model);867 console.error('Tokenizer not initialized:', model);
867 return response.send({});868 return response.send({});
868 }869 }
869 encodeFunction = (text) => new Uint32Array(instance.encodeIds(text));870 encodeFunction = (text) => new Uint32Array(instance.encodeIds(text));
@@ -1025,7 +1026,7 @@ router.post('/generate', jsonParser, function (request, response) {
1025 mergeObjectWithYaml(headers, request.body.custom_include_headers);1026 mergeObjectWithYaml(headers, request.body.custom_include_headers);
10261027
1027 if (request.body.custom_prompt_post_processing) {1028 if (request.body.custom_prompt_post_processing) {
1028 console.log('Applying custom prompt post-processing of type', request.body.custom_prompt_post_processing);1029 console.info('Applying custom prompt post-processing of type', request.body.custom_prompt_post_processing);
1029 request.body.messages = postProcessPrompt(1030 request.body.messages = postProcessPrompt(
1030 request.body.messages,1031 request.body.messages,
1031 request.body.custom_prompt_post_processing,1032 request.body.custom_prompt_post_processing,
@@ -1058,12 +1059,12 @@ router.post('/generate', jsonParser, function (request, response) {
1058 headers = {};1059 headers = {};
1059 bodyParams = {};1060 bodyParams = {};
1060 } else {1061 } else {
1061 console.log('This chat completion source is not supported yet.');1062 console.warn('This chat completion source is not supported yet.');
1062 return response.status(400).send({ error: true });1063 return response.status(400).send({ error: true });
1063 }1064 }
10641065
1065 if (!apiKey && !request.body.reverse_proxy && request.body.chat_completion_source !== CHAT_COMPLETION_SOURCES.CUSTOM) {1066 if (!apiKey && !request.body.reverse_proxy && request.body.chat_completion_source !== CHAT_COMPLETION_SOURCES.CUSTOM) {
1066 console.log('OpenAI API key is missing.');1067 console.warn('OpenAI API key is missing.');
1067 return response.status(400).send({ error: true });1068 return response.status(400).send({ error: true });
1068 }1069 }
10691070
@@ -1123,7 +1124,7 @@ router.post('/generate', jsonParser, function (request, response) {
1123 signal: controller.signal,1124 signal: controller.signal,
1124 };1125 };
11251126
1126 console.log(requestBody);1127 console.debug(requestBody);
11271128
1128 makeRequest(config, response, request);1129 makeRequest(config, response, request);
11291130
@@ -1140,7 +1141,7 @@ router.post('/generate', jsonParser, function (request, response) {
1140 const fetchResponse = await fetch(endpointUrl, config);1141 const fetchResponse = await fetch(endpointUrl, config);
11411142
1142 if (request.body.stream) {1143 if (request.body.stream) {
1143 console.log('Streaming request in progress');1144 console.info('Streaming request in progress');
1144 forwardFetchResponse(fetchResponse, response);1145 forwardFetchResponse(fetchResponse, response);
1145 return;1146 return;
1146 }1147 }
@@ -1149,10 +1150,10 @@ router.post('/generate', jsonParser, function (request, response) {
1149 /** @type {any} */1150 /** @type {any} */
1150 let json = await fetchResponse.json();1151 let json = await fetchResponse.json();
1151 response.send(json);1152 response.send(json);
1152 console.log(json);1153 console.debug(json);
1153 console.log(json?.choices?.[0]?.message);1154 console.debug(json?.choices?.[0]?.message);
1154 } else if (fetchResponse.status === 429 && retries > 0) {1155 } else if (fetchResponse.status === 429 && retries > 0) {
1155 console.log(`Out of quota, retrying in ${Math.round(timeout / 1000)}s`);1156 console.warn(`Out of quota, retrying in ${Math.round(timeout / 1000)}s`);
1156 setTimeout(() => {1157 setTimeout(() => {
1157 timeout *= 2;1158 timeout *= 2;
1158 makeRequest(config, response, request, retries - 1, timeout);1159 makeRequest(config, response, request, retries - 1, timeout);
@@ -1161,7 +1162,7 @@ router.post('/generate', jsonParser, function (request, response) {
1161 await handleErrorResponse(fetchResponse);1162 await handleErrorResponse(fetchResponse);
1162 }1163 }
1163 } catch (error) {1164 } catch (error) {
1164 console.log('Generation failed', error);1165 console.error('Generation failed', error);
1165 const message = error.code === 'ECONNREFUSED'1166 const message = error.code === 'ECONNREFUSED'
1166 ? `Connection refused: ${error.message}`1167 ? `Connection refused: ${error.message}`
1167 : error.message || 'Unknown error occurred';1168 : error.message || 'Unknown error occurred';
@@ -1183,7 +1184,7 @@ router.post('/generate', jsonParser, function (request, response) {
11831184
1184 const message = errorResponse.statusText || 'Unknown error occurred';1185 const message = errorResponse.statusText || 'Unknown error occurred';
1185 const quota_error = errorResponse.status === 429 && errorData?.error?.type === 'insufficient_quota';1186 const quota_error = errorResponse.status === 429 && errorData?.error?.type === 'insufficient_quota';
1186 console.log('Chat completion request error: ', message, responseText);1187 console.error('Chat completion request error: ', message, responseText);
11871188
1188 if (!response.headersSent) {1189 if (!response.headersSent) {
1189 response.send({ error: { message }, quota_error: quota_error });1190 response.send({ error: { message }, quota_error: quota_error });
src/endpoints/backends/kobold.js+14 -14
@@ -22,17 +22,17 @@ router.post('/generate', jsonParser, async function (request, response_generate)
22 request.socket.on('close', async function () {22 request.socket.on('close', async function () {
23 if (request.body.can_abort && !response_generate.writableEnded) {23 if (request.body.can_abort && !response_generate.writableEnded) {
24 try {24 try {
25 console.log('Aborting Kobold generation...');25 console.info('Aborting Kobold generation...');
26 // send abort signal to koboldcpp26 // send abort signal to koboldcpp
27 const abortResponse = await fetch(`${request.body.api_server}/extra/abort`, {27 const abortResponse = await fetch(`${request.body.api_server}/extra/abort`, {
28 method: 'POST',28 method: 'POST',
29 });29 });
3030
31 if (!abortResponse.ok) {31 if (!abortResponse.ok) {
32 console.log('Error sending abort request to Kobold:', abortResponse.status);32 console.error('Error sending abort request to Kobold:', abortResponse.status);
33 }33 }
34 } catch (error) {34 } catch (error) {
35 console.log(error);35 console.error(error);
36 }36 }
37 }37 }
38 controller.abort();38 controller.abort();
@@ -81,7 +81,7 @@ router.post('/generate', jsonParser, async function (request, response_generate)
81 }81 }
82 }82 }
8383
84 console.log(this_settings);84 console.debug(this_settings);
85 const args = {85 const args = {
86 body: JSON.stringify(this_settings),86 body: JSON.stringify(this_settings),
87 headers: Object.assign(87 headers: Object.assign(
@@ -105,7 +105,7 @@ router.post('/generate', jsonParser, async function (request, response_generate)
105 } else {105 } else {
106 if (!response.ok) {106 if (!response.ok) {
107 const errorText = await response.text();107 const errorText = await response.text();
108 console.log(`Kobold returned error: ${response.status} ${response.statusText} ${errorText}`);108 console.warn(`Kobold returned error: ${response.status} ${response.statusText} ${errorText}`);
109109
110 try {110 try {
111 const errorJson = JSON.parse(errorText);111 const errorJson = JSON.parse(errorText);
@@ -117,7 +117,7 @@ router.post('/generate', jsonParser, async function (request, response_generate)
117 }117 }
118118
119 const data = await response.json();119 const data = await response.json();
120 console.log('Endpoint response:', data);120 console.debug('Endpoint response:', data);
121 return response_generate.send(data);121 return response_generate.send(data);
122 }122 }
123 } catch (error) {123 } catch (error) {
@@ -125,19 +125,19 @@ router.post('/generate', jsonParser, async function (request, response_generate)
125 switch (error?.status) {125 switch (error?.status) {
126 case 403:126 case 403:
127 case 503: // retry in case of temporary service issue, possibly caused by a queue failure?127 case 503: // retry in case of temporary service issue, possibly caused by a queue failure?
128 console.debug(`KoboldAI is busy. Retry attempt ${i + 1} of ${MAX_RETRIES}...`);128 console.warn(`KoboldAI is busy. Retry attempt ${i + 1} of ${MAX_RETRIES}...`);
129 await delay(delayAmount);129 await delay(delayAmount);
130 break;130 break;
131 default:131 default:
132 if ('status' in error) {132 if ('status' in error) {
133 console.log('Status Code from Kobold:', error.status);133 console.error('Status Code from Kobold:', error.status);
134 }134 }
135 return response_generate.send({ error: true });135 return response_generate.send({ error: true });
136 }136 }
137 }137 }
138 }138 }
139139
140 console.log('Max retries exceeded. Giving up.');140 console.error('Max retries exceeded. Giving up.');
141 return response_generate.send({ error: true });141 return response_generate.send({ error: true });
142});142});
143143
@@ -193,16 +193,16 @@ router.post('/transcribe-audio', urlencodedParser, async function (request, resp
193 const server = request.body.server;193 const server = request.body.server;
194194
195 if (!server) {195 if (!server) {
196 console.log('Server is not set');196 console.error('Server is not set');
197 return response.sendStatus(400);197 return response.sendStatus(400);
198 }198 }
199199
200 if (!request.file) {200 if (!request.file) {
201 console.log('No audio file found');201 console.error('No audio file found');
202 return response.sendStatus(400);202 return response.sendStatus(400);
203 }203 }
204204
205 console.log('Transcribing audio with KoboldCpp', server);205 console.debug('Transcribing audio with KoboldCpp', server);
206206
207 const fileBase64 = fs.readFileSync(request.file.path).toString('base64');207 const fileBase64 = fs.readFileSync(request.file.path).toString('base64');
208 fs.rmSync(request.file.path);208 fs.rmSync(request.file.path);
@@ -226,12 +226,12 @@ router.post('/transcribe-audio', urlencodedParser, async function (request, resp
226226
227 if (!result.ok) {227 if (!result.ok) {
228 const text = await result.text();228 const text = await result.text();
229 console.log('KoboldCpp request failed', result.statusText, text);229 console.error('KoboldCpp request failed', result.statusText, text);
230 return response.status(500).send(text);230 return response.status(500).send(text);
231 }231 }
232232
233 const data = await result.json();233 const data = await result.json();
234 console.log('KoboldCpp transcription response', data);234 console.debug('KoboldCpp transcription response', data);
235 return response.json(data);235 return response.json(data);
236 } catch (error) {236 } catch (error) {
237 console.error('KoboldCpp transcription failed', error);237 console.error('KoboldCpp transcription failed', error);
src/endpoints/backends/scale-alt.js+5 -5
@@ -13,7 +13,7 @@ router.post('/generate', jsonParser, async function (request, response) {
13 const cookie = readSecret(request.user.directories, SECRET_KEYS.SCALE_COOKIE);13 const cookie = readSecret(request.user.directories, SECRET_KEYS.SCALE_COOKIE);
1414
15 if (!cookie) {15 if (!cookie) {
16 console.log('No Scale cookie found');16 console.error('No Scale cookie found');
17 return response.sendStatus(400);17 return response.sendStatus(400);
18 }18 }
1919
@@ -62,7 +62,7 @@ router.post('/generate', jsonParser, async function (request, response) {
62 },62 },
63 };63 };
6464
65 console.log('Scale request:', body);65 console.debug('Scale request:', body);
6666
67 const result = await fetch('https://dashboard.scale.com/spellbook/api/trpc/v2.variant.run', {67 const result = await fetch('https://dashboard.scale.com/spellbook/api/trpc/v2.variant.run', {
68 method: 'POST',68 method: 'POST',
@@ -75,7 +75,7 @@ router.post('/generate', jsonParser, async function (request, response) {
7575
76 if (!result.ok) {76 if (!result.ok) {
77 const text = await result.text();77 const text = await result.text();
78 console.log('Scale request failed', result.statusText, text);78 console.error('Scale request failed', result.statusText, text);
79 return response.status(500).send({ error: { message: result.statusText } });79 return response.status(500).send({ error: { message: result.statusText } });
80 }80 }
8181
@@ -83,7 +83,7 @@ router.post('/generate', jsonParser, async function (request, response) {
83 const data = await result.json();83 const data = await result.json();
84 const output = data?.result?.data?.json?.outputs?.[0] || '';84 const output = data?.result?.data?.json?.outputs?.[0] || '';
8585
86 console.log('Scale response:', data);86 console.debug('Scale response:', data);
8787
88 if (!output) {88 if (!output) {
89 console.warn('Scale response is empty');89 console.warn('Scale response is empty');
@@ -92,7 +92,7 @@ router.post('/generate', jsonParser, async function (request, response) {
9292
93 return response.json({ output });93 return response.json({ output });
94 } catch (error) {94 } catch (error) {
95 console.log(error);95 console.error(error);
96 return response.sendStatus(500);96 return response.sendStatus(500);
97 }97 }
98});98});
src/endpoints/backends/text-completions.js+39 -45
@@ -58,12 +58,12 @@ async function parseOllamaStream(jsonStream, request, response) {
58 });58 });
5959
60 jsonStream.body.on('end', () => {60 jsonStream.body.on('end', () => {
61 console.log('Streaming request finished');61 console.info('Streaming request finished');
62 response.write('data: [DONE]\n\n');62 response.write('data: [DONE]\n\n');
63 response.end();63 response.end();
64 });64 });
65 } catch (error) {65 } catch (error) {
66 console.log('Error forwarding streaming response:', error);66 console.error('Error forwarding streaming response:', error);
67 if (!response.headersSent) {67 if (!response.headersSent) {
68 return response.status(500).send({ error: true });68 return response.status(500).send({ error: true });
69 } else {69 } else {
@@ -79,16 +79,16 @@ async function parseOllamaStream(jsonStream, request, response) {
79 */79 */
80async function abortKoboldCppRequest(url) {80async function abortKoboldCppRequest(url) {
81 try {81 try {
82 console.log('Aborting Kobold generation...');82 console.info('Aborting Kobold generation...');
83 const abortResponse = await fetch(`${url}/api/extra/abort`, {83 const abortResponse = await fetch(`${url}/api/extra/abort`, {
84 method: 'POST',84 method: 'POST',
85 });85 });
8686
87 if (!abortResponse.ok) {87 if (!abortResponse.ok) {
88 console.log('Error sending abort request to Kobold:', abortResponse.status, abortResponse.statusText);88 console.error('Error sending abort request to Kobold:', abortResponse.status, abortResponse.statusText);
89 }89 }
90 } catch (error) {90 } catch (error) {
91 console.log(error);91 console.error(error);
92 }92 }
93}93}
9494
@@ -101,7 +101,7 @@ router.post('/status', jsonParser, async function (request, response) {
101 request.body.api_server = request.body.api_server.replace('localhost', '127.0.0.1');101 request.body.api_server = request.body.api_server.replace('localhost', '127.0.0.1');
102 }102 }
103103
104 console.log('Trying to connect to API:', request.body);104 console.debug('Trying to connect to API', request.body);
105 const baseUrl = trimV1(request.body.api_server);105 const baseUrl = trimV1(request.body.api_server);
106106
107 const args = {107 const args = {
@@ -123,6 +123,7 @@ router.post('/status', jsonParser, async function (request, response) {
123 case TEXTGEN_TYPES.LLAMACPP:123 case TEXTGEN_TYPES.LLAMACPP:
124 case TEXTGEN_TYPES.INFERMATICAI:124 case TEXTGEN_TYPES.INFERMATICAI:
125 case TEXTGEN_TYPES.OPENROUTER:125 case TEXTGEN_TYPES.OPENROUTER:
126 case TEXTGEN_TYPES.FEATHERLESS:
126 url += '/v1/models';127 url += '/v1/models';
127 break;128 break;
128 case TEXTGEN_TYPES.DREAMGEN:129 case TEXTGEN_TYPES.DREAMGEN:
@@ -140,9 +141,6 @@ router.post('/status', jsonParser, async function (request, response) {
140 case TEXTGEN_TYPES.OLLAMA:141 case TEXTGEN_TYPES.OLLAMA:
141 url += '/api/tags';142 url += '/api/tags';
142 break;143 break;
143 case TEXTGEN_TYPES.FEATHERLESS:
144 url += '/v1/models';
145 break;
146 case TEXTGEN_TYPES.HUGGINGFACE:144 case TEXTGEN_TYPES.HUGGINGFACE:
147 url += '/info';145 url += '/info';
148 break;146 break;
@@ -152,7 +150,7 @@ router.post('/status', jsonParser, async function (request, response) {
152 const isPossiblyLmStudio = modelsReply.headers.get('x-powered-by') === 'Express';150 const isPossiblyLmStudio = modelsReply.headers.get('x-powered-by') === 'Express';
153151
154 if (!modelsReply.ok) {152 if (!modelsReply.ok) {
155 console.log('Models endpoint is offline.');153 console.error('Models endpoint is offline.');
156 return response.sendStatus(400);154 return response.sendStatus(400);
157 }155 }
158156
@@ -173,12 +171,12 @@ router.post('/status', jsonParser, async function (request, response) {
173 }171 }
174172
175 if (!Array.isArray(data.data)) {173 if (!Array.isArray(data.data)) {
176 console.log('Models response is not an array.');174 console.error('Models response is not an array.');
177 return response.sendStatus(400);175 return response.sendStatus(400);
178 }176 }
179177
180 const modelIds = data.data.map(x => x.id);178 const modelIds = data.data.map(x => x.id);
181 console.log('Models available:', modelIds);179 console.info('Models available:', modelIds);
182180
183 // Set result to the first model ID181 // Set result to the first model ID
184 result = modelIds[0] || 'Valid';182 result = modelIds[0] || 'Valid';
@@ -191,7 +189,7 @@ router.post('/status', jsonParser, async function (request, response) {
191 if (modelInfoReply.ok) {189 if (modelInfoReply.ok) {
192 /** @type {any} */190 /** @type {any} */
193 const modelInfo = await modelInfoReply.json();191 const modelInfo = await modelInfoReply.json();
194 console.log('Ooba model info:', modelInfo);192 console.debug('Ooba model info:', modelInfo);
195193
196 const modelName = modelInfo?.model_name;194 const modelName = modelInfo?.model_name;
197 result = modelName || result;195 result = modelName || result;
@@ -208,7 +206,7 @@ router.post('/status', jsonParser, async function (request, response) {
208 if (modelInfoReply.ok) {206 if (modelInfoReply.ok) {
209 /** @type {any} */207 /** @type {any} */
210 const modelInfo = await modelInfoReply.json();208 const modelInfo = await modelInfoReply.json();
211 console.log('Tabby model info:', modelInfo);209 console.debug('Tabby model info:', modelInfo);
212210
213 const modelName = modelInfo?.id;211 const modelName = modelInfo?.id;
214 result = modelName || result;212 result = modelName || result;
@@ -255,7 +253,7 @@ router.post('/props', jsonParser, async function (request, response) {
255 props['chat_template'] = props['chat_template'].slice(0, -1) + '\n';253 props['chat_template'] = props['chat_template'].slice(0, -1) + '\n';
256 }254 }
257 props['chat_template_hash'] = createHash('sha256').update(props['chat_template']).digest('hex');255 props['chat_template_hash'] = createHash('sha256').update(props['chat_template']).digest('hex');
258 console.log(`Model properties: ${JSON.stringify(props)}`);256 console.debug(`Model properties: ${JSON.stringify(props)}`);
259 return response.send(props);257 return response.send(props);
260 } catch (error) {258 } catch (error) {
261 console.error(error);259 console.error(error);
@@ -273,7 +271,7 @@ router.post('/generate', jsonParser, async function (request, response) {
273271
274 const apiType = request.body.api_type;272 const apiType = request.body.api_type;
275 const baseUrl = request.body.api_server;273 const baseUrl = request.body.api_server;
276 console.log(request.body);274 console.debug(request.body);
277275
278 const controller = new AbortController();276 const controller = new AbortController();
279 request.socket.removeAllListeners('close');277 request.socket.removeAllListeners('close');
@@ -399,7 +397,7 @@ router.post('/generate', jsonParser, async function (request, response) {
399 if (completionsReply.ok) {397 if (completionsReply.ok) {
400 /** @type {any} */398 /** @type {any} */
401 const data = await completionsReply.json();399 const data = await completionsReply.json();
402 console.log('Endpoint response:', data);400 console.debug('Endpoint response:', data);
403401
404 // Map InfermaticAI response to OAI completions format402 // Map InfermaticAI response to OAI completions format
405 if (apiType === TEXTGEN_TYPES.INFERMATICAI) {403 if (apiType === TEXTGEN_TYPES.INFERMATICAI) {
@@ -411,24 +409,20 @@ router.post('/generate', jsonParser, async function (request, response) {
411 const text = await completionsReply.text();409 const text = await completionsReply.text();
412 const errorBody = { error: true, status: completionsReply.status, response: text };410 const errorBody = { error: true, status: completionsReply.status, response: text };
413411
414 if (!response.headersSent) {412 return !response.headersSent
415 return response.send(errorBody);413 ? response.send(errorBody)
416 }414 : response.end();
417
418 return response.end();
419 }415 }
420 }416 }
421 } catch (error) {417 } catch (error) {
422 const status = error?.status ?? error?.code ?? 'UNKNOWN';418 const status = error?.status ?? error?.code ?? 'UNKNOWN';
423 const text = error?.error ?? error?.statusText ?? error?.message ?? 'Unknown error on /generate endpoint';419 const text = error?.error ?? error?.statusText ?? error?.message ?? 'Unknown error on /generate endpoint';
424 let value = { error: true, status: status, response: text };420 let value = { error: true, status: status, response: text };
425 console.log('Endpoint error:', error);421 console.error('Endpoint error:', error);
426
427 if (!response.headersSent) {
428 return response.send(value);
429 }
430422
431 return response.end();423 return !response.headersSent
424 ? response.send(value)
425 : response.end();
432 }426 }
433});427});
434428
@@ -451,7 +445,7 @@ ollama.post('/download', jsonParser, async function (request, response) {
451 });445 });
452446
453 if (!fetchResponse.ok) {447 if (!fetchResponse.ok) {
454 console.log('Download error:', fetchResponse.status, fetchResponse.statusText);448 console.error('Download error:', fetchResponse.status, fetchResponse.statusText);
455 return response.status(fetchResponse.status).send({ error: true });449 return response.status(fetchResponse.status).send({ error: true });
456 }450 }
457451
@@ -468,7 +462,7 @@ ollama.post('/caption-image', jsonParser, async function (request, response) {
468 return response.sendStatus(400);462 return response.sendStatus(400);
469 }463 }
470464
471 console.log('Ollama caption request:', request.body);465 console.debug('Ollama caption request:', request.body);
472 const baseUrl = trimV1(request.body.server_url);466 const baseUrl = trimV1(request.body.server_url);
473467
474 const fetchResponse = await fetch(`${baseUrl}/api/generate`, {468 const fetchResponse = await fetch(`${baseUrl}/api/generate`, {
@@ -483,18 +477,18 @@ ollama.post('/caption-image', jsonParser, async function (request, response) {
483 });477 });
484478
485 if (!fetchResponse.ok) {479 if (!fetchResponse.ok) {
486 console.log('Ollama caption error:', fetchResponse.status, fetchResponse.statusText);480 console.error('Ollama caption error:', fetchResponse.status, fetchResponse.statusText);
487 return response.status(500).send({ error: true });481 return response.status(500).send({ error: true });
488 }482 }
489483
490 /** @type {any} */484 /** @type {any} */
491 const data = await fetchResponse.json();485 const data = await fetchResponse.json();
492 console.log('Ollama caption response:', data);486 console.debug('Ollama caption response:', data);
493487
494 const caption = data?.response || '';488 const caption = data?.response || '';
495489
496 if (!caption) {490 if (!caption) {
497 console.log('Ollama caption is empty.');491 console.error('Ollama caption is empty.');
498 return response.status(500).send({ error: true });492 return response.status(500).send({ error: true });
499 }493 }
500494
@@ -513,7 +507,7 @@ llamacpp.post('/caption-image', jsonParser, async function (request, response) {
513 return response.sendStatus(400);507 return response.sendStatus(400);
514 }508 }
515509
516 console.log('LlamaCpp caption request:', request.body);510 console.debug('LlamaCpp caption request:', request.body);
517 const baseUrl = trimV1(request.body.server_url);511 const baseUrl = trimV1(request.body.server_url);
518512
519 const fetchResponse = await fetch(`${baseUrl}/completion`, {513 const fetchResponse = await fetch(`${baseUrl}/completion`, {
@@ -529,18 +523,18 @@ llamacpp.post('/caption-image', jsonParser, async function (request, response) {
529 });523 });
530524
531 if (!fetchResponse.ok) {525 if (!fetchResponse.ok) {
532 console.log('LlamaCpp caption error:', fetchResponse.status, fetchResponse.statusText);526 console.error('LlamaCpp caption error:', fetchResponse.status, fetchResponse.statusText);
533 return response.status(500).send({ error: true });527 return response.status(500).send({ error: true });
534 }528 }
535529
536 /** @type {any} */530 /** @type {any} */
537 const data = await fetchResponse.json();531 const data = await fetchResponse.json();
538 console.log('LlamaCpp caption response:', data);532 console.debug('LlamaCpp caption response:', data);
539533
540 const caption = data?.content || '';534 const caption = data?.content || '';
541535
542 if (!caption) {536 if (!caption) {
543 console.log('LlamaCpp caption is empty.');537 console.error('LlamaCpp caption is empty.');
544 return response.status(500).send({ error: true });538 return response.status(500).send({ error: true });
545 }539 }
546540
@@ -558,7 +552,7 @@ llamacpp.post('/props', jsonParser, async function (request, response) {
558 return response.sendStatus(400);552 return response.sendStatus(400);
559 }553 }
560554
561 console.log('LlamaCpp props request:', request.body);555 console.debug('LlamaCpp props request:', request.body);
562 const baseUrl = trimV1(request.body.server_url);556 const baseUrl = trimV1(request.body.server_url);
563557
564 const fetchResponse = await fetch(`${baseUrl}/props`, {558 const fetchResponse = await fetch(`${baseUrl}/props`, {
@@ -566,12 +560,12 @@ llamacpp.post('/props', jsonParser, async function (request, response) {
566 });560 });
567561
568 if (!fetchResponse.ok) {562 if (!fetchResponse.ok) {
569 console.log('LlamaCpp props error:', fetchResponse.status, fetchResponse.statusText);563 console.error('LlamaCpp props error:', fetchResponse.status, fetchResponse.statusText);
570 return response.status(500).send({ error: true });564 return response.status(500).send({ error: true });
571 }565 }
572566
573 const data = await fetchResponse.json();567 const data = await fetchResponse.json();
574 console.log('LlamaCpp props response:', data);568 console.debug('LlamaCpp props response:', data);
575569
576 return response.send(data);570 return response.send(data);
577571
@@ -590,7 +584,7 @@ llamacpp.post('/slots', jsonParser, async function (request, response) {
590 return response.sendStatus(400);584 return response.sendStatus(400);
591 }585 }
592586
593 console.log('LlamaCpp slots request:', request.body);587 console.debug('LlamaCpp slots request:', request.body);
594 const baseUrl = trimV1(request.body.server_url);588 const baseUrl = trimV1(request.body.server_url);
595589
596 let fetchResponse;590 let fetchResponse;
@@ -616,12 +610,12 @@ llamacpp.post('/slots', jsonParser, async function (request, response) {
616 }610 }
617611
618 if (!fetchResponse.ok) {612 if (!fetchResponse.ok) {
619 console.log('LlamaCpp slots error:', fetchResponse.status, fetchResponse.statusText);613 console.error('LlamaCpp slots error:', fetchResponse.status, fetchResponse.statusText);
620 return response.status(500).send({ error: true });614 return response.status(500).send({ error: true });
621 }615 }
622616
623 const data = await fetchResponse.json();617 const data = await fetchResponse.json();
624 console.log('LlamaCpp slots response:', data);618 console.debug('LlamaCpp slots response:', data);
625619
626 return response.send(data);620 return response.send(data);
627621
@@ -659,14 +653,14 @@ tabby.post('/download', jsonParser, async function (request, response) {
659 return response.status(403).send({ error: true });653 return response.status(403).send({ error: true });
660 }654 }
661 } else {655 } else {
662 console.log('API Permission error:', permissionResponse.status, permissionResponse.statusText);656 console.error('API Permission error:', permissionResponse.status, permissionResponse.statusText);
663 return response.status(permissionResponse.status).send({ error: true });657 return response.status(permissionResponse.status).send({ error: true });
664 }658 }
665659
666 const fetchResponse = await fetch(`${baseUrl}/v1/download`, args);660 const fetchResponse = await fetch(`${baseUrl}/v1/download`, args);
667661
668 if (!fetchResponse.ok) {662 if (!fetchResponse.ok) {
669 console.log('Download error:', fetchResponse.status, fetchResponse.statusText);663 console.error('Download error:', fetchResponse.status, fetchResponse.statusText);
670 return response.status(fetchResponse.status).send({ error: true });664 return response.status(fetchResponse.status).send({ error: true });
671 }665 }
672666
src/endpoints/backgrounds.js+3 -3
@@ -27,7 +27,7 @@ router.post('/delete', jsonParser, getFileNameValidationFunction('bg'), function
27 const fileName = path.join(request.user.directories.backgrounds, sanitize(request.body.bg));27 const fileName = path.join(request.user.directories.backgrounds, sanitize(request.body.bg));
2828
29 if (!fs.existsSync(fileName)) {29 if (!fs.existsSync(fileName)) {
30 console.log('BG file not found');30 console.error('BG file not found');
31 return response.sendStatus(400);31 return response.sendStatus(400);
32 }32 }
3333
@@ -43,12 +43,12 @@ router.post('/rename', jsonParser, function (request, response) {
43 const newFileName = path.join(request.user.directories.backgrounds, sanitize(request.body.new_bg));43 const newFileName = path.join(request.user.directories.backgrounds, sanitize(request.body.new_bg));
4444
45 if (!fs.existsSync(oldFileName)) {45 if (!fs.existsSync(oldFileName)) {
46 console.log('BG file not found');46 console.error('BG file not found');
47 return response.sendStatus(400);47 return response.sendStatus(400);
48 }48 }
4949
50 if (fs.existsSync(newFileName)) {50 if (fs.existsSync(newFileName)) {
51 console.log('New BG file already exists');51 console.error('New BG file already exists');
52 return response.sendStatus(400);52 return response.sendStatus(400);
53 }53 }
5454
src/endpoints/caption.js+4 -4
@@ -2,10 +2,10 @@ import express from 'express';
2import { jsonParser } from '../express-common.js';2import { jsonParser } from '../express-common.js';
3import { getPipeline, getRawImage } from '../transformers.js';3import { getPipeline, getRawImage } from '../transformers.js';
44
5const TASK = 'image-to-text';
6
7export const router = express.Router();5export const router = express.Router();
86
7const TASK = 'image-to-text';
8
9router.post('/', jsonParser, async (req, res) => {9router.post('/', jsonParser, async (req, res) => {
10 try {10 try {
11 const { image } = req.body;11 const { image } = req.body;
@@ -13,14 +13,14 @@ router.post('/', jsonParser, async (req, res) => {
13 const rawImage = await getRawImage(image);13 const rawImage = await getRawImage(image);
1414
15 if (!rawImage) {15 if (!rawImage) {
16 console.log('Failed to parse captioned image');16 console.warn('Failed to parse captioned image');
17 return res.sendStatus(400);17 return res.sendStatus(400);
18 }18 }
1919
20 const pipe = await getPipeline(TASK);20 const pipe = await getPipeline(TASK);
21 const result = await pipe(rawImage);21 const result = await pipe(rawImage);
22 const text = result[0].generated_text;22 const text = result[0].generated_text;
23 console.log('Image caption:', text);23 console.info('Image caption:', text);
2424
25 return res.json({ caption: text });25 return res.json({ caption: text });
26 } catch (error) {26 } catch (error) {
src/endpoints/characters.js+34 -35
@@ -97,7 +97,7 @@ async function writeCharacterData(inputFile, data, outputFile, request, crop = u
97 writeFileAtomicSync(outputImagePath, outputImage);97 writeFileAtomicSync(outputImagePath, outputImage);
98 return true;98 return true;
99 } catch (err) {99 } catch (err) {
100 console.log(err);100 console.error(err);
101 return false;101 return false;
102 }102 }
103}103}
@@ -166,7 +166,7 @@ async function tryReadImage(imgPath, crop) {
166 }166 }
167 // If it's an unsupported type of image (APNG) - just read the file as buffer167 // If it's an unsupported type of image (APNG) - just read the file as buffer
168 catch (error) {168 catch (error) {
169 console.log(`Failed to read image: ${imgPath}`, error);169 console.error(`Failed to read image: ${imgPath}`, error);
170 return fs.readFileSync(imgPath);170 return fs.readFileSync(imgPath);
171 }171 }
172}172}
@@ -229,12 +229,12 @@ const processCharacter = async (item, directories) => {
229 return character;229 return character;
230 }230 }
231 catch (err) {231 catch (err) {
232 console.log(`Could not process character: ${item}`);232 console.error(`Could not process character: ${item}`);
233233
234 if (err instanceof SyntaxError) {234 if (err instanceof SyntaxError) {
235 console.log(`${item} does not contain a valid JSON object.`);235 console.error(`${item} does not contain a valid JSON object.`);
236 } else {236 } else {
237 console.log('An unexpected error occurred: ', err);237 console.error('An unexpected error occurred: ', err);
238 }238 }
239239
240 return {240 return {
@@ -322,7 +322,7 @@ function readFromV2(char) {
322 };322 };
323323
324 _.forEach(fieldMappings, (v2Path, charField) => {324 _.forEach(fieldMappings, (v2Path, charField) => {
325 //console.log(`Migrating field: ${charField} from ${v2Path}`);325 //console.info(`Migrating field: ${charField} from ${v2Path}`);
326 const v2Value = _.get(char.data, v2Path);326 const v2Value = _.get(char.data, v2Path);
327 if (_.isUndefined(v2Value)) {327 if (_.isUndefined(v2Value)) {
328 let defaultValue = undefined;328 let defaultValue = undefined;
@@ -337,15 +337,15 @@ function readFromV2(char) {
337 }337 }
338338
339 if (!_.isUndefined(defaultValue)) {339 if (!_.isUndefined(defaultValue)) {
340 //console.debug(`Spec v2 extension data missing for field: ${charField}, using default value: ${defaultValue}`);340 //console.warn(`Spec v2 extension data missing for field: ${charField}, using default value: ${defaultValue}`);
341 char[charField] = defaultValue;341 char[charField] = defaultValue;
342 } else {342 } else {
343 console.debug(`Char ${char['name']} has Spec v2 data missing for unknown field: ${charField}`);343 console.warn(`Char ${char['name']} has Spec v2 data missing for unknown field: ${charField}`);
344 return;344 return;
345 }345 }
346 }346 }
347 if (!_.isUndefined(char[charField]) && !_.isUndefined(v2Value) && String(char[charField]) !== String(v2Value)) {347 if (!_.isUndefined(char[charField]) && !_.isUndefined(v2Value) && String(char[charField]) !== String(v2Value)) {
348 console.debug(`Char ${char['name']} has Spec v2 data mismatch with Spec v1 for field: ${charField}`, char[charField], v2Value);348 console.warn(`Char ${char['name']} has Spec v2 data mismatch with Spec v1 for field: ${charField}`, char[charField], v2Value);
349 }349 }
350 char[charField] = v2Value;350 char[charField] = v2Value;
351 });351 });
@@ -442,7 +442,7 @@ function charaFormatData(data, directories) {
442 }442 }
443443
444 } catch {444 } catch {
445 console.debug(`Failed to read world info file: ${data.world}. Character book will not be available.`);445 console.warn(`Failed to read world info file: ${data.world}. Character book will not be available.`);
446 }446 }
447 }447 }
448448
@@ -452,7 +452,7 @@ function charaFormatData(data, directories) {
452 // Deep merge the extensions object452 // Deep merge the extensions object
453 _.set(char, 'data.extensions', deepMerge(char.data.extensions, extensions));453 _.set(char, 'data.extensions', deepMerge(char.data.extensions, extensions));
454 } catch {454 } catch {
455 console.debug(`Failed to parse extensions JSON: ${data.extensions}`);455 console.warn(`Failed to parse extensions JSON: ${data.extensions}`);
456 }456 }
457 }457 }
458458
@@ -526,7 +526,7 @@ async function importFromYaml(uploadPath, context, preservedFileName) {
526 const fileText = fs.readFileSync(uploadPath, 'utf8');526 const fileText = fs.readFileSync(uploadPath, 'utf8');
527 fs.rmSync(uploadPath);527 fs.rmSync(uploadPath);
528 const yamlData = yaml.parse(fileText);528 const yamlData = yaml.parse(fileText);
529 console.log('Importing from YAML');529 console.info('Importing from YAML');
530 yamlData.name = sanitize(yamlData.name);530 yamlData.name = sanitize(yamlData.name);
531 const fileName = preservedFileName || getPngName(yamlData.name, context.request.user.directories);531 const fileName = preservedFileName || getPngName(yamlData.name, context.request.user.directories);
532 let char = convertToV2({532 let char = convertToV2({
@@ -559,7 +559,7 @@ async function importFromYaml(uploadPath, context, preservedFileName) {
559async function importFromCharX(uploadPath, { request }, preservedFileName) {559async function importFromCharX(uploadPath, { request }, preservedFileName) {
560 const data = fs.readFileSync(uploadPath).buffer;560 const data = fs.readFileSync(uploadPath).buffer;
561 fs.rmSync(uploadPath);561 fs.rmSync(uploadPath);
562 console.log('Importing from CharX');562 console.info('Importing from CharX');
563 const cardBuffer = await extractFileFromZipBuffer(data, 'card.json');563 const cardBuffer = await extractFileFromZipBuffer(data, 'card.json');
564564
565 if (!cardBuffer) {565 if (!cardBuffer) {
@@ -608,7 +608,7 @@ async function importFromJson(uploadPath, { request }, preservedFileName) {
608 let jsonData = JSON.parse(data);608 let jsonData = JSON.parse(data);
609609
610 if (jsonData.spec !== undefined) {610 if (jsonData.spec !== undefined) {
611 console.log(`Importing from ${jsonData.spec} json`);611 console.info(`Importing from ${jsonData.spec} json`);
612 importRisuSprites(request.user.directories, jsonData);612 importRisuSprites(request.user.directories, jsonData);
613 unsetFavFlag(jsonData);613 unsetFavFlag(jsonData);
614 jsonData = readFromV2(jsonData);614 jsonData = readFromV2(jsonData);
@@ -618,7 +618,7 @@ async function importFromJson(uploadPath, { request }, preservedFileName) {
618 const result = await writeCharacterData(defaultAvatarPath, char, pngName, request);618 const result = await writeCharacterData(defaultAvatarPath, char, pngName, request);
619 return result ? pngName : '';619 return result ? pngName : '';
620 } else if (jsonData.name !== undefined) {620 } else if (jsonData.name !== undefined) {
621 console.log('Importing from v1 json');621 console.info('Importing from v1 json');
622 jsonData.name = sanitize(jsonData.name);622 jsonData.name = sanitize(jsonData.name);
623 if (jsonData.creator_notes) {623 if (jsonData.creator_notes) {
624 jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');624 jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');
@@ -644,7 +644,7 @@ async function importFromJson(uploadPath, { request }, preservedFileName) {
644 const result = await writeCharacterData(defaultAvatarPath, charJSON, pngName, request);644 const result = await writeCharacterData(defaultAvatarPath, charJSON, pngName, request);
645 return result ? pngName : '';645 return result ? pngName : '';
646 } else if (jsonData.char_name !== undefined) {//json Pygmalion notepad646 } else if (jsonData.char_name !== undefined) {//json Pygmalion notepad
647 console.log('Importing from gradio json');647 console.info('Importing from gradio json');
648 jsonData.char_name = sanitize(jsonData.char_name);648 jsonData.char_name = sanitize(jsonData.char_name);
649 if (jsonData.creator_notes) {649 if (jsonData.creator_notes) {
650 jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');650 jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');
@@ -691,7 +691,7 @@ async function importFromPng(uploadPath, { request }, preservedFileName) {
691 const pngName = preservedFileName || getPngName(jsonData.name, request.user.directories);691 const pngName = preservedFileName || getPngName(jsonData.name, request.user.directories);
692692
693 if (jsonData.spec !== undefined) {693 if (jsonData.spec !== undefined) {
694 console.log(`Found a ${jsonData.spec} character file.`);694 console.info(`Found a ${jsonData.spec} character file.`);
695 importRisuSprites(request.user.directories, jsonData);695 importRisuSprites(request.user.directories, jsonData);
696 unsetFavFlag(jsonData);696 unsetFavFlag(jsonData);
697 jsonData = readFromV2(jsonData);697 jsonData = readFromV2(jsonData);
@@ -701,7 +701,7 @@ async function importFromPng(uploadPath, { request }, preservedFileName) {
701 fs.unlinkSync(uploadPath);701 fs.unlinkSync(uploadPath);
702 return result ? pngName : '';702 return result ? pngName : '';
703 } else if (jsonData.name !== undefined) {703 } else if (jsonData.name !== undefined) {
704 console.log('Found a v1 character file.');704 console.info('Found a v1 character file.');
705705
706 if (jsonData.creator_notes) {706 if (jsonData.creator_notes) {
707 jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');707 jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');
@@ -812,13 +812,13 @@ router.post('/rename', jsonParser, validateAvatarUrlMiddleware, async function (
812812
813router.post('/edit', urlencodedParser, validateAvatarUrlMiddleware, async function (request, response) {813router.post('/edit', urlencodedParser, validateAvatarUrlMiddleware, async function (request, response) {
814 if (!request.body) {814 if (!request.body) {
815 console.error('Error: no response body detected');815 console.warn('Error: no response body detected');
816 response.status(400).send('Error: no response body detected');816 response.status(400).send('Error: no response body detected');
817 return;817 return;
818 }818 }
819819
820 if (request.body.ch_name === '' || request.body.ch_name === undefined || request.body.ch_name === '.') {820 if (request.body.ch_name === '' || request.body.ch_name === undefined || request.body.ch_name === '.') {
821 console.error('Error: invalid name.');821 console.warn('Error: invalid name.');
822 response.status(400).send('Error: invalid name.');822 response.status(400).send('Error: invalid name.');
823 return;823 return;
824 }824 }
@@ -860,14 +860,14 @@ router.post('/edit', urlencodedParser, validateAvatarUrlMiddleware, async functi
860 * @returns {void}860 * @returns {void}
861 */861 */
862router.post('/edit-attribute', jsonParser, validateAvatarUrlMiddleware, async function (request, response) {862router.post('/edit-attribute', jsonParser, validateAvatarUrlMiddleware, async function (request, response) {
863 console.log(request.body);863 console.debug(request.body);
864 if (!request.body) {864 if (!request.body) {
865 console.error('Error: no response body detected');865 console.warn('Error: no response body detected');
866 return response.status(400).send('Error: no response body detected');866 return response.status(400).send('Error: no response body detected');
867 }867 }
868868
869 if (request.body.ch_name === '' || request.body.ch_name === undefined || request.body.ch_name === '.') {869 if (request.body.ch_name === '' || request.body.ch_name === undefined || request.body.ch_name === '.') {
870 console.error('Error: invalid name.');870 console.warn('Error: invalid name.');
871 return response.status(400).send('Error: invalid name.');871 return response.status(400).send('Error: invalid name.');
872 }872 }
873873
@@ -879,7 +879,7 @@ router.post('/edit-attribute', jsonParser, validateAvatarUrlMiddleware, async fu
879 const char = JSON.parse(charJSON);879 const char = JSON.parse(charJSON);
880 //check if the field exists880 //check if the field exists
881 if (char[request.body.field] === undefined && char.data[request.body.field] === undefined) {881 if (char[request.body.field] === undefined && char.data[request.body.field] === undefined) {
882 console.error('Error: invalid field.');882 console.warn('Error: invalid field.');
883 response.status(400).send('Error: invalid field.');883 response.status(400).send('Error: invalid field.');
884 return;884 return;
885 }885 }
@@ -928,7 +928,7 @@ router.post('/merge-attributes', jsonParser, getFileNameValidationFunction('avat
928 await writeCharacterData(avatarPath, JSON.stringify(character), targetImg, request);928 await writeCharacterData(avatarPath, JSON.stringify(character), targetImg, request);
929 response.sendStatus(200);929 response.sendStatus(200);
930 } else {930 } else {
931 console.log(validator.lastValidationError);931 console.warn(validator.lastValidationError);
932 response.status(400).send({ message: `Validation failed for ${character.name}`, error: validator.lastValidationError });932 response.status(400).send({ message: `Validation failed for ${character.name}`, error: validator.lastValidationError });
933 }933 }
934 } catch (exception) {934 } catch (exception) {
@@ -1050,7 +1050,7 @@ router.post('/chats', jsonParser, validateAvatarUrlMiddleware, async function (r
1050 const fileSizeInKB = `${(stats.size / 1024).toFixed(2)}kb`;1050 const fileSizeInKB = `${(stats.size / 1024).toFixed(2)}kb`;
10511051
1052 if (stats.size === 0) {1052 if (stats.size === 0) {
1053 console.log(`Found an empty chat file: ${pathToFile}`);1053 console.warn(`Found an empty chat file: ${pathToFile}`);
1054 res({});1054 res({});
1055 return;1055 return;
1056 }1056 }
@@ -1082,7 +1082,7 @@ router.post('/chats', jsonParser, validateAvatarUrlMiddleware, async function (r
10821082
1083 res(chatData);1083 res(chatData);
1084 } else {1084 } else {
1085 console.log('Found an invalid or corrupted chat file:', pathToFile);1085 console.warn('Found an invalid or corrupted chat file:', pathToFile);
1086 res({});1086 res({});
1087 }1087 }
1088 }1088 }
@@ -1095,7 +1095,7 @@ router.post('/chats', jsonParser, validateAvatarUrlMiddleware, async function (r
10951095
1096 return response.send(validFiles);1096 return response.send(validFiles);
1097 } catch (error) {1097 } catch (error) {
1098 console.log(error);1098 console.error(error);
1099 return response.send({ error: true });1099 return response.send({ error: true });
1100 }1100 }
1101});1101});
@@ -1152,7 +1152,7 @@ router.post('/import', urlencodedParser, async function (request, response) {
1152 const fileName = await importFunction(uploadPath, { request, response }, preservedFileName);1152 const fileName = await importFunction(uploadPath, { request, response }, preservedFileName);
11531153
1154 if (!fileName) {1154 if (!fileName) {
1155 console.error('Failed to import character');1155 console.warn('Failed to import character');
1156 return response.sendStatus(400);1156 return response.sendStatus(400);
1157 }1157 }
11581158
@@ -1162,7 +1162,7 @@ router.post('/import', urlencodedParser, async function (request, response) {
11621162
1163 response.send({ file_name: fileName });1163 response.send({ file_name: fileName });
1164 } catch (err) {1164 } catch (err) {
1165 console.log(err);1165 console.error(err);
1166 response.send({ error: true });1166 response.send({ error: true });
1167 }1167 }
1168});1168});
@@ -1170,14 +1170,13 @@ router.post('/import', urlencodedParser, async function (request, response) {
1170router.post('/duplicate', jsonParser, validateAvatarUrlMiddleware, async function (request, response) {1170router.post('/duplicate', jsonParser, validateAvatarUrlMiddleware, async function (request, response) {
1171 try {1171 try {
1172 if (!request.body.avatar_url) {1172 if (!request.body.avatar_url) {
1173 console.log('avatar URL not found in request body');1173 console.warn('avatar URL not found in request body');
1174 console.log(request.body);1174 console.debug(request.body);
1175 return response.sendStatus(400);1175 return response.sendStatus(400);
1176 }1176 }
1177 let filename = path.join(request.user.directories.characters, sanitize(request.body.avatar_url));1177 let filename = path.join(request.user.directories.characters, sanitize(request.body.avatar_url));
1178 if (!fs.existsSync(filename)) {1178 if (!fs.existsSync(filename)) {
1179 console.log('file for dupe not found');1179 console.error('file for dupe not found', filename);
1180 console.log(filename);
1181 return response.sendStatus(404);1180 return response.sendStatus(404);
1182 }1181 }
1183 let suffix = 1;1182 let suffix = 1;
@@ -1205,7 +1204,7 @@ router.post('/duplicate', jsonParser, validateAvatarUrlMiddleware, async functio
1205 }1204 }
12061205
1207 fs.copyFileSync(filename, newFilename);1206 fs.copyFileSync(filename, newFilename);
1208 console.log(`${filename} was copied to ${newFilename}`);1207 console.info(`${filename} was copied to ${newFilename}`);
1209 response.send({ path: path.parse(newFilename).base });1208 response.send({ path: path.parse(newFilename).base });
1210 }1209 }
1211 catch (error) {1210 catch (error) {
src/endpoints/chats.js+17 -18
@@ -50,7 +50,7 @@ function backupChat(directory, name, chat) {
5050
51 removeOldBackups(directory, 'chat_', maxTotalChatBackups);51 removeOldBackups(directory, 'chat_', maxTotalChatBackups);
52 } catch (err) {52 } catch (err) {
53 console.log(`Could not backup chat for ${name}`, err);53 console.error(`Could not backup chat for ${name}`, err);
54 }54 }
55}55}
5656
@@ -306,8 +306,8 @@ router.post('/save', jsonParser, validateAvatarUrlMiddleware, function (request,
306 getBackupFunction(request.user.profile.handle)(request.user.directories.backups, directoryName, jsonlData);306 getBackupFunction(request.user.profile.handle)(request.user.directories.backups, directoryName, jsonlData);
307 return response.send({ result: 'ok' });307 return response.send({ result: 'ok' });
308 } catch (error) {308 } catch (error) {
309 response.send(error);309 console.error(error);
310 return console.log(error);310 return response.send(error);
311 }311 }
312});312});
313313
@@ -359,17 +359,17 @@ router.post('/rename', jsonParser, validateAvatarUrlMiddleware, async function (
359 const pathToOriginalFile = path.join(pathToFolder, sanitize(request.body.original_file));359 const pathToOriginalFile = path.join(pathToFolder, sanitize(request.body.original_file));
360 const pathToRenamedFile = path.join(pathToFolder, sanitize(request.body.renamed_file));360 const pathToRenamedFile = path.join(pathToFolder, sanitize(request.body.renamed_file));
361 const sanitizedFileName = path.parse(pathToRenamedFile).name;361 const sanitizedFileName = path.parse(pathToRenamedFile).name;
362 console.log('Old chat name', pathToOriginalFile);362 console.info('Old chat name', pathToOriginalFile);
363 console.log('New chat name', pathToRenamedFile);363 console.info('New chat name', pathToRenamedFile);
364364
365 if (!fs.existsSync(pathToOriginalFile) || fs.existsSync(pathToRenamedFile)) {365 if (!fs.existsSync(pathToOriginalFile) || fs.existsSync(pathToRenamedFile)) {
366 console.log('Either Source or Destination files are not available');366 console.error('Either Source or Destination files are not available');
367 return response.status(400).send({ error: true });367 return response.status(400).send({ error: true });
368 }368 }
369369
370 fs.copyFileSync(pathToOriginalFile, pathToRenamedFile);370 fs.copyFileSync(pathToOriginalFile, pathToRenamedFile);
371 fs.rmSync(pathToOriginalFile);371 fs.rmSync(pathToOriginalFile);
372 console.log('Successfully renamed.');372 console.info('Successfully renamed.');
373 return response.send({ ok: true, sanitizedFileName });373 return response.send({ ok: true, sanitizedFileName });
374});374});
375375
@@ -380,12 +380,12 @@ router.post('/delete', jsonParser, validateAvatarUrlMiddleware, function (reques
380 const chatFileExists = fs.existsSync(filePath);380 const chatFileExists = fs.existsSync(filePath);
381381
382 if (!chatFileExists) {382 if (!chatFileExists) {
383 console.log(`Chat file not found '${filePath}'`);383 console.error(`Chat file not found '${filePath}'`);
384 return response.sendStatus(400);384 return response.sendStatus(400);
385 }385 }
386386
387 fs.rmSync(filePath);387 fs.rmSync(filePath);
388 console.log('Deleted chat file: ' + filePath);388 console.info(`Deleted chat file: ${filePath}`);
389 return response.send('ok');389 return response.send('ok');
390});390});
391391
@@ -402,7 +402,7 @@ router.post('/export', jsonParser, validateAvatarUrlMiddleware, async function (
402 const errorMessage = {402 const errorMessage = {
403 message: `Could not find JSONL file to export. Source chat file: ${filename}.`,403 message: `Could not find JSONL file to export. Source chat file: ${filename}.`,
404 };404 };
405 console.log(errorMessage.message);405 console.error(errorMessage.message);
406 return response.status(404).json(errorMessage);406 return response.status(404).json(errorMessage);
407 }407 }
408 try {408 try {
@@ -415,14 +415,14 @@ router.post('/export', jsonParser, validateAvatarUrlMiddleware, async function (
415 result: rawFile,415 result: rawFile,
416 };416 };
417417
418 console.log(`Chat exported as ${exportfilename}`);418 console.info(`Chat exported as ${exportfilename}`);
419 return response.status(200).json(successMessage);419 return response.status(200).json(successMessage);
420 } catch (err) {420 } catch (err) {
421 console.error(err);421 console.error(err);
422 const errorMessage = {422 const errorMessage = {
423 message: `Could not read JSONL file to export. Source chat file: ${filename}.`,423 message: `Could not read JSONL file to export. Source chat file: ${filename}.`,
424 };424 };
425 console.log(errorMessage.message);425 console.error(errorMessage.message);
426 return response.status(500).json(errorMessage);426 return response.status(500).json(errorMessage);
427 }427 }
428 }428 }
@@ -449,12 +449,11 @@ router.post('/export', jsonParser, validateAvatarUrlMiddleware, async function (
449 message: `Chat saved to ${exportfilename}`,449 message: `Chat saved to ${exportfilename}`,
450 result: buffer,450 result: buffer,
451 };451 };
452 console.log(`Chat exported as ${exportfilename}`);452 console.info(`Chat exported as ${exportfilename}`);
453 return response.status(200).json(successMessage);453 return response.status(200).json(successMessage);
454 });454 });
455 } catch (err) {455 } catch (err) {
456 console.log('chat export failed.');456 console.error('chat export failed.', err);
457 console.log(err);
458 return response.sendStatus(400);457 return response.sendStatus(400);
459 }458 }
460});459});
@@ -513,7 +512,7 @@ router.post('/import', urlencodedParser, validateAvatarUrlMiddleware, function (
513 } else if (jsonData.type === 'risuChat') { // RisuAI format512 } else if (jsonData.type === 'risuChat') { // RisuAI format
514 importFunc = importRisuChat;513 importFunc = importRisuChat;
515 } else { // Unknown format514 } else { // Unknown format
516 console.log('Incorrect chat format .json');515 console.error('Incorrect chat format .json');
517 return response.send({ error: true });516 return response.send({ error: true });
518 }517 }
519518
@@ -541,7 +540,7 @@ router.post('/import', urlencodedParser, validateAvatarUrlMiddleware, function (
541 const jsonData = JSON.parse(header);540 const jsonData = JSON.parse(header);
542541
543 if (!(jsonData.user_name !== undefined || jsonData.name !== undefined)) {542 if (!(jsonData.user_name !== undefined || jsonData.name !== undefined)) {
544 console.log('Incorrect chat format .jsonl');543 console.error('Incorrect chat format .jsonl');
545 return response.send({ error: true });544 return response.send({ error: true });
546 }545 }
547546
@@ -647,7 +646,7 @@ router.post('/search', jsonParser, validateAvatarUrlMiddleware, function (reques
647 break;646 break;
648 }647 }
649 } catch (error) {648 } catch (error) {
650 console.error(groupFile, 'group file is corrupted:', error);649 console.warn(groupFile, 'group file is corrupted:', error);
651 }650 }
652 }651 }
653652
src/endpoints/classify.js+2 -2
@@ -44,9 +44,9 @@ router.post('/', jsonParser, async (req, res) => {
44 }44 }
45 }45 }
4646
47 console.log('Classify input:', text);47 console.debug('Classify input:', text);
48 const result = await getResult(text);48 const result = await getResult(text);
49 console.log('Classify output:', result);49 console.debug('Classify output:', result);
5050
51 return res.json({ classification: result });51 return res.json({ classification: result });
52 } catch (error) {52 } catch (error) {
src/endpoints/content-manager.js+26 -26
@@ -71,7 +71,7 @@ export function getDefaultPresets(directories) {
7171
72 return presets;72 return presets;
73 } catch (err) {73 } catch (err) {
74 console.log('Failed to get default presets', err);74 console.warn('Failed to get default presets', err);
75 return [];75 return [];
76 }76 }
77}77}
@@ -92,7 +92,7 @@ export function getDefaultPresetFile(filename) {
92 const fileContent = fs.readFileSync(contentPath, 'utf8');92 const fileContent = fs.readFileSync(contentPath, 'utf8');
93 return JSON.parse(fileContent);93 return JSON.parse(fileContent);
94 } catch (err) {94 } catch (err) {
95 console.log(`Failed to get default file ${filename}`, err);95 console.warn(`Failed to get default file ${filename}`, err);
96 return null;96 return null;
97 }97 }
98}98}
@@ -121,21 +121,21 @@ async function seedContentForUser(contentIndex, directories, forceCategories) {
121 }121 }
122122
123 if (!contentItem.folder) {123 if (!contentItem.folder) {
124 console.log(`Content file ${contentItem.filename} has no parent folder`);124 console.warn(`Content file ${contentItem.filename} has no parent folder`);
125 continue;125 continue;
126 }126 }
127127
128 const contentPath = path.join(contentItem.folder, contentItem.filename);128 const contentPath = path.join(contentItem.folder, contentItem.filename);
129129
130 if (!fs.existsSync(contentPath)) {130 if (!fs.existsSync(contentPath)) {
131 console.log(`Content file ${contentItem.filename} is missing`);131 console.warn(`Content file ${contentItem.filename} is missing`);
132 continue;132 continue;
133 }133 }
134134
135 const contentTarget = getTargetByType(contentItem.type, directories);135 const contentTarget = getTargetByType(contentItem.type, directories);
136136
137 if (!contentTarget) {137 if (!contentTarget) {
138 console.log(`Content file ${contentItem.filename} has unknown type ${contentItem.type}`);138 console.warn(`Content file ${contentItem.filename} has unknown type ${contentItem.type}`);
139 continue;139 continue;
140 }140 }
141141
@@ -144,12 +144,12 @@ async function seedContentForUser(contentIndex, directories, forceCategories) {
144 contentLog.push(contentItem.filename);144 contentLog.push(contentItem.filename);
145145
146 if (fs.existsSync(targetPath)) {146 if (fs.existsSync(targetPath)) {
147 console.log(`Content file ${contentItem.filename} already exists in ${contentTarget}`);147 console.warn(`Content file ${contentItem.filename} already exists in ${contentTarget}`);
148 continue;148 continue;
149 }149 }
150150
151 fs.cpSync(contentPath, targetPath, { recursive: true, force: false });151 fs.cpSync(contentPath, targetPath, { recursive: true, force: false });
152 console.log(`Content file ${contentItem.filename} copied to ${contentTarget}`);152 console.info(`Content file ${contentItem.filename} copied to ${contentTarget}`);
153 anyContentAdded = true;153 anyContentAdded = true;
154 }154 }
155155
@@ -182,12 +182,12 @@ export async function checkForNewContent(directoriesList, forceCategories = [])
182 }182 }
183183
184 if (anyContentAdded && !contentCheckSkip && forceCategories?.length === 0) {184 if (anyContentAdded && !contentCheckSkip && forceCategories?.length === 0) {
185 console.log();185 console.info();
186 console.log(`${color.blue('If you don\'t want to receive content updates in the future, set')} ${color.yellow('skipContentCheck')} ${color.blue('to true in the config.yaml file.')}`);186 console.info(`${color.blue('If you don\'t want to receive content updates in the future, set')} ${color.yellow('skipContentCheck')} ${color.blue('to true in the config.yaml file.')}`);
187 console.log();187 console.info();
188 }188 }
189 } catch (err) {189 } catch (err) {
190 console.log('Content check failed', err);190 console.error('Content check failed', err);
191 }191 }
192}192}
193193
@@ -331,7 +331,7 @@ async function downloadChubLorebook(id) {
331331
332 if (!result.ok) {332 if (!result.ok) {
333 const text = await result.text();333 const text = await result.text();
334 console.log('Chub returned error', result.statusText, text);334 console.error('Chub returned error', result.statusText, text);
335 throw new Error('Failed to download lorebook');335 throw new Error('Failed to download lorebook');
336 }336 }
337337
@@ -355,7 +355,7 @@ async function downloadChubCharacter(id) {
355355
356 if (!result.ok) {356 if (!result.ok) {
357 const text = await result.text();357 const text = await result.text();
358 console.log('Chub returned error', result.statusText, text);358 console.error('Chub returned error', result.statusText, text);
359 throw new Error('Failed to download character');359 throw new Error('Failed to download character');
360 }360 }
361361
@@ -376,7 +376,7 @@ async function downloadPygmalionCharacter(id) {
376376
377 if (!result.ok) {377 if (!result.ok) {
378 const text = await result.text();378 const text = await result.text();
379 console.log('Pygsite returned error', result.status, text);379 console.error('Pygsite returned error', result.status, text);
380 throw new Error('Failed to download character');380 throw new Error('Failed to download character');
381 }381 }
382382
@@ -485,7 +485,7 @@ async function downloadJannyCharacter(uuid) {
485 }485 }
486 }486 }
487487
488 console.log('Janny returned error', result.statusText, await result.text());488 console.error('Janny returned error', result.statusText, await result.text());
489 throw new Error('Failed to download character');489 throw new Error('Failed to download character');
490}490}
491491
@@ -577,7 +577,7 @@ async function downloadRisuCharacter(uuid) {
577577
578 if (!result.ok) {578 if (!result.ok) {
579 const text = await result.text();579 const text = await result.text();
580 console.log('RisuAI returned error', result.statusText, text);580 console.error('RisuAI returned error', result.statusText, text);
581 throw new Error('Failed to download character');581 throw new Error('Failed to download character');
582 }582 }
583583
@@ -673,11 +673,11 @@ router.post('/importURL', jsonParser, async (request, response) => {
673 type = chubParsed?.type;673 type = chubParsed?.type;
674674
675 if (chubParsed?.type === 'character') {675 if (chubParsed?.type === 'character') {
676 console.log('Downloading chub character:', chubParsed.id);676 console.info('Downloading chub character:', chubParsed.id);
677 result = await downloadChubCharacter(chubParsed.id);677 result = await downloadChubCharacter(chubParsed.id);
678 }678 }
679 else if (chubParsed?.type === 'lorebook') {679 else if (chubParsed?.type === 'lorebook') {
680 console.log('Downloading chub lorebook:', chubParsed.id);680 console.info('Downloading chub lorebook:', chubParsed.id);
681 result = await downloadChubLorebook(chubParsed.id);681 result = await downloadChubLorebook(chubParsed.id);
682 }682 }
683 else {683 else {
@@ -692,7 +692,7 @@ router.post('/importURL', jsonParser, async (request, response) => {
692 type = 'character';692 type = 'character';
693 result = await downloadRisuCharacter(uuid);693 result = await downloadRisuCharacter(uuid);
694 } else if (isGeneric) {694 } else if (isGeneric) {
695 console.log('Downloading from generic url.');695 console.info('Downloading from generic url.');
696 type = 'character';696 type = 'character';
697 result = await downloadGenericPng(url);697 result = await downloadGenericPng(url);
698 } else {698 } else {
@@ -708,7 +708,7 @@ router.post('/importURL', jsonParser, async (request, response) => {
708 response.set('X-Custom-Content-Type', type);708 response.set('X-Custom-Content-Type', type);
709 return response.send(result.buffer);709 return response.send(result.buffer);
710 } catch (error) {710 } catch (error) {
711 console.log('Importing custom content failed', error);711 console.error('Importing custom content failed', error);
712 return response.sendStatus(500);712 return response.sendStatus(500);
713 }713 }
714});714});
@@ -728,22 +728,22 @@ router.post('/importUUID', jsonParser, async (request, response) => {
728 const uuidType = uuid.includes('lorebook') ? 'lorebook' : 'character';728 const uuidType = uuid.includes('lorebook') ? 'lorebook' : 'character';
729729
730 if (isPygmalion) {730 if (isPygmalion) {
731 console.log('Downloading Pygmalion character:', uuid);731 console.info('Downloading Pygmalion character:', uuid);
732 result = await downloadPygmalionCharacter(uuid);732 result = await downloadPygmalionCharacter(uuid);
733 } else if (isJannny) {733 } else if (isJannny) {
734 console.log('Downloading Janitor character:', uuid.split('_')[0]);734 console.info('Downloading Janitor character:', uuid.split('_')[0]);
735 result = await downloadJannyCharacter(uuid.split('_')[0]);735 result = await downloadJannyCharacter(uuid.split('_')[0]);
736 } else if (isAICC) {736 } else if (isAICC) {
737 const [, author, card] = uuid.split('/');737 const [, author, card] = uuid.split('/');
738 console.log('Downloading AICC character:', `${author}/${card}`);738 console.info('Downloading AICC character:', `${author}/${card}`);
739 result = await downloadAICCCharacter(`${author}/${card}`);739 result = await downloadAICCCharacter(`${author}/${card}`);
740 } else {740 } else {
741 if (uuidType === 'character') {741 if (uuidType === 'character') {
742 console.log('Downloading chub character:', uuid);742 console.info('Downloading chub character:', uuid);
743 result = await downloadChubCharacter(uuid);743 result = await downloadChubCharacter(uuid);
744 }744 }
745 else if (uuidType === 'lorebook') {745 else if (uuidType === 'lorebook') {
746 console.log('Downloading chub lorebook:', uuid);746 console.info('Downloading chub lorebook:', uuid);
747 result = await downloadChubLorebook(uuid);747 result = await downloadChubLorebook(uuid);
748 }748 }
749 else {749 else {
@@ -756,7 +756,7 @@ router.post('/importUUID', jsonParser, async (request, response) => {
756 response.set('X-Custom-Content-Type', uuidType);756 response.set('X-Custom-Content-Type', uuidType);
757 return response.send(result.buffer);757 return response.send(result.buffer);
758 } catch (error) {758 } catch (error) {
759 console.log('Importing custom content failed', error);759 console.error('Importing custom content failed', error);
760 return response.sendStatus(500);760 return response.sendStatus(500);
761 }761 }
762});762});
src/endpoints/extensions.js+16 -16
@@ -80,7 +80,7 @@ router.post('/install', jsonParser, async (request, response) => {
80 const { url, global } = request.body;80 const { url, global } = request.body;
8181
82 if (global && !request.user.profile.admin) {82 if (global && !request.user.profile.admin) {
83 console.warn(`User ${request.user.profile.handle} does not have permission to install global extensions.`);83 console.error(`User ${request.user.profile.handle} does not have permission to install global extensions.`);
84 return response.status(403).send('Forbidden: No permission to install global extensions.');84 return response.status(403).send('Forbidden: No permission to install global extensions.');
85 }85 }
8686
@@ -92,13 +92,13 @@ router.post('/install', jsonParser, async (request, response) => {
92 }92 }
9393
94 await git.clone(url, extensionPath, { '--depth': 1 });94 await git.clone(url, extensionPath, { '--depth': 1 });
95 console.log(`Extension has been cloned at ${extensionPath}`);95 console.info(`Extension has been cloned at ${extensionPath}`);
9696
97 const { version, author, display_name } = await getManifest(extensionPath);97 const { version, author, display_name } = await getManifest(extensionPath);
9898
99 return response.send({ version, author, display_name, extensionPath });99 return response.send({ version, author, display_name, extensionPath });
100 } catch (error) {100 } catch (error) {
101 console.log('Importing custom content failed', error);101 console.error('Importing custom content failed', error);
102 return response.status(500).send(`Server Error: ${error.message}`);102 return response.status(500).send(`Server Error: ${error.message}`);
103 }103 }
104});104});
@@ -124,7 +124,7 @@ router.post('/update', jsonParser, async (request, response) => {
124 const { extensionName, global } = request.body;124 const { extensionName, global } = request.body;
125125
126 if (global && !request.user.profile.admin) {126 if (global && !request.user.profile.admin) {
127 console.warn(`User ${request.user.profile.handle} does not have permission to update global extensions.`);127 console.error(`User ${request.user.profile.handle} does not have permission to update global extensions.`);
128 return response.status(403).send('Forbidden: No permission to update global extensions.');128 return response.status(403).send('Forbidden: No permission to update global extensions.');
129 }129 }
130130
@@ -139,9 +139,9 @@ router.post('/update', jsonParser, async (request, response) => {
139 const currentBranch = await git.cwd(extensionPath).branch();139 const currentBranch = await git.cwd(extensionPath).branch();
140 if (!isUpToDate) {140 if (!isUpToDate) {
141 await git.cwd(extensionPath).pull('origin', currentBranch.current);141 await git.cwd(extensionPath).pull('origin', currentBranch.current);
142 console.log(`Extension has been updated at ${extensionPath}`);142 console.info(`Extension has been updated at ${extensionPath}`);
143 } else {143 } else {
144 console.log(`Extension is up to date at ${extensionPath}`);144 console.info(`Extension is up to date at ${extensionPath}`);
145 }145 }
146 await git.cwd(extensionPath).fetch('origin');146 await git.cwd(extensionPath).fetch('origin');
147 const fullCommitHash = await git.cwd(extensionPath).revparse(['HEAD']);147 const fullCommitHash = await git.cwd(extensionPath).revparse(['HEAD']);
@@ -150,7 +150,7 @@ router.post('/update', jsonParser, async (request, response) => {
150 return response.send({ shortCommitHash, extensionPath, isUpToDate, remoteUrl });150 return response.send({ shortCommitHash, extensionPath, isUpToDate, remoteUrl });
151151
152 } catch (error) {152 } catch (error) {
153 console.log('Updating custom content failed', error);153 console.error('Updating custom content failed', error);
154 return response.status(500).send(`Server Error: ${error.message}`);154 return response.status(500).send(`Server Error: ${error.message}`);
155 }155 }
156});156});
@@ -164,7 +164,7 @@ router.post('/move', jsonParser, async (request, response) => {
164 }164 }
165165
166 if (!request.user.profile.admin) {166 if (!request.user.profile.admin) {
167 console.warn(`User ${request.user.profile.handle} does not have permission to move extensions.`);167 console.error(`User ${request.user.profile.handle} does not have permission to move extensions.`);
168 return response.status(403).send('Forbidden: No permission to move extensions.');168 return response.status(403).send('Forbidden: No permission to move extensions.');
169 }169 }
170170
@@ -190,11 +190,11 @@ router.post('/move', jsonParser, async (request, response) => {
190190
191 fs.cpSync(sourcePath, destinationPath, { recursive: true, force: true });191 fs.cpSync(sourcePath, destinationPath, { recursive: true, force: true });
192 fs.rmSync(sourcePath, { recursive: true, force: true });192 fs.rmSync(sourcePath, { recursive: true, force: true });
193 console.log(`Extension has been moved from ${sourcePath} to ${destinationPath}`);193 console.info(`Extension has been moved from ${sourcePath} to ${destinationPath}`);
194194
195 return response.sendStatus(204);195 return response.sendStatus(204);
196 } catch (error) {196 } catch (error) {
197 console.log('Moving extension failed', error);197 console.error('Moving extension failed', error);
198 return response.status(500).send('Internal Server Error. Try again later.');198 return response.status(500).send('Internal Server Error. Try again later.');
199 }199 }
200});200});
@@ -237,13 +237,13 @@ router.post('/version', jsonParser, async (request, response) => {
237 // get only the working branch237 // get only the working branch
238 const currentBranchName = currentBranch.current;238 const currentBranchName = currentBranch.current;
239 await git.cwd(extensionPath).fetch('origin');239 await git.cwd(extensionPath).fetch('origin');
240 console.log(extensionName, currentBranchName, currentCommitHash);240 console.debug(extensionName, currentBranchName, currentCommitHash);
241 const { isUpToDate, remoteUrl } = await checkIfRepoIsUpToDate(extensionPath);241 const { isUpToDate, remoteUrl } = await checkIfRepoIsUpToDate(extensionPath);
242242
243 return response.send({ currentBranchName, currentCommitHash, isUpToDate, remoteUrl });243 return response.send({ currentBranchName, currentCommitHash, isUpToDate, remoteUrl });
244244
245 } catch (error) {245 } catch (error) {
246 console.log('Getting extension version failed', error);246 console.error('Getting extension version failed', error);
247 return response.status(500).send(`Server Error: ${error.message}`);247 return response.status(500).send(`Server Error: ${error.message}`);
248 }248 }
249});249});
@@ -265,7 +265,7 @@ router.post('/delete', jsonParser, async (request, response) => {
265 const { extensionName, global } = request.body;265 const { extensionName, global } = request.body;
266266
267 if (global && !request.user.profile.admin) {267 if (global && !request.user.profile.admin) {
268 console.warn(`User ${request.user.profile.handle} does not have permission to delete global extensions.`);268 console.error(`User ${request.user.profile.handle} does not have permission to delete global extensions.`);
269 return response.status(403).send('Forbidden: No permission to delete global extensions.');269 return response.status(403).send('Forbidden: No permission to delete global extensions.');
270 }270 }
271271
@@ -277,12 +277,12 @@ router.post('/delete', jsonParser, async (request, response) => {
277 }277 }
278278
279 await fs.promises.rm(extensionPath, { recursive: true });279 await fs.promises.rm(extensionPath, { recursive: true });
280 console.log(`Extension has been deleted at ${extensionPath}`);280 console.info(`Extension has been deleted at ${extensionPath}`);
281281
282 return response.send(`Extension has been deleted at ${extensionPath}`);282 return response.send(`Extension has been deleted at ${extensionPath}`);
283283
284 } catch (error) {284 } catch (error) {
285 console.log('Deleting custom content failed', error);285 console.error('Deleting custom content failed', error);
286 return response.status(500).send(`Server Error: ${error.message}`);286 return response.status(500).send(`Server Error: ${error.message}`);
287 }287 }
288});288});
@@ -323,7 +323,7 @@ router.get('/discover', jsonParser, function (request, response) {
323323
324 // Combine all extensions324 // Combine all extensions
325 const allExtensions = [...builtInExtensions, ...userExtensions, ...globalExtensions];325 const allExtensions = [...builtInExtensions, ...userExtensions, ...globalExtensions];
326 console.log('Extensions available for', request.user.profile.handle, allExtensions);326 console.info('Extensions available for', request.user.profile.handle, allExtensions);
327327
328 return response.send(allExtensions);328 return response.send(allExtensions);
329});329});
src/endpoints/files.js+7 -7
@@ -21,7 +21,7 @@ router.post('/sanitize-filename', jsonParser, async (request, response) => {
21 const sanitizedFilename = sanitize(fileName);21 const sanitizedFilename = sanitize(fileName);
22 return response.send({ fileName: sanitizedFilename });22 return response.send({ fileName: sanitizedFilename });
23 } catch (error) {23 } catch (error) {
24 console.log(error);24 console.error(error);
25 return response.sendStatus(500);25 return response.sendStatus(500);
26 }26 }
27});27});
@@ -44,10 +44,10 @@ router.post('/upload', jsonParser, async (request, response) => {
44 const pathToUpload = path.join(request.user.directories.files, request.body.name);44 const pathToUpload = path.join(request.user.directories.files, request.body.name);
45 writeFileSyncAtomic(pathToUpload, request.body.data, 'base64');45 writeFileSyncAtomic(pathToUpload, request.body.data, 'base64');
46 const url = clientRelativePath(request.user.directories.root, pathToUpload);46 const url = clientRelativePath(request.user.directories.root, pathToUpload);
47 console.log(`Uploaded file: ${url} from ${request.user.profile.handle}`);47 console.info(`Uploaded file: ${url} from ${request.user.profile.handle}`);
48 return response.send({ path: url });48 return response.send({ path: url });
49 } catch (error) {49 } catch (error) {
50 console.log(error);50 console.error(error);
51 return response.sendStatus(500);51 return response.sendStatus(500);
52 }52 }
53});53});
@@ -68,10 +68,10 @@ router.post('/delete', jsonParser, async (request, response) => {
68 }68 }
6969
70 fs.rmSync(pathToDelete);70 fs.rmSync(pathToDelete);
71 console.log(`Deleted file: ${request.body.path} from ${request.user.profile.handle}`);71 console.info(`Deleted file: ${request.body.path} from ${request.user.profile.handle}`);
72 return response.sendStatus(200);72 return response.sendStatus(200);
73 } catch (error) {73 } catch (error) {
74 console.log(error);74 console.error(error);
75 return response.sendStatus(500);75 return response.sendStatus(500);
76 }76 }
77});77});
@@ -87,7 +87,7 @@ router.post('/verify', jsonParser, async (request, response) => {
87 for (const url of request.body.urls) {87 for (const url of request.body.urls) {
88 const pathToVerify = path.join(request.user.directories.root, url);88 const pathToVerify = path.join(request.user.directories.root, url);
89 if (!pathToVerify.startsWith(request.user.directories.files)) {89 if (!pathToVerify.startsWith(request.user.directories.files)) {
90 console.debug(`File verification: Invalid path: ${pathToVerify}`);90 console.warn(`File verification: Invalid path: ${pathToVerify}`);
91 continue;91 continue;
92 }92 }
93 const fileExists = fs.existsSync(pathToVerify);93 const fileExists = fs.existsSync(pathToVerify);
@@ -96,7 +96,7 @@ router.post('/verify', jsonParser, async (request, response) => {
9696
97 return response.send(verified);97 return response.send(verified);
98 } catch (error) {98 } catch (error) {
99 console.log(error);99 console.error(error);
100 return response.sendStatus(500);100 return response.sendStatus(500);
101 }101 }
102});102});
src/endpoints/google.js+3 -3
@@ -34,7 +34,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
34 generationConfig: { maxOutputTokens: 1000 },34 generationConfig: { maxOutputTokens: 1000 },
35 };35 };
3636
37 console.log('Multimodal captioning request', model, body);37 console.debug('Multimodal captioning request', model, body);
3838
39 const result = await fetch(url, {39 const result = await fetch(url, {
40 body: JSON.stringify(body),40 body: JSON.stringify(body),
@@ -46,13 +46,13 @@ router.post('/caption-image', jsonParser, async (request, response) => {
4646
47 if (!result.ok) {47 if (!result.ok) {
48 const error = await result.json();48 const error = await result.json();
49 console.log(`Google AI Studio API returned error: ${result.status} ${result.statusText}`, error);49 console.error(`Google AI Studio API returned error: ${result.status} ${result.statusText}`, error);
50 return response.status(result.status).send({ error: true });50 return response.status(result.status).send({ error: true });
51 }51 }
5252
53 /** @type {any} */53 /** @type {any} */
54 const data = await result.json();54 const data = await result.json();
55 console.log('Multimodal captioning response', data);55 console.info('Multimodal captioning response', data);
5656
57 const candidates = data?.candidates;57 const candidates = data?.candidates;
58 if (!candidates) {58 if (!candidates) {
src/endpoints/groups.js+1 -1
@@ -114,7 +114,7 @@ router.post('/delete', jsonParser, async (request, response) => {
114114
115 if (group && Array.isArray(group.chats)) {115 if (group && Array.isArray(group.chats)) {
116 for (const chat of group.chats) {116 for (const chat of group.chats) {
117 console.log('Deleting group chat', chat);117 console.info('Deleting group chat', chat);
118 const pathToFile = path.join(request.user.directories.groupChats, `${id}.jsonl`);118 const pathToFile = path.join(request.user.directories.groupChats, `${id}.jsonl`);
119119
120 if (fs.existsSync(pathToFile)) {120 if (fs.existsSync(pathToFile)) {
src/endpoints/horde.js+15 -15
@@ -155,7 +155,7 @@ router.post('/cancel-task', jsonParser, async (request, response) => {
155 });155 });
156156
157 const data = await fetchResult.json();157 const data = await fetchResult.json();
158 console.log(`Cancelled Horde task ${taskId}`);158 console.info(`Cancelled Horde task ${taskId}`);
159 return response.send(data);159 return response.send(data);
160 } catch (error) {160 } catch (error) {
161 console.error(error);161 console.error(error);
@@ -174,7 +174,7 @@ router.post('/task-status', jsonParser, async (request, response) => {
174 });174 });
175175
176 const data = await fetchResult.json();176 const data = await fetchResult.json();
177 console.log(`Horde task ${taskId} status:`, data);177 console.info(`Horde task ${taskId} status:`, data);
178 return response.send(data);178 return response.send(data);
179 } catch (error) {179 } catch (error) {
180 console.error(error);180 console.error(error);
@@ -187,7 +187,7 @@ router.post('/generate-text', jsonParser, async (request, response) => {
187 const url = 'https://aihorde.net/api/v2/generate/text/async';187 const url = 'https://aihorde.net/api/v2/generate/text/async';
188 const agent = await getClientAgent();188 const agent = await getClientAgent();
189189
190 console.log(request.body);190 console.debug(request.body);
191 try {191 try {
192 const result = await fetch(url, {192 const result = await fetch(url, {
193 method: 'POST',193 method: 'POST',
@@ -201,14 +201,14 @@ router.post('/generate-text', jsonParser, async (request, response) => {
201201
202 if (!result.ok) {202 if (!result.ok) {
203 const message = await result.text();203 const message = await result.text();
204 console.log('Horde returned an error:', message);204 console.error('Horde returned an error:', message);
205 return response.send({ error: { message } });205 return response.send({ error: { message } });
206 }206 }
207207
208 const data = await result.json();208 const data = await result.json();
209 return response.send(data);209 return response.send(data);
210 } catch (error) {210 } catch (error) {
211 console.log(error);211 console.error(error);
212 return response.send({ error: true });212 return response.send({ error: true });
213 }213 }
214});214});
@@ -254,7 +254,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
254 for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {254 for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
255 await delay(CHECK_INTERVAL);255 await delay(CHECK_INTERVAL);
256 const status = await ai_horde.getInterrogationStatus(result.id);256 const status = await ai_horde.getInterrogationStatus(result.id);
257 console.log(status);257 console.info(status);
258258
259 if (status.state === HordeAsyncRequestStates.done) {259 if (status.state === HordeAsyncRequestStates.done) {
260260
@@ -263,7 +263,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
263 return response.sendStatus(500);263 return response.sendStatus(500);
264 }264 }
265265
266 console.log('Image interrogation result:', status);266 console.debug('Image interrogation result:', status);
267 const caption = status?.forms[0]?.result?.caption || '';267 const caption = status?.forms[0]?.result?.caption || '';
268268
269 if (!caption) {269 if (!caption) {
@@ -275,7 +275,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
275 }275 }
276276
277 if (status.state === HordeAsyncRequestStates.faulted || status.state === HordeAsyncRequestStates.cancelled) {277 if (status.state === HordeAsyncRequestStates.faulted || status.state === HordeAsyncRequestStates.cancelled) {
278 console.log('Image interrogation request is not successful.');278 console.error('Image interrogation request is not successful.');
279 return response.sendStatus(503);279 return response.sendStatus(503);
280 }280 }
281 }281 }
@@ -315,7 +315,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
315 try {315 try {
316 const maxLength = PROMPT_THRESHOLD - String(request.body.negative_prompt).length - 5;316 const maxLength = PROMPT_THRESHOLD - String(request.body.negative_prompt).length - 5;
317 if (String(request.body.prompt).length > maxLength) {317 if (String(request.body.prompt).length > maxLength) {
318 console.log('Stable Horde prompt is too long, truncating...');318 console.warn('Stable Horde prompt is too long, truncating...');
319 request.body.prompt = String(request.body.prompt).substring(0, maxLength);319 request.body.prompt = String(request.body.prompt).substring(0, maxLength);
320 }320 }
321321
@@ -324,14 +324,14 @@ router.post('/generate-image', jsonParser, async (request, response) => {
324 const sanitized = sanitizeHordeImagePrompt(request.body.prompt);324 const sanitized = sanitizeHordeImagePrompt(request.body.prompt);
325325
326 if (request.body.prompt !== sanitized) {326 if (request.body.prompt !== sanitized) {
327 console.log('Stable Horde prompt was sanitized.');327 console.info('Stable Horde prompt was sanitized.');
328 }328 }
329329
330 request.body.prompt = sanitized;330 request.body.prompt = sanitized;
331 }331 }
332332
333 const api_key_horde = readSecret(request.user.directories, SECRET_KEYS.HORDE) || ANONYMOUS_KEY;333 const api_key_horde = readSecret(request.user.directories, SECRET_KEYS.HORDE) || ANONYMOUS_KEY;
334 console.log('Stable Horde request:', request.body);334 console.debug('Stable Horde request:', request.body);
335335
336 const ai_horde = await getHordeClient();336 const ai_horde = await getHordeClient();
337 // noinspection JSCheckFunctionSignatures -- see @ts-ignore - use_gfpgan337 // noinspection JSCheckFunctionSignatures -- see @ts-ignore - use_gfpgan
@@ -360,16 +360,16 @@ router.post('/generate-image', jsonParser, async (request, response) => {
360 { token: api_key_horde });360 { token: api_key_horde });
361361
362 if (!generation.id) {362 if (!generation.id) {
363 console.error('Image generation request is not satisfyable:', generation.message || 'unknown error');363 console.warn('Image generation request is not satisfyable:', generation.message || 'unknown error');
364 return response.sendStatus(400);364 return response.sendStatus(400);
365 }365 }
366366
367 console.log('Horde image generation request:', generation);367 console.info('Horde image generation request:', generation);
368368
369 const controller = new AbortController();369 const controller = new AbortController();
370 request.socket.removeAllListeners('close');370 request.socket.removeAllListeners('close');
371 request.socket.on('close', function () {371 request.socket.on('close', function () {
372 console.log('Horde image generation request aborted.');372 console.warn('Horde image generation request aborted.');
373 controller.abort();373 controller.abort();
374 if (generation.id) ai_horde.deleteImageGenerationRequest(generation.id);374 if (generation.id) ai_horde.deleteImageGenerationRequest(generation.id);
375 });375 });
@@ -378,7 +378,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
378 controller.signal.throwIfAborted();378 controller.signal.throwIfAborted();
379 await delay(CHECK_INTERVAL);379 await delay(CHECK_INTERVAL);
380 const check = await ai_horde.getImageGenerationCheck(generation.id);380 const check = await ai_horde.getImageGenerationCheck(generation.id);
381 console.log(check);381 console.info(check);
382382
383 if (check.done) {383 if (check.done) {
384 const result = await ai_horde.getImageGenerationStatus(generation.id);384 const result = await ai_horde.getImageGenerationStatus(generation.id);
src/endpoints/images.js+1 -1
@@ -71,7 +71,7 @@ router.post('/upload', jsonParser, async (request, response) => {
71 await fs.promises.writeFile(pathToNewFile, new Uint8Array(imageBuffer));71 await fs.promises.writeFile(pathToNewFile, new Uint8Array(imageBuffer));
72 response.send({ path: clientRelativePath(request.user.directories.root, pathToNewFile) });72 response.send({ path: clientRelativePath(request.user.directories.root, pathToNewFile) });
73 } catch (error) {73 } catch (error) {
74 console.log(error);74 console.error(error);
75 response.status(500).send({ error: 'Failed to save the image' });75 response.status(500).send({ error: 'Failed to save the image' });
76 }76 }
77});77});
src/endpoints/novelai.js+16 -16
@@ -120,7 +120,7 @@ router.post('/status', jsonParser, async function (req, res) {
120 const api_key_novel = readSecret(req.user.directories, SECRET_KEYS.NOVEL);120 const api_key_novel = readSecret(req.user.directories, SECRET_KEYS.NOVEL);
121121
122 if (!api_key_novel) {122 if (!api_key_novel) {
123 console.log('NovelAI Access Token is missing.');123 console.warn('NovelAI Access Token is missing.');
124 return res.sendStatus(400);124 return res.sendStatus(400);
125 }125 }
126126
@@ -137,15 +137,15 @@ router.post('/status', jsonParser, async function (req, res) {
137 const data = await response.json();137 const data = await response.json();
138 return res.send(data);138 return res.send(data);
139 } else if (response.status == 401) {139 } else if (response.status == 401) {
140 console.log('NovelAI Access Token is incorrect.');140 console.error('NovelAI Access Token is incorrect.');
141 return res.send({ error: true });141 return res.send({ error: true });
142 }142 }
143 else {143 else {
144 console.log('NovelAI returned an error:', response.statusText);144 console.warn('NovelAI returned an error:', response.statusText);
145 return res.send({ error: true });145 return res.send({ error: true });
146 }146 }
147 } catch (error) {147 } catch (error) {
148 console.log(error);148 console.error(error);
149 return res.send({ error: true });149 return res.send({ error: true });
150 }150 }
151});151});
@@ -156,7 +156,7 @@ router.post('/generate', jsonParser, async function (req, res) {
156 const api_key_novel = readSecret(req.user.directories, SECRET_KEYS.NOVEL);156 const api_key_novel = readSecret(req.user.directories, SECRET_KEYS.NOVEL);
157157
158 if (!api_key_novel) {158 if (!api_key_novel) {
159 console.log('NovelAI Access Token is missing.');159 console.warn('NovelAI Access Token is missing.');
160 return res.sendStatus(400);160 return res.sendStatus(400);
161 }161 }
162162
@@ -241,7 +241,7 @@ router.post('/generate', jsonParser, async function (req, res) {
241 }241 }
242 }242 }
243243
244 console.log(util.inspect(data, { depth: 4 }));244 console.debug(util.inspect(data, { depth: 4 }));
245245
246 const args = {246 const args = {
247 body: JSON.stringify(data),247 body: JSON.stringify(data),
@@ -261,7 +261,7 @@ router.post('/generate', jsonParser, async function (req, res) {
261 if (!response.ok) {261 if (!response.ok) {
262 const text = await response.text();262 const text = await response.text();
263 let message = text;263 let message = text;
264 console.log(`Novel API returned error: ${response.status} ${response.statusText} ${text}`);264 console.warn(`Novel API returned error: ${response.status} ${response.statusText} ${text}`);
265265
266 try {266 try {
267 const data = JSON.parse(text);267 const data = JSON.parse(text);
@@ -276,7 +276,7 @@ router.post('/generate', jsonParser, async function (req, res) {
276276
277 /** @type {any} */277 /** @type {any} */
278 const data = await response.json();278 const data = await response.json();
279 console.log('NovelAI Output', data?.output);279 console.info('NovelAI Output', data?.output);
280 return res.send(data);280 return res.send(data);
281 }281 }
282 } catch (error) {282 } catch (error) {
@@ -292,12 +292,12 @@ router.post('/generate-image', jsonParser, async (request, response) => {
292 const key = readSecret(request.user.directories, SECRET_KEYS.NOVEL);292 const key = readSecret(request.user.directories, SECRET_KEYS.NOVEL);
293293
294 if (!key) {294 if (!key) {
295 console.log('NovelAI Access Token is missing.');295 console.warn('NovelAI Access Token is missing.');
296 return response.sendStatus(400);296 return response.sendStatus(400);
297 }297 }
298298
299 try {299 try {
300 console.log('NAI Diffusion request:', request.body);300 console.debug('NAI Diffusion request:', request.body);
301 const generateUrl = `${IMAGE_NOVELAI}/ai/generate-image`;301 const generateUrl = `${IMAGE_NOVELAI}/ai/generate-image`;
302 const generateResult = await fetch(generateUrl, {302 const generateResult = await fetch(generateUrl, {
303 method: 'POST',303 method: 'POST',
@@ -358,7 +358,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
358358
359 if (!generateResult.ok) {359 if (!generateResult.ok) {
360 const text = await generateResult.text();360 const text = await generateResult.text();
361 console.log('NovelAI returned an error.', generateResult.statusText, text);361 console.warn('NovelAI returned an error.', generateResult.statusText, text);
362 return response.sendStatus(500);362 return response.sendStatus(500);
363 }363 }
364364
@@ -366,7 +366,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
366 const imageBuffer = await extractFileFromZipBuffer(archiveBuffer, '.png');366 const imageBuffer = await extractFileFromZipBuffer(archiveBuffer, '.png');
367367
368 if (!imageBuffer) {368 if (!imageBuffer) {
369 console.warn('NovelAI generated an image, but the PNG file was not found.');369 console.error('NovelAI generated an image, but the PNG file was not found.');
370 return response.sendStatus(500);370 return response.sendStatus(500);
371 }371 }
372372
@@ -378,7 +378,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
378 }378 }
379379
380 try {380 try {
381 console.debug('Upscaling image...');381 console.info('Upscaling image...');
382 const upscaleUrl = `${API_NOVELAI}/ai/upscale`;382 const upscaleUrl = `${API_NOVELAI}/ai/upscale`;
383 const upscaleResult = await fetch(upscaleUrl, {383 const upscaleResult = await fetch(upscaleUrl, {
384 method: 'POST',384 method: 'POST',
@@ -413,7 +413,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
413 return response.send(originalBase64);413 return response.send(originalBase64);
414 }414 }
415 } catch (error) {415 } catch (error) {
416 console.log(error);416 console.error(error);
417 return response.sendStatus(500);417 return response.sendStatus(500);
418 }418 }
419});419});
@@ -422,7 +422,7 @@ router.post('/generate-voice', jsonParser, async (request, response) => {
422 const token = readSecret(request.user.directories, SECRET_KEYS.NOVEL);422 const token = readSecret(request.user.directories, SECRET_KEYS.NOVEL);
423423
424 if (!token) {424 if (!token) {
425 console.log('NovelAI Access Token is missing.');425 console.error('NovelAI Access Token is missing.');
426 return response.sendStatus(400);426 return response.sendStatus(400);
427 }427 }
428428
@@ -445,7 +445,7 @@ router.post('/generate-voice', jsonParser, async (request, response) => {
445445
446 if (!result.ok) {446 if (!result.ok) {
447 const errorText = await result.text();447 const errorText = await result.text();
448 console.log('NovelAI returned an error.', result.statusText, errorText);448 console.error('NovelAI returned an error.', result.statusText, errorText);
449 return response.sendStatus(500);449 return response.sendStatus(500);
450 }450 }
451451
src/endpoints/openai.js+16 -16
@@ -63,7 +63,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
63 }63 }
6464
65 if (!key && !request.body.reverse_proxy && ['custom', 'ooba', 'koboldcpp', 'vllm'].includes(request.body.api) === false) {65 if (!key && !request.body.reverse_proxy && ['custom', 'ooba', 'koboldcpp', 'vllm'].includes(request.body.api) === false) {
66 console.log('No key found for API', request.body.api);66 console.warn('No key found for API', request.body.api);
67 return response.sendStatus(400);67 return response.sendStatus(400);
68 }68 }
6969
@@ -93,7 +93,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
93 excludeKeysByYaml(body, request.body.custom_exclude_body);93 excludeKeysByYaml(body, request.body.custom_exclude_body);
94 }94 }
9595
96 console.log('Multimodal captioning request', body);96 console.debug('Multimodal captioning request', body);
9797
98 let apiUrl = '';98 let apiUrl = '';
9999
@@ -158,13 +158,13 @@ router.post('/caption-image', jsonParser, async (request, response) => {
158158
159 if (!result.ok) {159 if (!result.ok) {
160 const text = await result.text();160 const text = await result.text();
161 console.log('Multimodal captioning request failed', result.statusText, text);161 console.warn('Multimodal captioning request failed', result.statusText, text);
162 return response.status(500).send(text);162 return response.status(500).send(text);
163 }163 }
164164
165 /** @type {any} */165 /** @type {any} */
166 const data = await result.json();166 const data = await result.json();
167 console.log('Multimodal captioning response', data);167 console.info('Multimodal captioning response', data);
168 const caption = data?.choices[0]?.message?.content;168 const caption = data?.choices[0]?.message?.content;
169169
170 if (!caption) {170 if (!caption) {
@@ -184,17 +184,17 @@ router.post('/transcribe-audio', urlencodedParser, async (request, response) =>
184 const key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);184 const key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);
185185
186 if (!key) {186 if (!key) {
187 console.log('No OpenAI key found');187 console.warn('No OpenAI key found');
188 return response.sendStatus(400);188 return response.sendStatus(400);
189 }189 }
190190
191 if (!request.file) {191 if (!request.file) {
192 console.log('No audio file found');192 console.warn('No audio file found');
193 return response.sendStatus(400);193 return response.sendStatus(400);
194 }194 }
195195
196 const formData = new FormData();196 const formData = new FormData();
197 console.log('Processing audio file', request.file.path);197 console.info('Processing audio file', request.file.path);
198 formData.append('file', fs.createReadStream(request.file.path), { filename: 'audio.wav', contentType: 'audio/wav' });198 formData.append('file', fs.createReadStream(request.file.path), { filename: 'audio.wav', contentType: 'audio/wav' });
199 formData.append('model', request.body.model);199 formData.append('model', request.body.model);
200200
@@ -213,13 +213,13 @@ router.post('/transcribe-audio', urlencodedParser, async (request, response) =>
213213
214 if (!result.ok) {214 if (!result.ok) {
215 const text = await result.text();215 const text = await result.text();
216 console.log('OpenAI request failed', result.statusText, text);216 console.warn('OpenAI request failed', result.statusText, text);
217 return response.status(500).send(text);217 return response.status(500).send(text);
218 }218 }
219219
220 fs.rmSync(request.file.path);220 fs.rmSync(request.file.path);
221 const data = await result.json();221 const data = await result.json();
222 console.log('OpenAI transcription response', data);222 console.debug('OpenAI transcription response', data);
223 return response.json(data);223 return response.json(data);
224 } catch (error) {224 } catch (error) {
225 console.error('OpenAI transcription failed', error);225 console.error('OpenAI transcription failed', error);
@@ -232,7 +232,7 @@ router.post('/generate-voice', jsonParser, async (request, response) => {
232 const key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);232 const key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);
233233
234 if (!key) {234 if (!key) {
235 console.log('No OpenAI key found');235 console.warn('No OpenAI key found');
236 return response.sendStatus(400);236 return response.sendStatus(400);
237 }237 }
238238
@@ -253,7 +253,7 @@ router.post('/generate-voice', jsonParser, async (request, response) => {
253253
254 if (!result.ok) {254 if (!result.ok) {
255 const text = await result.text();255 const text = await result.text();
256 console.log('OpenAI request failed', result.statusText, text);256 console.warn('OpenAI request failed', result.statusText, text);
257 return response.status(500).send(text);257 return response.status(500).send(text);
258 }258 }
259259
@@ -271,11 +271,11 @@ router.post('/generate-image', jsonParser, async (request, response) => {
271 const key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);271 const key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);
272272
273 if (!key) {273 if (!key) {
274 console.log('No OpenAI key found');274 console.warn('No OpenAI key found');
275 return response.sendStatus(400);275 return response.sendStatus(400);
276 }276 }
277277
278 console.log('OpenAI request', request.body);278 console.debug('OpenAI request', request.body);
279279
280 const result = await fetch('https://api.openai.com/v1/images/generations', {280 const result = await fetch('https://api.openai.com/v1/images/generations', {
281 method: 'POST',281 method: 'POST',
@@ -288,7 +288,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
288288
289 if (!result.ok) {289 if (!result.ok) {
290 const text = await result.text();290 const text = await result.text();
291 console.log('OpenAI request failed', result.statusText, text);291 console.warn('OpenAI request failed', result.statusText, text);
292 return response.status(500).send(text);292 return response.status(500).send(text);
293 }293 }
294294
@@ -308,7 +308,7 @@ custom.post('/generate-voice', jsonParser, async (request, response) => {
308 const { input, provider_endpoint, response_format, voice, speed, model } = request.body;308 const { input, provider_endpoint, response_format, voice, speed, model } = request.body;
309309
310 if (!provider_endpoint) {310 if (!provider_endpoint) {
311 console.log('No OpenAI-compatible TTS provider endpoint provided');311 console.warn('No OpenAI-compatible TTS provider endpoint provided');
312 return response.sendStatus(400);312 return response.sendStatus(400);
313 }313 }
314314
@@ -329,7 +329,7 @@ custom.post('/generate-voice', jsonParser, async (request, response) => {
329329
330 if (!result.ok) {330 if (!result.ok) {
331 const text = await result.text();331 const text = await result.text();
332 console.log('OpenAI request failed', result.statusText, text);332 console.warn('OpenAI request failed', result.statusText, text);
333 return response.status(500).send(text);333 return response.status(500).send(text);
334 }334 }
335335
src/endpoints/presets.js+1 -1
@@ -96,7 +96,7 @@ router.post('/restore', jsonParser, function (request, response) {
9696
97 return response.send(result);97 return response.send(result);
98 } catch (error) {98 } catch (error) {
99 console.log(error);99 console.error(error);
100 return response.sendStatus(500);100 return response.sendStatus(500);
101 }101 }
102});102});
src/endpoints/search.js+21 -21
@@ -96,25 +96,25 @@ router.post('/serpapi', jsonParser, async (request, response) => {
96 const key = readSecret(request.user.directories, SECRET_KEYS.SERPAPI);96 const key = readSecret(request.user.directories, SECRET_KEYS.SERPAPI);
9797
98 if (!key) {98 if (!key) {
99 console.log('No SerpApi key found');99 console.error('No SerpApi key found');
100 return response.sendStatus(400);100 return response.sendStatus(400);
101 }101 }
102102
103 const { query } = request.body;103 const { query } = request.body;
104 const result = await fetch(`https://serpapi.com/search.json?q=${encodeURIComponent(query)}&api_key=${key}`);104 const result = await fetch(`https://serpapi.com/search.json?q=${encodeURIComponent(query)}&api_key=${key}`);
105105
106 console.log('SerpApi query', query);106 console.debug('SerpApi query', query);
107107
108 if (!result.ok) {108 if (!result.ok) {
109 const text = await result.text();109 const text = await result.text();
110 console.log('SerpApi request failed', result.statusText, text);110 console.error('SerpApi request failed', result.statusText, text);
111 return response.status(500).send(text);111 return response.status(500).send(text);
112 }112 }
113113
114 const data = await result.json();114 const data = await result.json();
115 return response.json(data);115 return response.json(data);
116 } catch (error) {116 } catch (error) {
117 console.log(error);117 console.error(error);
118 return response.sendStatus(500);118 return response.sendStatus(500);
119 }119 }
120});120});
@@ -130,7 +130,7 @@ router.post('/transcript', jsonParser, async (request, response) => {
130 const json = request.body.json;130 const json = request.body.json;
131131
132 if (!id) {132 if (!id) {
133 console.log('Id is required for /transcript');133 console.error('Id is required for /transcript');
134 return response.sendStatus(400);134 return response.sendStatus(400);
135 }135 }
136136
@@ -155,7 +155,7 @@ router.post('/transcript', jsonParser, async (request, response) => {
155 throw error;155 throw error;
156 }156 }
157 } catch (error) {157 } catch (error) {
158 console.log(error);158 console.error(error);
159 return response.sendStatus(500);159 return response.sendStatus(500);
160 }160 }
161});161});
@@ -165,17 +165,17 @@ router.post('/searxng', jsonParser, async (request, response) => {
165 const { baseUrl, query, preferences, categories } = request.body;165 const { baseUrl, query, preferences, categories } = request.body;
166166
167 if (!baseUrl || !query) {167 if (!baseUrl || !query) {
168 console.log('Missing required parameters for /searxng');168 console.error('Missing required parameters for /searxng');
169 return response.sendStatus(400);169 return response.sendStatus(400);
170 }170 }
171171
172 console.log('SearXNG query', baseUrl, query);172 console.debug('SearXNG query', baseUrl, query);
173173
174 const mainPageUrl = new URL(baseUrl);174 const mainPageUrl = new URL(baseUrl);
175 const mainPageRequest = await fetch(mainPageUrl, { headers: visitHeaders });175 const mainPageRequest = await fetch(mainPageUrl, { headers: visitHeaders });
176176
177 if (!mainPageRequest.ok) {177 if (!mainPageRequest.ok) {
178 console.log('SearXNG request failed', mainPageRequest.statusText);178 console.error('SearXNG request failed', mainPageRequest.statusText);
179 return response.sendStatus(500);179 return response.sendStatus(500);
180 }180 }
181181
@@ -202,14 +202,14 @@ router.post('/searxng', jsonParser, async (request, response) => {
202202
203 if (!searchResult.ok) {203 if (!searchResult.ok) {
204 const text = await searchResult.text();204 const text = await searchResult.text();
205 console.log('SearXNG request failed', searchResult.statusText, text);205 console.error('SearXNG request failed', searchResult.statusText, text);
206 return response.sendStatus(500);206 return response.sendStatus(500);
207 }207 }
208208
209 const data = await searchResult.text();209 const data = await searchResult.text();
210 return response.send(data);210 return response.send(data);
211 } catch (error) {211 } catch (error) {
212 console.log('SearXNG request failed', error);212 console.error('SearXNG request failed', error);
213 return response.sendStatus(500);213 return response.sendStatus(500);
214 }214 }
215});215});
@@ -219,7 +219,7 @@ router.post('/tavily', jsonParser, async (request, response) => {
219 const apiKey = readSecret(request.user.directories, SECRET_KEYS.TAVILY);219 const apiKey = readSecret(request.user.directories, SECRET_KEYS.TAVILY);
220220
221 if (!apiKey) {221 if (!apiKey) {
222 console.log('No Tavily key found');222 console.error('No Tavily key found');
223 return response.sendStatus(400);223 return response.sendStatus(400);
224 }224 }
225225
@@ -246,18 +246,18 @@ router.post('/tavily', jsonParser, async (request, response) => {
246 body: JSON.stringify(body),246 body: JSON.stringify(body),
247 });247 });
248248
249 console.log('Tavily query', query);249 console.debug('Tavily query', query);
250250
251 if (!result.ok) {251 if (!result.ok) {
252 const text = await result.text();252 const text = await result.text();
253 console.log('Tavily request failed', result.statusText, text);253 console.error('Tavily request failed', result.statusText, text);
254 return response.status(500).send(text);254 return response.status(500).send(text);
255 }255 }
256256
257 const data = await result.json();257 const data = await result.json();
258 return response.json(data);258 return response.json(data);
259 } catch (error) {259 } catch (error) {
260 console.log(error);260 console.error(error);
261 return response.sendStatus(500);261 return response.sendStatus(500);
262 }262 }
263});263});
@@ -303,7 +303,7 @@ router.post('/visit', jsonParser, async (request, response) => {
303 const html = Boolean(request.body.html ?? true);303 const html = Boolean(request.body.html ?? true);
304304
305 if (!url) {305 if (!url) {
306 console.log('No url provided for /visit');306 console.error('No url provided for /visit');
307 return response.sendStatus(400);307 return response.sendStatus(400);
308 }308 }
309309
@@ -330,16 +330,16 @@ router.post('/visit', jsonParser, async (request, response) => {
330 throw new Error('Invalid hostname');330 throw new Error('Invalid hostname');
331 }331 }
332 } catch (error) {332 } catch (error) {
333 console.log('Invalid url provided for /visit', url);333 console.error('Invalid url provided for /visit', url);
334 return response.sendStatus(400);334 return response.sendStatus(400);
335 }335 }
336336
337 console.log('Visiting web URL', url);337 console.info('Visiting web URL', url);
338338
339 const result = await fetch(url, { headers: visitHeaders });339 const result = await fetch(url, { headers: visitHeaders });
340340
341 if (!result.ok) {341 if (!result.ok) {
342 console.log(`Visit failed ${result.status} ${result.statusText}`);342 console.error(`Visit failed ${result.status} ${result.statusText}`);
343 return response.sendStatus(500);343 return response.sendStatus(500);
344 }344 }
345345
@@ -347,7 +347,7 @@ router.post('/visit', jsonParser, async (request, response) => {
347347
348 if (html) {348 if (html) {
349 if (!contentType.includes('text/html')) {349 if (!contentType.includes('text/html')) {
350 console.log(`Visit failed, content-type is ${contentType}, expected text/html`);350 console.error(`Visit failed, content-type is ${contentType}, expected text/html`);
351 return response.sendStatus(500);351 return response.sendStatus(500);
352 }352 }
353353
@@ -359,7 +359,7 @@ router.post('/visit', jsonParser, async (request, response) => {
359 const buffer = await result.arrayBuffer();359 const buffer = await result.arrayBuffer();
360 return response.send(Buffer.from(buffer));360 return response.send(Buffer.from(buffer));
361 } catch (error) {361 } catch (error) {
362 console.log(error);362 console.error(error);
363 return response.sendStatus(500);363 return response.sendStatus(500);
364 }364 }
365});365});
src/endpoints/secrets.js+1 -1
@@ -151,7 +151,7 @@ export function getAllSecrets(directories) {
151 const filePath = path.join(directories.root, SECRETS_FILE);151 const filePath = path.join(directories.root, SECRETS_FILE);
152152
153 if (!fs.existsSync(filePath)) {153 if (!fs.existsSync(filePath)) {
154 console.log('Secrets file does not exist');154 console.error('Secrets file does not exist');
155 return undefined;155 return undefined;
156 }156 }
157157
src/endpoints/settings.js+7 -7
@@ -105,7 +105,7 @@ function readPresetsFromDirectory(directoryPath, options = {}) {
105 fileNames.push(removeFileExtension ? item.replace(/\.[^/.]+$/, '') : item);105 fileNames.push(removeFileExtension ? item.replace(/\.[^/.]+$/, '') : item);
106 } catch {106 } catch {
107 // skip107 // skip
108 console.log(`${item} is not a valid JSON`);108 console.warn(`${item} is not a valid JSON`);
109 }109 }
110 });110 });
111111
@@ -120,7 +120,7 @@ async function backupSettings() {
120 backupUserSettings(handle, true);120 backupUserSettings(handle, true);
121 }121 }
122 } catch (err) {122 } catch (err) {
123 console.log('Could not backup settings file', err);123 console.error('Could not backup settings file', err);
124 }124 }
125}125}
126126
@@ -202,7 +202,7 @@ router.post('/save', jsonParser, function (request, response) {
202 triggerAutoSave(request.user.profile.handle);202 triggerAutoSave(request.user.profile.handle);
203 response.send({ result: 'ok' });203 response.send({ result: 'ok' });
204 } catch (err) {204 } catch (err) {
205 console.log(err);205 console.error(err);
206 response.send(err);206 response.send(err);
207 }207 }
208});208});
@@ -292,7 +292,7 @@ router.post('/get-snapshots', jsonParser, async (request, response) => {
292292
293 response.json(result);293 response.json(result);
294 } catch (error) {294 } catch (error) {
295 console.log(error);295 console.error(error);
296 response.sendStatus(500);296 response.sendStatus(500);
297 }297 }
298});298});
@@ -316,7 +316,7 @@ router.post('/load-snapshot', jsonParser, getFileNameValidationFunction('name'),
316316
317 response.send(content);317 response.send(content);
318 } catch (error) {318 } catch (error) {
319 console.log(error);319 console.error(error);
320 response.sendStatus(500);320 response.sendStatus(500);
321 }321 }
322});322});
@@ -326,7 +326,7 @@ router.post('/make-snapshot', jsonParser, async (request, response) => {
326 backupUserSettings(request.user.profile.handle, false);326 backupUserSettings(request.user.profile.handle, false);
327 response.sendStatus(204);327 response.sendStatus(204);
328 } catch (error) {328 } catch (error) {
329 console.log(error);329 console.error(error);
330 response.sendStatus(500);330 response.sendStatus(500);
331 }331 }
332});332});
@@ -352,7 +352,7 @@ router.post('/restore-snapshot', jsonParser, getFileNameValidationFunction('name
352352
353 response.sendStatus(204);353 response.sendStatus(204);
354 } catch (error) {354 } catch (error) {
355 console.log(error);355 console.error(error);
356 response.sendStatus(500);356 response.sendStatus(500);
357 }357 }
358});358});
src/endpoints/speech.js+3 -3
@@ -43,8 +43,8 @@ router.post('/recognize', jsonParser, async (req, res) => {
43 const start = performance.now();43 const start = performance.now();
44 const result = await pipe(wav, { language: lang || null, task: 'transcribe' });44 const result = await pipe(wav, { language: lang || null, task: 'transcribe' });
45 const end = performance.now();45 const end = performance.now();
46 console.log(`Execution duration: ${(end - start) / 1000} seconds`);46 console.info(`Execution duration: ${(end - start) / 1000} seconds`);
47 console.log('Transcribed audio:', result.text);47 console.info('Transcribed audio:', result.text);
4848
49 return res.json({ text: result.text });49 return res.json({ text: result.text });
50 } catch (error) {50 } catch (error) {
@@ -64,7 +64,7 @@ router.post('/synthesize', jsonParser, async (req, res) => {
64 const start = performance.now();64 const start = performance.now();
65 const result = await pipe(text, { speaker_embeddings: speaker_embeddings });65 const result = await pipe(text, { speaker_embeddings: speaker_embeddings });
66 const end = performance.now();66 const end = performance.now();
67 console.log(`Execution duration: ${(end - start) / 1000} seconds`);67 console.debug(`Execution duration: ${(end - start) / 1000} seconds`);
6868
69 const wav = new wavefile.WaveFile();69 const wav = new wavefile.WaveFile();
70 wav.fromScratch(1, result.sampling_rate, '32f', result.audio);70 wav.fromScratch(1, result.sampling_rate, '32f', result.audio);
src/endpoints/sprites.js+3 -3
@@ -82,14 +82,14 @@ export function importRisuSprites(directories, data) {
82 return;82 return;
83 }83 }
8484
85 console.log(`RisuAI: Found ${images.length} sprites for ${name}. Writing to disk.`);85 console.info(`RisuAI: Found ${images.length} sprites for ${name}. Writing to disk.`);
86 const files = fs.readdirSync(spritesPath);86 const files = fs.readdirSync(spritesPath);
8787
88 outer: for (const [label, fileBase64] of images) {88 outer: for (const [label, fileBase64] of images) {
89 // Remove existing sprite with the same label89 // Remove existing sprite with the same label
90 for (const file of files) {90 for (const file of files) {
91 if (path.parse(file).name === label) {91 if (path.parse(file).name === label) {
92 console.log(`RisuAI: The sprite ${label} for ${name} already exists. Skipping.`);92 console.warn(`RisuAI: The sprite ${label} for ${name} already exists. Skipping.`);
93 continue outer;93 continue outer;
94 }94 }
95 }95 }
@@ -133,7 +133,7 @@ router.get('/get', jsonParser, function (request, response) {
133 }133 }
134 }134 }
135 catch (err) {135 catch (err) {
136 console.log(err);136 console.error(err);
137 }137 }
138 return response.send(sprites);138 return response.send(sprites);
139});139});
src/endpoints/stable-diffusion.js+74 -74
@@ -45,7 +45,7 @@ router.post('/ping', jsonParser, async (request, response) => {
4545
46 return response.sendStatus(200);46 return response.sendStatus(200);
47 } catch (error) {47 } catch (error) {
48 console.log(error);48 console.error(error);
49 return response.sendStatus(500);49 return response.sendStatus(500);
50 }50 }
51});51});
@@ -99,7 +99,7 @@ router.post('/upscalers', jsonParser, async (request, response) => {
9999
100 return response.send(upscalers);100 return response.send(upscalers);
101 } catch (error) {101 } catch (error) {
102 console.log(error);102 console.error(error);
103 return response.sendStatus(500);103 return response.sendStatus(500);
104 }104 }
105});105});
@@ -131,7 +131,7 @@ router.post('/vaes', jsonParser, async (request, response) => {
131 const names = data.map(x => x.model_name);131 const names = data.map(x => x.model_name);
132 return response.send(names);132 return response.send(names);
133 } catch (error) {133 } catch (error) {
134 console.log(error);134 console.error(error);
135 return response.sendStatus(500);135 return response.sendStatus(500);
136 }136 }
137});137});
@@ -158,7 +158,7 @@ router.post('/samplers', jsonParser, async (request, response) => {
158 return response.send(names);158 return response.send(names);
159159
160 } catch (error) {160 } catch (error) {
161 console.log(error);161 console.error(error);
162 return response.sendStatus(500);162 return response.sendStatus(500);
163 }163 }
164});164});
@@ -184,7 +184,7 @@ router.post('/schedulers', jsonParser, async (request, response) => {
184 const names = data.map(x => x.name);184 const names = data.map(x => x.name);
185 return response.send(names);185 return response.send(names);
186 } catch (error) {186 } catch (error) {
187 console.log(error);187 console.error(error);
188 return response.sendStatus(500);188 return response.sendStatus(500);
189 }189 }
190});190});
@@ -210,7 +210,7 @@ router.post('/models', jsonParser, async (request, response) => {
210 const models = data.map(x => ({ value: x.title, text: x.title }));210 const models = data.map(x => ({ value: x.title, text: x.title }));
211 return response.send(models);211 return response.send(models);
212 } catch (error) {212 } catch (error) {
213 console.log(error);213 console.error(error);
214 return response.sendStatus(500);214 return response.sendStatus(500);
215 }215 }
216});216});
@@ -230,7 +230,7 @@ router.post('/get-model', jsonParser, async (request, response) => {
230 const data = await result.json();230 const data = await result.json();
231 return response.send(data['sd_model_checkpoint']);231 return response.send(data['sd_model_checkpoint']);
232 } catch (error) {232 } catch (error) {
233 console.log(error);233 console.error(error);
234 return response.sendStatus(500);234 return response.sendStatus(500);
235 }235 }
236});236});
@@ -283,13 +283,13 @@ router.post('/set-model', jsonParser, async (request, response) => {
283 break;283 break;
284 }284 }
285285
286 console.log(`Waiting for SD WebUI to finish model loading... Progress: ${progress}; Job count: ${jobCount}`);286 console.info(`Waiting for SD WebUI to finish model loading... Progress: ${progress}; Job count: ${jobCount}`);
287 await delay(CHECK_INTERVAL);287 await delay(CHECK_INTERVAL);
288 }288 }
289289
290 return response.sendStatus(200);290 return response.sendStatus(200);
291 } catch (error) {291 } catch (error) {
292 console.log(error);292 console.error(error);
293 return response.sendStatus(500);293 return response.sendStatus(500);
294 }294 }
295});295});
@@ -309,7 +309,7 @@ router.post('/generate', jsonParser, async (request, response) => {
309 }309 }
310 }310 }
311 } catch (error) {311 } catch (error) {
312 console.log('SD WebUI failed to get options:', error);312 console.error('SD WebUI failed to get options:', error);
313 }313 }
314314
315 const controller = new AbortController();315 const controller = new AbortController();
@@ -323,7 +323,7 @@ router.post('/generate', jsonParser, async (request, response) => {
323 controller.abort();323 controller.abort();
324 });324 });
325325
326 console.log('SD WebUI request:', request.body);326 console.debug('SD WebUI request:', request.body);
327 const txt2imgUrl = new URL(request.body.url);327 const txt2imgUrl = new URL(request.body.url);
328 txt2imgUrl.pathname = '/sdapi/v1/txt2img';328 txt2imgUrl.pathname = '/sdapi/v1/txt2img';
329 const result = await fetch(txt2imgUrl, {329 const result = await fetch(txt2imgUrl, {
@@ -344,7 +344,7 @@ router.post('/generate', jsonParser, async (request, response) => {
344 const data = await result.json();344 const data = await result.json();
345 return response.send(data);345 return response.send(data);
346 } catch (error) {346 } catch (error) {
347 console.log(error);347 console.error(error);
348 return response.sendStatus(500);348 return response.sendStatus(500);
349 }349 }
350});350});
@@ -377,7 +377,7 @@ router.post('/sd-next/upscalers', jsonParser, async (request, response) => {
377377
378 return response.send(names);378 return response.send(names);
379 } catch (error) {379 } catch (error) {
380 console.log(error);380 console.error(error);
381 return response.sendStatus(500);381 return response.sendStatus(500);
382 }382 }
383});383});
@@ -395,7 +395,7 @@ comfy.post('/ping', jsonParser, async (request, response) => {
395395
396 return response.sendStatus(200);396 return response.sendStatus(200);
397 } catch (error) {397 } catch (error) {
398 console.log(error);398 console.error(error);
399 return response.sendStatus(500);399 return response.sendStatus(500);
400 }400 }
401});401});
@@ -413,7 +413,7 @@ comfy.post('/samplers', jsonParser, async (request, response) => {
413 const data = await result.json();413 const data = await result.json();
414 return response.send(data.KSampler.input.required.sampler_name[0]);414 return response.send(data.KSampler.input.required.sampler_name[0]);
415 } catch (error) {415 } catch (error) {
416 console.log(error);416 console.error(error);
417 return response.sendStatus(500);417 return response.sendStatus(500);
418 }418 }
419});419});
@@ -441,7 +441,7 @@ comfy.post('/models', jsonParser, async (request, response) => {
441441
442 return response.send(models);442 return response.send(models);
443 } catch (error) {443 } catch (error) {
444 console.log(error);444 console.error(error);
445 return response.sendStatus(500);445 return response.sendStatus(500);
446 }446 }
447});447});
@@ -459,7 +459,7 @@ comfy.post('/schedulers', jsonParser, async (request, response) => {
459 const data = await result.json();459 const data = await result.json();
460 return response.send(data.KSampler.input.required.scheduler[0]);460 return response.send(data.KSampler.input.required.scheduler[0]);
461 } catch (error) {461 } catch (error) {
462 console.log(error);462 console.error(error);
463 return response.sendStatus(500);463 return response.sendStatus(500);
464 }464 }
465});465});
@@ -477,7 +477,7 @@ comfy.post('/vaes', jsonParser, async (request, response) => {
477 const data = await result.json();477 const data = await result.json();
478 return response.send(data.VAELoader.input.required.vae_name[0]);478 return response.send(data.VAELoader.input.required.vae_name[0]);
479 } catch (error) {479 } catch (error) {
480 console.log(error);480 console.error(error);
481 return response.sendStatus(500);481 return response.sendStatus(500);
482 }482 }
483});483});
@@ -487,7 +487,7 @@ comfy.post('/workflows', jsonParser, async (request, response) => {
487 const data = getComfyWorkflows(request.user.directories);487 const data = getComfyWorkflows(request.user.directories);
488 return response.send(data);488 return response.send(data);
489 } catch (error) {489 } catch (error) {
490 console.log(error);490 console.error(error);
491 return response.sendStatus(500);491 return response.sendStatus(500);
492 }492 }
493});493});
@@ -501,7 +501,7 @@ comfy.post('/workflow', jsonParser, async (request, response) => {
501 const data = fs.readFileSync(filePath, { encoding: 'utf-8' });501 const data = fs.readFileSync(filePath, { encoding: 'utf-8' });
502 return response.send(JSON.stringify(data));502 return response.send(JSON.stringify(data));
503 } catch (error) {503 } catch (error) {
504 console.log(error);504 console.error(error);
505 return response.sendStatus(500);505 return response.sendStatus(500);
506 }506 }
507});507});
@@ -513,7 +513,7 @@ comfy.post('/save-workflow', jsonParser, async (request, response) => {
513 const data = getComfyWorkflows(request.user.directories);513 const data = getComfyWorkflows(request.user.directories);
514 return response.send(data);514 return response.send(data);
515 } catch (error) {515 } catch (error) {
516 console.log(error);516 console.error(error);
517 return response.sendStatus(500);517 return response.sendStatus(500);
518 }518 }
519});519});
@@ -526,7 +526,7 @@ comfy.post('/delete-workflow', jsonParser, async (request, response) => {
526 }526 }
527 return response.sendStatus(200);527 return response.sendStatus(200);
528 } catch (error) {528 } catch (error) {
529 console.log(error);529 console.error(error);
530 return response.sendStatus(500);530 return response.sendStatus(500);
531 }531 }
532});532});
@@ -591,7 +591,7 @@ comfy.post('/generate', jsonParser, async (request, response) => {
591 const imgBuffer = await imgResponse.arrayBuffer();591 const imgBuffer = await imgResponse.arrayBuffer();
592 return response.send(Buffer.from(imgBuffer).toString('base64'));592 return response.send(Buffer.from(imgBuffer).toString('base64'));
593 } catch (error) {593 } catch (error) {
594 console.log('ComfyUI error:', error);594 console.error('ComfyUI error:', error);
595 response.status(500).send(error.message);595 response.status(500).send(error.message);
596 return response;596 return response;
597 }597 }
@@ -604,7 +604,7 @@ together.post('/models', jsonParser, async (request, response) => {
604 const key = readSecret(request.user.directories, SECRET_KEYS.TOGETHERAI);604 const key = readSecret(request.user.directories, SECRET_KEYS.TOGETHERAI);
605605
606 if (!key) {606 if (!key) {
607 console.log('TogetherAI key not found.');607 console.warn('TogetherAI key not found.');
608 return response.sendStatus(400);608 return response.sendStatus(400);
609 }609 }
610610
@@ -616,14 +616,14 @@ together.post('/models', jsonParser, async (request, response) => {
616 });616 });
617617
618 if (!modelsResponse.ok) {618 if (!modelsResponse.ok) {
619 console.log('TogetherAI returned an error.');619 console.warn('TogetherAI returned an error.');
620 return response.sendStatus(500);620 return response.sendStatus(500);
621 }621 }
622622
623 const data = await modelsResponse.json();623 const data = await modelsResponse.json();
624624
625 if (!Array.isArray(data)) {625 if (!Array.isArray(data)) {
626 console.log('TogetherAI returned invalid data.');626 console.warn('TogetherAI returned invalid data.');
627 return response.sendStatus(500);627 return response.sendStatus(500);
628 }628 }
629629
@@ -633,7 +633,7 @@ together.post('/models', jsonParser, async (request, response) => {
633633
634 return response.send(models);634 return response.send(models);
635 } catch (error) {635 } catch (error) {
636 console.log(error);636 console.error(error);
637 return response.sendStatus(500);637 return response.sendStatus(500);
638 }638 }
639});639});
@@ -643,11 +643,11 @@ together.post('/generate', jsonParser, async (request, response) => {
643 const key = readSecret(request.user.directories, SECRET_KEYS.TOGETHERAI);643 const key = readSecret(request.user.directories, SECRET_KEYS.TOGETHERAI);
644644
645 if (!key) {645 if (!key) {
646 console.log('TogetherAI key not found.');646 console.warn('TogetherAI key not found.');
647 return response.sendStatus(400);647 return response.sendStatus(400);
648 }648 }
649649
650 console.log('TogetherAI request:', request.body);650 console.debug('TogetherAI request:', request.body);
651651
652 const result = await fetch('https://api.together.xyz/v1/images/generations', {652 const result = await fetch('https://api.together.xyz/v1/images/generations', {
653 method: 'POST',653 method: 'POST',
@@ -669,13 +669,13 @@ together.post('/generate', jsonParser, async (request, response) => {
669 });669 });
670670
671 if (!result.ok) {671 if (!result.ok) {
672 console.log('TogetherAI returned an error.', { body: await result.text() });672 console.warn('TogetherAI returned an error.', { body: await result.text() });
673 return response.sendStatus(500);673 return response.sendStatus(500);
674 }674 }
675675
676 /** @type {any} */676 /** @type {any} */
677 const data = await result.json();677 const data = await result.json();
678 console.log('TogetherAI response:', data);678 console.debug('TogetherAI response:', data);
679679
680 const choice = data?.data?.[0];680 const choice = data?.data?.[0];
681 let b64_json = choice.b64_json;681 let b64_json = choice.b64_json;
@@ -687,7 +687,7 @@ together.post('/generate', jsonParser, async (request, response) => {
687687
688 return response.send({ format: 'jpg', data: b64_json });688 return response.send({ format: 'jpg', data: b64_json });
689 } catch (error) {689 } catch (error) {
690 console.log(error);690 console.error(error);
691 return response.sendStatus(500);691 return response.sendStatus(500);
692 }692 }
693});693});
@@ -709,7 +709,7 @@ drawthings.post('/ping', jsonParser, async (request, response) => {
709709
710 return response.sendStatus(200);710 return response.sendStatus(200);
711 } catch (error) {711 } catch (error) {
712 console.log(error);712 console.error(error);
713 return response.sendStatus(500);713 return response.sendStatus(500);
714 }714 }
715});715});
@@ -728,7 +728,7 @@ drawthings.post('/get-model', jsonParser, async (request, response) => {
728728
729 return response.send(data['model']);729 return response.send(data['model']);
730 } catch (error) {730 } catch (error) {
731 console.log(error);731 console.error(error);
732 return response.sendStatus(500);732 return response.sendStatus(500);
733 }733 }
734});734});
@@ -747,14 +747,14 @@ drawthings.post('/get-upscaler', jsonParser, async (request, response) => {
747747
748 return response.send(data['upscaler']);748 return response.send(data['upscaler']);
749 } catch (error) {749 } catch (error) {
750 console.log(error);750 console.error(error);
751 return response.sendStatus(500);751 return response.sendStatus(500);
752 }752 }
753});753});
754754
755drawthings.post('/generate', jsonParser, async (request, response) => {755drawthings.post('/generate', jsonParser, async (request, response) => {
756 try {756 try {
757 console.log('SD DrawThings API request:', request.body);757 console.debug('SD DrawThings API request:', request.body);
758758
759 const url = new URL(request.body.url);759 const url = new URL(request.body.url);
760 url.pathname = '/sdapi/v1/txt2img';760 url.pathname = '/sdapi/v1/txt2img';
@@ -781,7 +781,7 @@ drawthings.post('/generate', jsonParser, async (request, response) => {
781 const data = await result.json();781 const data = await result.json();
782 return response.send(data);782 return response.send(data);
783 } catch (error) {783 } catch (error) {
784 console.log(error);784 console.error(error);
785 return response.sendStatus(500);785 return response.sendStatus(500);
786 }786 }
787});787});
@@ -794,21 +794,21 @@ pollinations.post('/models', jsonParser, async (_request, response) => {
794 const result = await fetch(modelsUrl);794 const result = await fetch(modelsUrl);
795795
796 if (!result.ok) {796 if (!result.ok) {
797 console.log('Pollinations returned an error.', result.status, result.statusText);797 console.warn('Pollinations returned an error.', result.status, result.statusText);
798 throw new Error('Pollinations request failed.');798 throw new Error('Pollinations request failed.');
799 }799 }
800800
801 const data = await result.json();801 const data = await result.json();
802802
803 if (!Array.isArray(data)) {803 if (!Array.isArray(data)) {
804 console.log('Pollinations returned invalid data.');804 console.warn('Pollinations returned invalid data.');
805 throw new Error('Pollinations request failed.');805 throw new Error('Pollinations request failed.');
806 }806 }
807807
808 const models = data.map(x => ({ value: x, text: x }));808 const models = data.map(x => ({ value: x, text: x }));
809 return response.send(models);809 return response.send(models);
810 } catch (error) {810 } catch (error) {
811 console.log(error);811 console.error(error);
812 return response.sendStatus(500);812 return response.sendStatus(500);
813 }813 }
814});814});
@@ -829,12 +829,12 @@ pollinations.post('/generate', jsonParser, async (request, response) => {
829 });829 });
830 promptUrl.search = params.toString();830 promptUrl.search = params.toString();
831831
832 console.log('Pollinations request URL:', promptUrl.toString());832 console.info('Pollinations request URL:', promptUrl.toString());
833833
834 const result = await fetch(promptUrl);834 const result = await fetch(promptUrl);
835835
836 if (!result.ok) {836 if (!result.ok) {
837 console.log('Pollinations returned an error.', result.status, result.statusText);837 console.warn('Pollinations returned an error.', result.status, result.statusText);
838 throw new Error('Pollinations request failed.');838 throw new Error('Pollinations request failed.');
839 }839 }
840840
@@ -843,7 +843,7 @@ pollinations.post('/generate', jsonParser, async (request, response) => {
843843
844 return response.send({ image: base64 });844 return response.send({ image: base64 });
845 } catch (error) {845 } catch (error) {
846 console.log(error);846 console.error(error);
847 return response.sendStatus(500);847 return response.sendStatus(500);
848 }848 }
849});849});
@@ -855,13 +855,13 @@ stability.post('/generate', jsonParser, async (request, response) => {
855 const key = readSecret(request.user.directories, SECRET_KEYS.STABILITY);855 const key = readSecret(request.user.directories, SECRET_KEYS.STABILITY);
856856
857 if (!key) {857 if (!key) {
858 console.log('Stability AI key not found.');858 console.warn('Stability AI key not found.');
859 return response.sendStatus(400);859 return response.sendStatus(400);
860 }860 }
861861
862 const { payload, model } = request.body;862 const { payload, model } = request.body;
863863
864 console.log('Stability AI request:', model, payload);864 console.debug('Stability AI request:', model, payload);
865865
866 const formData = new FormData();866 const formData = new FormData();
867 for (const [key, value] of Object.entries(payload)) {867 for (const [key, value] of Object.entries(payload)) {
@@ -896,14 +896,14 @@ stability.post('/generate', jsonParser, async (request, response) => {
896896
897 if (!result.ok) {897 if (!result.ok) {
898 const text = await result.text();898 const text = await result.text();
899 console.log('Stability AI returned an error.', result.status, result.statusText, text);899 console.warn('Stability AI returned an error.', result.status, result.statusText, text);
900 return response.sendStatus(500);900 return response.sendStatus(500);
901 }901 }
902902
903 const buffer = await result.arrayBuffer();903 const buffer = await result.arrayBuffer();
904 return response.send(Buffer.from(buffer).toString('base64'));904 return response.send(Buffer.from(buffer).toString('base64'));
905 } catch (error) {905 } catch (error) {
906 console.log(error);906 console.error(error);
907 return response.sendStatus(500);907 return response.sendStatus(500);
908 }908 }
909});909});
@@ -915,7 +915,7 @@ blockentropy.post('/models', jsonParser, async (request, response) => {
915 const key = readSecret(request.user.directories, SECRET_KEYS.BLOCKENTROPY);915 const key = readSecret(request.user.directories, SECRET_KEYS.BLOCKENTROPY);
916916
917 if (!key) {917 if (!key) {
918 console.log('Block Entropy key not found.');918 console.warn('Block Entropy key not found.');
919 return response.sendStatus(400);919 return response.sendStatus(400);
920 }920 }
921921
@@ -927,21 +927,21 @@ blockentropy.post('/models', jsonParser, async (request, response) => {
927 });927 });
928928
929 if (!modelsResponse.ok) {929 if (!modelsResponse.ok) {
930 console.log('Block Entropy returned an error.');930 console.warn('Block Entropy returned an error.');
931 return response.sendStatus(500);931 return response.sendStatus(500);
932 }932 }
933933
934 const data = await modelsResponse.json();934 const data = await modelsResponse.json();
935935
936 if (!Array.isArray(data)) {936 if (!Array.isArray(data)) {
937 console.log('Block Entropy returned invalid data.');937 console.warn('Block Entropy returned invalid data.');
938 return response.sendStatus(500);938 return response.sendStatus(500);
939 }939 }
940 const models = data.map(x => ({ value: x.name, text: x.name }));940 const models = data.map(x => ({ value: x.name, text: x.name }));
941 return response.send(models);941 return response.send(models);
942942
943 } catch (error) {943 } catch (error) {
944 console.log(error);944 console.error(error);
945 return response.sendStatus(500);945 return response.sendStatus(500);
946 }946 }
947});947});
@@ -951,11 +951,11 @@ blockentropy.post('/generate', jsonParser, async (request, response) => {
951 const key = readSecret(request.user.directories, SECRET_KEYS.BLOCKENTROPY);951 const key = readSecret(request.user.directories, SECRET_KEYS.BLOCKENTROPY);
952952
953 if (!key) {953 if (!key) {
954 console.log('Block Entropy key not found.');954 console.warn('Block Entropy key not found.');
955 return response.sendStatus(400);955 return response.sendStatus(400);
956 }956 }
957957
958 console.log('Block Entropy request:', request.body);958 console.debug('Block Entropy request:', request.body);
959959
960 const result = await fetch('https://api.blockentropy.ai/sdapi/v1/txt2img', {960 const result = await fetch('https://api.blockentropy.ai/sdapi/v1/txt2img', {
961 method: 'POST',961 method: 'POST',
@@ -976,16 +976,16 @@ blockentropy.post('/generate', jsonParser, async (request, response) => {
976 });976 });
977977
978 if (!result.ok) {978 if (!result.ok) {
979 console.log('Block Entropy returned an error.');979 console.warn('Block Entropy returned an error.');
980 return response.sendStatus(500);980 return response.sendStatus(500);
981 }981 }
982982
983 const data = await result.json();983 const data = await result.json();
984 console.log('Block Entropy response:', data);984 console.debug('Block Entropy response:', data);
985985
986 return response.send(data);986 return response.send(data);
987 } catch (error) {987 } catch (error) {
988 console.log(error);988 console.error(error);
989 return response.sendStatus(500);989 return response.sendStatus(500);
990 }990 }
991});991});
@@ -998,11 +998,11 @@ huggingface.post('/generate', jsonParser, async (request, response) => {
998 const key = readSecret(request.user.directories, SECRET_KEYS.HUGGINGFACE);998 const key = readSecret(request.user.directories, SECRET_KEYS.HUGGINGFACE);
999999
1000 if (!key) {1000 if (!key) {
1001 console.log('Hugging Face key not found.');1001 console.warn('Hugging Face key not found.');
1002 return response.sendStatus(400);1002 return response.sendStatus(400);
1003 }1003 }
10041004
1005 console.log('Hugging Face request:', request.body);1005 console.debug('Hugging Face request:', request.body);
10061006
1007 const result = await fetch(`https://api-inference.huggingface.co/models/${request.body.model}`, {1007 const result = await fetch(`https://api-inference.huggingface.co/models/${request.body.model}`, {
1008 method: 'POST',1008 method: 'POST',
@@ -1016,7 +1016,7 @@ huggingface.post('/generate', jsonParser, async (request, response) => {
1016 });1016 });
10171017
1018 if (!result.ok) {1018 if (!result.ok) {
1019 console.log('Hugging Face returned an error.');1019 console.warn('Hugging Face returned an error.');
1020 return response.sendStatus(500);1020 return response.sendStatus(500);
1021 }1021 }
10221022
@@ -1025,7 +1025,7 @@ huggingface.post('/generate', jsonParser, async (request, response) => {
1025 image: Buffer.from(buffer).toString('base64'),1025 image: Buffer.from(buffer).toString('base64'),
1026 });1026 });
1027 } catch (error) {1027 } catch (error) {
1028 console.log(error);1028 console.error(error);
1029 return response.sendStatus(500);1029 return response.sendStatus(500);
1030 }1030 }
1031});1031});
@@ -1037,7 +1037,7 @@ nanogpt.post('/models', jsonParser, async (request, response) => {
1037 const key = readSecret(request.user.directories, SECRET_KEYS.NANOGPT);1037 const key = readSecret(request.user.directories, SECRET_KEYS.NANOGPT);
10381038
1039 if (!key) {1039 if (!key) {
1040 console.log('NanoGPT key not found.');1040 console.warn('NanoGPT key not found.');
1041 return response.sendStatus(400);1041 return response.sendStatus(400);
1042 }1042 }
10431043
@@ -1050,7 +1050,7 @@ nanogpt.post('/models', jsonParser, async (request, response) => {
1050 });1050 });
10511051
1052 if (!modelsResponse.ok) {1052 if (!modelsResponse.ok) {
1053 console.log('NanoGPT returned an error.');1053 console.warn('NanoGPT returned an error.');
1054 return response.sendStatus(500);1054 return response.sendStatus(500);
1055 }1055 }
10561056
@@ -1059,7 +1059,7 @@ nanogpt.post('/models', jsonParser, async (request, response) => {
1059 const imageModels = data?.models?.image;1059 const imageModels = data?.models?.image;
10601060
1061 if (!imageModels || typeof imageModels !== 'object') {1061 if (!imageModels || typeof imageModels !== 'object') {
1062 console.log('NanoGPT returned invalid data.');1062 console.warn('NanoGPT returned invalid data.');
1063 return response.sendStatus(500);1063 return response.sendStatus(500);
1064 }1064 }
10651065
@@ -1067,7 +1067,7 @@ nanogpt.post('/models', jsonParser, async (request, response) => {
1067 return response.send(models);1067 return response.send(models);
1068 }1068 }
1069 catch (error) {1069 catch (error) {
1070 console.log(error);1070 console.error(error);
1071 return response.sendStatus(500);1071 return response.sendStatus(500);
1072 }1072 }
1073});1073});
@@ -1077,11 +1077,11 @@ nanogpt.post('/generate', jsonParser, async (request, response) => {
1077 const key = readSecret(request.user.directories, SECRET_KEYS.NANOGPT);1077 const key = readSecret(request.user.directories, SECRET_KEYS.NANOGPT);
10781078
1079 if (!key) {1079 if (!key) {
1080 console.log('NanoGPT key not found.');1080 console.warn('NanoGPT key not found.');
1081 return response.sendStatus(400);1081 return response.sendStatus(400);
1082 }1082 }
10831083
1084 console.log('NanoGPT request:', request.body);1084 console.debug('NanoGPT request:', request.body);
10851085
1086 const result = await fetch('https://nano-gpt.com/api/generate-image', {1086 const result = await fetch('https://nano-gpt.com/api/generate-image', {
1087 method: 'POST',1087 method: 'POST',
@@ -1093,7 +1093,7 @@ nanogpt.post('/generate', jsonParser, async (request, response) => {
1093 });1093 });
10941094
1095 if (!result.ok) {1095 if (!result.ok) {
1096 console.log('NanoGPT returned an error.');1096 console.warn('NanoGPT returned an error.');
1097 return response.sendStatus(500);1097 return response.sendStatus(500);
1098 }1098 }
10991099
@@ -1102,14 +1102,14 @@ nanogpt.post('/generate', jsonParser, async (request, response) => {
11021102
1103 const image = data?.data?.[0]?.b64_json;1103 const image = data?.data?.[0]?.b64_json;
1104 if (!image) {1104 if (!image) {
1105 console.log('NanoGPT returned invalid data.');1105 console.warn('NanoGPT returned invalid data.');
1106 return response.sendStatus(500);1106 return response.sendStatus(500);
1107 }1107 }
11081108
1109 return response.send({ image });1109 return response.send({ image });
1110 }1110 }
1111 catch (error) {1111 catch (error) {
1112 console.log(error);1112 console.error(error);
1113 return response.sendStatus(500);1113 return response.sendStatus(500);
1114 }1114 }
1115});1115});
@@ -1121,7 +1121,7 @@ bfl.post('/generate', jsonParser, async (request, response) => {
1121 const key = readSecret(request.user.directories, SECRET_KEYS.BFL);1121 const key = readSecret(request.user.directories, SECRET_KEYS.BFL);
11221122
1123 if (!key) {1123 if (!key) {
1124 console.log('BFL key not found.');1124 console.warn('BFL key not found.');
1125 return response.sendStatus(400);1125 return response.sendStatus(400);
1126 }1126 }
11271127
@@ -1173,7 +1173,7 @@ bfl.post('/generate', jsonParser, async (request, response) => {
1173 delete requestBody.guidance;1173 delete requestBody.guidance;
1174 }1174 }
11751175
1176 console.log('BFL request:', requestBody);1176 console.debug('BFL request:', requestBody);
11771177
1178 const result = await fetch(`https://api.bfl.ml/v1/${request.body.model}`, {1178 const result = await fetch(`https://api.bfl.ml/v1/${request.body.model}`, {
1179 method: 'POST',1179 method: 'POST',
@@ -1185,7 +1185,7 @@ bfl.post('/generate', jsonParser, async (request, response) => {
1185 });1185 });
11861186
1187 if (!result.ok) {1187 if (!result.ok) {
1188 console.log('BFL returned an error.');1188 console.warn('BFL returned an error.');
1189 return response.sendStatus(500);1189 return response.sendStatus(500);
1190 }1190 }
11911191
@@ -1201,7 +1201,7 @@ bfl.post('/generate', jsonParser, async (request, response) => {
12011201
1202 if (!statusResult.ok) {1202 if (!statusResult.ok) {
1203 const text = await statusResult.text();1203 const text = await statusResult.text();
1204 console.log('BFL returned an error.', text);1204 console.warn('BFL returned an error.', text);
1205 return response.sendStatus(500);1205 return response.sendStatus(500);
1206 }1206 }
12071207
@@ -1223,7 +1223,7 @@ bfl.post('/generate', jsonParser, async (request, response) => {
1223 throw new Error('BFL failed to generate image.', { cause: statusData });1223 throw new Error('BFL failed to generate image.', { cause: statusData });
1224 }1224 }
1225 } catch (error) {1225 } catch (error) {
1226 console.log(error);1226 console.error(error);
1227 return response.sendStatus(500);1227 return response.sendStatus(500);
1228 }1228 }
1229});1229});
src/endpoints/stats.js+2 -2
@@ -148,7 +148,7 @@ async function collectAndCreateStats(chatsPath, charactersPath) {
148 * @param {string} charactersPath Path to the directory containing the character files.148 * @param {string} charactersPath Path to the directory containing the character files.
149 */149 */
150export async function recreateStats(handle, chatsPath, charactersPath) {150export async function recreateStats(handle, chatsPath, charactersPath) {
151 console.log('Collecting and creating stats for user:', handle);151 console.info('Collecting and creating stats for user:', handle);
152 const stats = await collectAndCreateStats(chatsPath, charactersPath);152 const stats = await collectAndCreateStats(chatsPath, charactersPath);
153 STATS.set(handle, stats);153 STATS.set(handle, stats);
154 await saveStatsToFile();154 await saveStatsToFile();
@@ -200,7 +200,7 @@ async function saveStatsToFile() {
200 await writeFileAtomic(statsFilePath, JSON.stringify(charStats));200 await writeFileAtomic(statsFilePath, JSON.stringify(charStats));
201 TIMESTAMPS.set(handle, Date.now());201 TIMESTAMPS.set(handle, Date.now());
202 } catch (error) {202 } catch (error) {
203 console.log('Failed to save stats to file.', error);203 console.error('Failed to save stats to file.', error);
204 }204 }
205 }205 }
206 }206 }
src/endpoints/thumbnails.js+3 -4
@@ -89,7 +89,6 @@ async function generateThumbnail(directories, type, file) {
89 let thumbnailFolder = getThumbnailFolder(directories, type);89 let thumbnailFolder = getThumbnailFolder(directories, type);
90 let originalFolder = getOriginalFolder(directories, type);90 let originalFolder = getOriginalFolder(directories, type);
91 if (thumbnailFolder === undefined || originalFolder === undefined) throw new Error('Invalid thumbnail type');91 if (thumbnailFolder === undefined || originalFolder === undefined) throw new Error('Invalid thumbnail type');
92
93 const pathToCachedFile = path.join(thumbnailFolder, file);92 const pathToCachedFile = path.join(thumbnailFolder, file);
94 const pathToOriginalFile = path.join(originalFolder, file);93 const pathToOriginalFile = path.join(originalFolder, file);
9594
@@ -104,7 +103,7 @@ async function generateThumbnail(directories, type, file) {
104 const cachedStat = fs.statSync(pathToCachedFile);103 const cachedStat = fs.statSync(pathToCachedFile);
105104
106 if (originalStat.mtimeMs > cachedStat.ctimeMs) {105 if (originalStat.mtimeMs > cachedStat.ctimeMs) {
107 //console.log('Original file changed. Regenerating thumbnail...');106 //console.warn('Original file changed. Regenerating thumbnail...');
108 shouldRegenerate = true;107 shouldRegenerate = true;
109 }108 }
110 }109 }
@@ -157,7 +156,7 @@ export async function ensureThumbnailCache() {
157 return;156 return;
158 }157 }
159158
160 console.log('Generating thumbnails cache. Please wait...');159 console.info('Generating thumbnails cache. Please wait...');
161160
162 const bgFiles = fs.readdirSync(directories.backgrounds);161 const bgFiles = fs.readdirSync(directories.backgrounds);
163 const tasks = [];162 const tasks = [];
@@ -167,7 +166,7 @@ export async function ensureThumbnailCache() {
167 }166 }
168167
169 await Promise.all(tasks);168 await Promise.all(tasks);
170 console.log(`Done! Generated: ${bgFiles.length} preview images`);169 console.info(`Done! Generated: ${bgFiles.length} preview images`);
171 }170 }
172}171}
173172
src/endpoints/tokenizers.js+17 -17
@@ -96,7 +96,7 @@ async function getPathToTokenizer(model, fallbackModel) {
96 throw new Error('Downloading tokenizers is disabled, the model is not cached');96 throw new Error('Downloading tokenizers is disabled, the model is not cached');
97 }97 }
9898
99 console.log('Downloading tokenizer model:', model);99 console.info('Downloading tokenizer model:', model);
100 const response = await fetch(model);100 const response = await fetch(model);
101 if (!response.ok) {101 if (!response.ok) {
102 throw new Error(`Failed to fetch the model: ${response.status} ${response.statusText}`);102 throw new Error(`Failed to fetch the model: ${response.status} ${response.statusText}`);
@@ -108,7 +108,7 @@ async function getPathToTokenizer(model, fallbackModel) {
108 } catch (error) {108 } catch (error) {
109 const getLastSegment = str => str?.split('/')?.pop() || '';109 const getLastSegment = str => str?.split('/')?.pop() || '';
110 if (fallbackModel) {110 if (fallbackModel) {
111 console.log(`Could not get a tokenizer from ${getLastSegment(model)}. Reason: ${error.message}. Using a fallback model: ${getLastSegment(fallbackModel)}.`);111 console.error(`Could not get a tokenizer from ${getLastSegment(model)}. Reason: ${error.message}. Using a fallback model: ${getLastSegment(fallbackModel)}.`);
112 return fallbackModel;112 return fallbackModel;
113 }113 }
114114
@@ -156,7 +156,7 @@ class SentencePieceTokenizer {
156 const pathToModel = await getPathToTokenizer(this.#model, this.#fallbackModel);156 const pathToModel = await getPathToTokenizer(this.#model, this.#fallbackModel);
157 this.#instance = new SentencePieceProcessor();157 this.#instance = new SentencePieceProcessor();
158 await this.#instance.load(pathToModel);158 await this.#instance.load(pathToModel);
159 console.log('Instantiated the tokenizer for', path.parse(pathToModel).name);159 console.info('Instantiated the tokenizer for', path.parse(pathToModel).name);
160 return this.#instance;160 return this.#instance;
161 } catch (error) {161 } catch (error) {
162 console.error('Sentencepiece tokenizer failed to load: ' + this.#model, error);162 console.error('Sentencepiece tokenizer failed to load: ' + this.#model, error);
@@ -205,7 +205,7 @@ class WebTokenizer {
205 const pathToModel = await getPathToTokenizer(this.#model, this.#fallbackModel);205 const pathToModel = await getPathToTokenizer(this.#model, this.#fallbackModel);
206 const arrayBuffer = fs.readFileSync(pathToModel).buffer;206 const arrayBuffer = fs.readFileSync(pathToModel).buffer;
207 this.#instance = await Tokenizer.fromJSON(arrayBuffer);207 this.#instance = await Tokenizer.fromJSON(arrayBuffer);
208 console.log('Instantiated the tokenizer for', path.parse(pathToModel).name);208 console.info('Instantiated the tokenizer for', path.parse(pathToModel).name);
209 return this.#instance;209 return this.#instance;
210 } catch (error) {210 } catch (error) {
211 console.error('Web tokenizer failed to load: ' + this.#model, error);211 console.error('Web tokenizer failed to load: ' + this.#model, error);
@@ -484,7 +484,7 @@ export function getTiktokenTokenizer(model) {
484 }484 }
485485
486 const tokenizer = tiktoken.encoding_for_model(model);486 const tokenizer = tiktoken.encoding_for_model(model);
487 console.log('Instantiated the tokenizer for', model);487 console.info('Instantiated the tokenizer for', model);
488 tokenizersCache[model] = tokenizer;488 tokenizersCache[model] = tokenizer;
489 return tokenizer;489 return tokenizer;
490}490}
@@ -531,7 +531,7 @@ function createSentencepieceEncodingHandler(tokenizer) {
531 const chunks = instance?.encodePieces(text);531 const chunks = instance?.encodePieces(text);
532 return response.send({ ids, count, chunks });532 return response.send({ ids, count, chunks });
533 } catch (error) {533 } catch (error) {
534 console.log(error);534 console.error(error);
535 return response.send({ ids: [], count: 0, chunks: [] });535 return response.send({ ids: [], count: 0, chunks: [] });
536 }536 }
537 };537 };
@@ -562,7 +562,7 @@ function createSentencepieceDecodingHandler(tokenizer) {
562 const text = chunks.join('');562 const text = chunks.join('');
563 return response.send({ text, chunks });563 return response.send({ text, chunks });
564 } catch (error) {564 } catch (error) {
565 console.log(error);565 console.error(error);
566 return response.send({ text: '', chunks: [] });566 return response.send({ text: '', chunks: [] });
567 }567 }
568 };568 };
@@ -591,7 +591,7 @@ function createTiktokenEncodingHandler(modelId) {
591 const chunks = await getTiktokenChunks(tokenizer, tokens);591 const chunks = await getTiktokenChunks(tokenizer, tokens);
592 return response.send({ ids: tokens, count: tokens.length, chunks });592 return response.send({ ids: tokens, count: tokens.length, chunks });
593 } catch (error) {593 } catch (error) {
594 console.log(error);594 console.error(error);
595 return response.send({ ids: [], count: 0, chunks: [] });595 return response.send({ ids: [], count: 0, chunks: [] });
596 }596 }
597 };597 };
@@ -620,7 +620,7 @@ function createTiktokenDecodingHandler(modelId) {
620 const text = new TextDecoder().decode(textBytes);620 const text = new TextDecoder().decode(textBytes);
621 return response.send({ text });621 return response.send({ text });
622 } catch (error) {622 } catch (error) {
623 console.log(error);623 console.error(error);
624 return response.send({ text: '' });624 return response.send({ text: '' });
625 }625 }
626 };626 };
@@ -650,7 +650,7 @@ function createWebTokenizerEncodingHandler(tokenizer) {
650 const chunks = getWebTokenizersChunks(instance, tokens);650 const chunks = getWebTokenizersChunks(instance, tokens);
651 return response.send({ ids: tokens, count: tokens.length, chunks });651 return response.send({ ids: tokens, count: tokens.length, chunks });
652 } catch (error) {652 } catch (error) {
653 console.log(error);653 console.error(error);
654 return response.send({ ids: [], count: 0, chunks: [] });654 return response.send({ ids: [], count: 0, chunks: [] });
655 }655 }
656 };656 };
@@ -681,7 +681,7 @@ function createWebTokenizerDecodingHandler(tokenizer) {
681 const text = instance.decode(new Int32Array(ids));681 const text = instance.decode(new Int32Array(ids));
682 return response.send({ text, chunks });682 return response.send({ text, chunks });
683 } catch (error) {683 } catch (error) {
684 console.log(error);684 console.error(error);
685 return response.send({ text: '', chunks: [] });685 return response.send({ text: '', chunks: [] });
686 }686 }
687 };687 };
@@ -781,7 +781,7 @@ router.post('/openai/encode', jsonParser, async function (req, res) {
781 const handler = createTiktokenEncodingHandler(model);781 const handler = createTiktokenEncodingHandler(model);
782 return handler(req, res);782 return handler(req, res);
783 } catch (error) {783 } catch (error) {
784 console.log(error);784 console.error(error);
785 return res.send({ ids: [], count: 0, chunks: [] });785 return res.send({ ids: [], count: 0, chunks: [] });
786 }786 }
787});787});
@@ -849,7 +849,7 @@ router.post('/openai/decode', jsonParser, async function (req, res) {
849 const handler = createTiktokenDecodingHandler(model);849 const handler = createTiktokenDecodingHandler(model);
850 return handler(req, res);850 return handler(req, res);
851 } catch (error) {851 } catch (error) {
852 console.log(error);852 console.error(error);
853 return res.send({ text: '' });853 return res.send({ text: '' });
854 }854 }
855});855});
@@ -988,7 +988,7 @@ router.post('/remote/kobold/count', jsonParser, async function (request, respons
988 const result = await fetch(url, args);988 const result = await fetch(url, args);
989989
990 if (!result.ok) {990 if (!result.ok) {
991 console.log(`API returned error: ${result.status} ${result.statusText}`);991 console.warn(`API returned error: ${result.status} ${result.statusText}`);
992 return response.send({ error: true });992 return response.send({ error: true });
993 }993 }
994994
@@ -997,7 +997,7 @@ router.post('/remote/kobold/count', jsonParser, async function (request, respons
997 const ids = data['ids'] ?? [];997 const ids = data['ids'] ?? [];
998 return response.send({ count, ids });998 return response.send({ count, ids });
999 } catch (error) {999 } catch (error) {
1000 console.log(error);1000 console.error(error);
1001 return response.send({ error: true });1001 return response.send({ error: true });
1002 }1002 }
1003});1003});
@@ -1052,7 +1052,7 @@ router.post('/remote/textgenerationwebui/encode', jsonParser, async function (re
1052 const result = await fetch(url, args);1052 const result = await fetch(url, args);
10531053
1054 if (!result.ok) {1054 if (!result.ok) {
1055 console.log(`API returned error: ${result.status} ${result.statusText}`);1055 console.warn(`API returned error: ${result.status} ${result.statusText}`);
1056 return response.send({ error: true });1056 return response.send({ error: true });
1057 }1057 }
10581058
@@ -1062,7 +1062,7 @@ router.post('/remote/textgenerationwebui/encode', jsonParser, async function (re
10621062
1063 return response.send({ count, ids });1063 return response.send({ count, ids });
1064 } catch (error) {1064 } catch (error) {
1065 console.log(error);1065 console.error(error);
1066 return response.send({ error: true });1066 return response.send({ error: true });
1067 }1067 }
1068});1068});
src/endpoints/translate.js+31 -31
@@ -35,7 +35,7 @@ function decodeBuffer(buffer) {
35 try {35 try {
36 return iconv.decode(Buffer.from(buffer), 'utf-8');36 return iconv.decode(Buffer.from(buffer), 'utf-8');
37 } catch (error) {37 } catch (error) {
38 console.log('Failed to decode buffer:', error);38 console.error('Failed to decode buffer:', error);
39 return Buffer.from(buffer).toString('utf-8');39 return Buffer.from(buffer).toString('utf-8');
40 }40 }
41}41}
@@ -46,7 +46,7 @@ router.post('/libre', jsonParser, async (request, response) => {
46 const url = readSecret(request.user.directories, SECRET_KEYS.LIBRE_URL);46 const url = readSecret(request.user.directories, SECRET_KEYS.LIBRE_URL);
4747
48 if (!url) {48 if (!url) {
49 console.log('LibreTranslate URL is not configured.');49 console.warn('LibreTranslate URL is not configured.');
50 return response.sendStatus(400);50 return response.sendStatus(400);
51 }51 }
5252
@@ -69,7 +69,7 @@ router.post('/libre', jsonParser, async (request, response) => {
69 return response.sendStatus(400);69 return response.sendStatus(400);
70 }70 }
7171
72 console.log('Input text: ' + text);72 console.debug('Input text: ' + text);
7373
74 const result = await fetch(url, {74 const result = await fetch(url, {
75 method: 'POST',75 method: 'POST',
@@ -85,17 +85,17 @@ router.post('/libre', jsonParser, async (request, response) => {
8585
86 if (!result.ok) {86 if (!result.ok) {
87 const error = await result.text();87 const error = await result.text();
88 console.log('LibreTranslate error: ', result.statusText, error);88 console.warn('LibreTranslate error: ', result.statusText, error);
89 return response.sendStatus(500);89 return response.sendStatus(500);
90 }90 }
9191
92 /** @type {any} */92 /** @type {any} */
93 const json = await result.json();93 const json = await result.json();
94 console.log('Translated text: ' + json.translatedText);94 console.debug('Translated text: ' + json.translatedText);
9595
96 return response.send(json.translatedText);96 return response.send(json.translatedText);
97 } catch (error) {97 } catch (error) {
98 console.log('Translation error: ' + error.message);98 console.error('Translation error: ' + error.message);
99 return response.sendStatus(500);99 return response.sendStatus(500);
100 }100 }
101});101});
@@ -109,14 +109,14 @@ router.post('/google', jsonParser, async (request, response) => {
109 return response.sendStatus(400);109 return response.sendStatus(400);
110 }110 }
111111
112 console.log('Input text: ' + text);112 console.debug('Input text: ' + text);
113113
114 const { generateRequestUrl, normaliseResponse } = getGoogleTranslateClient();114 const { generateRequestUrl, normaliseResponse } = getGoogleTranslateClient();
115 const requestUrl = generateRequestUrl(text, { to: lang });115 const requestUrl = generateRequestUrl(text, { to: lang });
116 const result = await fetch(requestUrl);116 const result = await fetch(requestUrl);
117117
118 if (!result.ok) {118 if (!result.ok) {
119 console.log('Google Translate error: ', result.statusText);119 console.warn('Google Translate error: ', result.statusText);
120 return response.sendStatus(500);120 return response.sendStatus(500);
121 }121 }
122122
@@ -125,10 +125,10 @@ router.post('/google', jsonParser, async (request, response) => {
125 const translatedText = translateResponse.text;125 const translatedText = translateResponse.text;
126126
127 response.setHeader('Content-Type', 'text/plain; charset=utf-8');127 response.setHeader('Content-Type', 'text/plain; charset=utf-8');
128 console.log('Translated text: ' + translatedText);128 console.debug('Translated text: ' + translatedText);
129 return response.send(translatedText);129 return response.send(translatedText);
130 } catch (error) {130 } catch (error) {
131 console.log('Translation error', error);131 console.error('Translation error', error);
132 return response.sendStatus(500);132 return response.sendStatus(500);
133 }133 }
134});134});
@@ -161,7 +161,7 @@ router.post('/yandex', jsonParser, async (request, response) => {
161 params.append('lang', lang);161 params.append('lang', lang);
162 const ucid = uuidv4().replaceAll('-', '');162 const ucid = uuidv4().replaceAll('-', '');
163163
164 console.log('Input text: ' + inputText);164 console.debug('Input text: ' + inputText);
165165
166 const result = await fetch(`https://translate.yandex.net/api/v1/tr.json/translate?ucid=${ucid}&srv=android&format=text`, {166 const result = await fetch(`https://translate.yandex.net/api/v1/tr.json/translate?ucid=${ucid}&srv=android&format=text`, {
167 method: 'POST',167 method: 'POST',
@@ -173,18 +173,18 @@ router.post('/yandex', jsonParser, async (request, response) => {
173173
174 if (!result.ok) {174 if (!result.ok) {
175 const error = await result.text();175 const error = await result.text();
176 console.log('Yandex error: ', result.statusText, error);176 console.warn('Yandex error: ', result.statusText, error);
177 return response.sendStatus(500);177 return response.sendStatus(500);
178 }178 }
179179
180 /** @type {any} */180 /** @type {any} */
181 const json = await result.json();181 const json = await result.json();
182 const translated = json.text.join();182 const translated = json.text.join();
183 console.log('Translated text: ' + translated);183 console.debug('Translated text: ' + translated);
184184
185 return response.send(translated);185 return response.send(translated);
186 } catch (error) {186 } catch (error) {
187 console.log('Translation error: ' + error.message);187 console.error('Translation error: ' + error.message);
188 return response.sendStatus(500);188 return response.sendStatus(500);
189 }189 }
190});190});
@@ -195,7 +195,7 @@ router.post('/lingva', jsonParser, async (request, response) => {
195 const baseUrl = secretUrl || LINGVA_DEFAULT;195 const baseUrl = secretUrl || LINGVA_DEFAULT;
196196
197 if (!secretUrl && baseUrl === LINGVA_DEFAULT) {197 if (!secretUrl && baseUrl === LINGVA_DEFAULT) {
198 console.log('Lingva URL is using default value.', LINGVA_DEFAULT);198 console.warn('Lingva URL is using default value.', LINGVA_DEFAULT);
199 }199 }
200200
201 if (request.body.lang === 'zh-CN' || request.body.lang === 'zh-TW') {201 if (request.body.lang === 'zh-CN' || request.body.lang === 'zh-TW') {
@@ -213,22 +213,22 @@ router.post('/lingva', jsonParser, async (request, response) => {
213 return response.sendStatus(400);213 return response.sendStatus(400);
214 }214 }
215215
216 console.log('Input text: ' + text);216 console.debug('Input text: ' + text);
217217
218 const url = urlJoin(baseUrl, 'auto', lang, encodeURIComponent(text));218 const url = urlJoin(baseUrl, 'auto', lang, encodeURIComponent(text));
219 const result = await fetch(url);219 const result = await fetch(url);
220220
221 if (!result.ok) {221 if (!result.ok) {
222 const error = await result.text();222 const error = await result.text();
223 console.log('Lingva error: ', result.statusText, error);223 console.warn('Lingva error: ', result.statusText, error);
224 }224 }
225225
226 /** @type {any} */226 /** @type {any} */
227 const data = await result.json();227 const data = await result.json();
228 console.log('Translated text: ' + data.translation);228 console.debug('Translated text: ' + data.translation);
229 return response.send(data.translation);229 return response.send(data.translation);
230 } catch (error) {230 } catch (error) {
231 console.log('Translation error', error);231 console.error('Translation error', error);
232 return response.sendStatus(500);232 return response.sendStatus(500);
233 }233 }
234});234});
@@ -238,7 +238,7 @@ router.post('/deepl', jsonParser, async (request, response) => {
238 const key = readSecret(request.user.directories, SECRET_KEYS.DEEPL);238 const key = readSecret(request.user.directories, SECRET_KEYS.DEEPL);
239239
240 if (!key) {240 if (!key) {
241 console.log('DeepL key is not configured.');241 console.warn('DeepL key is not configured.');
242 return response.sendStatus(400);242 return response.sendStatus(400);
243 }243 }
244244
@@ -254,7 +254,7 @@ router.post('/deepl', jsonParser, async (request, response) => {
254 return response.sendStatus(400);254 return response.sendStatus(400);
255 }255 }
256256
257 console.log('Input text: ' + text);257 console.debug('Input text: ' + text);
258258
259 const params = new URLSearchParams();259 const params = new URLSearchParams();
260 params.append('text', text);260 params.append('text', text);
@@ -280,17 +280,17 @@ router.post('/deepl', jsonParser, async (request, response) => {
280280
281 if (!result.ok) {281 if (!result.ok) {
282 const error = await result.text();282 const error = await result.text();
283 console.log('DeepL error: ', result.statusText, error);283 console.warn('DeepL error: ', result.statusText, error);
284 return response.sendStatus(500);284 return response.sendStatus(500);
285 }285 }
286286
287 /** @type {any} */287 /** @type {any} */
288 const json = await result.json();288 const json = await result.json();
289 console.log('Translated text: ' + json.translations[0].text);289 console.debug('Translated text: ' + json.translations[0].text);
290290
291 return response.send(json.translations[0].text);291 return response.send(json.translations[0].text);
292 } catch (error) {292 } catch (error) {
293 console.log('Translation error: ' + error.message);293 console.error('Translation error: ' + error.message);
294 return response.sendStatus(500);294 return response.sendStatus(500);
295 }295 }
296});296});
@@ -376,7 +376,7 @@ router.post('/deeplx', jsonParser, async (request, response) => {
376 return response.sendStatus(400);376 return response.sendStatus(400);
377 }377 }
378378
379 console.log('Input text: ' + text);379 console.debug('Input text: ' + text);
380380
381 const result = await fetch(url, {381 const result = await fetch(url, {
382 method: 'POST',382 method: 'POST',
@@ -393,17 +393,17 @@ router.post('/deeplx', jsonParser, async (request, response) => {
393393
394 if (!result.ok) {394 if (!result.ok) {
395 const error = await result.text();395 const error = await result.text();
396 console.log('DeepLX error: ', result.statusText, error);396 console.warn('DeepLX error: ', result.statusText, error);
397 return response.sendStatus(500);397 return response.sendStatus(500);
398 }398 }
399399
400 /** @type {any} */400 /** @type {any} */
401 const json = await result.json();401 const json = await result.json();
402 console.log('Translated text: ' + json.data);402 console.debug('Translated text: ' + json.data);
403403
404 return response.send(json.data);404 return response.send(json.data);
405 } catch (error) {405 } catch (error) {
406 console.log('DeepLX translation error: ' + error.message);406 console.error('DeepLX translation error: ' + error.message);
407 return response.sendStatus(500);407 return response.sendStatus(500);
408 }408 }
409});409});
@@ -429,14 +429,14 @@ router.post('/bing', jsonParser, async (request, response) => {
429 return response.sendStatus(400);429 return response.sendStatus(400);
430 }430 }
431431
432 console.log('Input text: ' + text);432 console.debug('Input text: ' + text);
433433
434 const result = await bingTranslate(text, null, lang);434 const result = await bingTranslate(text, null, lang);
435 const translatedText = result?.translation;435 const translatedText = result?.translation;
436 console.log('Translated text: ' + translatedText);436 console.debug('Translated text: ' + translatedText);
437 return response.send(translatedText);437 return response.send(translatedText);
438 } catch (error) {438 } catch (error) {
439 console.log('Translation error', error);439 console.error('Translation error', error);
440 return response.sendStatus(500);440 return response.sendStatus(500);
441 }441 }
442});442});
src/endpoints/users-admin.js+19 -19
@@ -53,12 +53,12 @@ router.post('/get', requireAdminMiddleware, jsonParser, async (_request, respons
53router.post('/disable', requireAdminMiddleware, jsonParser, async (request, response) => {53router.post('/disable', requireAdminMiddleware, jsonParser, async (request, response) => {
54 try {54 try {
55 if (!request.body.handle) {55 if (!request.body.handle) {
56 console.log('Disable user failed: Missing required fields');56 console.warn('Disable user failed: Missing required fields');
57 return response.status(400).json({ error: 'Missing required fields' });57 return response.status(400).json({ error: 'Missing required fields' });
58 }58 }
5959
60 if (request.body.handle === request.user.profile.handle) {60 if (request.body.handle === request.user.profile.handle) {
61 console.log('Disable user failed: Cannot disable yourself');61 console.warn('Disable user failed: Cannot disable yourself');
62 return response.status(400).json({ error: 'Cannot disable yourself' });62 return response.status(400).json({ error: 'Cannot disable yourself' });
63 }63 }
6464
@@ -66,7 +66,7 @@ router.post('/disable', requireAdminMiddleware, jsonParser, async (request, resp
66 const user = await storage.getItem(toKey(request.body.handle));66 const user = await storage.getItem(toKey(request.body.handle));
6767
68 if (!user) {68 if (!user) {
69 console.log('Disable user failed: User not found');69 console.error('Disable user failed: User not found');
70 return response.status(404).json({ error: 'User not found' });70 return response.status(404).json({ error: 'User not found' });
71 }71 }
7272
@@ -82,7 +82,7 @@ router.post('/disable', requireAdminMiddleware, jsonParser, async (request, resp
82router.post('/enable', requireAdminMiddleware, jsonParser, async (request, response) => {82router.post('/enable', requireAdminMiddleware, jsonParser, async (request, response) => {
83 try {83 try {
84 if (!request.body.handle) {84 if (!request.body.handle) {
85 console.log('Enable user failed: Missing required fields');85 console.warn('Enable user failed: Missing required fields');
86 return response.status(400).json({ error: 'Missing required fields' });86 return response.status(400).json({ error: 'Missing required fields' });
87 }87 }
8888
@@ -90,7 +90,7 @@ router.post('/enable', requireAdminMiddleware, jsonParser, async (request, respo
90 const user = await storage.getItem(toKey(request.body.handle));90 const user = await storage.getItem(toKey(request.body.handle));
9191
92 if (!user) {92 if (!user) {
93 console.log('Enable user failed: User not found');93 console.error('Enable user failed: User not found');
94 return response.status(404).json({ error: 'User not found' });94 return response.status(404).json({ error: 'User not found' });
95 }95 }
9696
@@ -106,7 +106,7 @@ router.post('/enable', requireAdminMiddleware, jsonParser, async (request, respo
106router.post('/promote', requireAdminMiddleware, jsonParser, async (request, response) => {106router.post('/promote', requireAdminMiddleware, jsonParser, async (request, response) => {
107 try {107 try {
108 if (!request.body.handle) {108 if (!request.body.handle) {
109 console.log('Promote user failed: Missing required fields');109 console.warn('Promote user failed: Missing required fields');
110 return response.status(400).json({ error: 'Missing required fields' });110 return response.status(400).json({ error: 'Missing required fields' });
111 }111 }
112112
@@ -114,7 +114,7 @@ router.post('/promote', requireAdminMiddleware, jsonParser, async (request, resp
114 const user = await storage.getItem(toKey(request.body.handle));114 const user = await storage.getItem(toKey(request.body.handle));
115115
116 if (!user) {116 if (!user) {
117 console.log('Promote user failed: User not found');117 console.error('Promote user failed: User not found');
118 return response.status(404).json({ error: 'User not found' });118 return response.status(404).json({ error: 'User not found' });
119 }119 }
120120
@@ -130,12 +130,12 @@ router.post('/promote', requireAdminMiddleware, jsonParser, async (request, resp
130router.post('/demote', requireAdminMiddleware, jsonParser, async (request, response) => {130router.post('/demote', requireAdminMiddleware, jsonParser, async (request, response) => {
131 try {131 try {
132 if (!request.body.handle) {132 if (!request.body.handle) {
133 console.log('Demote user failed: Missing required fields');133 console.warn('Demote user failed: Missing required fields');
134 return response.status(400).json({ error: 'Missing required fields' });134 return response.status(400).json({ error: 'Missing required fields' });
135 }135 }
136136
137 if (request.body.handle === request.user.profile.handle) {137 if (request.body.handle === request.user.profile.handle) {
138 console.log('Demote user failed: Cannot demote yourself');138 console.warn('Demote user failed: Cannot demote yourself');
139 return response.status(400).json({ error: 'Cannot demote yourself' });139 return response.status(400).json({ error: 'Cannot demote yourself' });
140 }140 }
141141
@@ -143,7 +143,7 @@ router.post('/demote', requireAdminMiddleware, jsonParser, async (request, respo
143 const user = await storage.getItem(toKey(request.body.handle));143 const user = await storage.getItem(toKey(request.body.handle));
144144
145 if (!user) {145 if (!user) {
146 console.log('Demote user failed: User not found');146 console.error('Demote user failed: User not found');
147 return response.status(404).json({ error: 'User not found' });147 return response.status(404).json({ error: 'User not found' });
148 }148 }
149149
@@ -159,7 +159,7 @@ router.post('/demote', requireAdminMiddleware, jsonParser, async (request, respo
159router.post('/create', requireAdminMiddleware, jsonParser, async (request, response) => {159router.post('/create', requireAdminMiddleware, jsonParser, async (request, response) => {
160 try {160 try {
161 if (!request.body.handle || !request.body.name) {161 if (!request.body.handle || !request.body.name) {
162 console.log('Create user failed: Missing required fields');162 console.warn('Create user failed: Missing required fields');
163 return response.status(400).json({ error: 'Missing required fields' });163 return response.status(400).json({ error: 'Missing required fields' });
164 }164 }
165165
@@ -167,12 +167,12 @@ router.post('/create', requireAdminMiddleware, jsonParser, async (request, respo
167 const handle = lodash.kebabCase(String(request.body.handle).toLowerCase().trim());167 const handle = lodash.kebabCase(String(request.body.handle).toLowerCase().trim());
168168
169 if (!handle) {169 if (!handle) {
170 console.log('Create user failed: Invalid handle');170 console.warn('Create user failed: Invalid handle');
171 return response.status(400).json({ error: 'Invalid handle' });171 return response.status(400).json({ error: 'Invalid handle' });
172 }172 }
173173
174 if (handles.some(x => x === handle)) {174 if (handles.some(x => x === handle)) {
175 console.log('Create user failed: User with that handle already exists');175 console.warn('Create user failed: User with that handle already exists');
176 return response.status(409).json({ error: 'User already exists' });176 return response.status(409).json({ error: 'User already exists' });
177 }177 }
178178
@@ -192,7 +192,7 @@ router.post('/create', requireAdminMiddleware, jsonParser, async (request, respo
192 await storage.setItem(toKey(handle), newUser);192 await storage.setItem(toKey(handle), newUser);
193193
194 // Create user directories194 // Create user directories
195 console.log('Creating data directories for', newUser.handle);195 console.info('Creating data directories for', newUser.handle);
196 await ensurePublicDirectoriesExist();196 await ensurePublicDirectoriesExist();
197 const directories = getUserDirectories(newUser.handle);197 const directories = getUserDirectories(newUser.handle);
198 await checkForNewContent([directories]);198 await checkForNewContent([directories]);
@@ -206,17 +206,17 @@ router.post('/create', requireAdminMiddleware, jsonParser, async (request, respo
206router.post('/delete', requireAdminMiddleware, jsonParser, async (request, response) => {206router.post('/delete', requireAdminMiddleware, jsonParser, async (request, response) => {
207 try {207 try {
208 if (!request.body.handle) {208 if (!request.body.handle) {
209 console.log('Delete user failed: Missing required fields');209 console.warn('Delete user failed: Missing required fields');
210 return response.status(400).json({ error: 'Missing required fields' });210 return response.status(400).json({ error: 'Missing required fields' });
211 }211 }
212212
213 if (request.body.handle === request.user.profile.handle) {213 if (request.body.handle === request.user.profile.handle) {
214 console.log('Delete user failed: Cannot delete yourself');214 console.warn('Delete user failed: Cannot delete yourself');
215 return response.status(400).json({ error: 'Cannot delete yourself' });215 return response.status(400).json({ error: 'Cannot delete yourself' });
216 }216 }
217217
218 if (request.body.handle === DEFAULT_USER.handle) {218 if (request.body.handle === DEFAULT_USER.handle) {
219 console.log('Delete user failed: Cannot delete default user');219 console.warn('Delete user failed: Cannot delete default user');
220 return response.status(400).json({ error: 'Sorry, but the default user cannot be deleted. It is required as a fallback.' });220 return response.status(400).json({ error: 'Sorry, but the default user cannot be deleted. It is required as a fallback.' });
221 }221 }
222222
@@ -224,7 +224,7 @@ router.post('/delete', requireAdminMiddleware, jsonParser, async (request, respo
224224
225 if (request.body.purge) {225 if (request.body.purge) {
226 const directories = getUserDirectories(request.body.handle);226 const directories = getUserDirectories(request.body.handle);
227 console.log('Deleting data directories for', request.body.handle);227 console.info('Deleting data directories for', request.body.handle);
228 await fsPromises.rm(directories.root, { recursive: true, force: true });228 await fsPromises.rm(directories.root, { recursive: true, force: true });
229 }229 }
230230
@@ -238,7 +238,7 @@ router.post('/delete', requireAdminMiddleware, jsonParser, async (request, respo
238router.post('/slugify', requireAdminMiddleware, jsonParser, async (request, response) => {238router.post('/slugify', requireAdminMiddleware, jsonParser, async (request, response) => {
239 try {239 try {
240 if (!request.body.text) {240 if (!request.body.text) {
241 console.log('Slugify failed: Missing required fields');241 console.warn('Slugify failed: Missing required fields');
242 return response.status(400).json({ error: 'Missing required fields' });242 return response.status(400).json({ error: 'Missing required fields' });
243 }243 }
244244
src/endpoints/users-private.js+22 -22
@@ -58,18 +58,18 @@ router.get('/me', async (request, response) => {
58router.post('/change-avatar', jsonParser, async (request, response) => {58router.post('/change-avatar', jsonParser, async (request, response) => {
59 try {59 try {
60 if (!request.body.handle) {60 if (!request.body.handle) {
61 console.log('Change avatar failed: Missing required fields');61 console.warn('Change avatar failed: Missing required fields');
62 return response.status(400).json({ error: 'Missing required fields' });62 return response.status(400).json({ error: 'Missing required fields' });
63 }63 }
6464
65 if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {65 if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {
66 console.log('Change avatar failed: Unauthorized');66 console.error('Change avatar failed: Unauthorized');
67 return response.status(403).json({ error: 'Unauthorized' });67 return response.status(403).json({ error: 'Unauthorized' });
68 }68 }
6969
70 // Avatar is not a data URL or not an empty string70 // Avatar is not a data URL or not an empty string
71 if (!request.body.avatar.startsWith('data:image/') && request.body.avatar !== '') {71 if (!request.body.avatar.startsWith('data:image/') && request.body.avatar !== '') {
72 console.log('Change avatar failed: Invalid data URL');72 console.warn('Change avatar failed: Invalid data URL');
73 return response.status(400).json({ error: 'Invalid data URL' });73 return response.status(400).json({ error: 'Invalid data URL' });
74 }74 }
7575
@@ -77,7 +77,7 @@ router.post('/change-avatar', jsonParser, async (request, response) => {
77 const user = await storage.getItem(toKey(request.body.handle));77 const user = await storage.getItem(toKey(request.body.handle));
7878
79 if (!user) {79 if (!user) {
80 console.log('Change avatar failed: User not found');80 console.error('Change avatar failed: User not found');
81 return response.status(404).json({ error: 'User not found' });81 return response.status(404).json({ error: 'User not found' });
82 }82 }
8383
@@ -93,12 +93,12 @@ router.post('/change-avatar', jsonParser, async (request, response) => {
93router.post('/change-password', jsonParser, async (request, response) => {93router.post('/change-password', jsonParser, async (request, response) => {
94 try {94 try {
95 if (!request.body.handle) {95 if (!request.body.handle) {
96 console.log('Change password failed: Missing required fields');96 console.warn('Change password failed: Missing required fields');
97 return response.status(400).json({ error: 'Missing required fields' });97 return response.status(400).json({ error: 'Missing required fields' });
98 }98 }
9999
100 if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {100 if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {
101 console.log('Change password failed: Unauthorized');101 console.error('Change password failed: Unauthorized');
102 return response.status(403).json({ error: 'Unauthorized' });102 return response.status(403).json({ error: 'Unauthorized' });
103 }103 }
104104
@@ -106,17 +106,17 @@ router.post('/change-password', jsonParser, async (request, response) => {
106 const user = await storage.getItem(toKey(request.body.handle));106 const user = await storage.getItem(toKey(request.body.handle));
107107
108 if (!user) {108 if (!user) {
109 console.log('Change password failed: User not found');109 console.error('Change password failed: User not found');
110 return response.status(404).json({ error: 'User not found' });110 return response.status(404).json({ error: 'User not found' });
111 }111 }
112112
113 if (!user.enabled) {113 if (!user.enabled) {
114 console.log('Change password failed: User is disabled');114 console.error('Change password failed: User is disabled');
115 return response.status(403).json({ error: 'User is disabled' });115 return response.status(403).json({ error: 'User is disabled' });
116 }116 }
117117
118 if (!request.user.profile.admin && user.password && user.password !== getPasswordHash(request.body.oldPassword, user.salt)) {118 if (!request.user.profile.admin && user.password && user.password !== getPasswordHash(request.body.oldPassword, user.salt)) {
119 console.log('Change password failed: Incorrect password');119 console.error('Change password failed: Incorrect password');
120 return response.status(403).json({ error: 'Incorrect password' });120 return response.status(403).json({ error: 'Incorrect password' });
121 }121 }
122122
@@ -142,12 +142,12 @@ router.post('/backup', jsonParser, async (request, response) => {
142 const handle = request.body.handle;142 const handle = request.body.handle;
143143
144 if (!handle) {144 if (!handle) {
145 console.log('Backup failed: Missing required fields');145 console.warn('Backup failed: Missing required fields');
146 return response.status(400).json({ error: 'Missing required fields' });146 return response.status(400).json({ error: 'Missing required fields' });
147 }147 }
148148
149 if (handle !== request.user.profile.handle && !request.user.profile.admin) {149 if (handle !== request.user.profile.handle && !request.user.profile.admin) {
150 console.log('Backup failed: Unauthorized');150 console.error('Backup failed: Unauthorized');
151 return response.status(403).json({ error: 'Unauthorized' });151 return response.status(403).json({ error: 'Unauthorized' });
152 }152 }
153153
@@ -163,7 +163,7 @@ router.post('/reset-settings', jsonParser, async (request, response) => {
163 const password = request.body.password;163 const password = request.body.password;
164164
165 if (request.user.profile.password && request.user.profile.password !== getPasswordHash(password, request.user.profile.salt)) {165 if (request.user.profile.password && request.user.profile.password !== getPasswordHash(password, request.user.profile.salt)) {
166 console.log('Reset settings failed: Incorrect password');166 console.warn('Reset settings failed: Incorrect password');
167 return response.status(403).json({ error: 'Incorrect password' });167 return response.status(403).json({ error: 'Incorrect password' });
168 }168 }
169169
@@ -181,12 +181,12 @@ router.post('/reset-settings', jsonParser, async (request, response) => {
181router.post('/change-name', jsonParser, async (request, response) => {181router.post('/change-name', jsonParser, async (request, response) => {
182 try {182 try {
183 if (!request.body.name || !request.body.handle) {183 if (!request.body.name || !request.body.handle) {
184 console.log('Change name failed: Missing required fields');184 console.warn('Change name failed: Missing required fields');
185 return response.status(400).json({ error: 'Missing required fields' });185 return response.status(400).json({ error: 'Missing required fields' });
186 }186 }
187187
188 if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {188 if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {
189 console.log('Change name failed: Unauthorized');189 console.error('Change name failed: Unauthorized');
190 return response.status(403).json({ error: 'Unauthorized' });190 return response.status(403).json({ error: 'Unauthorized' });
191 }191 }
192192
@@ -194,7 +194,7 @@ router.post('/change-name', jsonParser, async (request, response) => {
194 const user = await storage.getItem(toKey(request.body.handle));194 const user = await storage.getItem(toKey(request.body.handle));
195195
196 if (!user) {196 if (!user) {
197 console.log('Change name failed: User not found');197 console.warn('Change name failed: User not found');
198 return response.status(404).json({ error: 'User not found' });198 return response.status(404).json({ error: 'User not found' });
199 }199 }
200200
@@ -211,9 +211,9 @@ router.post('/change-name', jsonParser, async (request, response) => {
211router.post('/reset-step1', jsonParser, async (request, response) => {211router.post('/reset-step1', jsonParser, async (request, response) => {
212 try {212 try {
213 const resetCode = String(crypto.randomInt(1000, 9999));213 const resetCode = String(crypto.randomInt(1000, 9999));
214 console.log();214 console.info();
215 console.log(color.magenta(`${request.user.profile.name}, your account reset code is: `) + color.red(resetCode));215 console.info(color.magenta(`${request.user.profile.name}, your account reset code is: `) + color.red(resetCode));
216 console.log();216 console.info();
217 RESET_CACHE.set(request.user.profile.handle, resetCode);217 RESET_CACHE.set(request.user.profile.handle, resetCode);
218 return response.sendStatus(204);218 return response.sendStatus(204);
219 } catch (error) {219 } catch (error) {
@@ -225,23 +225,23 @@ router.post('/reset-step1', jsonParser, async (request, response) => {
225router.post('/reset-step2', jsonParser, async (request, response) => {225router.post('/reset-step2', jsonParser, async (request, response) => {
226 try {226 try {
227 if (!request.body.code) {227 if (!request.body.code) {
228 console.log('Recover step 2 failed: Missing required fields');228 console.warn('Recover step 2 failed: Missing required fields');
229 return response.status(400).json({ error: 'Missing required fields' });229 return response.status(400).json({ error: 'Missing required fields' });
230 }230 }
231231
232 if (request.user.profile.password && request.user.profile.password !== getPasswordHash(request.body.password, request.user.profile.salt)) {232 if (request.user.profile.password && request.user.profile.password !== getPasswordHash(request.body.password, request.user.profile.salt)) {
233 console.log('Recover step 2 failed: Incorrect password');233 console.warn('Recover step 2 failed: Incorrect password');
234 return response.status(400).json({ error: 'Incorrect password' });234 return response.status(400).json({ error: 'Incorrect password' });
235 }235 }
236236
237 const code = RESET_CACHE.get(request.user.profile.handle);237 const code = RESET_CACHE.get(request.user.profile.handle);
238238
239 if (!code || code !== request.body.code) {239 if (!code || code !== request.body.code) {
240 console.log('Recover step 2 failed: Incorrect code');240 console.warn('Recover step 2 failed: Incorrect code');
241 return response.status(400).json({ error: 'Incorrect code' });241 return response.status(400).json({ error: 'Incorrect code' });
242 }242 }
243243
244 console.log('Resetting account data:', request.user.profile.handle);244 console.info('Resetting account data:', request.user.profile.handle);
245 await fsPromises.rm(request.user.directories.root, { recursive: true, force: true });245 await fsPromises.rm(request.user.directories.root, { recursive: true, force: true });
246246
247 await ensurePublicDirectoriesExist();247 await ensurePublicDirectoriesExist();
src/endpoints/users-public.js+18 -18
@@ -56,7 +56,7 @@ router.post('/list', async (_request, response) => {
56router.post('/login', jsonParser, async (request, response) => {56router.post('/login', jsonParser, async (request, response) => {
57 try {57 try {
58 if (!request.body.handle) {58 if (!request.body.handle) {
59 console.log('Login failed: Missing required fields');59 console.warn('Login failed: Missing required fields');
60 return response.status(400).json({ error: 'Missing required fields' });60 return response.status(400).json({ error: 'Missing required fields' });
61 }61 }
6262
@@ -67,17 +67,17 @@ router.post('/login', jsonParser, async (request, response) => {
67 const user = await storage.getItem(toKey(request.body.handle));67 const user = await storage.getItem(toKey(request.body.handle));
6868
69 if (!user) {69 if (!user) {
70 console.log('Login failed: User', request.body.handle, 'not found');70 console.error('Login failed: User', request.body.handle, 'not found');
71 return response.status(403).json({ error: 'Incorrect credentials' });71 return response.status(403).json({ error: 'Incorrect credentials' });
72 }72 }
7373
74 if (!user.enabled) {74 if (!user.enabled) {
75 console.log('Login failed: User', user.handle, 'is disabled');75 console.warn('Login failed: User', user.handle, 'is disabled');
76 return response.status(403).json({ error: 'User is disabled' });76 return response.status(403).json({ error: 'User is disabled' });
77 }77 }
7878
79 if (user.password && user.password !== getPasswordHash(request.body.password, user.salt)) {79 if (user.password && user.password !== getPasswordHash(request.body.password, user.salt)) {
80 console.log('Login failed: Incorrect password for', user.handle);80 console.warn('Login failed: Incorrect password for', user.handle);
81 return response.status(403).json({ error: 'Incorrect credentials' });81 return response.status(403).json({ error: 'Incorrect credentials' });
82 }82 }
8383
@@ -88,11 +88,11 @@ router.post('/login', jsonParser, async (request, response) => {
8888
89 await loginLimiter.delete(ip);89 await loginLimiter.delete(ip);
90 request.session.handle = user.handle;90 request.session.handle = user.handle;
91 console.log('Login successful:', user.handle, 'from', ip, 'at', new Date().toLocaleString());91 console.info('Login successful:', user.handle, 'from', ip, 'at', new Date().toLocaleString());
92 return response.json({ handle: user.handle });92 return response.json({ handle: user.handle });
93 } catch (error) {93 } catch (error) {
94 if (error instanceof RateLimiterRes) {94 if (error instanceof RateLimiterRes) {
95 console.log('Login failed: Rate limited from', getIpFromRequest(request));95 console.error('Login failed: Rate limited from', getIpFromRequest(request));
96 return response.status(429).send({ error: 'Too many attempts. Try again later or recover your password.' });96 return response.status(429).send({ error: 'Too many attempts. Try again later or recover your password.' });
97 }97 }
9898
@@ -104,7 +104,7 @@ router.post('/login', jsonParser, async (request, response) => {
104router.post('/recover-step1', jsonParser, async (request, response) => {104router.post('/recover-step1', jsonParser, async (request, response) => {
105 try {105 try {
106 if (!request.body.handle) {106 if (!request.body.handle) {
107 console.log('Recover step 1 failed: Missing required fields');107 console.warn('Recover step 1 failed: Missing required fields');
108 return response.status(400).json({ error: 'Missing required fields' });108 return response.status(400).json({ error: 'Missing required fields' });
109 }109 }
110110
@@ -115,24 +115,24 @@ router.post('/recover-step1', jsonParser, async (request, response) => {
115 const user = await storage.getItem(toKey(request.body.handle));115 const user = await storage.getItem(toKey(request.body.handle));
116116
117 if (!user) {117 if (!user) {
118 console.log('Recover step 1 failed: User', request.body.handle, 'not found');118 console.error('Recover step 1 failed: User', request.body.handle, 'not found');
119 return response.status(404).json({ error: 'User not found' });119 return response.status(404).json({ error: 'User not found' });
120 }120 }
121121
122 if (!user.enabled) {122 if (!user.enabled) {
123 console.log('Recover step 1 failed: User', user.handle, 'is disabled');123 console.error('Recover step 1 failed: User', user.handle, 'is disabled');
124 return response.status(403).json({ error: 'User is disabled' });124 return response.status(403).json({ error: 'User is disabled' });
125 }125 }
126126
127 const mfaCode = String(crypto.randomInt(1000, 9999));127 const mfaCode = String(crypto.randomInt(1000, 9999));
128 console.log();128 console.info();
129 console.log(color.blue(`${user.name}, your password recovery code is: `) + color.magenta(mfaCode));129 console.info(color.blue(`${user.name}, your password recovery code is: `) + color.magenta(mfaCode));
130 console.log();130 console.info();
131 MFA_CACHE.set(user.handle, mfaCode);131 MFA_CACHE.set(user.handle, mfaCode);
132 return response.sendStatus(204);132 return response.sendStatus(204);
133 } catch (error) {133 } catch (error) {
134 if (error instanceof RateLimiterRes) {134 if (error instanceof RateLimiterRes) {
135 console.log('Recover step 1 failed: Rate limited from', getIpFromRequest(request));135 console.error('Recover step 1 failed: Rate limited from', getIpFromRequest(request));
136 return response.status(429).send({ error: 'Too many attempts. Try again later or contact your admin.' });136 return response.status(429).send({ error: 'Too many attempts. Try again later or contact your admin.' });
137 }137 }
138138
@@ -144,7 +144,7 @@ router.post('/recover-step1', jsonParser, async (request, response) => {
144router.post('/recover-step2', jsonParser, async (request, response) => {144router.post('/recover-step2', jsonParser, async (request, response) => {
145 try {145 try {
146 if (!request.body.handle || !request.body.code) {146 if (!request.body.handle || !request.body.code) {
147 console.log('Recover step 2 failed: Missing required fields');147 console.warn('Recover step 2 failed: Missing required fields');
148 return response.status(400).json({ error: 'Missing required fields' });148 return response.status(400).json({ error: 'Missing required fields' });
149 }149 }
150150
@@ -153,12 +153,12 @@ router.post('/recover-step2', jsonParser, async (request, response) => {
153 const ip = getIpFromRequest(request);153 const ip = getIpFromRequest(request);
154154
155 if (!user) {155 if (!user) {
156 console.log('Recover step 2 failed: User', request.body.handle, 'not found');156 console.error('Recover step 2 failed: User', request.body.handle, 'not found');
157 return response.status(404).json({ error: 'User not found' });157 return response.status(404).json({ error: 'User not found' });
158 }158 }
159159
160 if (!user.enabled) {160 if (!user.enabled) {
161 console.log('Recover step 2 failed: User', user.handle, 'is disabled');161 console.warn('Recover step 2 failed: User', user.handle, 'is disabled');
162 return response.status(403).json({ error: 'User is disabled' });162 return response.status(403).json({ error: 'User is disabled' });
163 }163 }
164164
@@ -166,7 +166,7 @@ router.post('/recover-step2', jsonParser, async (request, response) => {
166166
167 if (request.body.code !== mfaCode) {167 if (request.body.code !== mfaCode) {
168 await recoverLimiter.consume(ip);168 await recoverLimiter.consume(ip);
169 console.log('Recover step 2 failed: Incorrect code');169 console.warn('Recover step 2 failed: Incorrect code');
170 return response.status(403).json({ error: 'Incorrect code' });170 return response.status(403).json({ error: 'Incorrect code' });
171 }171 }
172172
@@ -186,7 +186,7 @@ router.post('/recover-step2', jsonParser, async (request, response) => {
186 return response.sendStatus(204);186 return response.sendStatus(204);
187 } catch (error) {187 } catch (error) {
188 if (error instanceof RateLimiterRes) {188 if (error instanceof RateLimiterRes) {
189 console.log('Recover step 2 failed: Rate limited from', getIpFromRequest(request));189 console.error('Recover step 2 failed: Rate limited from', getIpFromRequest(request));
190 return response.status(429).send({ error: 'Too many attempts. Try again later or contact your admin.' });190 return response.status(429).send({ error: 'Too many attempts. Try again later or contact your admin.' });
191 }191 }
192192
src/endpoints/vectors.js+3 -3
@@ -360,7 +360,7 @@ async function regenerateCorruptedIndexErrorHandler(req, res, error) {
360360
361 if (exists) {361 if (exists) {
362 const path = index.folderPath;362 const path = index.folderPath;
363 console.error(`Corrupted index detected at ${path}, regenerating...`);363 console.warn(`Corrupted index detected at ${path}, regenerating...`);
364 await index.deleteIndex();364 await index.deleteIndex();
365 return res.redirect(307, req.originalUrl + '?regenerated=true');365 return res.redirect(307, req.originalUrl + '?regenerated=true');
366 }366 }
@@ -474,7 +474,7 @@ router.post('/purge-all', jsonParser, async (req, res) => {
474 continue;474 continue;
475 }475 }
476 await fs.promises.rm(sourcePath, { recursive: true });476 await fs.promises.rm(sourcePath, { recursive: true });
477 console.log(`Deleted vector source store at ${sourcePath}`);477 console.info(`Deleted vector source store at ${sourcePath}`);
478 }478 }
479479
480 return res.sendStatus(200);480 return res.sendStatus(200);
@@ -498,7 +498,7 @@ router.post('/purge', jsonParser, async (req, res) => {
498 continue;498 continue;
499 }499 }
500 await fs.promises.rm(sourcePath, { recursive: true });500 await fs.promises.rm(sourcePath, { recursive: true });
501 console.log(`Deleted vector index at ${sourcePath}`);501 console.info(`Deleted vector index at ${sourcePath}`);
502 }502 }
503503
504 return res.sendStatus(200);504 return res.sendStatus(200);
src/endpoints/worldinfo.js+1 -1
@@ -25,7 +25,7 @@ export function readWorldInfoFile(directories, worldInfoName, allowDummy) {
25 const pathToWorldInfo = path.join(directories.worlds, filename);25 const pathToWorldInfo = path.join(directories.worlds, filename);
2626
27 if (!fs.existsSync(pathToWorldInfo)) {27 if (!fs.existsSync(pathToWorldInfo)) {
28 console.log(`World info file ${filename} doesn't exist.`);28 console.error(`World info file ${filename} doesn't exist.`);
29 return dummyObject;29 return dummyObject;
30 }30 }
3131
src/request-proxy.js+3 -3
@@ -43,9 +43,9 @@ export default function initRequestProxy({ enabled, url, bypass }) {
43 http.globalAgent = proxyAgent;43 http.globalAgent = proxyAgent;
44 https.globalAgent = proxyAgent;44 https.globalAgent = proxyAgent;
4545
46 console.log();46 console.info();
47 console.log(color.green(LOG_HEADER), 'Proxy URL is used:', color.blue(url));47 console.info(color.green(LOG_HEADER), 'Proxy URL is used:', color.blue(url));
48 console.log();48 console.info();
49 } catch (error) {49 } catch (error) {
50 console.error(color.red(LOG_HEADER), 'Failed to initialize request proxy:', error);50 console.error(color.red(LOG_HEADER), 'Failed to initialize request proxy:', error);
51 }51 }
src/util.js+20 -5
@@ -13,6 +13,7 @@ import _ from 'lodash';
13import yauzl from 'yauzl';13import yauzl from 'yauzl';
14import mime from 'mime-types';14import mime from 'mime-types';
15import { default as simpleGit } from 'simple-git';15import { default as simpleGit } from 'simple-git';
16import { LOG_LEVELS } from './constants.js';
1617
17/**18/**
18 * Parsed config object.19 * Parsed config object.
@@ -149,6 +150,7 @@ export function getHexString(length) {
149 */150 */
150export function formatBytes(bytes) {151export function formatBytes(bytes) {
151 if (bytes === 0) return '0 B';152 if (bytes === 0) return '0 B';
153
152 const k = 1024;154 const k = 1024;
153 const sizes = ['B', 'KB', 'MB', 'GB'];155 const sizes = ['B', 'KB', 'MB', 'GB'];
154 const i = Math.floor(Math.log(bytes) / Math.log(k));156 const i = Math.floor(Math.log(bytes) / Math.log(k));
@@ -163,9 +165,7 @@ export function formatBytes(bytes) {
163 */165 */
164export async function extractFileFromZipBuffer(archiveBuffer, fileExtension) {166export async function extractFileFromZipBuffer(archiveBuffer, fileExtension) {
165 return await new Promise((resolve, reject) => yauzl.fromBuffer(Buffer.from(archiveBuffer), { lazyEntries: true }, (err, zipfile) => {167 return await new Promise((resolve, reject) => yauzl.fromBuffer(Buffer.from(archiveBuffer), { lazyEntries: true }, (err, zipfile) => {
166 if (err) {168 if (err) reject(err);
167 reject(err);
168 }
169169
170 zipfile.readEntry();170 zipfile.readEntry();
171 zipfile.on('entry', (entry) => {171 zipfile.on('entry', (entry) => {
@@ -285,10 +285,11 @@ export function deepMerge(target, source) {
285 if (isObject(target) && isObject(source)) {285 if (isObject(target) && isObject(source)) {
286 Object.keys(source).forEach(key => {286 Object.keys(source).forEach(key => {
287 if (isObject(source[key])) {287 if (isObject(source[key])) {
288 if (!(key in target))288 if (!(key in target)) {
289 Object.assign(output, { [key]: source[key] });289 Object.assign(output, { [key]: source[key] });
290 else290 } else {
291 output[key] = deepMerge(target[key], source[key]);291 output[key] = deepMerge(target[key], source[key]);
292 }
292 } else {293 } else {
293 Object.assign(output, { [key]: source[key] });294 Object.assign(output, { [key]: source[key] });
294 }295 }
@@ -467,6 +468,7 @@ export function forwardFetchResponse(from, to) {
467468
468 to.socket.on('close', function () {469 to.socket.on('close', function () {
469 if (from.body instanceof Readable) from.body.destroy(); // Close the remote stream470 if (from.body instanceof Readable) from.body.destroy(); // Close the remote stream
471
470 to.end(); // End the Express response472 to.end(); // End the Express response
471 });473 });
472474
@@ -693,6 +695,19 @@ export function isValidUrl(url) {
693}695}
694696
695/**697/**
698 * Setup the minimum log level
699 */
700export function setupLogLevel() {
701 const logLevel = getConfigValue('minLogLevel', LOG_LEVELS.DEBUG);
702
703 globalThis.console.debug = logLevel <= LOG_LEVELS.DEBUG ? console.debug : () => {};
704 globalThis.console.log = logLevel <= LOG_LEVELS.INFO ? console.log : () => {};
705 globalThis.console.info = logLevel <= LOG_LEVELS.INFO ? console.info : () => {};
706 globalThis.console.warn = logLevel <= LOG_LEVELS.WARN ? console.warn : () => {};
707 globalThis.console.error = logLevel <= LOG_LEVELS.ERROR ? console.error : () => {};
708}
709
710/**
696 * MemoryLimitedMap class that limits the memory usage of string values.711 * MemoryLimitedMap class that limits the memory usage of string values.
697 */712 */
698export class MemoryLimitedMap {713export class MemoryLimitedMap {