Merge branch 'staging' into small-bookmark-updates

This commit is contained in:
Cohee
2024-09-12 23:10:33 +03:00
13 changed files with 833 additions and 216 deletions

View File

@@ -24,6 +24,7 @@ import {
main_api,
name1,
name2,
neutralCharacterName,
reloadCurrentChat,
removeMacros,
renameCharacter,
@@ -427,6 +428,7 @@ export function initDefaultSlashCommands() {
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'ask',
callback: askCharacter,
returns: 'the generated text',
namedArgumentList: [
SlashCommandNamedArgument.fromProps({
name: 'name',
@@ -438,7 +440,7 @@ export function initDefaultSlashCommands() {
],
unnamedArgumentList: [
new SlashCommandArgument(
'prompt', [ARGUMENT_TYPE.STRING], true, false,
'prompt', [ARGUMENT_TYPE.STRING], false, false,
),
],
helpString: 'Asks a specified character card a prompt. Character name must be provided in a named argument.',
@@ -2467,44 +2469,40 @@ async function askCharacter(args, text) {
// Not supported in group chats
// TODO: Maybe support group chats?
if (selected_group) {
toastr.error('Cannot run this command in a group chat!');
return '';
}
if (!text) {
console.warn('WARN: No text provided for /ask command');
toastr.warning('No text provided for /ask command');
toastr.error('Cannot run /ask command in a group chat!');
return '';
}
let name = '';
let mesText = '';
if (args?.name) {
name = args.name.trim();
mesText = text.trim();
if (!name && !mesText) {
toastr.warning('You must specify a name and text to ask.');
if (!name) {
toastr.warning('You must specify a name of the character to ask.');
return '';
}
}
mesText = getRegexedString(mesText, regex_placement.SLASH_COMMAND);
const prevChId = this_chid;
// Find the character
const chId = characters.findIndex((e) => e.name === name);
const chId = characters.findIndex((e) => e.name === name || e.avatar === name);
if (!characters[chId] || chId === -1) {
toastr.error('Character not found.');
return '';
}
if (text) {
const mesText = getRegexedString(text.trim(), regex_placement.SLASH_COMMAND);
// Sending a message implicitly saves the chat, so this needs to be done before changing the character
// Otherwise, a corruption will occur
await sendMessageAsUser(mesText, '');
}
// Override character and send a user message
setCharacterId(String(chId));
// TODO: Maybe look up by filename instead of name
const character = characters[chId];
let force_avatar, original_avatar;
@@ -2519,9 +2517,11 @@ async function askCharacter(args, text) {
setCharacterName(character.name);
await sendMessageAsUser(mesText, '');
const restoreCharacter = () => {
if (String(this_chid) !== String(chId)) {
return;
}
setCharacterId(prevChId);
setCharacterName(characters[prevChId].name);
@@ -2532,23 +2532,27 @@ async function askCharacter(args, text) {
lastMessage.force_avatar = force_avatar;
lastMessage.original_avatar = original_avatar;
}
// Kill this callback once the event fires
eventSource.removeListener(event_types.CHARACTER_MESSAGE_RENDERED, restoreCharacter);
};
// Run generate and restore previous character on error
let askResult = '';
// Run generate and restore previous character
try {
eventSource.once(event_types.MESSAGE_RECEIVED, restoreCharacter);
toastr.info(`Asking ${character.name} something...`);
await Generate('ask_command');
} catch {
askResult = await Generate('ask_command');
} catch (error) {
restoreCharacter();
console.error('Error running /ask command', error);
} finally {
if (String(this_chid) === String(prevChId)) {
await saveChatConditional();
} else {
toastr.error('It is strongly recommended to reload the page.', 'Something went wrong');
}
}
// Restore previous character once message renders
// Hack for generate
eventSource.on(event_types.CHARACTER_MESSAGE_RENDERED, restoreCharacter);
return '';
return askResult;
}
async function hideMessageCallback(_, arg) {
@@ -3129,7 +3133,13 @@ export async function sendMessageAs(args, text) {
const character = characters.find(x => x.avatar === name) ?? characters.find(x => x.name === name);
let force_avatar, original_avatar;
if (character && character.avatar !== 'none') {
const chatCharacter = this_chid !== undefined ? characters[this_chid] : null;
const isNeutralCharacter = !chatCharacter && name2 === neutralCharacterName && name === neutralCharacterName;
if (chatCharacter === character || isNeutralCharacter) {
// If the targeted character is the currently selected one in a solo chat, we don't need to force any avatars
}
else if (character && character.avatar !== 'none') {
force_avatar = getThumbnailUrl('avatar', character.avatar);
original_avatar = character.avatar;
}

View File

@@ -1,4 +1,4 @@
import { chat_metadata, characters, substituteParams, chat, extension_prompt_roles, extension_prompt_types } from '../../script.js';
import { chat_metadata, characters, substituteParams, chat, extension_prompt_roles, extension_prompt_types, name2, neutralCharacterName } from '../../script.js';
import { extension_settings } from '../extensions.js';
import { getGroupMembers, groups } from '../group-chats.js';
import { power_user } from '../power-user.js';
@@ -6,7 +6,9 @@ import { searchCharByName, getTagsList, tags } from '../tags.js';
import { world_names } from '../world-info.js';
import { SlashCommandClosure } from './SlashCommandClosure.js';
import { SlashCommandEnumValue, enumTypes } from './SlashCommandEnumValue.js';
import { SlashCommandScope } from "./SlashCommandScope.js";
/** @typedef {import('./SlashCommandExecutor.js').SlashCommandExecutor} SlashCommandExecutor */
/** @typedef {import('./SlashCommandScope.js').SlashCommandScope} SlashCommandScope */
/**
* A collection of regularly used enum icons
@@ -140,7 +142,7 @@ export const commonEnumProviders = {
* @param {...('global'|'local'|'scope'|'all')} type - The type of variables to include in the array. Can be 'all', 'global', or 'local'.
* @returns {(executor:SlashCommandExecutor, scope:SlashCommandScope) => SlashCommandEnumValue[]}
*/
variables: (...type) => (executor, scope) => {
variables: (...type) => (_, scope) => {
const types = type.flat();
const isAll = types.includes('all');
return [
@@ -160,6 +162,7 @@ export const commonEnumProviders = {
return [
...['all', 'character'].includes(mode) ? characters.map(char => new SlashCommandEnumValue(char.name, null, enumTypes.name, enumIcons.character)) : [],
...['all', 'group'].includes(mode) ? groups.map(group => new SlashCommandEnumValue(group.name, null, enumTypes.qr, enumIcons.group)) : [],
...(name2 === neutralCharacterName) ? [new SlashCommandEnumValue(neutralCharacterName, null, enumTypes.name, '🥸')] : [],
];
},
@@ -184,7 +187,7 @@ export const commonEnumProviders = {
* @param {('all' | 'existing' | 'not-existing')?} [mode='all'] - Which types of tags to show
* @returns {() => SlashCommandEnumValue[]}
*/
tagsForChar: (mode = 'all') => (/** @type {import('./SlashCommandExecutor.js').SlashCommandExecutor} */ executor) => {
tagsForChar: (mode = 'all') => (/** @type {SlashCommandExecutor} */ executor) => {
// Try to see if we can find the char during execution to filter down the tags list some more. Otherwise take all tags.
const charName = executor.namedArgumentList.find(it => it.name == 'name')?.value;
if (charName instanceof SlashCommandClosure) throw new Error('Argument \'name\' does not support closures');
@@ -202,13 +205,13 @@ export const commonEnumProviders = {
* @param {object} [options={}] - Optional arguments
* @param {boolean} [options.allowIdAfter=false] - Whether to add an enum option for the new message id after the last message
* @param {boolean} [options.allowVars=false] - Whether to add enum option for variable names
* @returns {() => SlashCommandEnumValue[]}
* @returns {(executor:SlashCommandExecutor, scope:SlashCommandScope) => SlashCommandEnumValue[]}
*/
messages: ({ allowIdAfter = false, allowVars = false } = {}) => () => {
messages: ({ allowIdAfter = false, allowVars = false } = {}) => (_, scope) => {
return [
...chat.map((message, index) => new SlashCommandEnumValue(String(index), `${message.name}: ${message.mes}`, enumTypes.number, message.is_user ? enumIcons.user : message.is_system ? enumIcons.system : enumIcons.assistant)),
...allowIdAfter ? [new SlashCommandEnumValue(String(chat.length), '>> After Last Message >>', enumTypes.enum, '')] : [],
...allowVars ? commonEnumProviders.variables('all')() : [],
...allowVars ? commonEnumProviders.variables('all')(_, scope) : [],
];
},