Merge branch 'staging' into pygimport
This commit is contained in:
commit
711fd0517f
|
@ -2270,12 +2270,21 @@ export async function generateQuietPrompt(quiet_prompt, quietToLoud, skipWIAN, q
|
|||
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) {
|
||||
if (!message || !message.trim().startsWith('/')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const previousText = String($('#send_textarea').val());
|
||||
const result = await executeSlashCommands(message);
|
||||
|
||||
if (!result || typeof result !== 'object') {
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
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}: `;
|
||||
|
||||
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) {
|
||||
//$("#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 = [
|
||||
'application/json',
|
||||
'image/png',
|
||||
|
@ -7776,14 +7791,20 @@ export async function processDroppedFiles(files) {
|
|||
|
||||
for (const file of files) {
|
||||
if (allowedMimeTypes.includes(file.type)) {
|
||||
await importCharacter(file);
|
||||
await importCharacter(file, preserveFileNames);
|
||||
} else {
|
||||
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+)$/);
|
||||
if (!ext || !(['json', 'png', 'yaml', 'yml'].includes(ext[1].toLowerCase()))) {
|
||||
return;
|
||||
|
@ -7794,6 +7815,7 @@ async function importCharacter(file) {
|
|||
const formData = new FormData();
|
||||
formData.append('avatar', file);
|
||||
formData.append('file_type', format);
|
||||
formData.append('preserve_file_name', String(preserveFileName));
|
||||
|
||||
const data = await jQuery.ajax({
|
||||
type: 'POST',
|
||||
|
@ -7851,9 +7873,9 @@ async function importFromURL(items, files) {
|
|||
}
|
||||
}
|
||||
|
||||
async function doImpersonate() {
|
||||
async function doImpersonate(_, prompt) {
|
||||
$('#send_textarea').val('');
|
||||
$('#option_impersonate').trigger('click', { fromSlashCommand: true });
|
||||
$('#option_impersonate').trigger('click', { fromSlashCommand: true, additionalPrompt: prompt });
|
||||
}
|
||||
|
||||
async function doDeleteChat() {
|
||||
|
@ -8020,7 +8042,7 @@ jQuery(async function () {
|
|||
|
||||
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('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('getchatname', doGetChatName, [], '– returns the name of the current chat file into the pipe', false, true);
|
||||
registerSlashCommand('closechat', doCloseChat, [], '– closes the current chat', true, true);
|
||||
|
@ -8669,6 +8691,13 @@ jQuery(async function () {
|
|||
const fromSlashCommand = customData?.fromSlashCommand || false;
|
||||
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 ((selected_group && !is_group_generating) || (this_chid !== undefined && !is_send_press) || fromSlashCommand) {
|
||||
await displayPastChats();
|
||||
|
@ -8704,7 +8733,7 @@ jQuery(async function () {
|
|||
}
|
||||
else {
|
||||
is_send_press = true;
|
||||
Generate('regenerate');
|
||||
Generate('regenerate', buildOrFillAdditionalArgs());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8712,14 +8741,14 @@ jQuery(async function () {
|
|||
else if (id == 'option_impersonate') {
|
||||
if (is_send_press == false || fromSlashCommand) {
|
||||
is_send_press = true;
|
||||
Generate('impersonate');
|
||||
Generate('impersonate', buildOrFillAdditionalArgs());
|
||||
}
|
||||
}
|
||||
|
||||
else if (id == 'option_continue') {
|
||||
if (is_send_press == false || fromSlashCommand) {
|
||||
is_send_press = true;
|
||||
Generate('continue');
|
||||
Generate('continue', buildOrFillAdditionalArgs());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -236,7 +236,7 @@ async function installAsset(url, assetType, filename) {
|
|||
console.debug(DEBUG_PREFIX, 'Importing character ', filename);
|
||||
const blob = await result.blob();
|
||||
const file = new File([blob], filename, { type: blob.type });
|
||||
await processDroppedFiles([file]);
|
||||
await processDroppedFiles([file], true);
|
||||
console.debug(DEBUG_PREFIX, 'Character downloaded.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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_100k_max = 99000;
|
||||
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 claude_max_temp = 1.0; //same as j2
|
||||
const j2_max_topk = 10.0;
|
||||
|
|
|
@ -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('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('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);
|
||||
|
@ -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('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('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('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);
|
||||
|
@ -1168,7 +1168,7 @@ async function openChat(id) {
|
|||
await reloadCurrentChat();
|
||||
}
|
||||
|
||||
function continueChatCallback() {
|
||||
function continueChatCallback(_, prompt) {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100);
|
||||
|
@ -1179,7 +1179,7 @@ function continueChatCallback() {
|
|||
|
||||
// Prevent infinite recursion
|
||||
$('#send_textarea').val('').trigger('input');
|
||||
$('#option_continue').trigger('click', { fromSlashCommand: true });
|
||||
$('#option_continue').trigger('click', { fromSlashCommand: true, additionalPrompt: prompt });
|
||||
}, 1);
|
||||
|
||||
return '';
|
||||
|
|
|
@ -783,6 +783,17 @@ function getPngName(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) {
|
||||
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 uploadPath = path.join(UPLOADS_PATH, filedata.filename);
|
||||
let format = request.body.file_type;
|
||||
const preservedFileName = getPreservedName(request);
|
||||
|
||||
if (format == 'yaml' || format == 'yml') {
|
||||
try {
|
||||
|
@ -881,7 +893,7 @@ router.post('/import', urlencodedParser, async function (request, response) {
|
|||
let jsonData = JSON.parse(img_data);
|
||||
|
||||
jsonData.name = sanitize(jsonData.data?.name || jsonData.name);
|
||||
png_name = getPngName(jsonData.name);
|
||||
png_name = preservedFileName || getPngName(jsonData.name);
|
||||
|
||||
if (jsonData.spec !== undefined) {
|
||||
console.log('Found a v2 character file.');
|
||||
|
|
Loading…
Reference in New Issue