Merge pull request #3496 from SillyTavern/parse-reasoning-command-args Add 'return' and 'strict' to `/reasoning-parse`, and make all reasoning parsing strict by default

580856064e05faeb7375eb2b95a63648aea0d028

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
2 files changed, +40 -9Ignore whitespace
public/scripts/reasoning.js+39 -9
@@ -10,7 +10,8 @@ import { Popup } from './popup.js';
10import { power_user } from './power-user.js';10import { power_user } from './power-user.js';
11import { SlashCommand } from './slash-commands/SlashCommand.js';11import { SlashCommand } from './slash-commands/SlashCommand.js';
12import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';12import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
13import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js';13import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
14import { enumTypes, SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
14import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';15import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
15import { textgen_types, textgenerationwebui_settings } from './textgen-settings.js';16import { textgen_types, textgenerationwebui_settings } from './textgen-settings.js';
16import { copyText, escapeRegex, isFalseBoolean, setDatasetProperty } from './utils.js';17import { copyText, escapeRegex, isFalseBoolean, setDatasetProperty } from './utils.js';
@@ -598,7 +599,26 @@ function registerReasoningSlashCommands() {
598 typeList: [ARGUMENT_TYPE.BOOLEAN],599 typeList: [ARGUMENT_TYPE.BOOLEAN],
599 defaultValue: 'true',600 defaultValue: 'true',
600 isRequired: false,601 isRequired: false,
601 enumProvider: commonEnumProviders.boolean('trueFalse'),602 enumList: commonEnumProviders.boolean('trueFalse')(),
603 }),
604 SlashCommandNamedArgument.fromProps({
605 name: 'return',
606 description: 'Whether to return the parsed reasoning or the content without reasoning',
607 typeList: [ARGUMENT_TYPE.STRING],
608 defaultValue: 'reasoning',
609 isRequired: false,
610 enumList: [
611 new SlashCommandEnumValue('reasoning', null, enumTypes.enum, enumIcons.reasoning),
612 new SlashCommandEnumValue('content', null, enumTypes.enum, enumIcons.message),
613 ],
614 }),
615 SlashCommandNamedArgument.fromProps({
616 name: 'strict',
617 description: 'Whether to require the reasoning block to be at the beginning of the string (excluding whitespaces).',
618 typeList: [ARGUMENT_TYPE.BOOLEAN],
619 defaultValue: 'true',
620 isRequired: false,
621 enumList: commonEnumProviders.boolean('trueFalse')(),
602 }),622 }),
603 ],623 ],
604 unnamedArgumentList: [624 unnamedArgumentList: [
@@ -608,19 +628,27 @@ function registerReasoningSlashCommands() {
608 }),628 }),
609 ],629 ],
610 callback: (args, value) => {630 callback: (args, value) => {
611 if (!value) {631 if (!value || typeof value !== 'string') {
612 return '';632 return '';
613 }633 }
614634
615 if (!power_user.reasoning.prefix || !power_user.reasoning.suffix) {635 if (!power_user.reasoning.prefix || !power_user.reasoning.suffix) {
616 toastr.warning(t`Both prefix and suffix must be set in the Reasoning Formatting settings.`);636 toastr.warning(t`Both prefix and suffix must be set in the Reasoning Formatting settings.`, t`Reasoning Parse`);
617 return String(value);637 return value;
638 }
639 if (typeof args.return !== 'string' || !['reasoning', 'content'].includes(args.return)) {
640 toastr.warning(t`Invalid return type '${args.return}', defaulting to 'reasoning'.`, t`Reasoning Parse`);
618 }641 }
619642
620 const parsedReasoning = parseReasoningFromString(String(value));643 const returnMessage = args.return === 'content';
621644
645 const parsedReasoning = parseReasoningFromString(value, { strict: !isFalseBoolean(String(args.strict ?? '')) });
622 if (!parsedReasoning) {646 if (!parsedReasoning) {
623 return '';647 return returnMessage ? value : '';
648 }
649
650 if (returnMessage) {
651 return parsedReasoning.content;
624 }652 }
625653
626 const applyRegex = !isFalseBoolean(String(args.regex ?? ''));654 const applyRegex = !isFalseBoolean(String(args.regex ?? ''));
@@ -819,16 +847,18 @@ export function removeReasoningFromString(str) {
819 * @property {string} reasoning Reasoning block847 * @property {string} reasoning Reasoning block
820 * @property {string} content Message content848 * @property {string} content Message content
821 * @param {string} str Content of the message849 * @param {string} str Content of the message
850 * @param {Object} options Optional arguments
851 * @param {boolean} [options.strict=true] Whether the reasoning block **has** to be at the beginning of the provided string (excluding whitespaces), or can be anywhere in it
822 * @returns {ParsedReasoning|null} Parsed reasoning block and message content852 * @returns {ParsedReasoning|null} Parsed reasoning block and message content
823 */853 */
824function parseReasoningFromString(str) {854function parseReasoningFromString(str, { strict = true } = {}) {
825 // Both prefix and suffix must be defined855 // Both prefix and suffix must be defined
826 if (!power_user.reasoning.prefix || !power_user.reasoning.suffix) {856 if (!power_user.reasoning.prefix || !power_user.reasoning.suffix) {
827 return null;857 return null;
828 }858 }
829859
830 try {860 try {
831 const regex = new RegExp(`${escapeRegex(power_user.reasoning.prefix)}(.*?)${escapeRegex(power_user.reasoning.suffix)}`, 's');861 const regex = new RegExp(`${(strict ? '^\\s*?' : '')}${escapeRegex(power_user.reasoning.prefix)}(.*?)${escapeRegex(power_user.reasoning.suffix)}`, 's');
832862
833 let didReplace = false;863 let didReplace = false;
834 let reasoning = '';864 let reasoning = '';
public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js+1 -0
@@ -34,6 +34,7 @@ export const enumIcons = {
34 preset: '⚙️',34 preset: '⚙️',
35 file: '📄',35 file: '📄',
36 message: '💬',36 message: '💬',
37 reasoning: '💡',
37 voice: '🎤',38 voice: '🎤',
38 server: '🖥️',39 server: '🖥️',
39 popup: '🗔',40 popup: '🗔',