mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
add aborting command execution
This commit is contained in:
@ -155,7 +155,7 @@ import {
|
|||||||
} from './scripts/utils.js';
|
} from './scripts/utils.js';
|
||||||
|
|
||||||
import { ModuleWorkerWrapper, doDailyExtensionUpdatesCheck, extension_settings, getContext, loadExtensionSettings, renderExtensionTemplate, renderExtensionTemplateAsync, runGenerationInterceptors, saveMetadataDebounced, writeExtensionField } from './scripts/extensions.js';
|
import { ModuleWorkerWrapper, doDailyExtensionUpdatesCheck, extension_settings, getContext, loadExtensionSettings, renderExtensionTemplate, renderExtensionTemplateAsync, runGenerationInterceptors, saveMetadataDebounced, writeExtensionField } from './scripts/extensions.js';
|
||||||
import { COMMENT_NAME_DEFAULT, executeSlashCommands, getSlashCommandsHelp, processChatSlashCommands, registerSlashCommand } from './scripts/slash-commands.js';
|
import { COMMENT_NAME_DEFAULT, executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, processChatSlashCommands, registerSlashCommand } from './scripts/slash-commands.js';
|
||||||
import {
|
import {
|
||||||
tag_map,
|
tag_map,
|
||||||
tags,
|
tags,
|
||||||
@ -2395,31 +2395,26 @@ export async function generateQuietPrompt(quiet_prompt, quietToLoud, skipWIAN, q
|
|||||||
* Executes slash commands and returns the new text and whether the generation was interrupted.
|
* Executes slash commands and returns the new text and whether the generation was interrupted.
|
||||||
* @param {string} message Text to be sent
|
* @param {string} message Text to be sent
|
||||||
* @returns {Promise<boolean>} Whether the message sending was interrupted
|
* @returns {Promise<boolean>} Whether the message sending was interrupted
|
||||||
|
* @param {AbortController} abortController
|
||||||
*/
|
*/
|
||||||
async function processCommands(message) {
|
async function processCommands(message, abortController) {
|
||||||
if (!message || !message.trim().startsWith('/')) {
|
if (!message || !message.trim().startsWith('/')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const previousText = String($('#send_textarea').val());
|
deactivateSendButtons();
|
||||||
const result = await executeSlashCommands(message, true, null, true);
|
is_send_press = true;
|
||||||
|
|
||||||
if (!result || typeof result !== 'object') {
|
document.querySelector('#send_textarea').value = '';
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentText = String($('#send_textarea').val());
|
await executeSlashCommandsWithOptions(message, {
|
||||||
|
abortController: abortController,
|
||||||
|
});
|
||||||
|
|
||||||
if (previousText === currentText) {
|
is_send_press = false;
|
||||||
$('#send_textarea').val(result.newText)[0].dispatchEvent(new Event('input', { bubbles:true }));
|
activateSendButtons();
|
||||||
}
|
|
||||||
|
|
||||||
// interrupt generation if the input was nothing but a command
|
return true;
|
||||||
if (message.length > 0 && result?.newText.length === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result?.interrupt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendSystemMessage(type, text, extra = {}) {
|
function sendSystemMessage(type, text, extra = {}) {
|
||||||
@ -3083,7 +3078,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(String($('#send_textarea').val()));
|
const interruptedByCommand = await processCommands(String($('#send_textarea').val()), abortController);
|
||||||
|
|
||||||
if (interruptedByCommand) {
|
if (interruptedByCommand) {
|
||||||
//$("#send_textarea").val('')[0].dispatchEvent(new Event('input', { bubbles:true }));
|
//$("#send_textarea").val('')[0].dispatchEvent(new Event('input', { bubbles:true }));
|
||||||
@ -10177,11 +10172,10 @@ jQuery(async function () {
|
|||||||
streamingProcessor = null;
|
streamingProcessor = null;
|
||||||
}
|
}
|
||||||
if (abortController) {
|
if (abortController) {
|
||||||
abortController.abort();
|
abortController.abort('Clicked stop button');
|
||||||
hideStopButton();
|
hideStopButton();
|
||||||
}
|
}
|
||||||
eventSource.emit(event_types.GENERATION_STOPPED);
|
eventSource.emit(event_types.GENERATION_STOPPED);
|
||||||
activateSendButtons();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.drawer-toggle').on('click', function () {
|
$('.drawer-toggle').on('click', function () {
|
||||||
|
@ -37,7 +37,7 @@ import {
|
|||||||
system_message_types,
|
system_message_types,
|
||||||
this_chid,
|
this_chid,
|
||||||
} from '../script.js';
|
} from '../script.js';
|
||||||
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
|
import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.js';
|
||||||
import { SlashCommandParserError } from './slash-commands/SlashCommandParserError.js';
|
import { SlashCommandParserError } from './slash-commands/SlashCommandParserError.js';
|
||||||
import { getMessageTimeStamp } from './RossAscends-mods.js';
|
import { getMessageTimeStamp } from './RossAscends-mods.js';
|
||||||
import { hideChatMessageRange } from './chats.js';
|
import { hideChatMessageRange } from './chats.js';
|
||||||
@ -61,7 +61,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '
|
|||||||
import { SlashCommandAutoComplete } from './slash-commands/SlashCommandAutoComplete.js';
|
import { SlashCommandAutoComplete } from './slash-commands/SlashCommandAutoComplete.js';
|
||||||
import { SlashCommand } from './slash-commands/SlashCommand.js';
|
import { SlashCommand } from './slash-commands/SlashCommand.js';
|
||||||
export {
|
export {
|
||||||
executeSlashCommands, getSlashCommandsHelp, registerSlashCommand,
|
executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const parser = new SlashCommandParser();
|
export const parser = new SlashCommandParser();
|
||||||
@ -2548,21 +2548,45 @@ function modelCallback(_, model) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} text Slash command txt
|
||||||
|
* @param {object} [options]
|
||||||
|
* @param {boolean} [options.handleParserErrors]
|
||||||
|
* @param {SlashCommandScope} [options.scope]
|
||||||
|
* @param {boolean} [options.handleExecutionErrors]
|
||||||
|
* @param {PARSER_FLAG[]} [options.parserFlags]
|
||||||
|
* @param {AbortController} [options.abortController]
|
||||||
|
* @returns {Promise<SlashCommandClosureResult>}
|
||||||
|
*/
|
||||||
|
async function executeSlashCommandsWithOptions(text, options = {}) {
|
||||||
|
return await executeSlashCommands(
|
||||||
|
text,
|
||||||
|
options.handleParserErrors ?? true,
|
||||||
|
options.scope ?? null,
|
||||||
|
options.handleExecutionErrors ?? false,
|
||||||
|
options.parserFlags ?? null,
|
||||||
|
options.abortController ?? null,
|
||||||
|
);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Executes slash commands in the provided text
|
* Executes slash commands in the provided text
|
||||||
* @param {string} text Slash command text
|
* @param {string} text Slash command text
|
||||||
* @param {boolean} handleParserErrors Whether to handle parser errors (show toast on error) or throw
|
* @param {boolean} handleParserErrors Whether to handle parser errors (show toast on error) or throw
|
||||||
* @param {SlashCommandScope} scope The scope to be used when executing the commands.
|
* @param {SlashCommandScope} scope The scope to be used when executing the commands.
|
||||||
|
* @param {boolean} handleExecutionErrors
|
||||||
|
* @param {PARSER_FLAG[]} parserFlags
|
||||||
|
* @param {AbortController} abortController
|
||||||
* @returns {Promise<SlashCommandClosureResult>}
|
* @returns {Promise<SlashCommandClosureResult>}
|
||||||
*/
|
*/
|
||||||
async function executeSlashCommands(text, handleParserErrors = true, scope = null, handleExecutionErrors = false, parserFlags = null) {
|
async function executeSlashCommands(text, handleParserErrors = true, scope = null, handleExecutionErrors = false, parserFlags = null, abortController = null) {
|
||||||
if (!text) {
|
if (!text) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let closure;
|
let closure;
|
||||||
try {
|
try {
|
||||||
closure = parser.parse(text, true, parserFlags);
|
closure = parser.parse(text, true, parserFlags, abortController);
|
||||||
closure.scope.parent = scope;
|
closure.scope.parent = scope;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (handleParserErrors && e instanceof SlashCommandParserError) {
|
if (handleParserErrors && e instanceof SlashCommandParserError) {
|
||||||
@ -2588,7 +2612,10 @@ async function executeSlashCommands(text, handleParserErrors = true, scope = nul
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await closure.execute();
|
const result = await closure.execute();
|
||||||
|
if (result.isAborted) {
|
||||||
|
toastr.warning(result.abortReason, 'Command execution aborted');
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (handleExecutionErrors) {
|
if (handleExecutionErrors) {
|
||||||
toastr.error(e.message);
|
toastr.error(e.message);
|
||||||
|
@ -7,13 +7,14 @@ import { SlashCommandScope } from './SlashCommandScope.js';
|
|||||||
|
|
||||||
export class SlashCommandClosure {
|
export class SlashCommandClosure {
|
||||||
/**@type {SlashCommandScope}*/ scope;
|
/**@type {SlashCommandScope}*/ scope;
|
||||||
/**@type {Boolean}*/ executeNow = false;
|
/**@type {boolean}*/ executeNow = false;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
/**@type {Object.<string,string|SlashCommandClosure>}*/ arguments = {};
|
/**@type {Object.<string,string|SlashCommandClosure>}*/ arguments = {};
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
/**@type {Object.<string,string|SlashCommandClosure>}*/ providedArguments = {};
|
/**@type {Object.<string,string|SlashCommandClosure>}*/ providedArguments = {};
|
||||||
/**@type {SlashCommandExecutor[]}*/ executorList = [];
|
/**@type {SlashCommandExecutor[]}*/ executorList = [];
|
||||||
/**@type {String}*/ keptText;
|
/**@type {string}*/ keptText;
|
||||||
|
/**@type {AbortController}*/ abortController;
|
||||||
|
|
||||||
constructor(parent) {
|
constructor(parent) {
|
||||||
this.scope = new SlashCommandScope(parent);
|
this.scope = new SlashCommandScope(parent);
|
||||||
@ -77,6 +78,7 @@ export class SlashCommandClosure {
|
|||||||
closure.providedArguments = this.providedArguments;
|
closure.providedArguments = this.providedArguments;
|
||||||
closure.executorList = this.executorList;
|
closure.executorList = this.executorList;
|
||||||
closure.keptText = this.keptText;
|
closure.keptText = this.keptText;
|
||||||
|
closure.abortController = this.abortController;
|
||||||
return closure;
|
return closure;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,11 +227,29 @@ export class SlashCommandClosure {
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let abortResult;
|
||||||
|
// eslint-disable-next-line no-cond-assign
|
||||||
|
if (abortResult = this.testAbortController()) {
|
||||||
|
return abortResult;
|
||||||
|
}
|
||||||
this.scope.pipe = await executor.command.callback(args, value ?? '');
|
this.scope.pipe = await executor.command.callback(args, value ?? '');
|
||||||
|
// eslint-disable-next-line no-cond-assign
|
||||||
|
if (abortResult = this.testAbortController()) {
|
||||||
|
return abortResult;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**@type {SlashCommandClosureResult} */
|
/**@type {SlashCommandClosureResult} */
|
||||||
const result = Object.assign(new SlashCommandClosureResult(), { interrupt, newText: this.keptText, pipe: this.scope.pipe });
|
const result = Object.assign(new SlashCommandClosureResult(), { interrupt, newText: this.keptText, pipe: this.scope.pipe });
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testAbortController() {
|
||||||
|
if (this.abortController?.signal?.aborted) {
|
||||||
|
const result = new SlashCommandClosureResult();
|
||||||
|
result.isAborted = true;
|
||||||
|
result.abortReason = this.abortController.signal.reason.toString();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
export class SlashCommandClosureResult {
|
export class SlashCommandClosureResult {
|
||||||
/**@type {Boolean}*/ interrupt = false;
|
/**@type {boolean}*/ interrupt = false;
|
||||||
/**@type {String}*/ newText = '';
|
/**@type {string}*/ newText = '';
|
||||||
/**@type {String}*/ pipe;
|
/**@type {string}*/ pipe;
|
||||||
|
/**@type {boolean}*/ isAborted = false;
|
||||||
|
/**@type {string}*/ abortReason;
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,7 @@ export class SlashCommandParser {
|
|||||||
/**@type {string}*/ text;
|
/**@type {string}*/ text;
|
||||||
/**@type {string}*/ keptText;
|
/**@type {string}*/ keptText;
|
||||||
/**@type {number}*/ index;
|
/**@type {number}*/ index;
|
||||||
|
/**@type {AbortController}*/ abortController;
|
||||||
/**@type {SlashCommandScope}*/ scope;
|
/**@type {SlashCommandScope}*/ scope;
|
||||||
/**@type {SlashCommandClosure}*/ closure;
|
/**@type {SlashCommandClosure}*/ closure;
|
||||||
|
|
||||||
@ -539,11 +540,12 @@ export class SlashCommandParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
parse(text, verifyCommandNames = true, flags = null) {
|
parse(text, verifyCommandNames = true, flags = null, abortController = null) {
|
||||||
this.verifyCommandNames = verifyCommandNames;
|
this.verifyCommandNames = verifyCommandNames;
|
||||||
for (const key of Object.keys(PARSER_FLAG)) {
|
for (const key of Object.keys(PARSER_FLAG)) {
|
||||||
this.flags[PARSER_FLAG[key]] = flags?.[PARSER_FLAG[key]] ?? power_user.stscript.parser.flags[PARSER_FLAG[key]] ?? false;
|
this.flags[PARSER_FLAG[key]] = flags?.[PARSER_FLAG[key]] ?? power_user.stscript.parser.flags[PARSER_FLAG[key]] ?? false;
|
||||||
}
|
}
|
||||||
|
this.abortController = abortController;
|
||||||
this.text = `{:${text}:}`;
|
this.text = `{:${text}:}`;
|
||||||
this.keptText = '';
|
this.keptText = '';
|
||||||
this.index = 0;
|
this.index = 0;
|
||||||
@ -569,6 +571,7 @@ export class SlashCommandParser {
|
|||||||
let injectPipe = true;
|
let injectPipe = true;
|
||||||
this.take(2); // discard opening {:
|
this.take(2); // discard opening {:
|
||||||
let closure = new SlashCommandClosure(this.scope);
|
let closure = new SlashCommandClosure(this.scope);
|
||||||
|
closure.abortController = this.abortController;
|
||||||
this.scope = closure.scope;
|
this.scope = closure.scope;
|
||||||
this.closure = closure;
|
this.closure = closure;
|
||||||
this.discardWhitespace();
|
this.discardWhitespace();
|
||||||
|
Reference in New Issue
Block a user