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
Signed| @@ -10,7 +10,8 @@ import { Popup } from './popup.js'; | |||
| 10 | import { power_user } from './power-user.js'; | 10 | import { power_user } from './power-user.js'; |
| 11 | import { SlashCommand } from './slash-commands/SlashCommand.js'; | 11 | import { SlashCommand } from './slash-commands/SlashCommand.js'; |
| 12 | import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; | 12 | import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; |
| 13 | import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js'; | 13 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 14 | import { enumTypes, SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js'; | ||
| 14 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; | 15 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| 15 | import { textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; | 16 | import { textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; |
| 16 | import { copyText, escapeRegex, isFalseBoolean, setDatasetProperty } from './utils.js'; | 17 | import { 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 | } |
| 614 | 634 | ||
| 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 | } |
| 619 | 642 | ||
| 620 | const parsedReasoning = parseReasoningFromString(String(value)); | 643 | const returnMessage = args.return === 'content'; |
| 621 | 644 | ||
| 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 | } |
| 625 | 653 | ||
| 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 block | 847 | * @property {string} reasoning Reasoning block |
| 820 | * @property {string} content Message content | 848 | * @property {string} content Message content |
| 821 | * @param {string} str Content of the message | 849 | * @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 content | 852 | * @returns {ParsedReasoning|null} Parsed reasoning block and message content |
| 823 | */ | 853 | */ |
| 824 | function parseReasoningFromString(str) { | 854 | function parseReasoningFromString(str, { strict = true } = {}) { |
| 825 | // Both prefix and suffix must be defined | 855 | // 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 | } |
| 829 | 859 | ||
| 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'); |
| 832 | 862 | ||
| 833 | let didReplace = false; | 863 | let didReplace = false; |
| 834 | let reasoning = ''; | 864 | let reasoning = ''; |
| @@ -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: '🗔', |