Merge branch 'staging' into pygimport

This commit is contained in:
Cohee
2024-02-21 11:26:47 +02:00
5 changed files with 59 additions and 18 deletions

View File

@@ -2270,12 +2270,21 @@ export async function generateQuietPrompt(quiet_prompt, quietToLoud, skipWIAN, q
return generateFinished; return generateFinished;
} }
/**
* Executes slash commands and returns the new text and whether the generation was interrupted.
* @param {string} message Text to be sent
* @returns {Promise<boolean>} Whether the message sending was interrupted
*/
async function processCommands(message) { async function processCommands(message) {
if (!message || !message.trim().startsWith('/')) {
return false;
}
const previousText = String($('#send_textarea').val()); const previousText = String($('#send_textarea').val());
const result = await executeSlashCommands(message); const result = await executeSlashCommands(message);
if (!result || typeof result !== 'object') { if (!result || typeof result !== 'object') {
return null; return false;
} }
const currentText = String($('#send_textarea').val()); const currentText = String($('#send_textarea').val());
@@ -2878,7 +2887,7 @@ async function Generate(type, { automatic_trigger, force_name2, quiet_prompt, qu
let message_already_generated = isImpersonate ? `${name1}: ` : `${name2}: `; let message_already_generated = isImpersonate ? `${name1}: ` : `${name2}: `;
if (!(dryRun || type == 'regenerate' || type == 'swipe' || type == 'quiet')) { if (!(dryRun || type == 'regenerate' || type == 'swipe' || type == 'quiet')) {
const interruptedByCommand = await processCommands($('#send_textarea').val()); const interruptedByCommand = await processCommands(String($('#send_textarea').val()));
if (interruptedByCommand) { if (interruptedByCommand) {
//$("#send_textarea").val('').trigger('input'); //$("#send_textarea").val('').trigger('input');
@@ -7764,7 +7773,13 @@ async function connectAPISlash(_, text) {
} }
} }
export async function processDroppedFiles(files) { /**
* Imports supported files dropped into the app window.
* @param {File[]} files Array of files to process
* @param {boolean?} preserveFileNames Whether to preserve original file names
* @returns {Promise<void>}
*/
export async function processDroppedFiles(files, preserveFileNames = false) {
const allowedMimeTypes = [ const allowedMimeTypes = [
'application/json', 'application/json',
'image/png', 'image/png',
@@ -7776,14 +7791,20 @@ export async function processDroppedFiles(files) {
for (const file of files) { for (const file of files) {
if (allowedMimeTypes.includes(file.type)) { if (allowedMimeTypes.includes(file.type)) {
await importCharacter(file); await importCharacter(file, preserveFileNames);
} else { } else {
toastr.warning('Unsupported file type: ' + file.name); toastr.warning('Unsupported file type: ' + file.name);
} }
} }
} }
async function importCharacter(file) { /**
* Imports a character from a file.
* @param {File} file File to import
* @param {boolean?} preserveFileName Whether to preserve original file name
* @returns {Promise<void>}
*/
async function importCharacter(file, preserveFileName = false) {
const ext = file.name.match(/\.(\w+)$/); const ext = file.name.match(/\.(\w+)$/);
if (!ext || !(['json', 'png', 'yaml', 'yml'].includes(ext[1].toLowerCase()))) { if (!ext || !(['json', 'png', 'yaml', 'yml'].includes(ext[1].toLowerCase()))) {
return; return;
@@ -7794,6 +7815,7 @@ async function importCharacter(file) {
const formData = new FormData(); const formData = new FormData();
formData.append('avatar', file); formData.append('avatar', file);
formData.append('file_type', format); formData.append('file_type', format);
formData.append('preserve_file_name', String(preserveFileName));
const data = await jQuery.ajax({ const data = await jQuery.ajax({
type: 'POST', type: 'POST',
@@ -7851,9 +7873,9 @@ async function importFromURL(items, files) {
} }
} }
async function doImpersonate() { async function doImpersonate(_, prompt) {
$('#send_textarea').val(''); $('#send_textarea').val('');
$('#option_impersonate').trigger('click', { fromSlashCommand: true }); $('#option_impersonate').trigger('click', { fromSlashCommand: true, additionalPrompt: prompt });
} }
async function doDeleteChat() { async function doDeleteChat() {
@@ -8020,7 +8042,7 @@ jQuery(async function () {
registerSlashCommand('dupe', DupeChar, [], ' duplicates the currently selected character', true, true); registerSlashCommand('dupe', DupeChar, [], ' duplicates the currently selected character', true, true);
registerSlashCommand('api', connectAPISlash, [], `<span class="monospace">(${Object.keys(CONNECT_API_MAP).join(', ')})</span> connect to an API`, true, true); registerSlashCommand('api', connectAPISlash, [], `<span class="monospace">(${Object.keys(CONNECT_API_MAP).join(', ')})</span> connect to an API`, true, true);
registerSlashCommand('impersonate', doImpersonate, ['imp'], ' calls an impersonation response', true, true); registerSlashCommand('impersonate', doImpersonate, ['imp'], '<span class="monospace">[prompt]</span> calls an impersonation response, with an optional additional prompt', true, true);
registerSlashCommand('delchat', doDeleteChat, [], ' deletes the current chat', true, true); registerSlashCommand('delchat', doDeleteChat, [], ' deletes the current chat', true, true);
registerSlashCommand('getchatname', doGetChatName, [], ' returns the name of the current chat file into the pipe', false, true); registerSlashCommand('getchatname', doGetChatName, [], ' returns the name of the current chat file into the pipe', false, true);
registerSlashCommand('closechat', doCloseChat, [], ' closes the current chat', true, true); registerSlashCommand('closechat', doCloseChat, [], ' closes the current chat', true, true);
@@ -8669,6 +8691,13 @@ jQuery(async function () {
const fromSlashCommand = customData?.fromSlashCommand || false; const fromSlashCommand = customData?.fromSlashCommand || false;
var id = $(this).attr('id'); var id = $(this).attr('id');
// Check whether a custom prompt was provided via custom data (for example through a slash command)
const additionalPrompt = customData?.additionalPrompt?.trim() || undefined;
const buildOrFillAdditionalArgs = (args = {}) => ({
...args,
...(additionalPrompt !== undefined && { quiet_prompt: additionalPrompt, quietToLoud: true }),
});
if (id == 'option_select_chat') { if (id == 'option_select_chat') {
if ((selected_group && !is_group_generating) || (this_chid !== undefined && !is_send_press) || fromSlashCommand) { if ((selected_group && !is_group_generating) || (this_chid !== undefined && !is_send_press) || fromSlashCommand) {
await displayPastChats(); await displayPastChats();
@@ -8704,7 +8733,7 @@ jQuery(async function () {
} }
else { else {
is_send_press = true; is_send_press = true;
Generate('regenerate'); Generate('regenerate', buildOrFillAdditionalArgs());
} }
} }
} }
@@ -8712,14 +8741,14 @@ jQuery(async function () {
else if (id == 'option_impersonate') { else if (id == 'option_impersonate') {
if (is_send_press == false || fromSlashCommand) { if (is_send_press == false || fromSlashCommand) {
is_send_press = true; is_send_press = true;
Generate('impersonate'); Generate('impersonate', buildOrFillAdditionalArgs());
} }
} }
else if (id == 'option_continue') { else if (id == 'option_continue') {
if (is_send_press == false || fromSlashCommand) { if (is_send_press == false || fromSlashCommand) {
is_send_press = true; is_send_press = true;
Generate('continue'); Generate('continue', buildOrFillAdditionalArgs());
} }
} }

View File

@@ -236,7 +236,7 @@ async function installAsset(url, assetType, filename) {
console.debug(DEBUG_PREFIX, 'Importing character ', filename); console.debug(DEBUG_PREFIX, 'Importing character ', filename);
const blob = await result.blob(); const blob = await result.blob();
const file = new File([blob], filename, { type: blob.type }); const file = new File([blob], filename, { type: blob.type });
await processDroppedFiles([file]); await processDroppedFiles([file], true);
console.debug(DEBUG_PREFIX, 'Character downloaded.'); console.debug(DEBUG_PREFIX, 'Character downloaded.');
} }
} }

View File

@@ -119,7 +119,7 @@ const scale_max = 8191;
const claude_max = 9000; // We have a proper tokenizer, so theoretically could be larger (up to 9k) const claude_max = 9000; // We have a proper tokenizer, so theoretically could be larger (up to 9k)
const claude_100k_max = 99000; const claude_100k_max = 99000;
let ai21_max = 9200; //can easily fit 9k gpt tokens because j2's tokenizer is efficient af let ai21_max = 9200; //can easily fit 9k gpt tokens because j2's tokenizer is efficient af
const unlocked_max = 100 * 1024; const unlocked_max = max_200k;
const oai_max_temp = 2.0; const oai_max_temp = 2.0;
const claude_max_temp = 1.0; //same as j2 const claude_max_temp = 1.0; //same as j2
const j2_max_topk = 10.0; const j2_max_topk = 10.0;

View File

@@ -140,7 +140,7 @@ const getSlashCommandsHelp = parser.getHelpString.bind(parser);
parser.addCommand('?', helpCommandCallback, ['help'], ' get help on macros, chat formatting and commands', true, true); parser.addCommand('?', helpCommandCallback, ['help'], ' get help on macros, chat formatting and commands', true, true);
parser.addCommand('name', setNameCallback, ['persona'], '<span class="monospace">(name)</span> sets user name and persona avatar (if set)', true, true); parser.addCommand('name', setNameCallback, ['persona'], '<span class="monospace">(name)</span> sets user name and persona avatar (if set)', true, true);
parser.addCommand('sync', syncCallback, [], ' syncs user name in user-attributed messages in the current chat', true, true); parser.addCommand('sync', syncCallback, [], ' syncs the user persona in user-attributed messages in the current chat', true, true);
parser.addCommand('lock', bindCallback, ['bind'], ' locks/unlocks a persona (name and avatar) to the current chat', true, true); parser.addCommand('lock', bindCallback, ['bind'], ' locks/unlocks a persona (name and avatar) to the current chat', true, true);
parser.addCommand('bg', setBackgroundCallback, ['background'], '<span class="monospace">(filename)</span> sets a background according to filename, partial names allowed', false, true); parser.addCommand('bg', setBackgroundCallback, ['background'], '<span class="monospace">(filename)</span> sets a background according to filename, partial names allowed', false, true);
parser.addCommand('sendas', sendMessageAs, [], ' sends message as a specific character. Uses character avatar if it exists in the characters list. Example that will send "Hello, guys!" from "Chloe": <tt>/sendas name="Chloe" Hello, guys!</tt>', true, true); parser.addCommand('sendas', sendMessageAs, [], ' sends message as a specific character. Uses character avatar if it exists in the characters list. Example that will send "Hello, guys!" from "Chloe": <tt>/sendas name="Chloe" Hello, guys!</tt>', true, true);
@@ -150,7 +150,7 @@ parser.addCommand('comment', sendCommentMessage, [], '<span class="monospace">(t
parser.addCommand('single', setStoryModeCallback, ['story'], ' sets the message style to single document mode without names or avatars visible', true, true); parser.addCommand('single', setStoryModeCallback, ['story'], ' sets the message style to single document mode without names or avatars visible', true, true);
parser.addCommand('bubble', setBubbleModeCallback, ['bubbles'], ' sets the message style to bubble chat mode', true, true); parser.addCommand('bubble', setBubbleModeCallback, ['bubbles'], ' sets the message style to bubble chat mode', true, true);
parser.addCommand('flat', setFlatModeCallback, ['default'], ' sets the message style to flat chat mode', true, true); parser.addCommand('flat', setFlatModeCallback, ['default'], ' sets the message style to flat chat mode', true, true);
parser.addCommand('continue', continueChatCallback, ['cont'], ' continues the last message in the chat', true, true); parser.addCommand('continue', continueChatCallback, ['cont'], '<span class="monospace">[prompt]</span> continues the last message in the chat, with an optional additional prompt', true, true);
parser.addCommand('go', goToCharacterCallback, ['char'], '<span class="monospace">(name)</span> opens up a chat with the character or group by its name', true, true); parser.addCommand('go', goToCharacterCallback, ['char'], '<span class="monospace">(name)</span> opens up a chat with the character or group by its name', true, true);
parser.addCommand('sysgen', generateSystemMessage, [], '<span class="monospace">(prompt)</span> generates a system message using a specified prompt', true, true); parser.addCommand('sysgen', generateSystemMessage, [], '<span class="monospace">(prompt)</span> generates a system message using a specified prompt', true, true);
parser.addCommand('ask', askCharacter, [], '<span class="monospace">(prompt)</span> asks a specified character card a prompt', true, true); parser.addCommand('ask', askCharacter, [], '<span class="monospace">(prompt)</span> asks a specified character card a prompt', true, true);
@@ -1168,7 +1168,7 @@ async function openChat(id) {
await reloadCurrentChat(); await reloadCurrentChat();
} }
function continueChatCallback() { function continueChatCallback(_, prompt) {
setTimeout(async () => { setTimeout(async () => {
try { try {
await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100); await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100);
@@ -1179,7 +1179,7 @@ function continueChatCallback() {
// Prevent infinite recursion // Prevent infinite recursion
$('#send_textarea').val('').trigger('input'); $('#send_textarea').val('').trigger('input');
$('#option_continue').trigger('click', { fromSlashCommand: true }); $('#option_continue').trigger('click', { fromSlashCommand: true, additionalPrompt: prompt });
}, 1); }, 1);
return ''; return '';

View File

@@ -783,6 +783,17 @@ function getPngName(file) {
return file; return file;
} }
/**
* Gets the preserved name for the uploaded file if the request is valid.
* @param {import("express").Request} request - Express request object
* @returns {string | undefined} - The preserved name if the request is valid, otherwise undefined
*/
function getPreservedName(request) {
return request.body.file_type === 'png' && request.body.preserve_file_name === 'true' && request.file?.originalname
? path.parse(request.file.originalname).name
: undefined;
}
router.post('/import', urlencodedParser, async function (request, response) { router.post('/import', urlencodedParser, async function (request, response) {
if (!request.body || !request.file) return response.sendStatus(400); if (!request.body || !request.file) return response.sendStatus(400);
@@ -790,6 +801,7 @@ router.post('/import', urlencodedParser, async function (request, response) {
let filedata = request.file; let filedata = request.file;
let uploadPath = path.join(UPLOADS_PATH, filedata.filename); let uploadPath = path.join(UPLOADS_PATH, filedata.filename);
let format = request.body.file_type; let format = request.body.file_type;
const preservedFileName = getPreservedName(request);
if (format == 'yaml' || format == 'yml') { if (format == 'yaml' || format == 'yml') {
try { try {
@@ -881,7 +893,7 @@ router.post('/import', urlencodedParser, async function (request, response) {
let jsonData = JSON.parse(img_data); let jsonData = JSON.parse(img_data);
jsonData.name = sanitize(jsonData.data?.name || jsonData.name); jsonData.name = sanitize(jsonData.data?.name || jsonData.name);
png_name = getPngName(jsonData.name); png_name = preservedFileName || getPngName(jsonData.name);
if (jsonData.spec !== undefined) { if (jsonData.spec !== undefined) {
console.log('Found a v2 character file.'); console.log('Found a v2 character file.');