Merge pull request #2683 from eugencowie/tokenizer-slash-command Add slash command to set tokenizer
Signed| @@ -52,7 +52,7 @@ import { chat_completion_sources, oai_settings, setupChatCompletionPromptManager | ||
| 52 | 52 | import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockState, togglePersonaLock, user_avatar } from './personas.js'; |
| 53 | 53 | import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js'; |
| 54 | 54 | import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; |
| 55 | 55 | import { decodeTextTokens, getAvailableTokenizers, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, selectTokenizer } from './tokenizers.js'; |
| 56 | 56 | import { debounce, delay, isFalseBoolean, isTrueBoolean, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js'; |
| 57 | 57 | import { registerVariableCommands, resolveVariable } from './variables.js'; |
| 58 | 58 | import { background_settings } from './backgrounds.js'; |
| @@ -1558,6 +1558,28 @@ export function initDefaultSlashCommands() { | ||
| 1558 | 1558 | </div> |
| 1559 | 1559 | `, |
| 1560 | 1560 | })); |
| 1561 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 1562 | + name: 'tokenizer', | |
| 1563 | + callback: selectTokenizerCallback, | |
| 1564 | + returns: 'current tokenizer', | |
| 1565 | + unnamedArgumentList: [ | |
| 1566 | + SlashCommandArgument.fromProps({ | |
| 1567 | + description: 'tokenizer name', | |
| 1568 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 1569 | + enumList: getAvailableTokenizers().map(tokenizer => | |
| 1570 | + new SlashCommandEnumValue(tokenizer.tokenizerKey, tokenizer.tokenizerName, enumTypes.enum, enumIcons.default)), | |
| 1571 | + }), | |
| 1572 | + ], | |
| 1573 | + helpString: ` | |
| 1574 | + <div> | |
| 1575 | + Selects tokenizer by name. Gets the current tokenizer if no name is provided. | |
| 1576 | + </div> | |
| 1577 | + <div> | |
| 1578 | + <strong>Available tokenizers:</strong> | |
| 1579 | + <pre><code>${getAvailableTokenizers().map(t => t.tokenizerKey).join(', ')}</code></pre> | |
| 1580 | + </div> | |
| 1581 | + `, | |
| 1582 | + })); | |
| 1561 | 1583 | |
| 1562 | 1584 | registerVariableCommands(); |
| 1563 | 1585 | } |
| @@ -3562,6 +3584,27 @@ async function setApiUrlCallback({ api = null, connect = 'true' }, url) { | ||
| 3562 | 3584 | return textgenerationwebui_settings.server_urls[type] ?? ''; |
| 3563 | 3585 | } |
| 3564 | 3586 | |
| 3587 | +async function selectTokenizerCallback(_, name) { | |
| 3588 | + if (!name) { | |
| 3589 | + return getAvailableTokenizers().find(tokenizer => tokenizer.tokenizerId === power_user.tokenizer)?.tokenizerKey ?? ''; | |
| 3590 | + } | |
| 3591 | + | |
| 3592 | + const tokenizers = getAvailableTokenizers(); | |
| 3593 | + const fuse = new Fuse(tokenizers, { keys: ['tokenizerKey', 'tokenizerName'] }); | |
| 3594 | + const result = fuse.search(name); | |
| 3595 | + | |
| 3596 | + if (result.length === 0) { | |
| 3597 | + toastr.warning(`Tokenizer "${name}" not found`); | |
| 3598 | + return ''; | |
| 3599 | + } | |
| 3600 | + | |
| 3601 | + /** @type {import('./tokenizers.js').Tokenizer} */ | |
| 3602 | + const foundTokenizer = result[0].item; | |
| 3603 | + selectTokenizer(foundTokenizer.tokenizerId); | |
| 3604 | + | |
| 3605 | + return foundTokenizer.tokenizerKey; | |
| 3606 | +} | |
| 3607 | + | |
| 3565 | 3608 | export let isExecutingCommandsFromChatInput = false; |
| 3566 | 3609 | export let commandsFromChatInputAbortController; |
| 3567 | 3610 | |
| @@ -148,9 +148,45 @@ async function resetTokenCache() { | ||
| 148 | 148 | } |
| 149 | 149 | |
| 150 | 150 | /** |
| 151 | + * @typedef {object} Tokenizer | |
| 152 | + * @property {number} tokenizerId - The id of the tokenizer option | |
| 153 | + * @property {string} tokenizerKey - Internal name/key of the tokenizer | |
| 154 | + * @property {string} tokenizerName - Human-readable detailed name of the tokenizer (as displayed in the UI) | |
| 155 | + */ | |
| 156 | + | |
| 157 | +/** | |
| 158 | + * Gets all tokenizers available to the user. | |
| 159 | + * @returns {Tokenizer[]} Tokenizer info. | |
| 160 | + */ | |
| 161 | +export function getAvailableTokenizers() { | |
| 162 | + const tokenizerOptions = $('#tokenizer').find('option').toArray(); | |
| 163 | + return tokenizerOptions.map(tokenizerOption => ({ | |
| 164 | + tokenizerId: Number(tokenizerOption.value), | |
| 165 | + tokenizerKey: Object.entries(tokenizers).find(([_, value]) => value === Number(tokenizerOption.value))[0].toLocaleLowerCase(), | |
| 166 | + tokenizerName: tokenizerOption.text, | |
| 167 | + })) | |
| 168 | +} | |
| 169 | + | |
| 170 | +/** | |
| 171 | + * Selects tokenizer if not already selected. | |
| 172 | + * @param {number} tokenizerId Tokenizer ID. | |
| 173 | + */ | |
| 174 | +export function selectTokenizer(tokenizerId) { | |
| 175 | + if (tokenizerId !== power_user.tokenizer) { | |
| 176 | + const tokenizer = getAvailableTokenizers().find(tokenizer => tokenizer.tokenizerId === tokenizerId); | |
| 177 | + if (!tokenizer) { | |
| 178 | + console.warn('Failed to find tokenizer with id', tokenizerId); | |
| 179 | + return; | |
| 180 | + } | |
| 181 | + $('#tokenizer').val(tokenizer.tokenizerId).trigger('change'); | |
| 182 | + toastr.info(`Tokenizer: "${tokenizer.tokenizerName}" selected`); | |
| 183 | + } | |
| 184 | +} | |
| 185 | + | |
| 186 | +/** | |
| 151 | 187 | * Gets the friendly name of the current tokenizer. |
| 152 | 188 | * @param {string} forApi API to get the tokenizer for. Defaults to the main API. |
| 153 | 189 | * @returns { { tokenizerName: string, tokenizerId: number } Tokenizer} Tokenizer info |
| 154 | 190 | */ |
| 155 | 191 | export function getFriendlyTokenizerName(forApi) { |
| 156 | 192 | if (!forApi) { |
| @@ -185,7 +221,9 @@ export function getFriendlyTokenizerName(forApi) { | ||
| 185 | 221 | ? tokenizers.OPENAI |
| 186 | 222 | : tokenizerId; |
| 187 | 223 | |
| 188 | - return { tokenizerName, tokenizerId }; | |
| 224 | + const tokenizerKey = Object.entries(tokenizers).find(([_, value]) => value === tokenizerId)[0].toLocaleLowerCase(); | |
| 225 | + | |
| 226 | + return { tokenizerName, tokenizerKey, tokenizerId }; | |
| 189 | 227 | } |
| 190 | 228 | |
| 191 | 229 | /** |