mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge branch 'staging' into support-multiple-expressions
This commit is contained in:
@@ -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,
|
||||
};
|
||||
|
@@ -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');
|
||||
|
@@ -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);
|
||||
}
|
||||
});
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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 });
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
});
|
||||
|
@@ -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 });
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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) {
|
||||
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
}
|
||||
});
|
||||
|
@@ -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);
|
||||
});
|
||||
|
@@ -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);
|
||||
}
|
||||
});
|
||||
|
@@ -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) {
|
||||
|
@@ -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)) {
|
||||
|
@@ -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);
|
||||
|
@@ -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' });
|
||||
}
|
||||
});
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
});
|
||||
|
@@ -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);
|
||||
}
|
||||
});
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
});
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
});
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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 });
|
||||
}
|
||||
});
|
||||
|
@@ -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);
|
||||
}
|
||||
});
|
||||
|
@@ -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' });
|
||||
}
|
||||
|
||||
|
@@ -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();
|
||||
|
@@ -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.' });
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
28
src/middleware/cacheBuster.js
Normal file
28
src/middleware/cacheBuster.js
Normal 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();
|
||||
};
|
||||
}
|
@@ -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`,
|
||||
),
|
||||
|
@@ -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.');
|
||||
}
|
||||
}
|
||||
|
@@ -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',
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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
|
||||
});
|
||||
|
||||
|
99
src/util.js
99
src/util.js
@@ -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.
|
||||
*/
|
||||
|
@@ -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');
|
||||
}
|
||||
|
||||
|
@@ -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');
|
||||
}
|
||||
|
||||
|
@@ -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');
|
||||
}
|
||||
|
||||
|
@@ -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');
|
||||
}
|
||||
|
||||
|
@@ -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');
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user