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 | 10 | import { power_user } from './power-user.js'; |
| 11 | 11 | import { SlashCommand } from './slash-commands/SlashCommand.js'; |
| 12 | 12 | import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; |
| 13 | 13 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 14 | +import { enumTypes, SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js'; | |
| 14 | 15 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| 15 | 16 | import { textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; |
| 16 | 17 | import { copyText, escapeRegex, isFalseBoolean, setDatasetProperty } from './utils.js'; |
| @@ -598,7 +599,26 @@ function registerReasoningSlashCommands() { | ||
| 598 | 599 | typeList: [ARGUMENT_TYPE.BOOLEAN], |
| 599 | 600 | defaultValue: 'true', |
| 600 | 601 | isRequired: false, |
| 601 | 602 | enumProviderenumList: 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 | 624 | unnamedArgumentList: [ |
| @@ -608,19 +628,27 @@ function registerReasoningSlashCommands() { | ||
| 608 | 628 | }), |
| 609 | 629 | ], |
| 610 | 630 | callback: (args, value) => { |
| 611 | 631 | if (!value || typeof value !== 'string') { |
| 612 | 632 | return ''; |
| 613 | 633 | } |
| 614 | 634 | |
| 615 | 635 | if (!power_user.reasoning.prefix || !power_user.reasoning.suffix) { |
| 616 | 636 | toastr.warning(t`Both prefix and suffix must be set in the Reasoning Formatting settings.`, t`Reasoning Parse`); |
| 617 | 637 | return String(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 | 646 | if (!parsedReasoning) { |
| 623 | 647 | return returnMessage ? value : ''; |
| 648 | + } | |
| 649 | + | |
| 650 | + if (returnMessage) { | |
| 651 | + return parsedReasoning.content; | |
| 624 | 652 | } |
| 625 | 653 | |
| 626 | 654 | const applyRegex = !isFalseBoolean(String(args.regex ?? '')); |
| @@ -819,16 +847,18 @@ export function removeReasoningFromString(str) { | ||
| 819 | 847 | * @property {string} reasoning Reasoning block |
| 820 | 848 | * @property {string} content Message content |
| 821 | 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 | 852 | * @returns {ParsedReasoning|null} Parsed reasoning block and message content |
| 823 | 853 | */ |
| 824 | 854 | function parseReasoningFromString(str, { strict = true } = {}) { |
| 825 | 855 | // Both prefix and suffix must be defined |
| 826 | 856 | if (!power_user.reasoning.prefix || !power_user.reasoning.suffix) { |
| 827 | 857 | return null; |
| 828 | 858 | } |
| 829 | 859 | |
| 830 | 860 | try { |
| 831 | 861 | const regex = new RegExp(`${(strict ? '^\\s*?' : '')}${escapeRegex(power_user.reasoning.prefix)}(.*?)${escapeRegex(power_user.reasoning.suffix)}`, 's'); |
| 832 | 862 | |
| 833 | 863 | let didReplace = false; |
| 834 | 864 | let reasoning = ''; |
| @@ -34,6 +34,7 @@ export const enumIcons = { | ||
| 34 | 34 | preset: '⚙️', |
| 35 | 35 | file: '📄', |
| 36 | 36 | message: '💬', |
| 37 | + reasoning: '💡', | |
| 37 | 38 | voice: '🎤', |
| 38 | 39 | server: '🖥️', |
| 39 | 40 | popup: '🗔', |