add Slash commands regenerate and swipe (#5243) * add Slash commands regenerate and swipe * Update /swipe slash command direction handling * Fix await not working in groups * Add SLASH_COMMAND source to swipe functionality and update constants --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
Signed| @@ -9692,7 +9692,7 @@ export async function swipe(event, direction, { source, repeated, message = chat | ||
| 9692 | 9692 | |
| 9693 | 9693 | const mesId = Number(forceMesId ?? event?.currentTarget?.closest('.mes')?.getAttribute('mesid') ?? messageIndex ?? chat.length - 1); |
| 9694 | 9694 | |
| 9695 | - if (source === SWIPE_SOURCE.DELETE || source === SWIPE_SOURCE.BACK || source === SWIPE_SOURCE.AUTO_SWIPE) { | |
| 9695 | + if ([SWIPE_SOURCE.DELETE, SWIPE_SOURCE.BACK, SWIPE_SOURCE.AUTO_SWIPE, SWIPE_SOURCE.SLASH_COMMAND].includes(source)) { | |
| 9696 | 9696 | console.info(`The ${direction} swipe source on message #${mesId} is ${source}, Most checks have been bypassed. `); |
| 9697 | 9697 | } else { |
| 9698 | 9698 | //Only show an error if swipes are not hidden and a message is generating. |
| @@ -175,6 +175,7 @@ export const SWIPE_SOURCE = { | ||
| 175 | 175 | KEYBOARD: 'keyboard', |
| 176 | 176 | BACK: 'back', |
| 177 | 177 | AUTO_SWIPE: 'auto_swipe', |
| 178 | + SLASH_COMMAND: 'slash_command', | |
| 178 | 179 | }; |
| 179 | 180 | |
| 180 | 181 | /** |
| @@ -183,7 +183,7 @@ async function regenerateGroup() { | ||
| 183 | 183 | |
| 184 | 184 | const abortController = new AbortController(); |
| 185 | 185 | setExternalAbortController(abortController); |
| 186 | 186 | return generateGroupWrapper(false, 'normal', { signal: abortController.signal }); |
| 187 | 187 | } |
| 188 | 188 | |
| 189 | 189 | /** |
| @@ -48,6 +48,7 @@ import { | ||
| 48 | 48 | setCharacterName, |
| 49 | 49 | setExtensionPrompt, |
| 50 | 50 | showMoreMessages, |
| 51 | + swipe, | |
| 51 | 52 | stopGeneration, |
| 52 | 53 | substituteParams, |
| 53 | 54 | syncMesToSwipe, |
| @@ -62,7 +63,7 @@ import { getMessageTimeStamp, isMobile } from './RossAscends-mods.js'; | ||
| 62 | 63 | import { hideChatMessageRange } from './chats.js'; |
| 63 | 64 | import { getContext, saveMetadataDebounced } from './extensions.js'; |
| 64 | 65 | import { getRegexedString, regex_placement } from './extensions/regex/engine.js'; |
| 65 | 66 | import { findGroupMemberId, groups, is_group_generating, openGroupById, regenerateGroup, resetSelectedGroup, saveGroupChat, selected_group, getGroupMembers } from './group-chats.js'; |
| 66 | 67 | import { chat_completion_sources, oai_settings, promptManager, ZAI_ENDPOINT } from './openai.js'; |
| 67 | 68 | import { user_avatar } from './personas.js'; |
| 68 | 69 | import { addEphemeralStoppingString, chat_styles, context_presets, flushEphemeralStoppingStrings, playMessageSound, power_user } from './power-user.js'; |
| @@ -90,7 +91,7 @@ import { SlashCommandScope } from './slash-commands/SlashCommandScope.js'; | ||
| 90 | 91 | import { t } from './i18n.js'; |
| 91 | 92 | import { kai_settings } from './kai-settings.js'; |
| 92 | 93 | import { instruct_presets, selectContextPreset, selectInstructPreset } from './instruct-mode.js'; |
| 93 | 94 | import { debounce_timeout, SWIPE_DIRECTION, SWIPE_SOURCE } from './constants.js'; |
| 94 | 95 | export { |
| 95 | 96 | executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand, |
| 96 | 97 | }; |
| @@ -1133,6 +1134,66 @@ export function initDefaultSlashCommands() { | ||
| 1133 | 1134 | `, |
| 1134 | 1135 | })); |
| 1135 | 1136 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1137 | + name: 'regenerate', | |
| 1138 | + callback: regenerateChatCallback, | |
| 1139 | + aliases: ['regen'], | |
| 1140 | + namedArgumentList: [ | |
| 1141 | + new SlashCommandNamedArgument( | |
| 1142 | + 'await', | |
| 1143 | + t`Whether to await for the regeneration before proceeding`, | |
| 1144 | + [ARGUMENT_TYPE.BOOLEAN], | |
| 1145 | + false, | |
| 1146 | + false, | |
| 1147 | + 'false', | |
| 1148 | + ), | |
| 1149 | + ], | |
| 1150 | + helpString: ` | |
| 1151 | + <div> | |
| 1152 | + ${t`Regenerates the latest reply in the chat.`} | |
| 1153 | + </div> | |
| 1154 | + <div> | |
| 1155 | + ${t`If <code>await=true</code> named argument is passed, the command will await for the regeneration before proceeding.`} | |
| 1156 | + </div> | |
| 1157 | + `, | |
| 1158 | + })); | |
| 1159 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 1160 | + name: 'swipe', | |
| 1161 | + callback: swipeChatCallback, | |
| 1162 | + namedArgumentList: [ | |
| 1163 | + new SlashCommandNamedArgument( | |
| 1164 | + 'direction', | |
| 1165 | + t`Swipe direction`, | |
| 1166 | + [ARGUMENT_TYPE.STRING], | |
| 1167 | + false, | |
| 1168 | + false, | |
| 1169 | + SWIPE_DIRECTION.RIGHT, | |
| 1170 | + [ | |
| 1171 | + new SlashCommandEnumValue(SWIPE_DIRECTION.RIGHT, t`Swipe to the next reply`, enumTypes.enum, enumIcons.default), | |
| 1172 | + new SlashCommandEnumValue(SWIPE_DIRECTION.LEFT, t`Swipe to the previous reply`, enumTypes.enum, enumIcons.default), | |
| 1173 | + ], | |
| 1174 | + [], | |
| 1175 | + null, | |
| 1176 | + true, | |
| 1177 | + ), | |
| 1178 | + new SlashCommandNamedArgument( | |
| 1179 | + 'await', | |
| 1180 | + t`Whether to await for the swipe action before proceeding`, | |
| 1181 | + [ARGUMENT_TYPE.BOOLEAN], | |
| 1182 | + false, | |
| 1183 | + false, | |
| 1184 | + 'false', | |
| 1185 | + ), | |
| 1186 | + ], | |
| 1187 | + helpString: ` | |
| 1188 | + <div> | |
| 1189 | + ${t`Swipes the latest reply. Defaults to <code>direction=right</code>; use <code>direction=left</code> to go to the previous reply. If no next swipe exists, behavior depends on message context.`} | |
| 1190 | + </div> | |
| 1191 | + <div> | |
| 1192 | + ${t`If <code>await=true</code> named argument is passed, the command will await for the swipe action before proceeding.`} | |
| 1193 | + </div> | |
| 1194 | + `, | |
| 1195 | + })); | |
| 1196 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 1136 | 1197 | name: 'go', |
| 1137 | 1198 | callback: goToCharacterCallback, |
| 1138 | 1199 | returns: t`The character/group name`, |
| @@ -4538,6 +4599,64 @@ async function continueChatCallback(args, prompt) { | ||
| 4538 | 4599 | return ''; |
| 4539 | 4600 | } |
| 4540 | 4601 | |
| 4602 | +async function regenerateChatCallback(args) { | |
| 4603 | + const shouldAwait = isTrueBoolean(args?.await); | |
| 4604 | + | |
| 4605 | + const outerPromise = new Promise((outerResolve) => setTimeout(async () => { | |
| 4606 | + try { | |
| 4607 | + await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100); | |
| 4608 | + } catch { | |
| 4609 | + console.warn('Timeout waiting for generation unlock'); | |
| 4610 | + toastr.warning(t`Cannot run /regenerate command while the reply is being generated.`); | |
| 4611 | + outerResolve(Promise.resolve('')); | |
| 4612 | + return ''; | |
| 4613 | + } | |
| 4614 | + | |
| 4615 | + if (selected_group) { | |
| 4616 | + outerResolve(Promise.resolve(regenerateGroup())); | |
| 4617 | + return ''; | |
| 4618 | + } | |
| 4619 | + | |
| 4620 | + outerResolve(new Promise(innerResolve => setTimeout(() => { | |
| 4621 | + innerResolve(Generate('regenerate')); | |
| 4622 | + }, 1))); | |
| 4623 | + return ''; | |
| 4624 | + }, 1)); | |
| 4625 | + | |
| 4626 | + if (shouldAwait) { | |
| 4627 | + const innerPromise = await outerPromise; | |
| 4628 | + await innerPromise; | |
| 4629 | + } | |
| 4630 | + | |
| 4631 | + return ''; | |
| 4632 | +} | |
| 4633 | + | |
| 4634 | +async function swipeChatCallback(args) { | |
| 4635 | + const shouldAwait = isTrueBoolean(args?.await); | |
| 4636 | + const direction = args?.direction === SWIPE_DIRECTION.LEFT ? SWIPE_DIRECTION.LEFT : SWIPE_DIRECTION.RIGHT; | |
| 4637 | + | |
| 4638 | + const outerPromise = new Promise((outerResolve) => setTimeout(async () => { | |
| 4639 | + try { | |
| 4640 | + await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100); | |
| 4641 | + } catch { | |
| 4642 | + console.warn('Timeout waiting for generation unlock'); | |
| 4643 | + toastr.warning(t`Cannot run /swipe command while the reply is being generated.`); | |
| 4644 | + outerResolve(Promise.resolve('')); | |
| 4645 | + return ''; | |
| 4646 | + } | |
| 4647 | + | |
| 4648 | + outerResolve(Promise.resolve(swipe(null, direction, { source: SWIPE_SOURCE.SLASH_COMMAND, repeated: false }))); | |
| 4649 | + return ''; | |
| 4650 | + }, 1)); | |
| 4651 | + | |
| 4652 | + if (shouldAwait) { | |
| 4653 | + const innerPromise = await outerPromise; | |
| 4654 | + await innerPromise; | |
| 4655 | + } | |
| 4656 | + | |
| 4657 | + return ''; | |
| 4658 | +} | |
| 4659 | + | |
| 4541 | 4660 | export async function generateSystemMessage(args, prompt) { |
| 4542 | 4661 | $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true })); |
| 4543 | 4662 | |