Merge branch 'staging' into support-multiple-expressions

This commit is contained in:
Wolfsblvt
2025-02-19 20:22:02 +01:00
124 changed files with 3369 additions and 1434 deletions

View File

@@ -139,19 +139,19 @@ export const UNSAFE_EXTENSIONS = [
export const GEMINI_SAFETY = [
{
category: 'HARM_CATEGORY_HARASSMENT',
threshold: 'BLOCK_NONE',
threshold: 'OFF',
},
{
category: 'HARM_CATEGORY_HATE_SPEECH',
threshold: 'BLOCK_NONE',
threshold: 'OFF',
},
{
category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
threshold: 'BLOCK_NONE',
threshold: 'OFF',
},
{
category: 'HARM_CATEGORY_DANGEROUS_CONTENT',
threshold: 'BLOCK_NONE',
threshold: 'OFF',
},
{
category: 'HARM_CATEGORY_CIVIC_INTEGRITY',
@@ -304,6 +304,7 @@ export const TOGETHERAI_KEYS = [
export const OLLAMA_KEYS = [
'num_predict',
'num_ctx',
'num_batch',
'stop',
'temperature',
'repeat_penalty',
@@ -414,3 +415,10 @@ export const VLLM_KEYS = [
'guided_decoding_backend',
'guided_whitespace_pattern',
];
export const LOG_LEVELS = {
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3,
};

View File

@@ -32,7 +32,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
max_tokens: 4096,
};
console.log('Multimodal captioning request', body);
console.debug('Multimodal captioning request', body);
const result = await fetch(url, {
body: JSON.stringify(body),
@@ -46,14 +46,14 @@ router.post('/caption-image', jsonParser, async (request, response) => {
if (!result.ok) {
const text = await result.text();
console.log(`Claude API returned error: ${result.status} ${result.statusText}`, text);
console.warn(`Claude API returned error: ${result.status} ${result.statusText}`, text);
return response.status(result.status).send({ error: true });
}
/** @type {any} */
const generateResponseJson = await result.json();
const caption = generateResponseJson.content[0].text;
console.log('Claude response:', generateResponseJson);
console.debug('Claude response:', generateResponseJson);
if (!caption) {
return response.status(500).send('No caption found');

View File

@@ -176,7 +176,7 @@ router.post('/get', jsonParser, async (request, response) => {
}
}
catch (err) {
console.log(err);
console.error(err);
}
return response.send(output);
});
@@ -200,7 +200,7 @@ router.post('/download', jsonParser, async (request, response) => {
category = i;
if (category === null) {
console.debug('Bad request: unsupported asset category.');
console.error('Bad request: unsupported asset category.');
return response.sendStatus(400);
}
@@ -212,7 +212,7 @@ router.post('/download', jsonParser, async (request, response) => {
const temp_path = path.join(request.user.directories.assets, 'temp', request.body.filename);
const file_path = path.join(request.user.directories.assets, category, request.body.filename);
console.debug('Request received to download', url, 'to', file_path);
console.info('Request received to download', url, 'to', file_path);
try {
// Download to temp
@@ -241,13 +241,13 @@ router.post('/download', jsonParser, async (request, response) => {
}
// Move into asset place
console.debug('Download finished, moving file from', temp_path, 'to', file_path);
console.info('Download finished, moving file from', temp_path, 'to', file_path);
fs.copyFileSync(temp_path, file_path);
fs.rmSync(temp_path);
response.sendStatus(200);
}
catch (error) {
console.log(error);
console.error(error);
response.sendStatus(500);
}
});
@@ -270,7 +270,7 @@ router.post('/delete', jsonParser, async (request, response) => {
category = i;
if (category === null) {
console.debug('Bad request: unsupported asset category.');
console.error('Bad request: unsupported asset category.');
return response.sendStatus(400);
}
@@ -280,7 +280,7 @@ router.post('/delete', jsonParser, async (request, response) => {
return response.status(400).send(validation.message);
const file_path = path.join(request.user.directories.assets, category, request.body.filename);
console.debug('Request received to delete', category, file_path);
console.info('Request received to delete', category, file_path);
try {
// Delete if previous download failed
@@ -288,17 +288,17 @@ router.post('/delete', jsonParser, async (request, response) => {
fs.unlink(file_path, (err) => {
if (err) throw err;
});
console.debug('Asset deleted.');
console.info('Asset deleted.');
}
else {
console.debug('Asset not found.');
console.error('Asset not found.');
response.sendStatus(400);
}
// Move into asset place
response.sendStatus(200);
}
catch (error) {
console.log(error);
console.error(error);
response.sendStatus(500);
}
});
@@ -314,6 +314,7 @@ router.post('/delete', jsonParser, async (request, response) => {
*/
router.post('/character', jsonParser, async (request, response) => {
if (request.query.name === undefined) return response.sendStatus(400);
// For backwards compatibility, don't reject invalid character names, just sanitize them
const name = sanitize(request.query.name.toString());
const inputCategory = request.query.category;
@@ -325,7 +326,7 @@ router.post('/character', jsonParser, async (request, response) => {
category = i;
if (category === null) {
console.debug('Bad request: unsupported asset category.');
console.error('Bad request: unsupported asset category.');
return response.sendStatus(400);
}
@@ -364,7 +365,7 @@ router.post('/character', jsonParser, async (request, response) => {
return response.send(output);
}
catch (err) {
console.log(err);
console.error(err);
return response.sendStatus(500);
}
});

View File

@@ -11,14 +11,14 @@ router.post('/list', jsonParser, async (req, res) => {
const key = readSecret(req.user.directories, SECRET_KEYS.AZURE_TTS);
if (!key) {
console.error('Azure TTS API Key not set');
console.warn('Azure TTS API Key not set');
return res.sendStatus(403);
}
const region = req.body.region;
if (!region) {
console.error('Azure TTS region not set');
console.warn('Azure TTS region not set');
return res.sendStatus(400);
}
@@ -32,7 +32,7 @@ router.post('/list', jsonParser, async (req, res) => {
});
if (!response.ok) {
console.error('Azure Request failed', response.status, response.statusText);
console.warn('Azure Request failed', response.status, response.statusText);
return res.sendStatus(500);
}
@@ -49,13 +49,13 @@ router.post('/generate', jsonParser, async (req, res) => {
const key = readSecret(req.user.directories, SECRET_KEYS.AZURE_TTS);
if (!key) {
console.error('Azure TTS API Key not set');
console.warn('Azure TTS API Key not set');
return res.sendStatus(403);
}
const { text, voice, region } = req.body;
if (!text || !voice || !region) {
console.error('Missing required parameters');
console.warn('Missing required parameters');
return res.sendStatus(400);
}
@@ -75,7 +75,7 @@ router.post('/generate', jsonParser, async (req, res) => {
});
if (!response.ok) {
console.error('Azure Request failed', response.status, response.statusText);
console.warn('Azure Request failed', response.status, response.statusText);
return res.sendStatus(500);
}

View File

@@ -114,7 +114,7 @@ async function sendClaudeRequest(request, response) {
}
if (!apiKey) {
console.log(color.red(`Claude API key is missing.\n${divider}`));
console.warn(color.red(`Claude API key is missing.\n${divider}`));
return response.status(400).send({ error: true });
}
@@ -179,7 +179,7 @@ async function sendClaudeRequest(request, response) {
additionalHeaders['anthropic-beta'] = 'prompt-caching-2024-07-31';
}
console.log('Claude request:', requestBody);
console.debug('Claude request:', requestBody);
const generateResponse = await fetch(apiUrl + '/messages', {
method: 'POST',
@@ -199,21 +199,21 @@ async function sendClaudeRequest(request, response) {
} else {
if (!generateResponse.ok) {
const generateResponseText = await generateResponse.text();
console.log(color.red(`Claude API returned error: ${generateResponse.status} ${generateResponse.statusText}\n${generateResponseText}\n${divider}`));
console.warn(color.red(`Claude API returned error: ${generateResponse.status} ${generateResponse.statusText}\n${generateResponseText}\n${divider}`));
return response.status(generateResponse.status).send({ error: true });
}
/** @type {any} */
const generateResponseJson = await generateResponse.json();
const responseText = generateResponseJson?.content?.[0]?.text || '';
console.log('Claude response:', generateResponseJson);
console.debug('Claude response:', generateResponseJson);
// Wrap it back to OAI format + save the original content
const reply = { choices: [{ 'message': { 'content': responseText } }], content: generateResponseJson.content };
return response.send(reply);
}
} catch (error) {
console.log(color.red(`Error communicating with Claude: ${error}\n${divider}`));
console.error(color.red(`Error communicating with Claude: ${error}\n${divider}`));
if (!response.headersSent) {
return response.status(500).send({ error: true });
}
@@ -230,12 +230,12 @@ async function sendScaleRequest(request, response) {
const apiKey = readSecret(request.user.directories, SECRET_KEYS.SCALE);
if (!apiKey) {
console.log('Scale API key is missing.');
console.warn('Scale API key is missing.');
return response.status(400).send({ error: true });
}
const requestPrompt = convertTextCompletionPrompt(request.body.messages);
console.log('Scale request:', requestPrompt);
console.debug('Scale request:', requestPrompt);
try {
const controller = new AbortController();
@@ -254,18 +254,18 @@ async function sendScaleRequest(request, response) {
});
if (!generateResponse.ok) {
console.log(`Scale API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
console.warn(`Scale API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
return response.status(500).send({ error: true });
}
/** @type {any} */
const generateResponseJson = await generateResponse.json();
console.log('Scale response:', generateResponseJson);
console.debug('Scale response:', generateResponseJson);
const reply = { choices: [{ 'message': { 'content': generateResponseJson.output } }] };
return response.send(reply);
} catch (error) {
console.log(error);
console.error(error);
if (!response.headersSent) {
return response.status(500).send({ error: true });
}
@@ -282,13 +282,12 @@ async function sendMakerSuiteRequest(request, response) {
const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);
if (!request.body.reverse_proxy && !apiKey) {
console.log('Google AI Studio API key is missing.');
console.warn('Google AI Studio API key is missing.');
return response.status(400).send({ error: true });
}
const model = String(request.body.model);
const stream = Boolean(request.body.stream);
const showThoughts = Boolean(request.body.include_reasoning);
const isThinking = model.includes('thinking');
const generationConfig = {
@@ -306,8 +305,9 @@ async function sendMakerSuiteRequest(request, response) {
}
const should_use_system_prompt = (
model.includes('gemini-2.0-pro') ||
model.includes('gemini-2.0-flash') ||
model.includes('gemini-2.0-flash-thinking-exp') ||
model.includes('gemini-2.0-flash-exp') ||
model.includes('gemini-1.5-flash') ||
model.includes('gemini-1.5-pro') ||
model.startsWith('gemini-exp')
@@ -316,9 +316,15 @@ async function sendMakerSuiteRequest(request, response) {
const prompt = convertGooglePrompt(request.body.messages, model, should_use_system_prompt, getPromptNames(request));
let safetySettings = GEMINI_SAFETY;
if (model.includes('gemini-2.0-flash-exp')) {
// These old models do not support setting the threshold to OFF at all.
if (['gemini-1.5-pro-001', 'gemini-1.5-flash-001', 'gemini-1.5-flash-8b-exp-0827', 'gemini-1.5-flash-8b-exp-0924', 'gemini-pro', 'gemini-1.0-pro', 'gemini-1.0-pro-001'].includes(model)) {
safetySettings = GEMINI_SAFETY.map(setting => ({ ...setting, threshold: 'BLOCK_NONE' }));
}
// Interestingly, Gemini 2.0 Flash does support setting the threshold for HARM_CATEGORY_CIVIC_INTEGRITY to OFF.
else if (['gemini-2.0-flash', 'gemini-2.0-flash-001', 'gemini-2.0-flash-exp'].includes(model)) {
safetySettings = GEMINI_SAFETY.map(setting => ({ ...setting, threshold: 'OFF' }));
}
// Most of the other models allow for setting the threshold of filters, except for HARM_CATEGORY_CIVIC_INTEGRITY, to OFF.
let body = {
contents: prompt.contents,
@@ -330,17 +336,11 @@ async function sendMakerSuiteRequest(request, response) {
body.systemInstruction = prompt.system_instruction;
}
if (isThinking && showThoughts) {
generationConfig.thinkingConfig = {
includeThoughts: true,
};
}
return body;
}
const body = getGeminiBody();
console.log('Google AI Studio request:', body);
console.debug('Google AI Studio request:', body);
try {
const controller = new AbortController();
@@ -366,14 +366,14 @@ async function sendMakerSuiteRequest(request, response) {
// Pipe remote SSE stream to Express response
forwardFetchResponse(generateResponse, response);
} catch (error) {
console.log('Error forwarding streaming response:', error);
console.error('Error forwarding streaming response:', error);
if (!response.headersSent) {
return response.status(500).send({ error: true });
}
}
} else {
if (!generateResponse.ok) {
console.log(`Google AI Studio API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
console.warn(`Google AI Studio API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
return response.status(generateResponse.status).send({ error: true });
}
@@ -383,7 +383,7 @@ async function sendMakerSuiteRequest(request, response) {
const candidates = generateResponseJson?.candidates;
if (!candidates || candidates.length === 0) {
let message = 'Google AI Studio API returned no candidate';
console.log(message, generateResponseJson);
console.warn(message, generateResponseJson);
if (generateResponseJson?.promptFeedback?.blockReason) {
message += `\nPrompt was blocked due to : ${generateResponseJson.promptFeedback.blockReason}`;
}
@@ -391,12 +391,12 @@ async function sendMakerSuiteRequest(request, response) {
}
const responseContent = candidates[0].content ?? candidates[0].output;
console.log('Google AI Studio response:', responseContent);
console.warn('Google AI Studio response:', responseContent);
const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.filter(part => !part.thought)?.map(part => part.text)?.join('\n\n');
if (!responseText) {
let message = 'Google AI Studio Candidate text empty';
console.log(message, generateResponseJson);
console.warn(message, generateResponseJson);
return response.send({ error: { message } });
}
@@ -405,7 +405,7 @@ async function sendMakerSuiteRequest(request, response) {
return response.send(reply);
}
} catch (error) {
console.log('Error communicating with Google AI Studio API: ', error);
console.error('Error communicating with Google AI Studio API: ', error);
if (!response.headersSent) {
return response.status(500).send({ error: true });
}
@@ -419,8 +419,9 @@ async function sendMakerSuiteRequest(request, response) {
*/
async function sendAI21Request(request, response) {
if (!request.body) return response.sendStatus(400);
const controller = new AbortController();
console.log(request.body.messages);
console.debug(request.body.messages);
request.socket.removeAllListeners('close');
request.socket.on('close', function () {
controller.abort();
@@ -446,7 +447,7 @@ async function sendAI21Request(request, response) {
signal: controller.signal,
};
console.log('AI21 request:', body);
console.debug('AI21 request:', body);
try {
const generateResponse = await fetch(API_AI21 + '/chat/completions', options);
@@ -455,16 +456,16 @@ async function sendAI21Request(request, response) {
} else {
if (!generateResponse.ok) {
const errorText = await generateResponse.text();
console.log(`AI21 API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
console.warn(`AI21 API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
const errorJson = tryParse(errorText) ?? { error: true };
return response.status(500).send(errorJson);
}
const generateResponseJson = await generateResponse.json();
console.log('AI21 response:', generateResponseJson);
console.debug('AI21 response:', generateResponseJson);
return response.send(generateResponseJson);
}
} catch (error) {
console.log('Error communicating with AI21 API: ', error);
console.error('Error communicating with AI21 API: ', error);
if (!response.headersSent) {
response.send({ error: true });
} else {
@@ -483,7 +484,7 @@ async function sendMistralAIRequest(request, response) {
const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MISTRALAI);
if (!apiKey) {
console.log('MistralAI API key is missing.');
console.warn('MistralAI API key is missing.');
return response.status(400).send({ error: true });
}
@@ -524,7 +525,7 @@ async function sendMistralAIRequest(request, response) {
timeout: 0,
};
console.log('MisralAI request:', requestBody);
console.debug('MisralAI request:', requestBody);
const generateResponse = await fetch(apiUrl + '/chat/completions', config);
if (request.body.stream) {
@@ -532,16 +533,16 @@ async function sendMistralAIRequest(request, response) {
} else {
if (!generateResponse.ok) {
const errorText = await generateResponse.text();
console.log(`MistralAI API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
console.warn(`MistralAI API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
const errorJson = tryParse(errorText) ?? { error: true };
return response.status(500).send(errorJson);
}
const generateResponseJson = await generateResponse.json();
console.log('MistralAI response:', generateResponseJson);
console.debug('MistralAI response:', generateResponseJson);
return response.send(generateResponseJson);
}
} catch (error) {
console.log('Error communicating with MistralAI API: ', error);
console.error('Error communicating with MistralAI API: ', error);
if (!response.headersSent) {
response.send({ error: true });
} else {
@@ -564,7 +565,7 @@ async function sendCohereRequest(request, response) {
});
if (!apiKey) {
console.log('Cohere API key is missing.');
console.warn('Cohere API key is missing.');
return response.status(400).send({ error: true });
}
@@ -603,7 +604,7 @@ async function sendCohereRequest(request, response) {
requestBody.safety_mode = 'OFF';
}
console.log('Cohere request:', requestBody);
console.debug('Cohere request:', requestBody);
const config = {
method: 'POST',
@@ -625,16 +626,16 @@ async function sendCohereRequest(request, response) {
const generateResponse = await fetch(apiUrl, config);
if (!generateResponse.ok) {
const errorText = await generateResponse.text();
console.log(`Cohere API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
console.warn(`Cohere API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
const errorJson = tryParse(errorText) ?? { error: true };
return response.status(500).send(errorJson);
}
const generateResponseJson = await generateResponse.json();
console.log('Cohere response:', generateResponseJson);
console.debug('Cohere response:', generateResponseJson);
return response.send(generateResponseJson);
}
} catch (error) {
console.log('Error communicating with Cohere API: ', error);
console.error('Error communicating with Cohere API: ', error);
if (!response.headersSent) {
response.send({ error: true });
} else {
@@ -653,7 +654,7 @@ async function sendDeepSeekRequest(request, response) {
const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK);
if (!apiKey && !request.body.reverse_proxy) {
console.log('DeepSeek API key is missing.');
console.warn('DeepSeek API key is missing.');
return response.status(400).send({ error: true });
}
@@ -671,6 +672,11 @@ async function sendDeepSeekRequest(request, response) {
bodyParams['logprobs'] = true;
}
if (Array.isArray(request.body.tools) && request.body.tools.length > 0) {
bodyParams['tools'] = request.body.tools;
bodyParams['tool_choice'] = request.body.tool_choice;
}
const postProcessType = String(request.body.model).endsWith('-reasoner') ? 'deepseek-reasoner' : 'deepseek';
const processedMessages = postProcessPrompt(request.body.messages, postProcessType, getPromptNames(request));
@@ -698,7 +704,7 @@ async function sendDeepSeekRequest(request, response) {
signal: controller.signal,
};
console.log('DeepSeek request:', requestBody);
console.debug('DeepSeek request:', requestBody);
const generateResponse = await fetch(apiUrl + '/chat/completions', config);
@@ -707,16 +713,16 @@ async function sendDeepSeekRequest(request, response) {
} else {
if (!generateResponse.ok) {
const errorText = await generateResponse.text();
console.log(`DeepSeek API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
console.warn(`DeepSeek API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
const errorJson = tryParse(errorText) ?? { error: true };
return response.status(500).send(errorJson);
}
const generateResponseJson = await generateResponse.json();
console.log('DeepSeek response:', generateResponseJson);
console.debug('DeepSeek response:', generateResponseJson);
return response.send(generateResponseJson);
}
} catch (error) {
console.log('Error communicating with DeepSeek API: ', error);
console.error('Error communicating with DeepSeek API: ', error);
if (!response.headersSent) {
response.send({ error: true });
} else {
@@ -774,12 +780,12 @@ router.post('/status', jsonParser, async function (request, response_getstatus_o
api_key_openai = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK);
headers = {};
} else {
console.log('This chat completion source is not supported yet.');
console.warn('This chat completion source is not supported yet.');
return response_getstatus_openai.status(400).send({ error: true });
}
if (!api_key_openai && !request.body.reverse_proxy && request.body.chat_completion_source !== CHAT_COMPLETION_SOURCES.CUSTOM) {
console.log('Chat Completion API key is missing.');
console.warn('Chat Completion API key is missing.');
return response_getstatus_openai.status(400).send({ error: true });
}
@@ -814,23 +820,23 @@ router.post('/status', jsonParser, async function (request, response_getstatus_o
};
});
console.log('Available OpenRouter models:', models);
console.info('Available OpenRouter models:', models);
} else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MISTRALAI) {
const models = data?.data;
console.log(models);
console.info(models);
} else {
const models = data?.data;
if (Array.isArray(models)) {
const modelIds = models.filter(x => x && typeof x === 'object').map(x => x.id).sort();
console.log('Available models:', modelIds);
console.info('Available models:', modelIds);
} else {
console.log('Chat Completion endpoint did not return a list of models.');
console.warn('Chat Completion endpoint did not return a list of models.');
}
}
}
else {
console.log('Chat Completion status check failed. Either Access Token is incorrect or API endpoint is down.');
console.error('Chat Completion status check failed. Either Access Token is incorrect or API endpoint is down.');
response_getstatus_openai.send({ error: true, can_bypass: true, data: { data: [] } });
}
} catch (e) {
@@ -863,7 +869,7 @@ router.post('/bias', jsonParser, async function (request, response) {
const tokenizer = getSentencepiceTokenizer(model);
const instance = await tokenizer?.get();
if (!instance) {
console.warn('Tokenizer not initialized:', model);
console.error('Tokenizer not initialized:', model);
return response.send({});
}
encodeFunction = (text) => new Uint32Array(instance.encodeIds(text));
@@ -1025,7 +1031,7 @@ router.post('/generate', jsonParser, function (request, response) {
mergeObjectWithYaml(headers, request.body.custom_include_headers);
if (request.body.custom_prompt_post_processing) {
console.log('Applying custom prompt post-processing of type', request.body.custom_prompt_post_processing);
console.info('Applying custom prompt post-processing of type', request.body.custom_prompt_post_processing);
request.body.messages = postProcessPrompt(
request.body.messages,
request.body.custom_prompt_post_processing,
@@ -1058,12 +1064,19 @@ router.post('/generate', jsonParser, function (request, response) {
headers = {};
bodyParams = {};
} else {
console.log('This chat completion source is not supported yet.');
console.warn('This chat completion source is not supported yet.');
return response.status(400).send({ error: true });
}
// A few of OpenAIs reasoning models support reasoning effort
if ([CHAT_COMPLETION_SOURCES.CUSTOM, CHAT_COMPLETION_SOURCES.OPENAI].includes(request.body.chat_completion_source)) {
if (['o1', 'o3-mini', 'o3-mini-2025-01-31'].includes(request.body.model)) {
bodyParams['reasoning_effort'] = request.body.reasoning_effort;
}
}
if (!apiKey && !request.body.reverse_proxy && request.body.chat_completion_source !== CHAT_COMPLETION_SOURCES.CUSTOM) {
console.log('OpenAI API key is missing.');
console.warn('OpenAI API key is missing.');
return response.status(400).send({ error: true });
}
@@ -1123,7 +1136,7 @@ router.post('/generate', jsonParser, function (request, response) {
signal: controller.signal,
};
console.log(requestBody);
console.debug(requestBody);
makeRequest(config, response, request);
@@ -1140,7 +1153,7 @@ router.post('/generate', jsonParser, function (request, response) {
const fetchResponse = await fetch(endpointUrl, config);
if (request.body.stream) {
console.log('Streaming request in progress');
console.info('Streaming request in progress');
forwardFetchResponse(fetchResponse, response);
return;
}
@@ -1149,10 +1162,10 @@ router.post('/generate', jsonParser, function (request, response) {
/** @type {any} */
let json = await fetchResponse.json();
response.send(json);
console.log(json);
console.log(json?.choices?.[0]?.message);
console.debug(json);
console.debug(json?.choices?.[0]?.message);
} else if (fetchResponse.status === 429 && retries > 0) {
console.log(`Out of quota, retrying in ${Math.round(timeout / 1000)}s`);
console.warn(`Out of quota, retrying in ${Math.round(timeout / 1000)}s`);
setTimeout(() => {
timeout *= 2;
makeRequest(config, response, request, retries - 1, timeout);
@@ -1161,7 +1174,7 @@ router.post('/generate', jsonParser, function (request, response) {
await handleErrorResponse(fetchResponse);
}
} catch (error) {
console.log('Generation failed', error);
console.error('Generation failed', error);
const message = error.code === 'ECONNREFUSED'
? `Connection refused: ${error.message}`
: error.message || 'Unknown error occurred';
@@ -1183,7 +1196,7 @@ router.post('/generate', jsonParser, function (request, response) {
const message = errorResponse.statusText || 'Unknown error occurred';
const quota_error = errorResponse.status === 429 && errorData?.error?.type === 'insufficient_quota';
console.log('Chat completion request error: ', message, responseText);
console.error('Chat completion request error: ', message, responseText);
if (!response.headersSent) {
response.send({ error: { message }, quota_error: quota_error });

View File

@@ -22,17 +22,17 @@ router.post('/generate', jsonParser, async function (request, response_generate)
request.socket.on('close', async function () {
if (request.body.can_abort && !response_generate.writableEnded) {
try {
console.log('Aborting Kobold generation...');
console.info('Aborting Kobold generation...');
// send abort signal to koboldcpp
const abortResponse = await fetch(`${request.body.api_server}/extra/abort`, {
method: 'POST',
});
if (!abortResponse.ok) {
console.log('Error sending abort request to Kobold:', abortResponse.status);
console.error('Error sending abort request to Kobold:', abortResponse.status);
}
} catch (error) {
console.log(error);
console.error(error);
}
}
controller.abort();
@@ -81,7 +81,7 @@ router.post('/generate', jsonParser, async function (request, response_generate)
}
}
console.log(this_settings);
console.debug(this_settings);
const args = {
body: JSON.stringify(this_settings),
headers: Object.assign(
@@ -105,7 +105,7 @@ router.post('/generate', jsonParser, async function (request, response_generate)
} else {
if (!response.ok) {
const errorText = await response.text();
console.log(`Kobold returned error: ${response.status} ${response.statusText} ${errorText}`);
console.warn(`Kobold returned error: ${response.status} ${response.statusText} ${errorText}`);
try {
const errorJson = JSON.parse(errorText);
@@ -117,7 +117,7 @@ router.post('/generate', jsonParser, async function (request, response_generate)
}
const data = await response.json();
console.log('Endpoint response:', data);
console.debug('Endpoint response:', data);
return response_generate.send(data);
}
} catch (error) {
@@ -125,19 +125,19 @@ router.post('/generate', jsonParser, async function (request, response_generate)
switch (error?.status) {
case 403:
case 503: // retry in case of temporary service issue, possibly caused by a queue failure?
console.debug(`KoboldAI is busy. Retry attempt ${i + 1} of ${MAX_RETRIES}...`);
console.warn(`KoboldAI is busy. Retry attempt ${i + 1} of ${MAX_RETRIES}...`);
await delay(delayAmount);
break;
default:
if ('status' in error) {
console.log('Status Code from Kobold:', error.status);
console.error('Status Code from Kobold:', error.status);
}
return response_generate.send({ error: true });
}
}
}
console.log('Max retries exceeded. Giving up.');
console.error('Max retries exceeded. Giving up.');
return response_generate.send({ error: true });
});
@@ -193,16 +193,16 @@ router.post('/transcribe-audio', urlencodedParser, async function (request, resp
const server = request.body.server;
if (!server) {
console.log('Server is not set');
console.error('Server is not set');
return response.sendStatus(400);
}
if (!request.file) {
console.log('No audio file found');
console.error('No audio file found');
return response.sendStatus(400);
}
console.log('Transcribing audio with KoboldCpp', server);
console.debug('Transcribing audio with KoboldCpp', server);
const fileBase64 = fs.readFileSync(request.file.path).toString('base64');
fs.rmSync(request.file.path);
@@ -226,12 +226,12 @@ router.post('/transcribe-audio', urlencodedParser, async function (request, resp
if (!result.ok) {
const text = await result.text();
console.log('KoboldCpp request failed', result.statusText, text);
console.error('KoboldCpp request failed', result.statusText, text);
return response.status(500).send(text);
}
const data = await result.json();
console.log('KoboldCpp transcription response', data);
console.debug('KoboldCpp transcription response', data);
return response.json(data);
} catch (error) {
console.error('KoboldCpp transcription failed', error);

View File

@@ -13,7 +13,7 @@ router.post('/generate', jsonParser, async function (request, response) {
const cookie = readSecret(request.user.directories, SECRET_KEYS.SCALE_COOKIE);
if (!cookie) {
console.log('No Scale cookie found');
console.error('No Scale cookie found');
return response.sendStatus(400);
}
@@ -62,7 +62,7 @@ router.post('/generate', jsonParser, async function (request, response) {
},
};
console.log('Scale request:', body);
console.debug('Scale request:', body);
const result = await fetch('https://dashboard.scale.com/spellbook/api/trpc/v2.variant.run', {
method: 'POST',
@@ -75,7 +75,7 @@ router.post('/generate', jsonParser, async function (request, response) {
if (!result.ok) {
const text = await result.text();
console.log('Scale request failed', result.statusText, text);
console.error('Scale request failed', result.statusText, text);
return response.status(500).send({ error: { message: result.statusText } });
}
@@ -83,7 +83,7 @@ router.post('/generate', jsonParser, async function (request, response) {
const data = await result.json();
const output = data?.result?.data?.json?.outputs?.[0] || '';
console.log('Scale response:', data);
console.debug('Scale response:', data);
if (!output) {
console.warn('Scale response is empty');
@@ -92,7 +92,7 @@ router.post('/generate', jsonParser, async function (request, response) {
return response.json({ output });
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});

View File

@@ -58,12 +58,12 @@ async function parseOllamaStream(jsonStream, request, response) {
});
jsonStream.body.on('end', () => {
console.log('Streaming request finished');
console.info('Streaming request finished');
response.write('data: [DONE]\n\n');
response.end();
});
} catch (error) {
console.log('Error forwarding streaming response:', error);
console.error('Error forwarding streaming response:', error);
if (!response.headersSent) {
return response.status(500).send({ error: true });
} else {
@@ -79,16 +79,16 @@ async function parseOllamaStream(jsonStream, request, response) {
*/
async function abortKoboldCppRequest(url) {
try {
console.log('Aborting Kobold generation...');
console.info('Aborting Kobold generation...');
const abortResponse = await fetch(`${url}/api/extra/abort`, {
method: 'POST',
});
if (!abortResponse.ok) {
console.log('Error sending abort request to Kobold:', abortResponse.status, abortResponse.statusText);
console.error('Error sending abort request to Kobold:', abortResponse.status, abortResponse.statusText);
}
} catch (error) {
console.log(error);
console.error(error);
}
}
@@ -101,7 +101,7 @@ router.post('/status', jsonParser, async function (request, response) {
request.body.api_server = request.body.api_server.replace('localhost', '127.0.0.1');
}
console.log('Trying to connect to API:', request.body);
console.debug('Trying to connect to API', request.body);
const baseUrl = trimV1(request.body.api_server);
const args = {
@@ -123,6 +123,7 @@ router.post('/status', jsonParser, async function (request, response) {
case TEXTGEN_TYPES.LLAMACPP:
case TEXTGEN_TYPES.INFERMATICAI:
case TEXTGEN_TYPES.OPENROUTER:
case TEXTGEN_TYPES.FEATHERLESS:
url += '/v1/models';
break;
case TEXTGEN_TYPES.DREAMGEN:
@@ -140,9 +141,6 @@ router.post('/status', jsonParser, async function (request, response) {
case TEXTGEN_TYPES.OLLAMA:
url += '/api/tags';
break;
case TEXTGEN_TYPES.FEATHERLESS:
url += '/v1/models';
break;
case TEXTGEN_TYPES.HUGGINGFACE:
url += '/info';
break;
@@ -152,7 +150,7 @@ router.post('/status', jsonParser, async function (request, response) {
const isPossiblyLmStudio = modelsReply.headers.get('x-powered-by') === 'Express';
if (!modelsReply.ok) {
console.log('Models endpoint is offline.');
console.error('Models endpoint is offline.');
return response.sendStatus(400);
}
@@ -173,12 +171,12 @@ router.post('/status', jsonParser, async function (request, response) {
}
if (!Array.isArray(data.data)) {
console.log('Models response is not an array.');
console.error('Models response is not an array.');
return response.sendStatus(400);
}
const modelIds = data.data.map(x => x.id);
console.log('Models available:', modelIds);
console.info('Models available:', modelIds);
// Set result to the first model ID
result = modelIds[0] || 'Valid';
@@ -191,7 +189,7 @@ router.post('/status', jsonParser, async function (request, response) {
if (modelInfoReply.ok) {
/** @type {any} */
const modelInfo = await modelInfoReply.json();
console.log('Ooba model info:', modelInfo);
console.debug('Ooba model info:', modelInfo);
const modelName = modelInfo?.model_name;
result = modelName || result;
@@ -208,7 +206,7 @@ router.post('/status', jsonParser, async function (request, response) {
if (modelInfoReply.ok) {
/** @type {any} */
const modelInfo = await modelInfoReply.json();
console.log('Tabby model info:', modelInfo);
console.debug('Tabby model info:', modelInfo);
const modelName = modelInfo?.id;
result = modelName || result;
@@ -255,7 +253,7 @@ router.post('/props', jsonParser, async function (request, response) {
props['chat_template'] = props['chat_template'].slice(0, -1) + '\n';
}
props['chat_template_hash'] = createHash('sha256').update(props['chat_template']).digest('hex');
console.log(`Model properties: ${JSON.stringify(props)}`);
console.debug(`Model properties: ${JSON.stringify(props)}`);
return response.send(props);
} catch (error) {
console.error(error);
@@ -273,7 +271,7 @@ router.post('/generate', jsonParser, async function (request, response) {
const apiType = request.body.api_type;
const baseUrl = request.body.api_server;
console.log(request.body);
console.debug(request.body);
const controller = new AbortController();
request.socket.removeAllListeners('close');
@@ -375,6 +373,10 @@ router.post('/generate', jsonParser, async function (request, response) {
if (request.body.api_type === TEXTGEN_TYPES.OLLAMA) {
const keepAlive = getConfigValue('ollama.keepAlive', -1);
const numBatch = getConfigValue('ollama.batchSize', -1);
if (numBatch > 0) {
request.body['num_batch'] = numBatch;
}
args.body = JSON.stringify({
model: request.body.model,
prompt: request.body.prompt,
@@ -399,7 +401,7 @@ router.post('/generate', jsonParser, async function (request, response) {
if (completionsReply.ok) {
/** @type {any} */
const data = await completionsReply.json();
console.log('Endpoint response:', data);
console.debug('Endpoint response:', data);
// Map InfermaticAI response to OAI completions format
if (apiType === TEXTGEN_TYPES.INFERMATICAI) {
@@ -411,24 +413,20 @@ router.post('/generate', jsonParser, async function (request, response) {
const text = await completionsReply.text();
const errorBody = { error: true, status: completionsReply.status, response: text };
if (!response.headersSent) {
return response.send(errorBody);
}
return response.end();
return !response.headersSent
? response.send(errorBody)
: response.end();
}
}
} catch (error) {
const status = error?.status ?? error?.code ?? 'UNKNOWN';
const text = error?.error ?? error?.statusText ?? error?.message ?? 'Unknown error on /generate endpoint';
let value = { error: true, status: status, response: text };
console.log('Endpoint error:', error);
console.error('Endpoint error:', error);
if (!response.headersSent) {
return response.send(value);
}
return response.end();
return !response.headersSent
? response.send(value)
: response.end();
}
});
@@ -451,7 +449,7 @@ ollama.post('/download', jsonParser, async function (request, response) {
});
if (!fetchResponse.ok) {
console.log('Download error:', fetchResponse.status, fetchResponse.statusText);
console.error('Download error:', fetchResponse.status, fetchResponse.statusText);
return response.status(fetchResponse.status).send({ error: true });
}
@@ -468,7 +466,7 @@ ollama.post('/caption-image', jsonParser, async function (request, response) {
return response.sendStatus(400);
}
console.log('Ollama caption request:', request.body);
console.debug('Ollama caption request:', request.body);
const baseUrl = trimV1(request.body.server_url);
const fetchResponse = await fetch(`${baseUrl}/api/generate`, {
@@ -483,18 +481,18 @@ ollama.post('/caption-image', jsonParser, async function (request, response) {
});
if (!fetchResponse.ok) {
console.log('Ollama caption error:', fetchResponse.status, fetchResponse.statusText);
console.error('Ollama caption error:', fetchResponse.status, fetchResponse.statusText);
return response.status(500).send({ error: true });
}
/** @type {any} */
const data = await fetchResponse.json();
console.log('Ollama caption response:', data);
console.debug('Ollama caption response:', data);
const caption = data?.response || '';
if (!caption) {
console.log('Ollama caption is empty.');
console.error('Ollama caption is empty.');
return response.status(500).send({ error: true });
}
@@ -513,7 +511,7 @@ llamacpp.post('/caption-image', jsonParser, async function (request, response) {
return response.sendStatus(400);
}
console.log('LlamaCpp caption request:', request.body);
console.debug('LlamaCpp caption request:', request.body);
const baseUrl = trimV1(request.body.server_url);
const fetchResponse = await fetch(`${baseUrl}/completion`, {
@@ -529,18 +527,18 @@ llamacpp.post('/caption-image', jsonParser, async function (request, response) {
});
if (!fetchResponse.ok) {
console.log('LlamaCpp caption error:', fetchResponse.status, fetchResponse.statusText);
console.error('LlamaCpp caption error:', fetchResponse.status, fetchResponse.statusText);
return response.status(500).send({ error: true });
}
/** @type {any} */
const data = await fetchResponse.json();
console.log('LlamaCpp caption response:', data);
console.debug('LlamaCpp caption response:', data);
const caption = data?.content || '';
if (!caption) {
console.log('LlamaCpp caption is empty.');
console.error('LlamaCpp caption is empty.');
return response.status(500).send({ error: true });
}
@@ -558,7 +556,7 @@ llamacpp.post('/props', jsonParser, async function (request, response) {
return response.sendStatus(400);
}
console.log('LlamaCpp props request:', request.body);
console.debug('LlamaCpp props request:', request.body);
const baseUrl = trimV1(request.body.server_url);
const fetchResponse = await fetch(`${baseUrl}/props`, {
@@ -566,12 +564,12 @@ llamacpp.post('/props', jsonParser, async function (request, response) {
});
if (!fetchResponse.ok) {
console.log('LlamaCpp props error:', fetchResponse.status, fetchResponse.statusText);
console.error('LlamaCpp props error:', fetchResponse.status, fetchResponse.statusText);
return response.status(500).send({ error: true });
}
const data = await fetchResponse.json();
console.log('LlamaCpp props response:', data);
console.debug('LlamaCpp props response:', data);
return response.send(data);
@@ -590,7 +588,7 @@ llamacpp.post('/slots', jsonParser, async function (request, response) {
return response.sendStatus(400);
}
console.log('LlamaCpp slots request:', request.body);
console.debug('LlamaCpp slots request:', request.body);
const baseUrl = trimV1(request.body.server_url);
let fetchResponse;
@@ -616,12 +614,12 @@ llamacpp.post('/slots', jsonParser, async function (request, response) {
}
if (!fetchResponse.ok) {
console.log('LlamaCpp slots error:', fetchResponse.status, fetchResponse.statusText);
console.error('LlamaCpp slots error:', fetchResponse.status, fetchResponse.statusText);
return response.status(500).send({ error: true });
}
const data = await fetchResponse.json();
console.log('LlamaCpp slots response:', data);
console.debug('LlamaCpp slots response:', data);
return response.send(data);
@@ -659,14 +657,14 @@ tabby.post('/download', jsonParser, async function (request, response) {
return response.status(403).send({ error: true });
}
} else {
console.log('API Permission error:', permissionResponse.status, permissionResponse.statusText);
console.error('API Permission error:', permissionResponse.status, permissionResponse.statusText);
return response.status(permissionResponse.status).send({ error: true });
}
const fetchResponse = await fetch(`${baseUrl}/v1/download`, args);
if (!fetchResponse.ok) {
console.log('Download error:', fetchResponse.status, fetchResponse.statusText);
console.error('Download error:', fetchResponse.status, fetchResponse.statusText);
return response.status(fetchResponse.status).send({ error: true });
}

View File

@@ -27,7 +27,7 @@ router.post('/delete', jsonParser, getFileNameValidationFunction('bg'), function
const fileName = path.join(request.user.directories.backgrounds, sanitize(request.body.bg));
if (!fs.existsSync(fileName)) {
console.log('BG file not found');
console.error('BG file not found');
return response.sendStatus(400);
}
@@ -43,12 +43,12 @@ router.post('/rename', jsonParser, function (request, response) {
const newFileName = path.join(request.user.directories.backgrounds, sanitize(request.body.new_bg));
if (!fs.existsSync(oldFileName)) {
console.log('BG file not found');
console.error('BG file not found');
return response.sendStatus(400);
}
if (fs.existsSync(newFileName)) {
console.log('New BG file already exists');
console.error('New BG file already exists');
return response.sendStatus(400);
}

View File

@@ -2,10 +2,10 @@ import express from 'express';
import { jsonParser } from '../express-common.js';
import { getPipeline, getRawImage } from '../transformers.js';
const TASK = 'image-to-text';
export const router = express.Router();
const TASK = 'image-to-text';
router.post('/', jsonParser, async (req, res) => {
try {
const { image } = req.body;
@@ -13,14 +13,14 @@ router.post('/', jsonParser, async (req, res) => {
const rawImage = await getRawImage(image);
if (!rawImage) {
console.log('Failed to parse captioned image');
console.warn('Failed to parse captioned image');
return res.sendStatus(400);
}
const pipe = await getPipeline(TASK);
const result = await pipe(rawImage);
const text = result[0].generated_text;
console.log('Image caption:', text);
console.info('Image caption:', text);
return res.json({ caption: text });
} catch (error) {

View File

@@ -97,7 +97,7 @@ async function writeCharacterData(inputFile, data, outputFile, request, crop = u
writeFileAtomicSync(outputImagePath, outputImage);
return true;
} catch (err) {
console.log(err);
console.error(err);
return false;
}
}
@@ -166,7 +166,7 @@ async function tryReadImage(imgPath, crop) {
}
// If it's an unsupported type of image (APNG) - just read the file as buffer
catch (error) {
console.log(`Failed to read image: ${imgPath}`, error);
console.error(`Failed to read image: ${imgPath}`, error);
return fs.readFileSync(imgPath);
}
}
@@ -229,12 +229,12 @@ const processCharacter = async (item, directories) => {
return character;
}
catch (err) {
console.log(`Could not process character: ${item}`);
console.error(`Could not process character: ${item}`);
if (err instanceof SyntaxError) {
console.log(`${item} does not contain a valid JSON object.`);
console.error(`${item} does not contain a valid JSON object.`);
} else {
console.log('An unexpected error occurred: ', err);
console.error('An unexpected error occurred: ', err);
}
return {
@@ -322,7 +322,7 @@ function readFromV2(char) {
};
_.forEach(fieldMappings, (v2Path, charField) => {
//console.log(`Migrating field: ${charField} from ${v2Path}`);
//console.info(`Migrating field: ${charField} from ${v2Path}`);
const v2Value = _.get(char.data, v2Path);
if (_.isUndefined(v2Value)) {
let defaultValue = undefined;
@@ -337,15 +337,15 @@ function readFromV2(char) {
}
if (!_.isUndefined(defaultValue)) {
//console.debug(`Spec v2 extension data missing for field: ${charField}, using default value: ${defaultValue}`);
//console.warn(`Spec v2 extension data missing for field: ${charField}, using default value: ${defaultValue}`);
char[charField] = defaultValue;
} else {
console.debug(`Char ${char['name']} has Spec v2 data missing for unknown field: ${charField}`);
console.warn(`Char ${char['name']} has Spec v2 data missing for unknown field: ${charField}`);
return;
}
}
if (!_.isUndefined(char[charField]) && !_.isUndefined(v2Value) && String(char[charField]) !== String(v2Value)) {
console.debug(`Char ${char['name']} has Spec v2 data mismatch with Spec v1 for field: ${charField}`, char[charField], v2Value);
console.warn(`Char ${char['name']} has Spec v2 data mismatch with Spec v1 for field: ${charField}`, char[charField], v2Value);
}
char[charField] = v2Value;
});
@@ -442,7 +442,7 @@ function charaFormatData(data, directories) {
}
} catch {
console.debug(`Failed to read world info file: ${data.world}. Character book will not be available.`);
console.warn(`Failed to read world info file: ${data.world}. Character book will not be available.`);
}
}
@@ -452,7 +452,7 @@ function charaFormatData(data, directories) {
// Deep merge the extensions object
_.set(char, 'data.extensions', deepMerge(char.data.extensions, extensions));
} catch {
console.debug(`Failed to parse extensions JSON: ${data.extensions}`);
console.warn(`Failed to parse extensions JSON: ${data.extensions}`);
}
}
@@ -526,7 +526,7 @@ async function importFromYaml(uploadPath, context, preservedFileName) {
const fileText = fs.readFileSync(uploadPath, 'utf8');
fs.rmSync(uploadPath);
const yamlData = yaml.parse(fileText);
console.log('Importing from YAML');
console.info('Importing from YAML');
yamlData.name = sanitize(yamlData.name);
const fileName = preservedFileName || getPngName(yamlData.name, context.request.user.directories);
let char = convertToV2({
@@ -559,7 +559,7 @@ async function importFromYaml(uploadPath, context, preservedFileName) {
async function importFromCharX(uploadPath, { request }, preservedFileName) {
const data = fs.readFileSync(uploadPath).buffer;
fs.rmSync(uploadPath);
console.log('Importing from CharX');
console.info('Importing from CharX');
const cardBuffer = await extractFileFromZipBuffer(data, 'card.json');
if (!cardBuffer) {
@@ -608,7 +608,7 @@ async function importFromJson(uploadPath, { request }, preservedFileName) {
let jsonData = JSON.parse(data);
if (jsonData.spec !== undefined) {
console.log(`Importing from ${jsonData.spec} json`);
console.info(`Importing from ${jsonData.spec} json`);
importRisuSprites(request.user.directories, jsonData);
unsetFavFlag(jsonData);
jsonData = readFromV2(jsonData);
@@ -618,7 +618,7 @@ async function importFromJson(uploadPath, { request }, preservedFileName) {
const result = await writeCharacterData(defaultAvatarPath, char, pngName, request);
return result ? pngName : '';
} else if (jsonData.name !== undefined) {
console.log('Importing from v1 json');
console.info('Importing from v1 json');
jsonData.name = sanitize(jsonData.name);
if (jsonData.creator_notes) {
jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');
@@ -644,7 +644,7 @@ async function importFromJson(uploadPath, { request }, preservedFileName) {
const result = await writeCharacterData(defaultAvatarPath, charJSON, pngName, request);
return result ? pngName : '';
} else if (jsonData.char_name !== undefined) {//json Pygmalion notepad
console.log('Importing from gradio json');
console.info('Importing from gradio json');
jsonData.char_name = sanitize(jsonData.char_name);
if (jsonData.creator_notes) {
jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');
@@ -691,7 +691,7 @@ async function importFromPng(uploadPath, { request }, preservedFileName) {
const pngName = preservedFileName || getPngName(jsonData.name, request.user.directories);
if (jsonData.spec !== undefined) {
console.log(`Found a ${jsonData.spec} character file.`);
console.info(`Found a ${jsonData.spec} character file.`);
importRisuSprites(request.user.directories, jsonData);
unsetFavFlag(jsonData);
jsonData = readFromV2(jsonData);
@@ -701,7 +701,7 @@ async function importFromPng(uploadPath, { request }, preservedFileName) {
fs.unlinkSync(uploadPath);
return result ? pngName : '';
} else if (jsonData.name !== undefined) {
console.log('Found a v1 character file.');
console.info('Found a v1 character file.');
if (jsonData.creator_notes) {
jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');
@@ -812,13 +812,13 @@ router.post('/rename', jsonParser, validateAvatarUrlMiddleware, async function (
router.post('/edit', urlencodedParser, validateAvatarUrlMiddleware, async function (request, response) {
if (!request.body) {
console.error('Error: no response body detected');
console.warn('Error: no response body detected');
response.status(400).send('Error: no response body detected');
return;
}
if (request.body.ch_name === '' || request.body.ch_name === undefined || request.body.ch_name === '.') {
console.error('Error: invalid name.');
console.warn('Error: invalid name.');
response.status(400).send('Error: invalid name.');
return;
}
@@ -839,6 +839,9 @@ router.post('/edit', urlencodedParser, validateAvatarUrlMiddleware, async functi
invalidateThumbnail(request.user.directories, 'avatar', request.body.avatar_url);
await writeCharacterData(newAvatarPath, char, targetFile, request, crop);
fs.unlinkSync(newAvatarPath);
// Bust cache to reload the new avatar
response.setHeader('Clear-Site-Data', '"cache"');
}
return response.sendStatus(200);
@@ -860,14 +863,14 @@ router.post('/edit', urlencodedParser, validateAvatarUrlMiddleware, async functi
* @returns {void}
*/
router.post('/edit-attribute', jsonParser, validateAvatarUrlMiddleware, async function (request, response) {
console.log(request.body);
console.debug(request.body);
if (!request.body) {
console.error('Error: no response body detected');
console.warn('Error: no response body detected');
return response.status(400).send('Error: no response body detected');
}
if (request.body.ch_name === '' || request.body.ch_name === undefined || request.body.ch_name === '.') {
console.error('Error: invalid name.');
console.warn('Error: invalid name.');
return response.status(400).send('Error: invalid name.');
}
@@ -879,7 +882,7 @@ router.post('/edit-attribute', jsonParser, validateAvatarUrlMiddleware, async fu
const char = JSON.parse(charJSON);
//check if the field exists
if (char[request.body.field] === undefined && char.data[request.body.field] === undefined) {
console.error('Error: invalid field.');
console.warn('Error: invalid field.');
response.status(400).send('Error: invalid field.');
return;
}
@@ -928,7 +931,7 @@ router.post('/merge-attributes', jsonParser, getFileNameValidationFunction('avat
await writeCharacterData(avatarPath, JSON.stringify(character), targetImg, request);
response.sendStatus(200);
} else {
console.log(validator.lastValidationError);
console.warn(validator.lastValidationError);
response.status(400).send({ message: `Validation failed for ${character.name}`, error: validator.lastValidationError });
}
} catch (exception) {
@@ -1050,7 +1053,7 @@ router.post('/chats', jsonParser, validateAvatarUrlMiddleware, async function (r
const fileSizeInKB = `${(stats.size / 1024).toFixed(2)}kb`;
if (stats.size === 0) {
console.log(`Found an empty chat file: ${pathToFile}`);
console.warn(`Found an empty chat file: ${pathToFile}`);
res({});
return;
}
@@ -1082,7 +1085,7 @@ router.post('/chats', jsonParser, validateAvatarUrlMiddleware, async function (r
res(chatData);
} else {
console.log('Found an invalid or corrupted chat file:', pathToFile);
console.warn('Found an invalid or corrupted chat file:', pathToFile);
res({});
}
}
@@ -1095,7 +1098,7 @@ router.post('/chats', jsonParser, validateAvatarUrlMiddleware, async function (r
return response.send(validFiles);
} catch (error) {
console.log(error);
console.error(error);
return response.send({ error: true });
}
});
@@ -1152,7 +1155,7 @@ router.post('/import', urlencodedParser, async function (request, response) {
const fileName = await importFunction(uploadPath, { request, response }, preservedFileName);
if (!fileName) {
console.error('Failed to import character');
console.warn('Failed to import character');
return response.sendStatus(400);
}
@@ -1162,7 +1165,7 @@ router.post('/import', urlencodedParser, async function (request, response) {
response.send({ file_name: fileName });
} catch (err) {
console.log(err);
console.error(err);
response.send({ error: true });
}
});
@@ -1170,14 +1173,13 @@ router.post('/import', urlencodedParser, async function (request, response) {
router.post('/duplicate', jsonParser, validateAvatarUrlMiddleware, async function (request, response) {
try {
if (!request.body.avatar_url) {
console.log('avatar URL not found in request body');
console.log(request.body);
console.warn('avatar URL not found in request body');
console.debug(request.body);
return response.sendStatus(400);
}
let filename = path.join(request.user.directories.characters, sanitize(request.body.avatar_url));
if (!fs.existsSync(filename)) {
console.log('file for dupe not found');
console.log(filename);
console.error('file for dupe not found', filename);
return response.sendStatus(404);
}
let suffix = 1;
@@ -1205,7 +1207,7 @@ router.post('/duplicate', jsonParser, validateAvatarUrlMiddleware, async functio
}
fs.copyFileSync(filename, newFilename);
console.log(`${filename} was copied to ${newFilename}`);
console.info(`${filename} was copied to ${newFilename}`);
response.send({ path: path.parse(newFilename).base });
}
catch (error) {

View File

@@ -50,7 +50,7 @@ function backupChat(directory, name, chat) {
removeOldBackups(directory, 'chat_', maxTotalChatBackups);
} catch (err) {
console.log(`Could not backup chat for ${name}`, err);
console.error(`Could not backup chat for ${name}`, err);
}
}
@@ -306,8 +306,8 @@ router.post('/save', jsonParser, validateAvatarUrlMiddleware, function (request,
getBackupFunction(request.user.profile.handle)(request.user.directories.backups, directoryName, jsonlData);
return response.send({ result: 'ok' });
} catch (error) {
response.send(error);
return console.log(error);
console.error(error);
return response.send(error);
}
});
@@ -359,17 +359,17 @@ router.post('/rename', jsonParser, validateAvatarUrlMiddleware, async function (
const pathToOriginalFile = path.join(pathToFolder, sanitize(request.body.original_file));
const pathToRenamedFile = path.join(pathToFolder, sanitize(request.body.renamed_file));
const sanitizedFileName = path.parse(pathToRenamedFile).name;
console.log('Old chat name', pathToOriginalFile);
console.log('New chat name', pathToRenamedFile);
console.info('Old chat name', pathToOriginalFile);
console.info('New chat name', pathToRenamedFile);
if (!fs.existsSync(pathToOriginalFile) || fs.existsSync(pathToRenamedFile)) {
console.log('Either Source or Destination files are not available');
console.error('Either Source or Destination files are not available');
return response.status(400).send({ error: true });
}
fs.copyFileSync(pathToOriginalFile, pathToRenamedFile);
fs.rmSync(pathToOriginalFile);
console.log('Successfully renamed.');
console.info('Successfully renamed.');
return response.send({ ok: true, sanitizedFileName });
});
@@ -380,12 +380,12 @@ router.post('/delete', jsonParser, validateAvatarUrlMiddleware, function (reques
const chatFileExists = fs.existsSync(filePath);
if (!chatFileExists) {
console.log(`Chat file not found '${filePath}'`);
console.error(`Chat file not found '${filePath}'`);
return response.sendStatus(400);
}
fs.rmSync(filePath);
console.log('Deleted chat file: ' + filePath);
console.info(`Deleted chat file: ${filePath}`);
return response.send('ok');
});
@@ -402,7 +402,7 @@ router.post('/export', jsonParser, validateAvatarUrlMiddleware, async function (
const errorMessage = {
message: `Could not find JSONL file to export. Source chat file: ${filename}.`,
};
console.log(errorMessage.message);
console.error(errorMessage.message);
return response.status(404).json(errorMessage);
}
try {
@@ -415,14 +415,14 @@ router.post('/export', jsonParser, validateAvatarUrlMiddleware, async function (
result: rawFile,
};
console.log(`Chat exported as ${exportfilename}`);
console.info(`Chat exported as ${exportfilename}`);
return response.status(200).json(successMessage);
} catch (err) {
console.error(err);
const errorMessage = {
message: `Could not read JSONL file to export. Source chat file: ${filename}.`,
};
console.log(errorMessage.message);
console.error(errorMessage.message);
return response.status(500).json(errorMessage);
}
}
@@ -449,12 +449,11 @@ router.post('/export', jsonParser, validateAvatarUrlMiddleware, async function (
message: `Chat saved to ${exportfilename}`,
result: buffer,
};
console.log(`Chat exported as ${exportfilename}`);
console.info(`Chat exported as ${exportfilename}`);
return response.status(200).json(successMessage);
});
} catch (err) {
console.log('chat export failed.');
console.log(err);
console.error('chat export failed.', err);
return response.sendStatus(400);
}
});
@@ -513,7 +512,7 @@ router.post('/import', urlencodedParser, validateAvatarUrlMiddleware, function (
} else if (jsonData.type === 'risuChat') { // RisuAI format
importFunc = importRisuChat;
} else { // Unknown format
console.log('Incorrect chat format .json');
console.error('Incorrect chat format .json');
return response.send({ error: true });
}
@@ -541,7 +540,7 @@ router.post('/import', urlencodedParser, validateAvatarUrlMiddleware, function (
const jsonData = JSON.parse(header);
if (!(jsonData.user_name !== undefined || jsonData.name !== undefined)) {
console.log('Incorrect chat format .jsonl');
console.error('Incorrect chat format .jsonl');
return response.send({ error: true });
}
@@ -647,7 +646,7 @@ router.post('/search', jsonParser, validateAvatarUrlMiddleware, function (reques
break;
}
} catch (error) {
console.error(groupFile, 'group file is corrupted:', error);
console.warn(groupFile, 'group file is corrupted:', error);
}
}

View File

@@ -44,9 +44,9 @@ router.post('/', jsonParser, async (req, res) => {
}
}
console.log('Classify input:', text);
console.debug('Classify input:', text);
const result = await getResult(text);
console.log('Classify output:', result);
console.debug('Classify output:', result);
return res.json({ classification: result });
} catch (error) {

View File

@@ -71,7 +71,7 @@ export function getDefaultPresets(directories) {
return presets;
} catch (err) {
console.log('Failed to get default presets', err);
console.warn('Failed to get default presets', err);
return [];
}
}
@@ -92,7 +92,7 @@ export function getDefaultPresetFile(filename) {
const fileContent = fs.readFileSync(contentPath, 'utf8');
return JSON.parse(fileContent);
} catch (err) {
console.log(`Failed to get default file ${filename}`, err);
console.warn(`Failed to get default file ${filename}`, err);
return null;
}
}
@@ -121,21 +121,21 @@ async function seedContentForUser(contentIndex, directories, forceCategories) {
}
if (!contentItem.folder) {
console.log(`Content file ${contentItem.filename} has no parent folder`);
console.warn(`Content file ${contentItem.filename} has no parent folder`);
continue;
}
const contentPath = path.join(contentItem.folder, contentItem.filename);
if (!fs.existsSync(contentPath)) {
console.log(`Content file ${contentItem.filename} is missing`);
console.warn(`Content file ${contentItem.filename} is missing`);
continue;
}
const contentTarget = getTargetByType(contentItem.type, directories);
if (!contentTarget) {
console.log(`Content file ${contentItem.filename} has unknown type ${contentItem.type}`);
console.warn(`Content file ${contentItem.filename} has unknown type ${contentItem.type}`);
continue;
}
@@ -144,12 +144,12 @@ async function seedContentForUser(contentIndex, directories, forceCategories) {
contentLog.push(contentItem.filename);
if (fs.existsSync(targetPath)) {
console.log(`Content file ${contentItem.filename} already exists in ${contentTarget}`);
console.warn(`Content file ${contentItem.filename} already exists in ${contentTarget}`);
continue;
}
fs.cpSync(contentPath, targetPath, { recursive: true, force: false });
console.log(`Content file ${contentItem.filename} copied to ${contentTarget}`);
console.info(`Content file ${contentItem.filename} copied to ${contentTarget}`);
anyContentAdded = true;
}
@@ -182,12 +182,12 @@ export async function checkForNewContent(directoriesList, forceCategories = [])
}
if (anyContentAdded && !contentCheckSkip && forceCategories?.length === 0) {
console.log();
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.')}`);
console.log();
console.info();
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.')}`);
console.info();
}
} catch (err) {
console.log('Content check failed', err);
console.error('Content check failed', err);
}
}
@@ -331,7 +331,7 @@ async function downloadChubLorebook(id) {
if (!result.ok) {
const text = await result.text();
console.log('Chub returned error', result.statusText, text);
console.error('Chub returned error', result.statusText, text);
throw new Error('Failed to download lorebook');
}
@@ -355,7 +355,7 @@ async function downloadChubCharacter(id) {
if (!result.ok) {
const text = await result.text();
console.log('Chub returned error', result.statusText, text);
console.error('Chub returned error', result.statusText, text);
throw new Error('Failed to download character');
}
@@ -376,7 +376,7 @@ async function downloadPygmalionCharacter(id) {
if (!result.ok) {
const text = await result.text();
console.log('Pygsite returned error', result.status, text);
console.error('Pygsite returned error', result.status, text);
throw new Error('Failed to download character');
}
@@ -485,7 +485,7 @@ async function downloadJannyCharacter(uuid) {
}
}
console.log('Janny returned error', result.statusText, await result.text());
console.error('Janny returned error', result.statusText, await result.text());
throw new Error('Failed to download character');
}
@@ -577,7 +577,7 @@ async function downloadRisuCharacter(uuid) {
if (!result.ok) {
const text = await result.text();
console.log('RisuAI returned error', result.statusText, text);
console.error('RisuAI returned error', result.statusText, text);
throw new Error('Failed to download character');
}
@@ -673,11 +673,11 @@ router.post('/importURL', jsonParser, async (request, response) => {
type = chubParsed?.type;
if (chubParsed?.type === 'character') {
console.log('Downloading chub character:', chubParsed.id);
console.info('Downloading chub character:', chubParsed.id);
result = await downloadChubCharacter(chubParsed.id);
}
else if (chubParsed?.type === 'lorebook') {
console.log('Downloading chub lorebook:', chubParsed.id);
console.info('Downloading chub lorebook:', chubParsed.id);
result = await downloadChubLorebook(chubParsed.id);
}
else {
@@ -692,7 +692,7 @@ router.post('/importURL', jsonParser, async (request, response) => {
type = 'character';
result = await downloadRisuCharacter(uuid);
} else if (isGeneric) {
console.log('Downloading from generic url.');
console.info('Downloading from generic url.');
type = 'character';
result = await downloadGenericPng(url);
} else {
@@ -708,7 +708,7 @@ router.post('/importURL', jsonParser, async (request, response) => {
response.set('X-Custom-Content-Type', type);
return response.send(result.buffer);
} catch (error) {
console.log('Importing custom content failed', error);
console.error('Importing custom content failed', error);
return response.sendStatus(500);
}
});
@@ -728,22 +728,22 @@ router.post('/importUUID', jsonParser, async (request, response) => {
const uuidType = uuid.includes('lorebook') ? 'lorebook' : 'character';
if (isPygmalion) {
console.log('Downloading Pygmalion character:', uuid);
console.info('Downloading Pygmalion character:', uuid);
result = await downloadPygmalionCharacter(uuid);
} else if (isJannny) {
console.log('Downloading Janitor character:', uuid.split('_')[0]);
console.info('Downloading Janitor character:', uuid.split('_')[0]);
result = await downloadJannyCharacter(uuid.split('_')[0]);
} else if (isAICC) {
const [, author, card] = uuid.split('/');
console.log('Downloading AICC character:', `${author}/${card}`);
console.info('Downloading AICC character:', `${author}/${card}`);
result = await downloadAICCCharacter(`${author}/${card}`);
} else {
if (uuidType === 'character') {
console.log('Downloading chub character:', uuid);
console.info('Downloading chub character:', uuid);
result = await downloadChubCharacter(uuid);
}
else if (uuidType === 'lorebook') {
console.log('Downloading chub lorebook:', uuid);
console.info('Downloading chub lorebook:', uuid);
result = await downloadChubLorebook(uuid);
}
else {
@@ -756,7 +756,7 @@ router.post('/importUUID', jsonParser, async (request, response) => {
response.set('X-Custom-Content-Type', uuidType);
return response.send(result.buffer);
} catch (error) {
console.log('Importing custom content failed', error);
console.error('Importing custom content failed', error);
return response.sendStatus(500);
}
});

View File

@@ -80,7 +80,7 @@ router.post('/install', jsonParser, async (request, response) => {
const { url, global } = request.body;
if (global && !request.user.profile.admin) {
console.warn(`User ${request.user.profile.handle} does not have permission to install global extensions.`);
console.error(`User ${request.user.profile.handle} does not have permission to install global extensions.`);
return response.status(403).send('Forbidden: No permission to install global extensions.');
}
@@ -92,13 +92,13 @@ router.post('/install', jsonParser, async (request, response) => {
}
await git.clone(url, extensionPath, { '--depth': 1 });
console.log(`Extension has been cloned at ${extensionPath}`);
console.info(`Extension has been cloned at ${extensionPath}`);
const { version, author, display_name } = await getManifest(extensionPath);
return response.send({ version, author, display_name, extensionPath });
} catch (error) {
console.log('Importing custom content failed', error);
console.error('Importing custom content failed', error);
return response.status(500).send(`Server Error: ${error.message}`);
}
});
@@ -124,7 +124,7 @@ router.post('/update', jsonParser, async (request, response) => {
const { extensionName, global } = request.body;
if (global && !request.user.profile.admin) {
console.warn(`User ${request.user.profile.handle} does not have permission to update global extensions.`);
console.error(`User ${request.user.profile.handle} does not have permission to update global extensions.`);
return response.status(403).send('Forbidden: No permission to update global extensions.');
}
@@ -139,9 +139,9 @@ router.post('/update', jsonParser, async (request, response) => {
const currentBranch = await git.cwd(extensionPath).branch();
if (!isUpToDate) {
await git.cwd(extensionPath).pull('origin', currentBranch.current);
console.log(`Extension has been updated at ${extensionPath}`);
console.info(`Extension has been updated at ${extensionPath}`);
} else {
console.log(`Extension is up to date at ${extensionPath}`);
console.info(`Extension is up to date at ${extensionPath}`);
}
await git.cwd(extensionPath).fetch('origin');
const fullCommitHash = await git.cwd(extensionPath).revparse(['HEAD']);
@@ -150,7 +150,7 @@ router.post('/update', jsonParser, async (request, response) => {
return response.send({ shortCommitHash, extensionPath, isUpToDate, remoteUrl });
} catch (error) {
console.log('Updating custom content failed', error);
console.error('Updating custom content failed', error);
return response.status(500).send(`Server Error: ${error.message}`);
}
});
@@ -164,7 +164,7 @@ router.post('/move', jsonParser, async (request, response) => {
}
if (!request.user.profile.admin) {
console.warn(`User ${request.user.profile.handle} does not have permission to move extensions.`);
console.error(`User ${request.user.profile.handle} does not have permission to move extensions.`);
return response.status(403).send('Forbidden: No permission to move extensions.');
}
@@ -190,11 +190,11 @@ router.post('/move', jsonParser, async (request, response) => {
fs.cpSync(sourcePath, destinationPath, { recursive: true, force: true });
fs.rmSync(sourcePath, { recursive: true, force: true });
console.log(`Extension has been moved from ${sourcePath} to ${destinationPath}`);
console.info(`Extension has been moved from ${sourcePath} to ${destinationPath}`);
return response.sendStatus(204);
} catch (error) {
console.log('Moving extension failed', error);
console.error('Moving extension failed', error);
return response.status(500).send('Internal Server Error. Try again later.');
}
});
@@ -237,13 +237,13 @@ router.post('/version', jsonParser, async (request, response) => {
// get only the working branch
const currentBranchName = currentBranch.current;
await git.cwd(extensionPath).fetch('origin');
console.log(extensionName, currentBranchName, currentCommitHash);
console.debug(extensionName, currentBranchName, currentCommitHash);
const { isUpToDate, remoteUrl } = await checkIfRepoIsUpToDate(extensionPath);
return response.send({ currentBranchName, currentCommitHash, isUpToDate, remoteUrl });
} catch (error) {
console.log('Getting extension version failed', error);
console.error('Getting extension version failed', error);
return response.status(500).send(`Server Error: ${error.message}`);
}
});
@@ -265,7 +265,7 @@ router.post('/delete', jsonParser, async (request, response) => {
const { extensionName, global } = request.body;
if (global && !request.user.profile.admin) {
console.warn(`User ${request.user.profile.handle} does not have permission to delete global extensions.`);
console.error(`User ${request.user.profile.handle} does not have permission to delete global extensions.`);
return response.status(403).send('Forbidden: No permission to delete global extensions.');
}
@@ -277,12 +277,12 @@ router.post('/delete', jsonParser, async (request, response) => {
}
await fs.promises.rm(extensionPath, { recursive: true });
console.log(`Extension has been deleted at ${extensionPath}`);
console.info(`Extension has been deleted at ${extensionPath}`);
return response.send(`Extension has been deleted at ${extensionPath}`);
} catch (error) {
console.log('Deleting custom content failed', error);
console.error('Deleting custom content failed', error);
return response.status(500).send(`Server Error: ${error.message}`);
}
});
@@ -323,7 +323,7 @@ router.get('/discover', jsonParser, function (request, response) {
// Combine all extensions
const allExtensions = [...builtInExtensions, ...userExtensions, ...globalExtensions];
console.log('Extensions available for', request.user.profile.handle, allExtensions);
console.info('Extensions available for', request.user.profile.handle, allExtensions);
return response.send(allExtensions);
});

View File

@@ -21,7 +21,7 @@ router.post('/sanitize-filename', jsonParser, async (request, response) => {
const sanitizedFilename = sanitize(fileName);
return response.send({ fileName: sanitizedFilename });
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -44,10 +44,10 @@ router.post('/upload', jsonParser, async (request, response) => {
const pathToUpload = path.join(request.user.directories.files, request.body.name);
writeFileSyncAtomic(pathToUpload, request.body.data, 'base64');
const url = clientRelativePath(request.user.directories.root, pathToUpload);
console.log(`Uploaded file: ${url} from ${request.user.profile.handle}`);
console.info(`Uploaded file: ${url} from ${request.user.profile.handle}`);
return response.send({ path: url });
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -68,10 +68,10 @@ router.post('/delete', jsonParser, async (request, response) => {
}
fs.rmSync(pathToDelete);
console.log(`Deleted file: ${request.body.path} from ${request.user.profile.handle}`);
console.info(`Deleted file: ${request.body.path} from ${request.user.profile.handle}`);
return response.sendStatus(200);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -87,7 +87,7 @@ router.post('/verify', jsonParser, async (request, response) => {
for (const url of request.body.urls) {
const pathToVerify = path.join(request.user.directories.root, url);
if (!pathToVerify.startsWith(request.user.directories.files)) {
console.debug(`File verification: Invalid path: ${pathToVerify}`);
console.warn(`File verification: Invalid path: ${pathToVerify}`);
continue;
}
const fileExists = fs.existsSync(pathToVerify);
@@ -96,7 +96,7 @@ router.post('/verify', jsonParser, async (request, response) => {
return response.send(verified);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});

View File

@@ -34,7 +34,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
generationConfig: { maxOutputTokens: 1000 },
};
console.log('Multimodal captioning request', model, body);
console.debug('Multimodal captioning request', model, body);
const result = await fetch(url, {
body: JSON.stringify(body),
@@ -46,13 +46,13 @@ router.post('/caption-image', jsonParser, async (request, response) => {
if (!result.ok) {
const error = await result.json();
console.log(`Google AI Studio API returned error: ${result.status} ${result.statusText}`, error);
console.error(`Google AI Studio API returned error: ${result.status} ${result.statusText}`, error);
return response.status(result.status).send({ error: true });
}
/** @type {any} */
const data = await result.json();
console.log('Multimodal captioning response', data);
console.info('Multimodal captioning response', data);
const candidates = data?.candidates;
if (!candidates) {

View File

@@ -114,7 +114,7 @@ router.post('/delete', jsonParser, async (request, response) => {
if (group && Array.isArray(group.chats)) {
for (const chat of group.chats) {
console.log('Deleting group chat', chat);
console.info('Deleting group chat', chat);
const pathToFile = path.join(request.user.directories.groupChats, `${id}.jsonl`);
if (fs.existsSync(pathToFile)) {

View File

@@ -155,7 +155,7 @@ router.post('/cancel-task', jsonParser, async (request, response) => {
});
const data = await fetchResult.json();
console.log(`Cancelled Horde task ${taskId}`);
console.info(`Cancelled Horde task ${taskId}`);
return response.send(data);
} catch (error) {
console.error(error);
@@ -174,7 +174,7 @@ router.post('/task-status', jsonParser, async (request, response) => {
});
const data = await fetchResult.json();
console.log(`Horde task ${taskId} status:`, data);
console.info(`Horde task ${taskId} status:`, data);
return response.send(data);
} catch (error) {
console.error(error);
@@ -187,7 +187,7 @@ router.post('/generate-text', jsonParser, async (request, response) => {
const url = 'https://aihorde.net/api/v2/generate/text/async';
const agent = await getClientAgent();
console.log(request.body);
console.debug(request.body);
try {
const result = await fetch(url, {
method: 'POST',
@@ -201,14 +201,14 @@ router.post('/generate-text', jsonParser, async (request, response) => {
if (!result.ok) {
const message = await result.text();
console.log('Horde returned an error:', message);
console.error('Horde returned an error:', message);
return response.send({ error: { message } });
}
const data = await result.json();
return response.send(data);
} catch (error) {
console.log(error);
console.error(error);
return response.send({ error: true });
}
});
@@ -254,7 +254,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
await delay(CHECK_INTERVAL);
const status = await ai_horde.getInterrogationStatus(result.id);
console.log(status);
console.info(status);
if (status.state === HordeAsyncRequestStates.done) {
@@ -263,7 +263,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
return response.sendStatus(500);
}
console.log('Image interrogation result:', status);
console.debug('Image interrogation result:', status);
const caption = status?.forms[0]?.result?.caption || '';
if (!caption) {
@@ -275,7 +275,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
}
if (status.state === HordeAsyncRequestStates.faulted || status.state === HordeAsyncRequestStates.cancelled) {
console.log('Image interrogation request is not successful.');
console.error('Image interrogation request is not successful.');
return response.sendStatus(503);
}
}
@@ -315,7 +315,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
try {
const maxLength = PROMPT_THRESHOLD - String(request.body.negative_prompt).length - 5;
if (String(request.body.prompt).length > maxLength) {
console.log('Stable Horde prompt is too long, truncating...');
console.warn('Stable Horde prompt is too long, truncating...');
request.body.prompt = String(request.body.prompt).substring(0, maxLength);
}
@@ -324,14 +324,14 @@ router.post('/generate-image', jsonParser, async (request, response) => {
const sanitized = sanitizeHordeImagePrompt(request.body.prompt);
if (request.body.prompt !== sanitized) {
console.log('Stable Horde prompt was sanitized.');
console.info('Stable Horde prompt was sanitized.');
}
request.body.prompt = sanitized;
}
const api_key_horde = readSecret(request.user.directories, SECRET_KEYS.HORDE) || ANONYMOUS_KEY;
console.log('Stable Horde request:', request.body);
console.debug('Stable Horde request:', request.body);
const ai_horde = await getHordeClient();
// noinspection JSCheckFunctionSignatures -- see @ts-ignore - use_gfpgan
@@ -360,16 +360,16 @@ router.post('/generate-image', jsonParser, async (request, response) => {
{ token: api_key_horde });
if (!generation.id) {
console.error('Image generation request is not satisfyable:', generation.message || 'unknown error');
console.warn('Image generation request is not satisfyable:', generation.message || 'unknown error');
return response.sendStatus(400);
}
console.log('Horde image generation request:', generation);
console.info('Horde image generation request:', generation);
const controller = new AbortController();
request.socket.removeAllListeners('close');
request.socket.on('close', function () {
console.log('Horde image generation request aborted.');
console.warn('Horde image generation request aborted.');
controller.abort();
if (generation.id) ai_horde.deleteImageGenerationRequest(generation.id);
});
@@ -378,7 +378,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
controller.signal.throwIfAborted();
await delay(CHECK_INTERVAL);
const check = await ai_horde.getImageGenerationCheck(generation.id);
console.log(check);
console.info(check);
if (check.done) {
const result = await ai_horde.getImageGenerationStatus(generation.id);

View File

@@ -71,7 +71,7 @@ router.post('/upload', jsonParser, async (request, response) => {
await fs.promises.writeFile(pathToNewFile, new Uint8Array(imageBuffer));
response.send({ path: clientRelativePath(request.user.directories.root, pathToNewFile) });
} catch (error) {
console.log(error);
console.error(error);
response.status(500).send({ error: 'Failed to save the image' });
}
});

View File

@@ -120,7 +120,7 @@ router.post('/status', jsonParser, async function (req, res) {
const api_key_novel = readSecret(req.user.directories, SECRET_KEYS.NOVEL);
if (!api_key_novel) {
console.log('NovelAI Access Token is missing.');
console.warn('NovelAI Access Token is missing.');
return res.sendStatus(400);
}
@@ -137,15 +137,15 @@ router.post('/status', jsonParser, async function (req, res) {
const data = await response.json();
return res.send(data);
} else if (response.status == 401) {
console.log('NovelAI Access Token is incorrect.');
console.error('NovelAI Access Token is incorrect.');
return res.send({ error: true });
}
else {
console.log('NovelAI returned an error:', response.statusText);
console.warn('NovelAI returned an error:', response.statusText);
return res.send({ error: true });
}
} catch (error) {
console.log(error);
console.error(error);
return res.send({ error: true });
}
});
@@ -156,7 +156,7 @@ router.post('/generate', jsonParser, async function (req, res) {
const api_key_novel = readSecret(req.user.directories, SECRET_KEYS.NOVEL);
if (!api_key_novel) {
console.log('NovelAI Access Token is missing.');
console.warn('NovelAI Access Token is missing.');
return res.sendStatus(400);
}
@@ -241,7 +241,7 @@ router.post('/generate', jsonParser, async function (req, res) {
}
}
console.log(util.inspect(data, { depth: 4 }));
console.debug(util.inspect(data, { depth: 4 }));
const args = {
body: JSON.stringify(data),
@@ -261,7 +261,7 @@ router.post('/generate', jsonParser, async function (req, res) {
if (!response.ok) {
const text = await response.text();
let message = text;
console.log(`Novel API returned error: ${response.status} ${response.statusText} ${text}`);
console.warn(`Novel API returned error: ${response.status} ${response.statusText} ${text}`);
try {
const data = JSON.parse(text);
@@ -276,7 +276,7 @@ router.post('/generate', jsonParser, async function (req, res) {
/** @type {any} */
const data = await response.json();
console.log('NovelAI Output', data?.output);
console.info('NovelAI Output', data?.output);
return res.send(data);
}
} catch (error) {
@@ -292,12 +292,12 @@ router.post('/generate-image', jsonParser, async (request, response) => {
const key = readSecret(request.user.directories, SECRET_KEYS.NOVEL);
if (!key) {
console.log('NovelAI Access Token is missing.');
console.warn('NovelAI Access Token is missing.');
return response.sendStatus(400);
}
try {
console.log('NAI Diffusion request:', request.body);
console.debug('NAI Diffusion request:', request.body);
const generateUrl = `${IMAGE_NOVELAI}/ai/generate-image`;
const generateResult = await fetch(generateUrl, {
method: 'POST',
@@ -358,7 +358,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
if (!generateResult.ok) {
const text = await generateResult.text();
console.log('NovelAI returned an error.', generateResult.statusText, text);
console.warn('NovelAI returned an error.', generateResult.statusText, text);
return response.sendStatus(500);
}
@@ -366,7 +366,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
const imageBuffer = await extractFileFromZipBuffer(archiveBuffer, '.png');
if (!imageBuffer) {
console.warn('NovelAI generated an image, but the PNG file was not found.');
console.error('NovelAI generated an image, but the PNG file was not found.');
return response.sendStatus(500);
}
@@ -378,7 +378,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
}
try {
console.debug('Upscaling image...');
console.info('Upscaling image...');
const upscaleUrl = `${API_NOVELAI}/ai/upscale`;
const upscaleResult = await fetch(upscaleUrl, {
method: 'POST',
@@ -413,7 +413,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
return response.send(originalBase64);
}
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -422,7 +422,7 @@ router.post('/generate-voice', jsonParser, async (request, response) => {
const token = readSecret(request.user.directories, SECRET_KEYS.NOVEL);
if (!token) {
console.log('NovelAI Access Token is missing.');
console.error('NovelAI Access Token is missing.');
return response.sendStatus(400);
}
@@ -445,7 +445,7 @@ router.post('/generate-voice', jsonParser, async (request, response) => {
if (!result.ok) {
const errorText = await result.text();
console.log('NovelAI returned an error.', result.statusText, errorText);
console.error('NovelAI returned an error.', result.statusText, errorText);
return response.sendStatus(500);
}

View File

@@ -63,7 +63,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
}
if (!key && !request.body.reverse_proxy && ['custom', 'ooba', 'koboldcpp', 'vllm'].includes(request.body.api) === false) {
console.log('No key found for API', request.body.api);
console.warn('No key found for API', request.body.api);
return response.sendStatus(400);
}
@@ -93,7 +93,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
excludeKeysByYaml(body, request.body.custom_exclude_body);
}
console.log('Multimodal captioning request', body);
console.debug('Multimodal captioning request', body);
let apiUrl = '';
@@ -158,13 +158,13 @@ router.post('/caption-image', jsonParser, async (request, response) => {
if (!result.ok) {
const text = await result.text();
console.log('Multimodal captioning request failed', result.statusText, text);
console.warn('Multimodal captioning request failed', result.statusText, text);
return response.status(500).send(text);
}
/** @type {any} */
const data = await result.json();
console.log('Multimodal captioning response', data);
console.info('Multimodal captioning response', data);
const caption = data?.choices[0]?.message?.content;
if (!caption) {
@@ -184,17 +184,17 @@ router.post('/transcribe-audio', urlencodedParser, async (request, response) =>
const key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);
if (!key) {
console.log('No OpenAI key found');
console.warn('No OpenAI key found');
return response.sendStatus(400);
}
if (!request.file) {
console.log('No audio file found');
console.warn('No audio file found');
return response.sendStatus(400);
}
const formData = new FormData();
console.log('Processing audio file', request.file.path);
console.info('Processing audio file', request.file.path);
formData.append('file', fs.createReadStream(request.file.path), { filename: 'audio.wav', contentType: 'audio/wav' });
formData.append('model', request.body.model);
@@ -213,13 +213,13 @@ router.post('/transcribe-audio', urlencodedParser, async (request, response) =>
if (!result.ok) {
const text = await result.text();
console.log('OpenAI request failed', result.statusText, text);
console.warn('OpenAI request failed', result.statusText, text);
return response.status(500).send(text);
}
fs.rmSync(request.file.path);
const data = await result.json();
console.log('OpenAI transcription response', data);
console.debug('OpenAI transcription response', data);
return response.json(data);
} catch (error) {
console.error('OpenAI transcription failed', error);
@@ -232,7 +232,7 @@ router.post('/generate-voice', jsonParser, async (request, response) => {
const key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);
if (!key) {
console.log('No OpenAI key found');
console.warn('No OpenAI key found');
return response.sendStatus(400);
}
@@ -253,7 +253,7 @@ router.post('/generate-voice', jsonParser, async (request, response) => {
if (!result.ok) {
const text = await result.text();
console.log('OpenAI request failed', result.statusText, text);
console.warn('OpenAI request failed', result.statusText, text);
return response.status(500).send(text);
}
@@ -271,11 +271,11 @@ router.post('/generate-image', jsonParser, async (request, response) => {
const key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);
if (!key) {
console.log('No OpenAI key found');
console.warn('No OpenAI key found');
return response.sendStatus(400);
}
console.log('OpenAI request', request.body);
console.debug('OpenAI request', request.body);
const result = await fetch('https://api.openai.com/v1/images/generations', {
method: 'POST',
@@ -288,7 +288,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
if (!result.ok) {
const text = await result.text();
console.log('OpenAI request failed', result.statusText, text);
console.warn('OpenAI request failed', result.statusText, text);
return response.status(500).send(text);
}
@@ -308,7 +308,7 @@ custom.post('/generate-voice', jsonParser, async (request, response) => {
const { input, provider_endpoint, response_format, voice, speed, model } = request.body;
if (!provider_endpoint) {
console.log('No OpenAI-compatible TTS provider endpoint provided');
console.warn('No OpenAI-compatible TTS provider endpoint provided');
return response.sendStatus(400);
}
@@ -329,7 +329,7 @@ custom.post('/generate-voice', jsonParser, async (request, response) => {
if (!result.ok) {
const text = await result.text();
console.log('OpenAI request failed', result.statusText, text);
console.warn('OpenAI request failed', result.statusText, text);
return response.status(500).send(text);
}

View File

@@ -4,6 +4,31 @@ import { jsonParser } from '../express-common.js';
export const router = express.Router();
const API_OPENROUTER = 'https://openrouter.ai/api/v1';
router.post('/models/providers', jsonParser, async (req, res) => {
try {
const { model } = req.body;
const response = await fetch(`${API_OPENROUTER}/models/${model}/endpoints`, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
});
if (!response.ok) {
return res.json([]);
}
const data = await response.json();
const endpoints = data?.data?.endpoints || [];
const providerNames = endpoints.map(e => e.provider_name);
return res.json(providerNames);
} catch (error) {
console.error(error);
return res.sendStatus(500);
}
});
router.post('/models/multimodal', jsonParser, async (_req, res) => {
try {
// The endpoint is available without authentication

View File

@@ -96,7 +96,7 @@ router.post('/restore', jsonParser, function (request, response) {
return response.send(result);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});

View File

@@ -96,25 +96,26 @@ router.post('/serpapi', jsonParser, async (request, response) => {
const key = readSecret(request.user.directories, SECRET_KEYS.SERPAPI);
if (!key) {
console.log('No SerpApi key found');
console.error('No SerpApi key found');
return response.sendStatus(400);
}
const { query } = request.body;
const result = await fetch(`https://serpapi.com/search.json?q=${encodeURIComponent(query)}&api_key=${key}`);
console.log('SerpApi query', query);
console.debug('SerpApi query', query);
if (!result.ok) {
const text = await result.text();
console.log('SerpApi request failed', result.statusText, text);
console.error('SerpApi request failed', result.statusText, text);
return response.status(500).send(text);
}
const data = await result.json();
console.debug('SerpApi response', data);
return response.json(data);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -130,7 +131,7 @@ router.post('/transcript', jsonParser, async (request, response) => {
const json = request.body.json;
if (!id) {
console.log('Id is required for /transcript');
console.error('Id is required for /transcript');
return response.sendStatus(400);
}
@@ -155,7 +156,7 @@ router.post('/transcript', jsonParser, async (request, response) => {
throw error;
}
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -165,17 +166,17 @@ router.post('/searxng', jsonParser, async (request, response) => {
const { baseUrl, query, preferences, categories } = request.body;
if (!baseUrl || !query) {
console.log('Missing required parameters for /searxng');
console.error('Missing required parameters for /searxng');
return response.sendStatus(400);
}
console.log('SearXNG query', baseUrl, query);
console.debug('SearXNG query', baseUrl, query);
const mainPageUrl = new URL(baseUrl);
const mainPageRequest = await fetch(mainPageUrl, { headers: visitHeaders });
if (!mainPageRequest.ok) {
console.log('SearXNG request failed', mainPageRequest.statusText);
console.error('SearXNG request failed', mainPageRequest.statusText);
return response.sendStatus(500);
}
@@ -202,14 +203,14 @@ router.post('/searxng', jsonParser, async (request, response) => {
if (!searchResult.ok) {
const text = await searchResult.text();
console.log('SearXNG request failed', searchResult.statusText, text);
console.error('SearXNG request failed', searchResult.statusText, text);
return response.sendStatus(500);
}
const data = await searchResult.text();
return response.send(data);
} catch (error) {
console.log('SearXNG request failed', error);
console.error('SearXNG request failed', error);
return response.sendStatus(500);
}
});
@@ -219,7 +220,7 @@ router.post('/tavily', jsonParser, async (request, response) => {
const apiKey = readSecret(request.user.directories, SECRET_KEYS.TAVILY);
if (!apiKey) {
console.log('No Tavily key found');
console.error('No Tavily key found');
return response.sendStatus(400);
}
@@ -246,18 +247,19 @@ router.post('/tavily', jsonParser, async (request, response) => {
body: JSON.stringify(body),
});
console.log('Tavily query', query);
console.debug('Tavily query', query);
if (!result.ok) {
const text = await result.text();
console.log('Tavily request failed', result.statusText, text);
console.error('Tavily request failed', result.statusText, text);
return response.status(500).send(text);
}
const data = await result.json();
console.debug('Tavily response', data);
return response.json(data);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -290,6 +292,49 @@ router.post('/koboldcpp', jsonParser, async (request, response) => {
}
const data = await result.json();
console.debug('KoboldCpp search response', data);
return response.json(data);
} catch (error) {
console.error(error);
return response.sendStatus(500);
}
});
router.post('/serper', jsonParser, async (request, response) => {
try {
const key = readSecret(request.user.directories, SECRET_KEYS.SERPER);
if (!key) {
console.error('No Serper key found');
return response.sendStatus(400);
}
const { query, images } = request.body;
const url = images
? 'https://google.serper.dev/images'
: 'https://google.serper.dev/search';
const result = await fetch(url, {
method: 'POST',
headers: {
'X-API-KEY': key,
'Content-Type': 'application/json',
},
redirect: 'follow',
body: JSON.stringify({ q: query }),
});
console.debug('Serper query', query);
if (!result.ok) {
const text = await result.text();
console.warn('Serper request failed', result.statusText, text);
return response.status(500).send(text);
}
const data = await result.json();
console.debug('Serper response', data);
return response.json(data);
} catch (error) {
console.error(error);
@@ -303,7 +348,7 @@ router.post('/visit', jsonParser, async (request, response) => {
const html = Boolean(request.body.html ?? true);
if (!url) {
console.log('No url provided for /visit');
console.error('No url provided for /visit');
return response.sendStatus(400);
}
@@ -330,16 +375,16 @@ router.post('/visit', jsonParser, async (request, response) => {
throw new Error('Invalid hostname');
}
} catch (error) {
console.log('Invalid url provided for /visit', url);
console.error('Invalid url provided for /visit', url);
return response.sendStatus(400);
}
console.log('Visiting web URL', url);
console.info('Visiting web URL', url);
const result = await fetch(url, { headers: visitHeaders });
if (!result.ok) {
console.log(`Visit failed ${result.status} ${result.statusText}`);
console.error(`Visit failed ${result.status} ${result.statusText}`);
return response.sendStatus(500);
}
@@ -347,7 +392,7 @@ router.post('/visit', jsonParser, async (request, response) => {
if (html) {
if (!contentType.includes('text/html')) {
console.log(`Visit failed, content-type is ${contentType}, expected text/html`);
console.error(`Visit failed, content-type is ${contentType}, expected text/html`);
return response.sendStatus(500);
}
@@ -359,7 +404,7 @@ router.post('/visit', jsonParser, async (request, response) => {
const buffer = await result.arrayBuffer();
return response.send(Buffer.from(buffer));
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});

View File

@@ -50,8 +50,10 @@ export const SECRET_KEYS = {
TAVILY: 'api_key_tavily',
NANOGPT: 'api_key_nanogpt',
BFL: 'api_key_bfl',
FALAI: 'api_key_falai',
GENERIC: 'api_key_generic',
DEEPSEEK: 'api_key_deepseek',
SERPER: 'api_key_serper',
};
// These are the keys that are safe to expose, even if allowKeysExposure is false
@@ -151,7 +153,7 @@ export function getAllSecrets(directories) {
const filePath = path.join(directories.root, SECRETS_FILE);
if (!fs.existsSync(filePath)) {
console.log('Secrets file does not exist');
console.error('Secrets file does not exist');
return undefined;
}

View File

@@ -105,7 +105,7 @@ function readPresetsFromDirectory(directoryPath, options = {}) {
fileNames.push(removeFileExtension ? item.replace(/\.[^/.]+$/, '') : item);
} catch {
// skip
console.log(`${item} is not a valid JSON`);
console.warn(`${item} is not a valid JSON`);
}
});
@@ -120,7 +120,7 @@ async function backupSettings() {
backupUserSettings(handle, true);
}
} catch (err) {
console.log('Could not backup settings file', err);
console.error('Could not backup settings file', err);
}
}
@@ -202,7 +202,7 @@ router.post('/save', jsonParser, function (request, response) {
triggerAutoSave(request.user.profile.handle);
response.send({ result: 'ok' });
} catch (err) {
console.log(err);
console.error(err);
response.send(err);
}
});
@@ -292,7 +292,7 @@ router.post('/get-snapshots', jsonParser, async (request, response) => {
response.json(result);
} catch (error) {
console.log(error);
console.error(error);
response.sendStatus(500);
}
});
@@ -316,7 +316,7 @@ router.post('/load-snapshot', jsonParser, getFileNameValidationFunction('name'),
response.send(content);
} catch (error) {
console.log(error);
console.error(error);
response.sendStatus(500);
}
});
@@ -326,7 +326,7 @@ router.post('/make-snapshot', jsonParser, async (request, response) => {
backupUserSettings(request.user.profile.handle, false);
response.sendStatus(204);
} catch (error) {
console.log(error);
console.error(error);
response.sendStatus(500);
}
});
@@ -352,7 +352,7 @@ router.post('/restore-snapshot', jsonParser, getFileNameValidationFunction('name
response.sendStatus(204);
} catch (error) {
console.log(error);
console.error(error);
response.sendStatus(500);
}
});

View File

@@ -43,8 +43,8 @@ router.post('/recognize', jsonParser, async (req, res) => {
const start = performance.now();
const result = await pipe(wav, { language: lang || null, task: 'transcribe' });
const end = performance.now();
console.log(`Execution duration: ${(end - start) / 1000} seconds`);
console.log('Transcribed audio:', result.text);
console.info(`Execution duration: ${(end - start) / 1000} seconds`);
console.info('Transcribed audio:', result.text);
return res.json({ text: result.text });
} catch (error) {
@@ -64,7 +64,7 @@ router.post('/synthesize', jsonParser, async (req, res) => {
const start = performance.now();
const result = await pipe(text, { speaker_embeddings: speaker_embeddings });
const end = performance.now();
console.log(`Execution duration: ${(end - start) / 1000} seconds`);
console.debug(`Execution duration: ${(end - start) / 1000} seconds`);
const wav = new wavefile.WaveFile();
wav.fromScratch(1, result.sampling_rate, '32f', result.audio);

View File

@@ -82,14 +82,14 @@ export function importRisuSprites(directories, data) {
return;
}
console.log(`RisuAI: Found ${images.length} sprites for ${name}. Writing to disk.`);
console.info(`RisuAI: Found ${images.length} sprites for ${name}. Writing to disk.`);
const files = fs.readdirSync(spritesPath);
outer: for (const [label, fileBase64] of images) {
// Remove existing sprite with the same label
for (const file of files) {
if (path.parse(file).name === label) {
console.log(`RisuAI: The sprite ${label} for ${name} already exists. Skipping.`);
console.warn(`RisuAI: The sprite ${label} for ${name} already exists. Skipping.`);
continue outer;
}
}
@@ -139,7 +139,7 @@ router.get('/get', jsonParser, function (request, response) {
}
}
catch (err) {
console.log(err);
console.error(err);
}
return response.send(sprites);
});

View File

@@ -45,7 +45,7 @@ router.post('/ping', jsonParser, async (request, response) => {
return response.sendStatus(200);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -99,7 +99,7 @@ router.post('/upscalers', jsonParser, async (request, response) => {
return response.send(upscalers);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -131,7 +131,7 @@ router.post('/vaes', jsonParser, async (request, response) => {
const names = data.map(x => x.model_name);
return response.send(names);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -158,7 +158,7 @@ router.post('/samplers', jsonParser, async (request, response) => {
return response.send(names);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -184,7 +184,7 @@ router.post('/schedulers', jsonParser, async (request, response) => {
const names = data.map(x => x.name);
return response.send(names);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -210,7 +210,7 @@ router.post('/models', jsonParser, async (request, response) => {
const models = data.map(x => ({ value: x.title, text: x.title }));
return response.send(models);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -230,7 +230,7 @@ router.post('/get-model', jsonParser, async (request, response) => {
const data = await result.json();
return response.send(data['sd_model_checkpoint']);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -283,13 +283,13 @@ router.post('/set-model', jsonParser, async (request, response) => {
break;
}
console.log(`Waiting for SD WebUI to finish model loading... Progress: ${progress}; Job count: ${jobCount}`);
console.info(`Waiting for SD WebUI to finish model loading... Progress: ${progress}; Job count: ${jobCount}`);
await delay(CHECK_INTERVAL);
}
return response.sendStatus(200);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -309,7 +309,7 @@ router.post('/generate', jsonParser, async (request, response) => {
}
}
} catch (error) {
console.log('SD WebUI failed to get options:', error);
console.error('SD WebUI failed to get options:', error);
}
const controller = new AbortController();
@@ -323,7 +323,7 @@ router.post('/generate', jsonParser, async (request, response) => {
controller.abort();
});
console.log('SD WebUI request:', request.body);
console.debug('SD WebUI request:', request.body);
const txt2imgUrl = new URL(request.body.url);
txt2imgUrl.pathname = '/sdapi/v1/txt2img';
const result = await fetch(txt2imgUrl, {
@@ -344,7 +344,7 @@ router.post('/generate', jsonParser, async (request, response) => {
const data = await result.json();
return response.send(data);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -377,7 +377,7 @@ router.post('/sd-next/upscalers', jsonParser, async (request, response) => {
return response.send(names);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -395,7 +395,7 @@ comfy.post('/ping', jsonParser, async (request, response) => {
return response.sendStatus(200);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -413,7 +413,7 @@ comfy.post('/samplers', jsonParser, async (request, response) => {
const data = await result.json();
return response.send(data.KSampler.input.required.sampler_name[0]);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -441,7 +441,7 @@ comfy.post('/models', jsonParser, async (request, response) => {
return response.send(models);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -459,7 +459,7 @@ comfy.post('/schedulers', jsonParser, async (request, response) => {
const data = await result.json();
return response.send(data.KSampler.input.required.scheduler[0]);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -477,7 +477,7 @@ comfy.post('/vaes', jsonParser, async (request, response) => {
const data = await result.json();
return response.send(data.VAELoader.input.required.vae_name[0]);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -487,7 +487,7 @@ comfy.post('/workflows', jsonParser, async (request, response) => {
const data = getComfyWorkflows(request.user.directories);
return response.send(data);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -501,7 +501,7 @@ comfy.post('/workflow', jsonParser, async (request, response) => {
const data = fs.readFileSync(filePath, { encoding: 'utf-8' });
return response.send(JSON.stringify(data));
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -513,7 +513,7 @@ comfy.post('/save-workflow', jsonParser, async (request, response) => {
const data = getComfyWorkflows(request.user.directories);
return response.send(data);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -526,7 +526,7 @@ comfy.post('/delete-workflow', jsonParser, async (request, response) => {
}
return response.sendStatus(200);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -591,7 +591,7 @@ comfy.post('/generate', jsonParser, async (request, response) => {
const imgBuffer = await imgResponse.arrayBuffer();
return response.send(Buffer.from(imgBuffer).toString('base64'));
} catch (error) {
console.log('ComfyUI error:', error);
console.error('ComfyUI error:', error);
response.status(500).send(error.message);
return response;
}
@@ -604,7 +604,7 @@ together.post('/models', jsonParser, async (request, response) => {
const key = readSecret(request.user.directories, SECRET_KEYS.TOGETHERAI);
if (!key) {
console.log('TogetherAI key not found.');
console.warn('TogetherAI key not found.');
return response.sendStatus(400);
}
@@ -616,14 +616,14 @@ together.post('/models', jsonParser, async (request, response) => {
});
if (!modelsResponse.ok) {
console.log('TogetherAI returned an error.');
console.warn('TogetherAI returned an error.');
return response.sendStatus(500);
}
const data = await modelsResponse.json();
if (!Array.isArray(data)) {
console.log('TogetherAI returned invalid data.');
console.warn('TogetherAI returned invalid data.');
return response.sendStatus(500);
}
@@ -633,7 +633,7 @@ together.post('/models', jsonParser, async (request, response) => {
return response.send(models);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -643,11 +643,11 @@ together.post('/generate', jsonParser, async (request, response) => {
const key = readSecret(request.user.directories, SECRET_KEYS.TOGETHERAI);
if (!key) {
console.log('TogetherAI key not found.');
console.warn('TogetherAI key not found.');
return response.sendStatus(400);
}
console.log('TogetherAI request:', request.body);
console.debug('TogetherAI request:', request.body);
const result = await fetch('https://api.together.xyz/v1/images/generations', {
method: 'POST',
@@ -669,13 +669,13 @@ together.post('/generate', jsonParser, async (request, response) => {
});
if (!result.ok) {
console.log('TogetherAI returned an error.', { body: await result.text() });
console.warn('TogetherAI returned an error.', { body: await result.text() });
return response.sendStatus(500);
}
/** @type {any} */
const data = await result.json();
console.log('TogetherAI response:', data);
console.debug('TogetherAI response:', data);
const choice = data?.data?.[0];
let b64_json = choice.b64_json;
@@ -687,7 +687,7 @@ together.post('/generate', jsonParser, async (request, response) => {
return response.send({ format: 'jpg', data: b64_json });
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -709,7 +709,7 @@ drawthings.post('/ping', jsonParser, async (request, response) => {
return response.sendStatus(200);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -728,7 +728,7 @@ drawthings.post('/get-model', jsonParser, async (request, response) => {
return response.send(data['model']);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -747,14 +747,14 @@ drawthings.post('/get-upscaler', jsonParser, async (request, response) => {
return response.send(data['upscaler']);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
drawthings.post('/generate', jsonParser, async (request, response) => {
try {
console.log('SD DrawThings API request:', request.body);
console.debug('SD DrawThings API request:', request.body);
const url = new URL(request.body.url);
url.pathname = '/sdapi/v1/txt2img';
@@ -781,7 +781,7 @@ drawthings.post('/generate', jsonParser, async (request, response) => {
const data = await result.json();
return response.send(data);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -794,21 +794,21 @@ pollinations.post('/models', jsonParser, async (_request, response) => {
const result = await fetch(modelsUrl);
if (!result.ok) {
console.log('Pollinations returned an error.', result.status, result.statusText);
console.warn('Pollinations returned an error.', result.status, result.statusText);
throw new Error('Pollinations request failed.');
}
const data = await result.json();
if (!Array.isArray(data)) {
console.log('Pollinations returned invalid data.');
console.warn('Pollinations returned invalid data.');
throw new Error('Pollinations request failed.');
}
const models = data.map(x => ({ value: x, text: x }));
return response.send(models);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -829,12 +829,12 @@ pollinations.post('/generate', jsonParser, async (request, response) => {
});
promptUrl.search = params.toString();
console.log('Pollinations request URL:', promptUrl.toString());
console.info('Pollinations request URL:', promptUrl.toString());
const result = await fetch(promptUrl);
if (!result.ok) {
console.log('Pollinations returned an error.', result.status, result.statusText);
console.warn('Pollinations returned an error.', result.status, result.statusText);
throw new Error('Pollinations request failed.');
}
@@ -843,7 +843,7 @@ pollinations.post('/generate', jsonParser, async (request, response) => {
return response.send({ image: base64 });
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -855,13 +855,13 @@ stability.post('/generate', jsonParser, async (request, response) => {
const key = readSecret(request.user.directories, SECRET_KEYS.STABILITY);
if (!key) {
console.log('Stability AI key not found.');
console.warn('Stability AI key not found.');
return response.sendStatus(400);
}
const { payload, model } = request.body;
console.log('Stability AI request:', model, payload);
console.debug('Stability AI request:', model, payload);
const formData = new FormData();
for (const [key, value] of Object.entries(payload)) {
@@ -896,14 +896,14 @@ stability.post('/generate', jsonParser, async (request, response) => {
if (!result.ok) {
const text = await result.text();
console.log('Stability AI returned an error.', result.status, result.statusText, text);
console.warn('Stability AI returned an error.', result.status, result.statusText, text);
return response.sendStatus(500);
}
const buffer = await result.arrayBuffer();
return response.send(Buffer.from(buffer).toString('base64'));
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -915,7 +915,7 @@ blockentropy.post('/models', jsonParser, async (request, response) => {
const key = readSecret(request.user.directories, SECRET_KEYS.BLOCKENTROPY);
if (!key) {
console.log('Block Entropy key not found.');
console.warn('Block Entropy key not found.');
return response.sendStatus(400);
}
@@ -927,21 +927,21 @@ blockentropy.post('/models', jsonParser, async (request, response) => {
});
if (!modelsResponse.ok) {
console.log('Block Entropy returned an error.');
console.warn('Block Entropy returned an error.');
return response.sendStatus(500);
}
const data = await modelsResponse.json();
if (!Array.isArray(data)) {
console.log('Block Entropy returned invalid data.');
console.warn('Block Entropy returned invalid data.');
return response.sendStatus(500);
}
const models = data.map(x => ({ value: x.name, text: x.name }));
return response.send(models);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -951,11 +951,11 @@ blockentropy.post('/generate', jsonParser, async (request, response) => {
const key = readSecret(request.user.directories, SECRET_KEYS.BLOCKENTROPY);
if (!key) {
console.log('Block Entropy key not found.');
console.warn('Block Entropy key not found.');
return response.sendStatus(400);
}
console.log('Block Entropy request:', request.body);
console.debug('Block Entropy request:', request.body);
const result = await fetch('https://api.blockentropy.ai/sdapi/v1/txt2img', {
method: 'POST',
@@ -976,16 +976,16 @@ blockentropy.post('/generate', jsonParser, async (request, response) => {
});
if (!result.ok) {
console.log('Block Entropy returned an error.');
console.warn('Block Entropy returned an error.');
return response.sendStatus(500);
}
const data = await result.json();
console.log('Block Entropy response:', data);
console.debug('Block Entropy response:', data);
return response.send(data);
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -998,11 +998,11 @@ huggingface.post('/generate', jsonParser, async (request, response) => {
const key = readSecret(request.user.directories, SECRET_KEYS.HUGGINGFACE);
if (!key) {
console.log('Hugging Face key not found.');
console.warn('Hugging Face key not found.');
return response.sendStatus(400);
}
console.log('Hugging Face request:', request.body);
console.debug('Hugging Face request:', request.body);
const result = await fetch(`https://api-inference.huggingface.co/models/${request.body.model}`, {
method: 'POST',
@@ -1016,7 +1016,7 @@ huggingface.post('/generate', jsonParser, async (request, response) => {
});
if (!result.ok) {
console.log('Hugging Face returned an error.');
console.warn('Hugging Face returned an error.');
return response.sendStatus(500);
}
@@ -1025,7 +1025,7 @@ huggingface.post('/generate', jsonParser, async (request, response) => {
image: Buffer.from(buffer).toString('base64'),
});
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -1037,7 +1037,7 @@ nanogpt.post('/models', jsonParser, async (request, response) => {
const key = readSecret(request.user.directories, SECRET_KEYS.NANOGPT);
if (!key) {
console.log('NanoGPT key not found.');
console.warn('NanoGPT key not found.');
return response.sendStatus(400);
}
@@ -1050,7 +1050,7 @@ nanogpt.post('/models', jsonParser, async (request, response) => {
});
if (!modelsResponse.ok) {
console.log('NanoGPT returned an error.');
console.warn('NanoGPT returned an error.');
return response.sendStatus(500);
}
@@ -1059,7 +1059,7 @@ nanogpt.post('/models', jsonParser, async (request, response) => {
const imageModels = data?.models?.image;
if (!imageModels || typeof imageModels !== 'object') {
console.log('NanoGPT returned invalid data.');
console.warn('NanoGPT returned invalid data.');
return response.sendStatus(500);
}
@@ -1067,7 +1067,7 @@ nanogpt.post('/models', jsonParser, async (request, response) => {
return response.send(models);
}
catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -1077,11 +1077,11 @@ nanogpt.post('/generate', jsonParser, async (request, response) => {
const key = readSecret(request.user.directories, SECRET_KEYS.NANOGPT);
if (!key) {
console.log('NanoGPT key not found.');
console.warn('NanoGPT key not found.');
return response.sendStatus(400);
}
console.log('NanoGPT request:', request.body);
console.debug('NanoGPT request:', request.body);
const result = await fetch('https://nano-gpt.com/api/generate-image', {
method: 'POST',
@@ -1093,7 +1093,7 @@ nanogpt.post('/generate', jsonParser, async (request, response) => {
});
if (!result.ok) {
console.log('NanoGPT returned an error.');
console.warn('NanoGPT returned an error.');
return response.sendStatus(500);
}
@@ -1102,14 +1102,14 @@ nanogpt.post('/generate', jsonParser, async (request, response) => {
const image = data?.data?.[0]?.b64_json;
if (!image) {
console.log('NanoGPT returned invalid data.');
console.warn('NanoGPT returned invalid data.');
return response.sendStatus(500);
}
return response.send({ image });
}
catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
@@ -1121,7 +1121,7 @@ bfl.post('/generate', jsonParser, async (request, response) => {
const key = readSecret(request.user.directories, SECRET_KEYS.BFL);
if (!key) {
console.log('BFL key not found.');
console.warn('BFL key not found.');
return response.sendStatus(400);
}
@@ -1173,7 +1173,7 @@ bfl.post('/generate', jsonParser, async (request, response) => {
delete requestBody.guidance;
}
console.log('BFL request:', requestBody);
console.debug('BFL request:', requestBody);
const result = await fetch(`https://api.bfl.ml/v1/${request.body.model}`, {
method: 'POST',
@@ -1185,7 +1185,7 @@ bfl.post('/generate', jsonParser, async (request, response) => {
});
if (!result.ok) {
console.log('BFL returned an error.');
console.warn('BFL returned an error.');
return response.sendStatus(500);
}
@@ -1201,7 +1201,7 @@ bfl.post('/generate', jsonParser, async (request, response) => {
if (!statusResult.ok) {
const text = await statusResult.text();
console.log('BFL returned an error.', text);
console.warn('BFL returned an error.', text);
return response.sendStatus(500);
}
@@ -1223,11 +1223,137 @@ bfl.post('/generate', jsonParser, async (request, response) => {
throw new Error('BFL failed to generate image.', { cause: statusData });
}
} catch (error) {
console.log(error);
console.error(error);
return response.sendStatus(500);
}
});
const falai = express.Router();
falai.post('/models', jsonParser, async (_request, response) => {
try {
const modelsUrl = new URL('https://fal.ai/api/models?categories=text-to-image');
const result = await fetch(modelsUrl);
if (!result.ok) {
console.warn('FAL.AI returned an error.', result.status, result.statusText);
throw new Error('FAL.AI request failed.');
}
const data = await result.json();
if (!Array.isArray(data)) {
console.warn('FAL.AI returned invalid data.');
throw new Error('FAL.AI request failed.');
}
const models = data
.filter(x => !x.title.toLowerCase().includes('inpainting') &&
!x.title.toLowerCase().includes('control') &&
!x.title.toLowerCase().includes('upscale'))
.sort((a, b) => a.title.localeCompare(b.title))
.map(x => ({ value: x.modelUrl.split('fal-ai/')[1], text: x.title }));
return response.send(models);
} catch (error) {
console.error(error);
return response.sendStatus(500);
}
});
falai.post('/generate', jsonParser, async (request, response) => {
try {
const key = readSecret(request.user.directories, SECRET_KEYS.FALAI);
if (!key) {
console.warn('FAL.AI key not found.');
return response.sendStatus(400);
}
const requestBody = {
prompt: request.body.prompt,
image_size: { 'width': request.body.width, 'height': request.body.height },
num_inference_steps: request.body.steps,
seed: request.body.seed ?? null,
guidance_scale: request.body.guidance,
enable_safety_checker: false, // Disable general safety checks
safety_tolerance: 6 // Make Flux the least strict
};
console.debug('FAL.AI request:', requestBody);
const result = await fetch(`https://queue.fal.run/fal-ai/${request.body.model}`, {
method: 'POST',
body: JSON.stringify(requestBody),
headers: {
'Content-Type': 'application/json',
'Authorization': `Key ${key}`,
},
});
if (!result.ok) {
console.warn('FAL.AI returned an error.');
return response.sendStatus(500);
}
/** @type {any} */
const taskData = await result.json();
const { status_url } = taskData;
const MAX_ATTEMPTS = 100;
for (let i = 0; i < MAX_ATTEMPTS; i++) {
await delay(2500);
const statusResult = await fetch(status_url, {
headers: {
'Authorization': `Key ${key}`,
},
});
if (!statusResult.ok) {
const text = await statusResult.text();
console.warn('FAL.AI returned an error.', text);
return response.sendStatus(500);
}
/** @type {any} */
const statusData = await statusResult.json();
if (statusData?.status === 'IN_QUEUE' || statusData?.status === 'IN_PROGRESS') {
continue;
}
if (statusData?.status === 'COMPLETED') {
const resultFetch = await fetch(statusData?.response_url, {
method: 'GET',
headers: {
'Authorization': `Key ${key}`,
},
});
const resultData = await resultFetch.json();
if (resultData.detail !== null && resultData.detail !== undefined) {
throw new Error('FAL.AI failed to generate image.', { cause: `${resultData.detail[0].loc[1]}: ${resultData.detail[0].msg}` });
}
const imageFetch = await fetch(resultData?.images[0].url, {
headers: {
'Authorization': `Key ${key}`,
},
});
const fetchData = await imageFetch.arrayBuffer();
const image = Buffer.from(fetchData).toString('base64');
return response.send({ image: image });
}
throw new Error('FAL.AI failed to generate image.', { cause: statusData });
}
} catch (error) {
console.error(error);
return response.status(500).send(error.cause || error.message);
}
});
router.use('/comfy', comfy);
router.use('/together', together);
router.use('/drawthings', drawthings);
@@ -1237,3 +1363,4 @@ router.use('/blockentropy', blockentropy);
router.use('/huggingface', huggingface);
router.use('/nanogpt', nanogpt);
router.use('/bfl', bfl);
router.use('/falai', falai);

View File

@@ -148,7 +148,7 @@ async function collectAndCreateStats(chatsPath, charactersPath) {
* @param {string} charactersPath Path to the directory containing the character files.
*/
export async function recreateStats(handle, chatsPath, charactersPath) {
console.log('Collecting and creating stats for user:', handle);
console.info('Collecting and creating stats for user:', handle);
const stats = await collectAndCreateStats(chatsPath, charactersPath);
STATS.set(handle, stats);
await saveStatsToFile();
@@ -200,7 +200,7 @@ async function saveStatsToFile() {
await writeFileAtomic(statsFilePath, JSON.stringify(charStats));
TIMESTAMPS.set(handle, Date.now());
} catch (error) {
console.log('Failed to save stats to file.', error);
console.error('Failed to save stats to file.', error);
}
}
}

View File

@@ -89,7 +89,6 @@ async function generateThumbnail(directories, type, file) {
let thumbnailFolder = getThumbnailFolder(directories, type);
let originalFolder = getOriginalFolder(directories, type);
if (thumbnailFolder === undefined || originalFolder === undefined) throw new Error('Invalid thumbnail type');
const pathToCachedFile = path.join(thumbnailFolder, file);
const pathToOriginalFile = path.join(originalFolder, file);
@@ -104,7 +103,7 @@ async function generateThumbnail(directories, type, file) {
const cachedStat = fs.statSync(pathToCachedFile);
if (originalStat.mtimeMs > cachedStat.ctimeMs) {
//console.log('Original file changed. Regenerating thumbnail...');
//console.warn('Original file changed. Regenerating thumbnail...');
shouldRegenerate = true;
}
}
@@ -157,7 +156,7 @@ export async function ensureThumbnailCache() {
return;
}
console.log('Generating thumbnails cache. Please wait...');
console.info('Generating thumbnails cache. Please wait...');
const bgFiles = fs.readdirSync(directories.backgrounds);
const tasks = [];
@@ -167,7 +166,7 @@ export async function ensureThumbnailCache() {
}
await Promise.all(tasks);
console.log(`Done! Generated: ${bgFiles.length} preview images`);
console.info(`Done! Generated: ${bgFiles.length} preview images`);
}
}

View File

@@ -96,7 +96,7 @@ async function getPathToTokenizer(model, fallbackModel) {
throw new Error('Downloading tokenizers is disabled, the model is not cached');
}
console.log('Downloading tokenizer model:', model);
console.info('Downloading tokenizer model:', model);
const response = await fetch(model);
if (!response.ok) {
throw new Error(`Failed to fetch the model: ${response.status} ${response.statusText}`);
@@ -108,7 +108,7 @@ async function getPathToTokenizer(model, fallbackModel) {
} catch (error) {
const getLastSegment = str => str?.split('/')?.pop() || '';
if (fallbackModel) {
console.log(`Could not get a tokenizer from ${getLastSegment(model)}. Reason: ${error.message}. Using a fallback model: ${getLastSegment(fallbackModel)}.`);
console.error(`Could not get a tokenizer from ${getLastSegment(model)}. Reason: ${error.message}. Using a fallback model: ${getLastSegment(fallbackModel)}.`);
return fallbackModel;
}
@@ -156,7 +156,7 @@ class SentencePieceTokenizer {
const pathToModel = await getPathToTokenizer(this.#model, this.#fallbackModel);
this.#instance = new SentencePieceProcessor();
await this.#instance.load(pathToModel);
console.log('Instantiated the tokenizer for', path.parse(pathToModel).name);
console.info('Instantiated the tokenizer for', path.parse(pathToModel).name);
return this.#instance;
} catch (error) {
console.error('Sentencepiece tokenizer failed to load: ' + this.#model, error);
@@ -205,7 +205,7 @@ class WebTokenizer {
const pathToModel = await getPathToTokenizer(this.#model, this.#fallbackModel);
const arrayBuffer = fs.readFileSync(pathToModel).buffer;
this.#instance = await Tokenizer.fromJSON(arrayBuffer);
console.log('Instantiated the tokenizer for', path.parse(pathToModel).name);
console.info('Instantiated the tokenizer for', path.parse(pathToModel).name);
return this.#instance;
} catch (error) {
console.error('Web tokenizer failed to load: ' + this.#model, error);
@@ -484,7 +484,7 @@ export function getTiktokenTokenizer(model) {
}
const tokenizer = tiktoken.encoding_for_model(model);
console.log('Instantiated the tokenizer for', model);
console.info('Instantiated the tokenizer for', model);
tokenizersCache[model] = tokenizer;
return tokenizer;
}
@@ -531,7 +531,7 @@ function createSentencepieceEncodingHandler(tokenizer) {
const chunks = instance?.encodePieces(text);
return response.send({ ids, count, chunks });
} catch (error) {
console.log(error);
console.error(error);
return response.send({ ids: [], count: 0, chunks: [] });
}
};
@@ -562,7 +562,7 @@ function createSentencepieceDecodingHandler(tokenizer) {
const text = chunks.join('');
return response.send({ text, chunks });
} catch (error) {
console.log(error);
console.error(error);
return response.send({ text: '', chunks: [] });
}
};
@@ -591,7 +591,7 @@ function createTiktokenEncodingHandler(modelId) {
const chunks = await getTiktokenChunks(tokenizer, tokens);
return response.send({ ids: tokens, count: tokens.length, chunks });
} catch (error) {
console.log(error);
console.error(error);
return response.send({ ids: [], count: 0, chunks: [] });
}
};
@@ -620,7 +620,7 @@ function createTiktokenDecodingHandler(modelId) {
const text = new TextDecoder().decode(textBytes);
return response.send({ text });
} catch (error) {
console.log(error);
console.error(error);
return response.send({ text: '' });
}
};
@@ -650,7 +650,7 @@ function createWebTokenizerEncodingHandler(tokenizer) {
const chunks = getWebTokenizersChunks(instance, tokens);
return response.send({ ids: tokens, count: tokens.length, chunks });
} catch (error) {
console.log(error);
console.error(error);
return response.send({ ids: [], count: 0, chunks: [] });
}
};
@@ -681,7 +681,7 @@ function createWebTokenizerDecodingHandler(tokenizer) {
const text = instance.decode(new Int32Array(ids));
return response.send({ text, chunks });
} catch (error) {
console.log(error);
console.error(error);
return response.send({ text: '', chunks: [] });
}
};
@@ -781,7 +781,7 @@ router.post('/openai/encode', jsonParser, async function (req, res) {
const handler = createTiktokenEncodingHandler(model);
return handler(req, res);
} catch (error) {
console.log(error);
console.error(error);
return res.send({ ids: [], count: 0, chunks: [] });
}
});
@@ -849,7 +849,7 @@ router.post('/openai/decode', jsonParser, async function (req, res) {
const handler = createTiktokenDecodingHandler(model);
return handler(req, res);
} catch (error) {
console.log(error);
console.error(error);
return res.send({ text: '' });
}
});
@@ -988,7 +988,7 @@ router.post('/remote/kobold/count', jsonParser, async function (request, respons
const result = await fetch(url, args);
if (!result.ok) {
console.log(`API returned error: ${result.status} ${result.statusText}`);
console.warn(`API returned error: ${result.status} ${result.statusText}`);
return response.send({ error: true });
}
@@ -997,7 +997,7 @@ router.post('/remote/kobold/count', jsonParser, async function (request, respons
const ids = data['ids'] ?? [];
return response.send({ count, ids });
} catch (error) {
console.log(error);
console.error(error);
return response.send({ error: true });
}
});
@@ -1052,7 +1052,7 @@ router.post('/remote/textgenerationwebui/encode', jsonParser, async function (re
const result = await fetch(url, args);
if (!result.ok) {
console.log(`API returned error: ${result.status} ${result.statusText}`);
console.warn(`API returned error: ${result.status} ${result.statusText}`);
return response.send({ error: true });
}
@@ -1062,7 +1062,7 @@ router.post('/remote/textgenerationwebui/encode', jsonParser, async function (re
return response.send({ count, ids });
} catch (error) {
console.log(error);
console.error(error);
return response.send({ error: true });
}
});

View File

@@ -35,7 +35,7 @@ function decodeBuffer(buffer) {
try {
return iconv.decode(Buffer.from(buffer), 'utf-8');
} catch (error) {
console.log('Failed to decode buffer:', error);
console.error('Failed to decode buffer:', error);
return Buffer.from(buffer).toString('utf-8');
}
}
@@ -46,7 +46,7 @@ router.post('/libre', jsonParser, async (request, response) => {
const url = readSecret(request.user.directories, SECRET_KEYS.LIBRE_URL);
if (!url) {
console.log('LibreTranslate URL is not configured.');
console.warn('LibreTranslate URL is not configured.');
return response.sendStatus(400);
}
@@ -69,7 +69,7 @@ router.post('/libre', jsonParser, async (request, response) => {
return response.sendStatus(400);
}
console.log('Input text: ' + text);
console.debug('Input text: ' + text);
const result = await fetch(url, {
method: 'POST',
@@ -85,17 +85,17 @@ router.post('/libre', jsonParser, async (request, response) => {
if (!result.ok) {
const error = await result.text();
console.log('LibreTranslate error: ', result.statusText, error);
console.warn('LibreTranslate error: ', result.statusText, error);
return response.sendStatus(500);
}
/** @type {any} */
const json = await result.json();
console.log('Translated text: ' + json.translatedText);
console.debug('Translated text: ' + json.translatedText);
return response.send(json.translatedText);
} catch (error) {
console.log('Translation error: ' + error.message);
console.error('Translation error: ' + error.message);
return response.sendStatus(500);
}
});
@@ -109,14 +109,14 @@ router.post('/google', jsonParser, async (request, response) => {
return response.sendStatus(400);
}
console.log('Input text: ' + text);
console.debug('Input text: ' + text);
const { generateRequestUrl, normaliseResponse } = getGoogleTranslateClient();
const requestUrl = generateRequestUrl(text, { to: lang });
const result = await fetch(requestUrl);
if (!result.ok) {
console.log('Google Translate error: ', result.statusText);
console.warn('Google Translate error: ', result.statusText);
return response.sendStatus(500);
}
@@ -125,10 +125,10 @@ router.post('/google', jsonParser, async (request, response) => {
const translatedText = translateResponse.text;
response.setHeader('Content-Type', 'text/plain; charset=utf-8');
console.log('Translated text: ' + translatedText);
console.debug('Translated text: ' + translatedText);
return response.send(translatedText);
} catch (error) {
console.log('Translation error', error);
console.error('Translation error', error);
return response.sendStatus(500);
}
});
@@ -161,7 +161,7 @@ router.post('/yandex', jsonParser, async (request, response) => {
params.append('lang', lang);
const ucid = uuidv4().replaceAll('-', '');
console.log('Input text: ' + inputText);
console.debug('Input text: ' + inputText);
const result = await fetch(`https://translate.yandex.net/api/v1/tr.json/translate?ucid=${ucid}&srv=android&format=text`, {
method: 'POST',
@@ -173,18 +173,18 @@ router.post('/yandex', jsonParser, async (request, response) => {
if (!result.ok) {
const error = await result.text();
console.log('Yandex error: ', result.statusText, error);
console.warn('Yandex error: ', result.statusText, error);
return response.sendStatus(500);
}
/** @type {any} */
const json = await result.json();
const translated = json.text.join();
console.log('Translated text: ' + translated);
console.debug('Translated text: ' + translated);
return response.send(translated);
} catch (error) {
console.log('Translation error: ' + error.message);
console.error('Translation error: ' + error.message);
return response.sendStatus(500);
}
});
@@ -195,7 +195,7 @@ router.post('/lingva', jsonParser, async (request, response) => {
const baseUrl = secretUrl || LINGVA_DEFAULT;
if (!secretUrl && baseUrl === LINGVA_DEFAULT) {
console.log('Lingva URL is using default value.', LINGVA_DEFAULT);
console.warn('Lingva URL is using default value.', LINGVA_DEFAULT);
}
if (request.body.lang === 'zh-CN' || request.body.lang === 'zh-TW') {
@@ -213,22 +213,22 @@ router.post('/lingva', jsonParser, async (request, response) => {
return response.sendStatus(400);
}
console.log('Input text: ' + text);
console.debug('Input text: ' + text);
const url = urlJoin(baseUrl, 'auto', lang, encodeURIComponent(text));
const result = await fetch(url);
if (!result.ok) {
const error = await result.text();
console.log('Lingva error: ', result.statusText, error);
console.warn('Lingva error: ', result.statusText, error);
}
/** @type {any} */
const data = await result.json();
console.log('Translated text: ' + data.translation);
console.debug('Translated text: ' + data.translation);
return response.send(data.translation);
} catch (error) {
console.log('Translation error', error);
console.error('Translation error', error);
return response.sendStatus(500);
}
});
@@ -238,7 +238,7 @@ router.post('/deepl', jsonParser, async (request, response) => {
const key = readSecret(request.user.directories, SECRET_KEYS.DEEPL);
if (!key) {
console.log('DeepL key is not configured.');
console.warn('DeepL key is not configured.');
return response.sendStatus(400);
}
@@ -254,7 +254,7 @@ router.post('/deepl', jsonParser, async (request, response) => {
return response.sendStatus(400);
}
console.log('Input text: ' + text);
console.debug('Input text: ' + text);
const params = new URLSearchParams();
params.append('text', text);
@@ -280,17 +280,17 @@ router.post('/deepl', jsonParser, async (request, response) => {
if (!result.ok) {
const error = await result.text();
console.log('DeepL error: ', result.statusText, error);
console.warn('DeepL error: ', result.statusText, error);
return response.sendStatus(500);
}
/** @type {any} */
const json = await result.json();
console.log('Translated text: ' + json.translations[0].text);
console.debug('Translated text: ' + json.translations[0].text);
return response.send(json.translations[0].text);
} catch (error) {
console.log('Translation error: ' + error.message);
console.error('Translation error: ' + error.message);
return response.sendStatus(500);
}
});
@@ -301,12 +301,12 @@ router.post('/onering', jsonParser, async (request, response) => {
const url = secretUrl || ONERING_URL_DEFAULT;
if (!url) {
console.log('OneRing URL is not configured.');
console.warn('OneRing URL is not configured.');
return response.sendStatus(400);
}
if (!secretUrl && url === ONERING_URL_DEFAULT) {
console.log('OneRing URL is using default value.', ONERING_URL_DEFAULT);
console.info('OneRing URL is using default value.', ONERING_URL_DEFAULT);
}
if (request.body.lang === 'pt-BR' || request.body.lang === 'pt-PT') {
@@ -326,7 +326,7 @@ router.post('/onering', jsonParser, async (request, response) => {
params.append('from_lang', from_lang);
params.append('to_lang', to_lang);
console.log('Input text: ' + text);
console.debug('Input text: ' + text);
const fetchUrl = new URL(url);
fetchUrl.search = params.toString();
@@ -337,17 +337,17 @@ router.post('/onering', jsonParser, async (request, response) => {
if (!result.ok) {
const error = await result.text();
console.log('OneRing error: ', result.statusText, error);
console.warn('OneRing error: ', result.statusText, error);
return response.sendStatus(500);
}
/** @type {any} */
const data = await result.json();
console.log('Translated text: ' + data.result);
console.debug('Translated text: ' + data.result);
return response.send(data.result);
} catch (error) {
console.log('Translation error: ' + error.message);
console.error('Translation error: ' + error.message);
return response.sendStatus(500);
}
});
@@ -358,12 +358,12 @@ router.post('/deeplx', jsonParser, async (request, response) => {
const url = secretUrl || DEEPLX_URL_DEFAULT;
if (!url) {
console.log('DeepLX URL is not configured.');
console.warn('DeepLX URL is not configured.');
return response.sendStatus(400);
}
if (!secretUrl && url === DEEPLX_URL_DEFAULT) {
console.log('DeepLX URL is using default value.', DEEPLX_URL_DEFAULT);
console.info('DeepLX URL is using default value.', DEEPLX_URL_DEFAULT);
}
const text = request.body.text;
@@ -376,7 +376,7 @@ router.post('/deeplx', jsonParser, async (request, response) => {
return response.sendStatus(400);
}
console.log('Input text: ' + text);
console.debug('Input text: ' + text);
const result = await fetch(url, {
method: 'POST',
@@ -393,17 +393,17 @@ router.post('/deeplx', jsonParser, async (request, response) => {
if (!result.ok) {
const error = await result.text();
console.log('DeepLX error: ', result.statusText, error);
console.warn('DeepLX error: ', result.statusText, error);
return response.sendStatus(500);
}
/** @type {any} */
const json = await result.json();
console.log('Translated text: ' + json.data);
console.debug('Translated text: ' + json.data);
return response.send(json.data);
} catch (error) {
console.log('DeepLX translation error: ' + error.message);
console.error('DeepLX translation error: ' + error.message);
return response.sendStatus(500);
}
});
@@ -429,14 +429,14 @@ router.post('/bing', jsonParser, async (request, response) => {
return response.sendStatus(400);
}
console.log('Input text: ' + text);
console.debug('Input text: ' + text);
const result = await bingTranslate(text, null, lang);
const translatedText = result?.translation;
console.log('Translated text: ' + translatedText);
console.debug('Translated text: ' + translatedText);
return response.send(translatedText);
} catch (error) {
console.log('Translation error', error);
console.error('Translation error', error);
return response.sendStatus(500);
}
});

View File

@@ -53,12 +53,12 @@ router.post('/get', requireAdminMiddleware, jsonParser, async (_request, respons
router.post('/disable', requireAdminMiddleware, jsonParser, async (request, response) => {
try {
if (!request.body.handle) {
console.log('Disable user failed: Missing required fields');
console.warn('Disable user failed: Missing required fields');
return response.status(400).json({ error: 'Missing required fields' });
}
if (request.body.handle === request.user.profile.handle) {
console.log('Disable user failed: Cannot disable yourself');
console.warn('Disable user failed: Cannot disable yourself');
return response.status(400).json({ error: 'Cannot disable yourself' });
}
@@ -66,7 +66,7 @@ router.post('/disable', requireAdminMiddleware, jsonParser, async (request, resp
const user = await storage.getItem(toKey(request.body.handle));
if (!user) {
console.log('Disable user failed: User not found');
console.error('Disable user failed: User not found');
return response.status(404).json({ error: 'User not found' });
}
@@ -82,7 +82,7 @@ router.post('/disable', requireAdminMiddleware, jsonParser, async (request, resp
router.post('/enable', requireAdminMiddleware, jsonParser, async (request, response) => {
try {
if (!request.body.handle) {
console.log('Enable user failed: Missing required fields');
console.warn('Enable user failed: Missing required fields');
return response.status(400).json({ error: 'Missing required fields' });
}
@@ -90,7 +90,7 @@ router.post('/enable', requireAdminMiddleware, jsonParser, async (request, respo
const user = await storage.getItem(toKey(request.body.handle));
if (!user) {
console.log('Enable user failed: User not found');
console.error('Enable user failed: User not found');
return response.status(404).json({ error: 'User not found' });
}
@@ -106,7 +106,7 @@ router.post('/enable', requireAdminMiddleware, jsonParser, async (request, respo
router.post('/promote', requireAdminMiddleware, jsonParser, async (request, response) => {
try {
if (!request.body.handle) {
console.log('Promote user failed: Missing required fields');
console.warn('Promote user failed: Missing required fields');
return response.status(400).json({ error: 'Missing required fields' });
}
@@ -114,7 +114,7 @@ router.post('/promote', requireAdminMiddleware, jsonParser, async (request, resp
const user = await storage.getItem(toKey(request.body.handle));
if (!user) {
console.log('Promote user failed: User not found');
console.error('Promote user failed: User not found');
return response.status(404).json({ error: 'User not found' });
}
@@ -130,12 +130,12 @@ router.post('/promote', requireAdminMiddleware, jsonParser, async (request, resp
router.post('/demote', requireAdminMiddleware, jsonParser, async (request, response) => {
try {
if (!request.body.handle) {
console.log('Demote user failed: Missing required fields');
console.warn('Demote user failed: Missing required fields');
return response.status(400).json({ error: 'Missing required fields' });
}
if (request.body.handle === request.user.profile.handle) {
console.log('Demote user failed: Cannot demote yourself');
console.warn('Demote user failed: Cannot demote yourself');
return response.status(400).json({ error: 'Cannot demote yourself' });
}
@@ -143,7 +143,7 @@ router.post('/demote', requireAdminMiddleware, jsonParser, async (request, respo
const user = await storage.getItem(toKey(request.body.handle));
if (!user) {
console.log('Demote user failed: User not found');
console.error('Demote user failed: User not found');
return response.status(404).json({ error: 'User not found' });
}
@@ -159,7 +159,7 @@ router.post('/demote', requireAdminMiddleware, jsonParser, async (request, respo
router.post('/create', requireAdminMiddleware, jsonParser, async (request, response) => {
try {
if (!request.body.handle || !request.body.name) {
console.log('Create user failed: Missing required fields');
console.warn('Create user failed: Missing required fields');
return response.status(400).json({ error: 'Missing required fields' });
}
@@ -167,12 +167,12 @@ router.post('/create', requireAdminMiddleware, jsonParser, async (request, respo
const handle = lodash.kebabCase(String(request.body.handle).toLowerCase().trim());
if (!handle) {
console.log('Create user failed: Invalid handle');
console.warn('Create user failed: Invalid handle');
return response.status(400).json({ error: 'Invalid handle' });
}
if (handles.some(x => x === handle)) {
console.log('Create user failed: User with that handle already exists');
console.warn('Create user failed: User with that handle already exists');
return response.status(409).json({ error: 'User already exists' });
}
@@ -192,7 +192,7 @@ router.post('/create', requireAdminMiddleware, jsonParser, async (request, respo
await storage.setItem(toKey(handle), newUser);
// Create user directories
console.log('Creating data directories for', newUser.handle);
console.info('Creating data directories for', newUser.handle);
await ensurePublicDirectoriesExist();
const directories = getUserDirectories(newUser.handle);
await checkForNewContent([directories]);
@@ -206,17 +206,17 @@ router.post('/create', requireAdminMiddleware, jsonParser, async (request, respo
router.post('/delete', requireAdminMiddleware, jsonParser, async (request, response) => {
try {
if (!request.body.handle) {
console.log('Delete user failed: Missing required fields');
console.warn('Delete user failed: Missing required fields');
return response.status(400).json({ error: 'Missing required fields' });
}
if (request.body.handle === request.user.profile.handle) {
console.log('Delete user failed: Cannot delete yourself');
console.warn('Delete user failed: Cannot delete yourself');
return response.status(400).json({ error: 'Cannot delete yourself' });
}
if (request.body.handle === DEFAULT_USER.handle) {
console.log('Delete user failed: Cannot delete default user');
console.warn('Delete user failed: Cannot delete default user');
return response.status(400).json({ error: 'Sorry, but the default user cannot be deleted. It is required as a fallback.' });
}
@@ -224,7 +224,7 @@ router.post('/delete', requireAdminMiddleware, jsonParser, async (request, respo
if (request.body.purge) {
const directories = getUserDirectories(request.body.handle);
console.log('Deleting data directories for', request.body.handle);
console.info('Deleting data directories for', request.body.handle);
await fsPromises.rm(directories.root, { recursive: true, force: true });
}
@@ -238,7 +238,7 @@ router.post('/delete', requireAdminMiddleware, jsonParser, async (request, respo
router.post('/slugify', requireAdminMiddleware, jsonParser, async (request, response) => {
try {
if (!request.body.text) {
console.log('Slugify failed: Missing required fields');
console.warn('Slugify failed: Missing required fields');
return response.status(400).json({ error: 'Missing required fields' });
}

View File

@@ -58,18 +58,18 @@ router.get('/me', async (request, response) => {
router.post('/change-avatar', jsonParser, async (request, response) => {
try {
if (!request.body.handle) {
console.log('Change avatar failed: Missing required fields');
console.warn('Change avatar failed: Missing required fields');
return response.status(400).json({ error: 'Missing required fields' });
}
if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {
console.log('Change avatar failed: Unauthorized');
console.error('Change avatar failed: Unauthorized');
return response.status(403).json({ error: 'Unauthorized' });
}
// Avatar is not a data URL or not an empty string
if (!request.body.avatar.startsWith('data:image/') && request.body.avatar !== '') {
console.log('Change avatar failed: Invalid data URL');
console.warn('Change avatar failed: Invalid data URL');
return response.status(400).json({ error: 'Invalid data URL' });
}
@@ -77,7 +77,7 @@ router.post('/change-avatar', jsonParser, async (request, response) => {
const user = await storage.getItem(toKey(request.body.handle));
if (!user) {
console.log('Change avatar failed: User not found');
console.error('Change avatar failed: User not found');
return response.status(404).json({ error: 'User not found' });
}
@@ -93,12 +93,12 @@ router.post('/change-avatar', jsonParser, async (request, response) => {
router.post('/change-password', jsonParser, async (request, response) => {
try {
if (!request.body.handle) {
console.log('Change password failed: Missing required fields');
console.warn('Change password failed: Missing required fields');
return response.status(400).json({ error: 'Missing required fields' });
}
if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {
console.log('Change password failed: Unauthorized');
console.error('Change password failed: Unauthorized');
return response.status(403).json({ error: 'Unauthorized' });
}
@@ -106,17 +106,17 @@ router.post('/change-password', jsonParser, async (request, response) => {
const user = await storage.getItem(toKey(request.body.handle));
if (!user) {
console.log('Change password failed: User not found');
console.error('Change password failed: User not found');
return response.status(404).json({ error: 'User not found' });
}
if (!user.enabled) {
console.log('Change password failed: User is disabled');
console.error('Change password failed: User is disabled');
return response.status(403).json({ error: 'User is disabled' });
}
if (!request.user.profile.admin && user.password && user.password !== getPasswordHash(request.body.oldPassword, user.salt)) {
console.log('Change password failed: Incorrect password');
console.error('Change password failed: Incorrect password');
return response.status(403).json({ error: 'Incorrect password' });
}
@@ -142,12 +142,12 @@ router.post('/backup', jsonParser, async (request, response) => {
const handle = request.body.handle;
if (!handle) {
console.log('Backup failed: Missing required fields');
console.warn('Backup failed: Missing required fields');
return response.status(400).json({ error: 'Missing required fields' });
}
if (handle !== request.user.profile.handle && !request.user.profile.admin) {
console.log('Backup failed: Unauthorized');
console.error('Backup failed: Unauthorized');
return response.status(403).json({ error: 'Unauthorized' });
}
@@ -163,7 +163,7 @@ router.post('/reset-settings', jsonParser, async (request, response) => {
const password = request.body.password;
if (request.user.profile.password && request.user.profile.password !== getPasswordHash(password, request.user.profile.salt)) {
console.log('Reset settings failed: Incorrect password');
console.warn('Reset settings failed: Incorrect password');
return response.status(403).json({ error: 'Incorrect password' });
}
@@ -181,12 +181,12 @@ router.post('/reset-settings', jsonParser, async (request, response) => {
router.post('/change-name', jsonParser, async (request, response) => {
try {
if (!request.body.name || !request.body.handle) {
console.log('Change name failed: Missing required fields');
console.warn('Change name failed: Missing required fields');
return response.status(400).json({ error: 'Missing required fields' });
}
if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {
console.log('Change name failed: Unauthorized');
console.error('Change name failed: Unauthorized');
return response.status(403).json({ error: 'Unauthorized' });
}
@@ -194,7 +194,7 @@ router.post('/change-name', jsonParser, async (request, response) => {
const user = await storage.getItem(toKey(request.body.handle));
if (!user) {
console.log('Change name failed: User not found');
console.warn('Change name failed: User not found');
return response.status(404).json({ error: 'User not found' });
}
@@ -225,23 +225,23 @@ router.post('/reset-step1', jsonParser, async (request, response) => {
router.post('/reset-step2', jsonParser, async (request, response) => {
try {
if (!request.body.code) {
console.log('Recover step 2 failed: Missing required fields');
console.warn('Recover step 2 failed: Missing required fields');
return response.status(400).json({ error: 'Missing required fields' });
}
if (request.user.profile.password && request.user.profile.password !== getPasswordHash(request.body.password, request.user.profile.salt)) {
console.log('Recover step 2 failed: Incorrect password');
console.warn('Recover step 2 failed: Incorrect password');
return response.status(400).json({ error: 'Incorrect password' });
}
const code = RESET_CACHE.get(request.user.profile.handle);
if (!code || code !== request.body.code) {
console.log('Recover step 2 failed: Incorrect code');
console.warn('Recover step 2 failed: Incorrect code');
return response.status(400).json({ error: 'Incorrect code' });
}
console.log('Resetting account data:', request.user.profile.handle);
console.info('Resetting account data:', request.user.profile.handle);
await fsPromises.rm(request.user.directories.root, { recursive: true, force: true });
await ensurePublicDirectoriesExist();

View File

@@ -56,7 +56,7 @@ router.post('/list', async (_request, response) => {
router.post('/login', jsonParser, async (request, response) => {
try {
if (!request.body.handle) {
console.log('Login failed: Missing required fields');
console.warn('Login failed: Missing required fields');
return response.status(400).json({ error: 'Missing required fields' });
}
@@ -67,17 +67,17 @@ router.post('/login', jsonParser, async (request, response) => {
const user = await storage.getItem(toKey(request.body.handle));
if (!user) {
console.log('Login failed: User', request.body.handle, 'not found');
console.error('Login failed: User', request.body.handle, 'not found');
return response.status(403).json({ error: 'Incorrect credentials' });
}
if (!user.enabled) {
console.log('Login failed: User', user.handle, 'is disabled');
console.warn('Login failed: User', user.handle, 'is disabled');
return response.status(403).json({ error: 'User is disabled' });
}
if (user.password && user.password !== getPasswordHash(request.body.password, user.salt)) {
console.log('Login failed: Incorrect password for', user.handle);
console.warn('Login failed: Incorrect password for', user.handle);
return response.status(403).json({ error: 'Incorrect credentials' });
}
@@ -88,11 +88,11 @@ router.post('/login', jsonParser, async (request, response) => {
await loginLimiter.delete(ip);
request.session.handle = user.handle;
console.log('Login successful:', user.handle, 'from', ip, 'at', new Date().toLocaleString());
console.info('Login successful:', user.handle, 'from', ip, 'at', new Date().toLocaleString());
return response.json({ handle: user.handle });
} catch (error) {
if (error instanceof RateLimiterRes) {
console.log('Login failed: Rate limited from', getIpFromRequest(request));
console.error('Login failed: Rate limited from', getIpFromRequest(request));
return response.status(429).send({ error: 'Too many attempts. Try again later or recover your password.' });
}
@@ -104,7 +104,7 @@ router.post('/login', jsonParser, async (request, response) => {
router.post('/recover-step1', jsonParser, async (request, response) => {
try {
if (!request.body.handle) {
console.log('Recover step 1 failed: Missing required fields');
console.warn('Recover step 1 failed: Missing required fields');
return response.status(400).json({ error: 'Missing required fields' });
}
@@ -115,12 +115,12 @@ router.post('/recover-step1', jsonParser, async (request, response) => {
const user = await storage.getItem(toKey(request.body.handle));
if (!user) {
console.log('Recover step 1 failed: User', request.body.handle, 'not found');
console.error('Recover step 1 failed: User', request.body.handle, 'not found');
return response.status(404).json({ error: 'User not found' });
}
if (!user.enabled) {
console.log('Recover step 1 failed: User', user.handle, 'is disabled');
console.error('Recover step 1 failed: User', user.handle, 'is disabled');
return response.status(403).json({ error: 'User is disabled' });
}
@@ -132,7 +132,7 @@ router.post('/recover-step1', jsonParser, async (request, response) => {
return response.sendStatus(204);
} catch (error) {
if (error instanceof RateLimiterRes) {
console.log('Recover step 1 failed: Rate limited from', getIpFromRequest(request));
console.error('Recover step 1 failed: Rate limited from', getIpFromRequest(request));
return response.status(429).send({ error: 'Too many attempts. Try again later or contact your admin.' });
}
@@ -144,7 +144,7 @@ router.post('/recover-step1', jsonParser, async (request, response) => {
router.post('/recover-step2', jsonParser, async (request, response) => {
try {
if (!request.body.handle || !request.body.code) {
console.log('Recover step 2 failed: Missing required fields');
console.warn('Recover step 2 failed: Missing required fields');
return response.status(400).json({ error: 'Missing required fields' });
}
@@ -153,12 +153,12 @@ router.post('/recover-step2', jsonParser, async (request, response) => {
const ip = getIpFromRequest(request);
if (!user) {
console.log('Recover step 2 failed: User', request.body.handle, 'not found');
console.error('Recover step 2 failed: User', request.body.handle, 'not found');
return response.status(404).json({ error: 'User not found' });
}
if (!user.enabled) {
console.log('Recover step 2 failed: User', user.handle, 'is disabled');
console.warn('Recover step 2 failed: User', user.handle, 'is disabled');
return response.status(403).json({ error: 'User is disabled' });
}
@@ -166,7 +166,7 @@ router.post('/recover-step2', jsonParser, async (request, response) => {
if (request.body.code !== mfaCode) {
await recoverLimiter.consume(ip);
console.log('Recover step 2 failed: Incorrect code');
console.warn('Recover step 2 failed: Incorrect code');
return response.status(403).json({ error: 'Incorrect code' });
}
@@ -186,7 +186,7 @@ router.post('/recover-step2', jsonParser, async (request, response) => {
return response.sendStatus(204);
} catch (error) {
if (error instanceof RateLimiterRes) {
console.log('Recover step 2 failed: Rate limited from', getIpFromRequest(request));
console.error('Recover step 2 failed: Rate limited from', getIpFromRequest(request));
return response.status(429).send({ error: 'Too many attempts. Try again later or contact your admin.' });
}

View File

@@ -132,35 +132,35 @@ function getSourceSettings(source, request) {
switch (source) {
case 'togetherai':
return {
model: String(request.headers['x-togetherai-model']),
model: String(request.body.model),
};
case 'openai':
return {
model: String(request.headers['x-openai-model']),
model: String(request.body.model),
};
case 'cohere':
return {
model: String(request.headers['x-cohere-model']),
model: String(request.body.model),
};
case 'llamacpp':
return {
apiUrl: String(request.headers['x-llamacpp-url']),
apiUrl: String(request.body.apiUrl),
};
case 'vllm':
return {
apiUrl: String(request.headers['x-vllm-url']),
model: String(request.headers['x-vllm-model']),
apiUrl: String(request.body.apiUrl),
model: String(request.body.model),
};
case 'ollama':
return {
apiUrl: String(request.headers['x-ollama-url']),
model: String(request.headers['x-ollama-model']),
keep: Boolean(request.headers['x-ollama-keep']),
apiUrl: String(request.body.apiUrl),
model: String(request.body.model),
keep: Boolean(request.body.keep),
};
case 'extras':
return {
extrasUrl: String(request.headers['x-extras-url']),
extrasKey: String(request.headers['x-extras-key']),
extrasUrl: String(request.body.extrasUrl),
extrasKey: String(request.body.extrasKey),
};
case 'transformers':
return {
@@ -360,7 +360,7 @@ async function regenerateCorruptedIndexErrorHandler(req, res, error) {
if (exists) {
const path = index.folderPath;
console.error(`Corrupted index detected at ${path}, regenerating...`);
console.warn(`Corrupted index detected at ${path}, regenerating...`);
await index.deleteIndex();
return res.redirect(307, req.originalUrl + '?regenerated=true');
}
@@ -474,7 +474,7 @@ router.post('/purge-all', jsonParser, async (req, res) => {
continue;
}
await fs.promises.rm(sourcePath, { recursive: true });
console.log(`Deleted vector source store at ${sourcePath}`);
console.info(`Deleted vector source store at ${sourcePath}`);
}
return res.sendStatus(200);
@@ -498,7 +498,7 @@ router.post('/purge', jsonParser, async (req, res) => {
continue;
}
await fs.promises.rm(sourcePath, { recursive: true });
console.log(`Deleted vector index at ${sourcePath}`);
console.info(`Deleted vector index at ${sourcePath}`);
}
return res.sendStatus(200);

View File

@@ -25,7 +25,7 @@ export function readWorldInfoFile(directories, worldInfoName, allowDummy) {
const pathToWorldInfo = path.join(directories.worlds, filename);
if (!fs.existsSync(pathToWorldInfo)) {
console.log(`World info file ${filename} doesn't exist.`);
console.error(`World info file ${filename} doesn't exist.`);
return dummyObject;
}

View File

@@ -0,0 +1,28 @@
import crypto from 'node:crypto';
import { DEFAULT_USER } from '../constants.js';
/**
* Middleware to bust the browser cache for the current user.
* @returns {import('express').RequestHandler}
*/
export default function getCacheBusterMiddleware() {
/**
* @type {Set<string>} Handles/User-Agents that have already been busted.
*/
const keys = new Set();
return (request, response, next) => {
const handle = request.user?.profile?.handle || DEFAULT_USER.handle;
const userAgent = request.headers['user-agent'] || '';
const hash = crypto.createHash('sha256').update(userAgent).digest('hex');
const key = `${handle}-${hash}`;
if (keys.has(key)) {
return next();
}
keys.add(key);
response.setHeader('Clear-Site-Data', '"cache"');
next();
};
}

View File

@@ -12,6 +12,8 @@ const enableForwardedWhitelist = getConfigValue('enableForwardedWhitelist', fals
let whitelist = getConfigValue('whitelist', []);
let knownIPs = new Set();
export const getAccessLogPath = () => path.join(globalThis.DATA_ROOT, 'access.log');
if (fs.existsSync(whitelistPath)) {
try {
let whitelistTxt = fs.readFileSync(whitelistPath, 'utf-8');
@@ -46,6 +48,23 @@ function getForwardedIp(req) {
return undefined;
}
export function migrateAccessLog() {
try {
if (!fs.existsSync('access.log')) {
return;
}
const logPath = getAccessLogPath();
if (fs.existsSync(logPath)) {
return;
}
fs.renameSync('access.log', logPath);
console.log(color.yellow('Migrated access.log to new location:'), logPath);
} catch (e) {
console.error('Failed to migrate access log:', e);
console.info('Please move access.log to the data directory manually.');
}
}
/**
* Returns a middleware function that checks if the client IP is in the whitelist.
* @param {boolean} whitelistMode If whitelist mode is enabled via config or command line
@@ -63,13 +82,14 @@ export default function whitelistMiddleware(whitelistMode, listen) {
const userAgent = req.headers['user-agent'];
if (listen && !knownIPs.has(clientIp)) {
console.log(color.yellow(`New connection from ${clientIp}; User Agent: ${userAgent}\n`));
console.info(color.yellow(`New connection from ${clientIp}; User Agent: ${userAgent}\n`));
knownIPs.add(clientIp);
// Write access log
const logPath = getAccessLogPath();
const timestamp = new Date().toISOString();
const log = `${timestamp} ${clientIp} ${userAgent}\n`;
fs.appendFile('access.log', log, (err) => {
fs.appendFile(logPath, log, (err) => {
if (err) {
console.error('Failed to write access log:', err);
}
@@ -84,7 +104,7 @@ export default function whitelistMiddleware(whitelistMode, listen) {
const ipDetails = forwardedIp
? `${clientIp} (forwarded from ${forwardedIp})`
: clientIp;
console.log(
console.warn(
color.red(
`Blocked connection from ${clientIp}; User Agent: ${userAgent}\n\tTo allow this connection, add its IP address to the whitelist or disable whitelist mode by editing config.yaml in the root directory of your SillyTavern installation.\n`,
),

View File

@@ -3,8 +3,12 @@ import path from 'node:path';
import url from 'node:url';
import express from 'express';
import { getConfigValue } from './util.js';
const enableServerPlugins = getConfigValue('enableServerPlugins', false);
import { default as git } from 'simple-git';
import { sync as commandExistsSync } from 'command-exists';
import { getConfigValue, color } from './util.js';
const enableServerPlugins = !!getConfigValue('enableServerPlugins', false);
const enableServerPluginsAutoUpdate = !!getConfigValue('enableServerPluginsAutoUpdate', true);
/**
* Map of loaded plugins.
@@ -54,6 +58,8 @@ export async function loadPlugins(app, pluginsPath) {
return emptyFn;
}
await updatePlugins(pluginsPath);
for (const file of files) {
const pluginFilePath = path.join(pluginsPath, file);
@@ -70,6 +76,10 @@ export async function loadPlugins(app, pluginsPath) {
await loadFromFile(app, pluginFilePath, exitHooks);
}
if (loadedPlugins.size > 0) {
console.log(`${loadedPlugins.size} server plugin(s) are currently loaded. Make sure you know exactly what they do, and only install plugins from trusted sources!`);
}
// Call all plugin "exit" functions at once and wait for them to finish
return () => Promise.all(exitHooks.map(exitFn => exitFn()));
}
@@ -214,3 +224,65 @@ async function initPlugin(app, plugin, exitHooks) {
return true;
}
/**
* Automatically update all git plugins in the ./plugins directory
* @param {string} pluginsPath Path to plugins directory
*/
async function updatePlugins(pluginsPath) {
if (!enableServerPluginsAutoUpdate) {
return;
}
const directories = fs.readdirSync(pluginsPath)
.filter(file => !file.startsWith('.'))
.filter(file => fs.statSync(path.join(pluginsPath, file)).isDirectory());
if (directories.length === 0) {
return;
}
console.log(color.blue('Auto-updating server plugins... Set'), color.yellow('enableServerPluginsAutoUpdate: false'), color.blue('in config.yaml to disable this feature.'));
if (!commandExistsSync('git')) {
console.error(color.red('Git is not installed. Please install Git to enable auto-updating of server plugins.'));
return;
}
let pluginsToUpdate = 0;
for (const directory of directories) {
try {
const pluginPath = path.join(pluginsPath, directory);
const pluginRepo = git(pluginPath);
const isRepo = await pluginRepo.checkIsRepo();
if (!isRepo) {
continue;
}
await pluginRepo.fetch();
const commitHash = await pluginRepo.revparse(['HEAD']);
const trackingBranch = await pluginRepo.revparse(['--abbrev-ref', '@{u}']);
const log = await pluginRepo.log({
from: commitHash,
to: trackingBranch,
});
if (log.total === 0) {
continue;
}
pluginsToUpdate++;
await pluginRepo.pull();
const latestCommit = await pluginRepo.revparse(['HEAD']);
console.log(`Plugin ${color.green(directory)} updated to commit ${color.cyan(latestCommit)}`);
} catch (error) {
console.error(color.red(`Failed to update plugin ${directory}: ${error.message}`));
}
}
if (pluginsToUpdate === 0) {
console.log('All plugins are up to date.');
}
}

View File

@@ -360,6 +360,12 @@ export function convertCohereMessages(messages, names) {
*/
export function convertGooglePrompt(messages, model, useSysPrompt, names) {
const visionSupportedModels = [
'gemini-2.0-pro-exp',
'gemini-2.0-pro-exp-02-05',
'gemini-2.0-flash-lite-preview',
'gemini-2.0-flash-lite-preview-02-05',
'gemini-2.0-flash',
'gemini-2.0-flash-001',
'gemini-2.0-flash-thinking-exp',
'gemini-2.0-flash-thinking-exp-01-21',
'gemini-2.0-flash-thinking-exp-1219',

View File

@@ -43,9 +43,9 @@ export default function initRequestProxy({ enabled, url, bypass }) {
http.globalAgent = proxyAgent;
https.globalAgent = proxyAgent;
console.log();
console.log(color.green(LOG_HEADER), 'Proxy URL is used:', color.blue(url));
console.log();
console.info();
console.info(color.green(LOG_HEADER), 'Proxy URL is used:', color.blue(url));
console.info();
} catch (error) {
console.error(color.red(LOG_HEADER), 'Failed to initialize request proxy:', error);
}

View File

@@ -840,7 +840,7 @@ export function requireAdminMiddleware(request, response, next) {
export async function createBackupArchive(handle, response) {
const directories = getUserDirectories(handle);
console.log('Backup requested for', handle);
console.info('Backup requested for', handle);
const archive = archiver('zip');
archive.on('error', function (err) {
@@ -849,7 +849,7 @@ export async function createBackupArchive(handle, response) {
// On stream closed we can end the request
archive.on('end', function () {
console.log('Archive wrote %d bytes', archive.pointer());
console.info('Archive wrote %d bytes', archive.pointer());
response.end(); // End the Express response
});

View File

@@ -5,6 +5,7 @@ import process from 'node:process';
import { Readable } from 'node:stream';
import { createRequire } from 'node:module';
import { Buffer } from 'node:buffer';
import { promises as dnsPromise } from 'node:dns';
import yaml from 'yaml';
import { sync as commandExistsSync } from 'command-exists';
@@ -13,6 +14,7 @@ import _ from 'lodash';
import yauzl from 'yauzl';
import mime from 'mime-types';
import { default as simpleGit } from 'simple-git';
import { LOG_LEVELS } from './constants.js';
/**
* Parsed config object.
@@ -149,6 +151,7 @@ export function getHexString(length) {
*/
export function formatBytes(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
@@ -163,14 +166,12 @@ export function formatBytes(bytes) {
*/
export async function extractFileFromZipBuffer(archiveBuffer, fileExtension) {
return await new Promise((resolve, reject) => yauzl.fromBuffer(Buffer.from(archiveBuffer), { lazyEntries: true }, (err, zipfile) => {
if (err) {
reject(err);
}
if (err) reject(err);
zipfile.readEntry();
zipfile.on('entry', (entry) => {
if (entry.fileName.endsWith(fileExtension) && !entry.fileName.startsWith('__MACOSX')) {
console.log(`Extracting ${entry.fileName}`);
console.info(`Extracting ${entry.fileName}`);
zipfile.openReadStream(entry, (err, readStream) => {
if (err) {
reject(err);
@@ -218,7 +219,7 @@ export async function getImageBuffers(zipFilePath) {
zipfile.on('entry', (entry) => {
const mimeType = mime.lookup(entry.fileName);
if (mimeType && mimeType.startsWith('image/') && !entry.fileName.startsWith('__MACOSX')) {
console.log(`Extracting ${entry.fileName}`);
console.info(`Extracting ${entry.fileName}`);
zipfile.openReadStream(entry, (err, readStream) => {
if (err) {
reject(err);
@@ -285,10 +286,11 @@ export function deepMerge(target, source) {
if (isObject(target) && isObject(source)) {
Object.keys(source).forEach(key => {
if (isObject(source[key])) {
if (!(key in target))
if (!(key in target)) {
Object.assign(output, { [key]: source[key] });
else
} else {
output[key] = deepMerge(target[key], source[key]);
}
} else {
Object.assign(output, { [key]: source[key] });
}
@@ -447,7 +449,7 @@ export function forwardFetchResponse(from, to) {
let statusText = from.statusText;
if (!from.ok) {
console.log(`Streaming request failed with status ${statusCode} ${statusText}`);
console.warn(`Streaming request failed with status ${statusCode} ${statusText}`);
}
// Avoid sending 401 responses as they reset the client Basic auth.
@@ -467,11 +469,12 @@ export function forwardFetchResponse(from, to) {
to.socket.on('close', function () {
if (from.body instanceof Readable) from.body.destroy(); // Close the remote stream
to.end(); // End the Express response
});
from.body.on('end', function () {
console.log('Streaming request finished');
console.info('Streaming request finished');
to.end();
});
} else {
@@ -516,7 +519,7 @@ export function makeHttp2Request(endpoint, method, body, headers) {
});
req.on('end', () => {
console.log(data);
console.debug(data);
resolve(data);
});
});
@@ -692,6 +695,82 @@ export function isValidUrl(url) {
}
}
/**
* removes starting `[` or ending `]` from hostname.
* @param {string} hostname hostname to use
* @returns {string} hostname plus the modifications
*/
export function urlHostnameToIPv6(hostname) {
if (hostname.startsWith('[')) {
hostname = hostname.slice(1);
}
if (hostname.endsWith(']')) {
hostname = hostname.slice(0, -1);
}
return hostname;
}
/**
* Test if can resolve a dns name.
* @param {string} name Domain name to use
* @param {boolean} useIPv6 If use IPv6
* @param {boolean} useIPv4 If use IPv4
* @returns Promise<boolean> If the URL is valid
*/
export async function canResolve(name, useIPv6 = true, useIPv4 = true) {
try {
let v6Resolved = false;
let v4Resolved = false;
if (useIPv6) {
try {
await dnsPromise.resolve6(name);
v6Resolved = true;
} catch (error) {
v6Resolved = false;
}
}
if (useIPv4) {
try {
await dnsPromise.resolve(name);
v4Resolved = true;
} catch (error) {
v4Resolved = false;
}
}
return v6Resolved || v4Resolved;
} catch (error) {
return false;
}
}
/**
* converts string to boolean accepts 'true' or 'false' else it returns the string put in
* @param {string|null} str Input string or null
* @returns {boolean|string|null} boolean else original input string or null if input is
*/
export function stringToBool(str) {
if (str === 'true') return true;
if (str === 'false') return false;
return str;
}
/**
* Setup the minimum log level
*/
export function setupLogLevel() {
const logLevel = getConfigValue('minLogLevel', LOG_LEVELS.DEBUG);
globalThis.console.debug = logLevel <= LOG_LEVELS.DEBUG ? console.debug : () => {};
globalThis.console.info = logLevel <= LOG_LEVELS.INFO ? console.info : () => {};
globalThis.console.warn = logLevel <= LOG_LEVELS.WARN ? console.warn : () => {};
globalThis.console.error = logLevel <= LOG_LEVELS.ERROR ? console.error : () => {};
}
/**
* MemoryLimitedMap class that limits the memory usage of string values.
*/

View File

@@ -13,7 +13,7 @@ export async function getCohereBatchVector(texts, isQuery, directories, model) {
const key = readSecret(directories, SECRET_KEYS.COHERE);
if (!key) {
console.log('No API key found');
console.warn('No API key found');
throw new Error('No API key found');
}
@@ -34,14 +34,14 @@ export async function getCohereBatchVector(texts, isQuery, directories, model) {
if (!response.ok) {
const text = await response.text();
console.log('API request failed', response.statusText, text);
console.warn('API request failed', response.statusText, text);
throw new Error('API request failed');
}
/** @type {any} */
const data = await response.json();
if (!Array.isArray(data?.embeddings?.float)) {
console.log('API response was not an array');
console.warn('API response was not an array');
throw new Error('API response was not an array');
}

View File

@@ -36,8 +36,8 @@ async function getExtrasVectorImpl(text, apiUrl, apiKey) {
url.pathname = '/api/embeddings/compute';
}
catch (error) {
console.log('Failed to set up Extras API call:', error);
console.log('Extras API URL given was:', apiUrl);
console.error('Failed to set up Extras API call:', error);
console.debug('Extras API URL given was:', apiUrl);
throw error;
}
@@ -62,7 +62,7 @@ async function getExtrasVectorImpl(text, apiUrl, apiKey) {
if (!response.ok) {
const text = await response.text();
console.log('Extras request failed', response.statusText, text);
console.warn('Extras request failed', response.statusText, text);
throw new Error('Extras request failed');
}

View File

@@ -23,7 +23,7 @@ export async function getMakerSuiteVector(text, directories) {
const key = readSecret(directories, SECRET_KEYS.MAKERSUITE);
if (!key) {
console.log('No Google AI Studio key found');
console.warn('No Google AI Studio key found');
throw new Error('No Google AI Studio key found');
}
@@ -48,7 +48,7 @@ export async function getMakerSuiteVector(text, directories) {
if (!response.ok) {
const text = await response.text();
console.log('Google AI Studio request failed', response.statusText, text);
console.warn('Google AI Studio request failed', response.statusText, text);
throw new Error('Google AI Studio request failed');
}

View File

@@ -20,14 +20,14 @@ export async function getNomicAIBatchVector(texts, source, directories) {
const config = SOURCES[source];
if (!config) {
console.log('Unknown source', source);
console.error('Unknown source', source);
throw new Error('Unknown source');
}
const key = readSecret(directories, config.secretKey);
if (!key) {
console.log('No API key found');
console.warn('No API key found');
throw new Error('No API key found');
}
@@ -47,14 +47,14 @@ export async function getNomicAIBatchVector(texts, source, directories) {
if (!response.ok) {
const text = await response.text();
console.log('API request failed', response.statusText, text);
console.warn('API request failed', response.statusText, text);
throw new Error('API request failed');
}
/** @type {any} */
const data = await response.json();
if (!Array.isArray(data?.embeddings)) {
console.log('API response was not an array');
console.warn('API response was not an array');
throw new Error('API response was not an array');
}

View File

@@ -31,14 +31,14 @@ export async function getOpenAIBatchVector(texts, source, directories, model = '
const config = SOURCES[source];
if (!config) {
console.log('Unknown source', source);
console.error('Unknown source', source);
throw new Error('Unknown source');
}
const key = readSecret(directories, config.secretKey);
if (!key) {
console.log('No API key found');
console.warn('No API key found');
throw new Error('No API key found');
}
@@ -57,7 +57,7 @@ export async function getOpenAIBatchVector(texts, source, directories, model = '
if (!response.ok) {
const text = await response.text();
console.log('API request failed', response.statusText, text);
console.warn('API request failed', response.statusText, text);
throw new Error('API request failed');
}
@@ -65,7 +65,7 @@ export async function getOpenAIBatchVector(texts, source, directories, model = '
const data = await response.json();
if (!Array.isArray(data?.data)) {
console.log('API response was not an array');
console.warn('API response was not an array');
throw new Error('API response was not an array');
}