diff --git a/.eslintrc.cjs b/.eslintrc.cjs index f0b2085d6..42978485c 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -54,6 +54,7 @@ module.exports = { }, // These scripts are loaded in HTML; tell ESLint not to complain about them being undefined globals: { + globalThis: 'readonly', ePub: 'readonly', pdfjsLib: 'readonly', toastr: 'readonly', diff --git a/public/global.d.ts b/public/global.d.ts index cb459a97b..202d48443 100644 --- a/public/global.d.ts +++ b/public/global.d.ts @@ -1,4 +1,5 @@ import libs from './lib'; +import getContext from './scripts/st-context'; // Global namespace modules declare var ai; @@ -6,7 +7,7 @@ declare var pdfjsLib; declare var ePub; declare var SillyTavern: { - getContext(): any; + getContext(): typeof getContext; llm: any; libs: typeof libs; }; diff --git a/public/jsconfig.json b/public/jsconfig.json index 8ef6e356e..5fbb79a68 100644 --- a/public/jsconfig.json +++ b/public/jsconfig.json @@ -5,7 +5,8 @@ "module": "ESNext", "moduleResolution": "node", "allowUmdGlobalAccess": true, - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "strictBindCallApply": true }, "exclude": [ "**/node_modules/**", diff --git a/public/script.js b/public/script.js index 18e8e7f16..99f673f21 100644 --- a/public/script.js +++ b/public/script.js @@ -13,7 +13,7 @@ import { default as libs, } from './lib.js'; -import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, initRossMods, shouldSendOnEnter } from './scripts/RossAscends-mods.js'; +import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, initRossMods } from './scripts/RossAscends-mods.js'; import { userStatsHandler, statMesProcess, initStats } from './scripts/stats.js'; import { generateKoboldWithStreaming, @@ -37,8 +37,6 @@ import { parseTabbyLogprobs, } from './scripts/textgen-settings.js'; -const { MANCER, TOGETHERAI, OOBA, VLLM, APHRODITE, TABBY, OLLAMA, INFERMATICAI, DREAMGEN, OPENROUTER, FEATHERLESS } = textgen_types; - import { world_info, getWorldInfoPrompt, @@ -67,9 +65,7 @@ import { getGroupChat, renameGroupMember, createNewGroupChat, - getGroupPastChats, getGroupAvatar, - openGroupChat, editGroup, deleteGroupChat, renameGroupChat, @@ -173,8 +169,8 @@ import { } from './scripts/utils.js'; import { debounce_timeout } from './scripts/constants.js'; -import { ModuleWorkerWrapper, doDailyExtensionUpdatesCheck, extension_settings, getContext, initExtensions, loadExtensionSettings, renderExtensionTemplate, renderExtensionTemplateAsync, runGenerationInterceptors, saveMetadataDebounced, writeExtensionField } from './scripts/extensions.js'; -import { COMMENT_NAME_DEFAULT, executeSlashCommands, executeSlashCommandsOnChatInput, executeSlashCommandsWithOptions, getSlashCommandsHelp, initDefaultSlashCommands, isExecutingCommandsFromChatInput, pauseScriptExecution, processChatSlashCommands, registerSlashCommand, stopScriptExecution } from './scripts/slash-commands.js'; +import { doDailyExtensionUpdatesCheck, extension_settings, initExtensions, loadExtensionSettings, runGenerationInterceptors, saveMetadataDebounced } from './scripts/extensions.js'; +import { COMMENT_NAME_DEFAULT, executeSlashCommandsOnChatInput, getSlashCommandsHelp, initDefaultSlashCommands, isExecutingCommandsFromChatInput, pauseScriptExecution, processChatSlashCommands, stopScriptExecution } from './scripts/slash-commands.js'; import { tag_map, tags, @@ -224,8 +220,8 @@ import { instruct_presets, selectContextPreset, } from './scripts/instruct-mode.js'; -import { initLocales, t, translate } from './scripts/i18n.js'; -import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, getTokenizerModel, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js'; +import { initLocales, t } from './scripts/i18n.js'; +import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js'; import { user_avatar, getUserAvatars, @@ -241,11 +237,11 @@ import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay import { loadFeatherlessModels, loadMancerModels, loadOllamaModels, loadTogetherAIModels, loadInfermaticAIModels, loadOpenRouterModels, loadVllmModels, loadAphroditeModels, loadDreamGenModels, initTextGenModels, loadTabbyModels } from './scripts/textgen-models.js'; import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat } from './scripts/chats.js'; import { getPresetManager, initPresetManager } from './scripts/preset-manager.js'; -import { MacrosParser, evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js'; +import { evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js'; import { currentUser, setUserControls } from './scripts/user.js'; import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup, fixToastrForDialogs } from './scripts/popup.js'; import { renderTemplate, renderTemplateAsync } from './scripts/templates.js'; -import { initScrapers, ScraperManager } from './scripts/scrapers.js'; +import { initScrapers } from './scripts/scrapers.js'; import { SlashCommandParser } from './scripts/slash-commands/SlashCommandParser.js'; import { SlashCommand } from './scripts/slash-commands/SlashCommand.js'; import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './scripts/slash-commands/SlashCommandArgument.js'; @@ -267,6 +263,13 @@ import { initServerHistory } from './scripts/server-history.js'; import { initSettingsSearch } from './scripts/setting-search.js'; import { initBulkEdit } from './scripts/bulk-edit.js'; import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js'; +import { getContext } from './scripts/st-context.js'; + +// API OBJECT FOR EXTERNAL WIRING +globalThis.SillyTavern = { + libs, + getContext, +}; //exporting functions and vars for mods export { @@ -426,10 +429,6 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => { } }); -// API OBJECT FOR EXTERNAL WIRING -window['SillyTavern'] = {}; -window['SillyTavern'].libs = libs; - // Event source init export const event_types = { APP_READY: 'app_ready', @@ -535,7 +534,7 @@ let displayVersion = 'SillyTavern'; let generatedPromptCache = ''; let generation_started = new Date(); -/** @type {import('scripts/char-data.js').v1CharData[]} */ +/** @type {import('./scripts/char-data.js').v1CharData[]} */ export let characters = []; export let this_chid; let saveCharactersPage = 0; @@ -811,7 +810,7 @@ export let menu_type = ''; export let selected_button = ''; //which button pressed //create pole save -let create_save = { +export let create_save = { name: '', description: '', creator_notes: '', @@ -863,7 +862,7 @@ export let amount_gen = 80; //default max length of AI generated responses export let max_context = 2048; var swipes = true; -let extension_prompts = {}; +export let extension_prompts = {}; export let main_api;// = "kobold"; //novel settings @@ -1174,7 +1173,7 @@ async function getStatusTextgen() { return resultCheckStatus(); } - if (textgen_settings.type == OOBA && textgen_settings.bypass_status_check) { + if (textgen_settings.type == textgen_types.OOBA && textgen_settings.bypass_status_check) { setOnlineStatus('Status check bypassed'); return resultCheckStatus(); } @@ -1192,34 +1191,34 @@ async function getStatusTextgen() { const data = await response.json(); - if (textgen_settings.type === MANCER) { + if (textgen_settings.type === textgen_types.MANCER) { loadMancerModels(data?.data); setOnlineStatus(textgen_settings.mancer_model); - } else if (textgen_settings.type === TOGETHERAI) { + } else if (textgen_settings.type === textgen_types.TOGETHERAI) { loadTogetherAIModels(data?.data); setOnlineStatus(textgen_settings.togetherai_model); - } else if (textgen_settings.type === OLLAMA) { + } else if (textgen_settings.type === textgen_types.OLLAMA) { loadOllamaModels(data?.data); setOnlineStatus(textgen_settings.ollama_model || 'Connected'); - } else if (textgen_settings.type === INFERMATICAI) { + } else if (textgen_settings.type === textgen_types.INFERMATICAI) { loadInfermaticAIModels(data?.data); setOnlineStatus(textgen_settings.infermaticai_model); - } else if (textgen_settings.type === DREAMGEN) { + } else if (textgen_settings.type === textgen_types.DREAMGEN) { loadDreamGenModels(data?.data); setOnlineStatus(textgen_settings.dreamgen_model); - } else if (textgen_settings.type === OPENROUTER) { + } else if (textgen_settings.type === textgen_types.OPENROUTER) { loadOpenRouterModels(data?.data); setOnlineStatus(textgen_settings.openrouter_model); - } else if (textgen_settings.type === VLLM) { + } else if (textgen_settings.type === textgen_types.VLLM) { loadVllmModels(data?.data); setOnlineStatus(textgen_settings.vllm_model); - } else if (textgen_settings.type === APHRODITE) { + } else if (textgen_settings.type === textgen_types.APHRODITE) { loadAphroditeModels(data?.data); setOnlineStatus(textgen_settings.aphrodite_model); - } else if (textgen_settings.type === FEATHERLESS) { + } else if (textgen_settings.type === textgen_types.FEATHERLESS) { loadFeatherlessModels(data?.data); setOnlineStatus(textgen_settings.featherless_model); - } else if (textgen_settings.type === TABBY) { + } else if (textgen_settings.type === textgen_types.TABBY) { loadTabbyModels(data?.data); setOnlineStatus(textgen_settings.tabby_model || data?.result); } else { @@ -5541,7 +5540,7 @@ function extractMultiSwipes(data, type) { return swipes; } - if (main_api === 'openai' || (main_api === 'textgenerationwebui' && [MANCER, VLLM, APHRODITE, TABBY, INFERMATICAI].includes(textgen_settings.type))) { + if (main_api === 'openai' || (main_api === 'textgenerationwebui' && [textgen_types.MANCER, textgen_types.VLLM, textgen_types.APHRODITE, textgen_types.TABBY, textgen_types.INFERMATICAI].includes(textgen_settings.type))) { if (!Array.isArray(data.choices)) { return swipes; } @@ -5704,7 +5703,7 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc return getMessage; } -async function saveReply(type, getMessage, fromStreaming, title, swipes) { +export async function saveReply(type, getMessage, fromStreaming, title, swipes) { if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (chat[chat.length - 1]['swipe_id'] === undefined || chat[chat.length - 1]['is_user'])) { type = 'normal'; @@ -8234,102 +8233,6 @@ async function createOrEditCharacter(e) { } } -window['SillyTavern'].getContext = function () { - return { - chat: chat, - characters: characters, - groups: groups, - name1: name1, - name2: name2, - characterId: this_chid, - groupId: selected_group, - chatId: selected_group - ? groups.find(x => x.id == selected_group)?.chat_id - : (this_chid && characters[this_chid] && characters[this_chid].chat), - getCurrentChatId: getCurrentChatId, - getRequestHeaders: getRequestHeaders, - reloadCurrentChat: reloadCurrentChat, - renameChat: renameChat, - saveSettingsDebounced: saveSettingsDebounced, - onlineStatus: online_status, - maxContext: Number(max_context), - chatMetadata: chat_metadata, - streamingProcessor, - eventSource: eventSource, - eventTypes: event_types, - addOneMessage: addOneMessage, - generate: Generate, - sendStreamingRequest: sendStreamingRequest, - sendGenerationRequest: sendGenerationRequest, - stopGeneration: stopGeneration, - getTokenCount: getTokenCount, - extensionPrompts: extension_prompts, - setExtensionPrompt: setExtensionPrompt, - updateChatMetadata: updateChatMetadata, - saveChat: saveChatConditional, - openCharacterChat: openCharacterChat, - openGroupChat: openGroupChat, - saveMetadata: saveMetadata, - sendSystemMessage: sendSystemMessage, - activateSendButtons, - deactivateSendButtons, - saveReply, - substituteParams, - substituteParamsExtended, - SlashCommandParser, - SlashCommand, - SlashCommandArgument, - SlashCommandNamedArgument, - ARGUMENT_TYPE, - executeSlashCommandsWithOptions, - /** @deprecated Use SlashCommandParser.addCommandObject() instead */ - registerSlashCommand: registerSlashCommand, - /** @deprecated Use executeSlashCommandWithOptions instead */ - executeSlashCommands: executeSlashCommands, - timestampToMoment: timestampToMoment, - /** @deprecated Handlebars for extensions are no longer supported. */ - registerHelper: () => { }, - registerMacro: MacrosParser.registerMacro.bind(MacrosParser), - unregisterMacro: MacrosParser.unregisterMacro.bind(MacrosParser), - registerFunctionTool: ToolManager.registerFunctionTool.bind(ToolManager), - unregisterFunctionTool: ToolManager.unregisterFunctionTool.bind(ToolManager), - isToolCallingSupported: ToolManager.isToolCallingSupported.bind(ToolManager), - canPerformToolCalls: ToolManager.canPerformToolCalls.bind(ToolManager), - registerDebugFunction: registerDebugFunction, - /** @deprecated Use renderExtensionTemplateAsync instead. */ - renderExtensionTemplate: renderExtensionTemplate, - renderExtensionTemplateAsync: renderExtensionTemplateAsync, - registerDataBankScraper: ScraperManager.registerDataBankScraper, - /** @deprecated Use callGenericPopup or Popup instead. */ - callPopup: callPopup, - callGenericPopup: callGenericPopup, - showLoader: showLoader, - hideLoader: hideLoader, - mainApi: main_api, - extensionSettings: extension_settings, - ModuleWorkerWrapper: ModuleWorkerWrapper, - getTokenizerModel: getTokenizerModel, - generateQuietPrompt: generateQuietPrompt, - writeExtensionField: writeExtensionField, - getThumbnailUrl: getThumbnailUrl, - selectCharacterById: selectCharacterById, - messageFormatting: messageFormatting, - shouldSendOnEnter: shouldSendOnEnter, - isMobile: isMobile, - t: t, - translate: translate, - tags: tags, - tagMap: tag_map, - menuType: menu_type, - createCharacterData: create_save, - /** @deprecated Legacy snake-case naming, compatibility with old extensions */ - event_types: event_types, - Popup: Popup, - POPUP_TYPE: POPUP_TYPE, - POPUP_RESULT: POPUP_RESULT, - }; -}; - /** * Formats a counter for a swipe view. * @param {number} current The current number of items. diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js index e9a45f540..9eeb98a24 100644 --- a/public/scripts/extensions.js +++ b/public/scripts/extensions.js @@ -5,6 +5,7 @@ import { showLoader } from './loader.js'; import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; import { renderTemplate, renderTemplateAsync } from './templates.js'; import { isSubsetOf, setValueByPath } from './utils.js'; +import { getContext } from './st-context.js'; export { getContext, getApiUrl, @@ -174,7 +175,6 @@ const extension_settings = { let modules = []; let activeExtensions = new Set(); -const getContext = () => window['SillyTavern'].getContext(); const getApiUrl = () => extension_settings.apiUrl; let connectedToApi = false; diff --git a/public/scripts/st-context.js b/public/scripts/st-context.js new file mode 100644 index 000000000..4f0abdd8d --- /dev/null +++ b/public/scripts/st-context.js @@ -0,0 +1,171 @@ +import { + activateSendButtons, + addOneMessage, + callPopup, + characters, + chat, + chat_metadata, + create_save, + deactivateSendButtons, + event_types, + eventSource, + extension_prompts, + Generate, + generateQuietPrompt, + getCurrentChatId, + getRequestHeaders, + getThumbnailUrl, + main_api, + max_context, + menu_type, + messageFormatting, + name1, + name2, + online_status, + openCharacterChat, + reloadCurrentChat, + renameChat, + saveChatConditional, + saveMetadata, + saveReply, + saveSettingsDebounced, + selectCharacterById, + sendGenerationRequest, + sendStreamingRequest, + sendSystemMessage, + setExtensionPrompt, + stopGeneration, + streamingProcessor, + substituteParams, + substituteParamsExtended, + this_chid, + updateChatMetadata, +} from '../script.js'; +import { + extension_settings, + ModuleWorkerWrapper, + renderExtensionTemplate, + renderExtensionTemplateAsync, + writeExtensionField, +} from './extensions.js'; +import { groups, openGroupChat, selected_group } from './group-chats.js'; +import { t, translate } from './i18n.js'; +import { hideLoader, showLoader } from './loader.js'; +import { MacrosParser } from './macros.js'; +import { oai_settings } from './openai.js'; +import { callGenericPopup, Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js'; +import { power_user, registerDebugFunction } from './power-user.js'; +import { isMobile, shouldSendOnEnter } from './RossAscends-mods.js'; +import { ScraperManager } from './scrapers.js'; +import { executeSlashCommands, executeSlashCommandsWithOptions, registerSlashCommand } from './slash-commands.js'; +import { SlashCommand } from './slash-commands/SlashCommand.js'; +import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; +import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; +import { tag_map, tags } from './tags.js'; +import { textgenerationwebui_settings } from './textgen-settings.js'; +import { getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js'; +import { ToolManager } from './tool-calling.js'; +import { timestampToMoment } from './utils.js'; + +export function getContext() { + return { + chat, + characters, + groups, + name1, + name2, + characterId: this_chid, + groupId: selected_group, + chatId: selected_group + ? groups.find(x => x.id == selected_group)?.chat_id + : (characters[this_chid]?.chat), + getCurrentChatId, + getRequestHeaders, + reloadCurrentChat, + renameChat, + saveSettingsDebounced, + onlineStatus: online_status, + maxContext: Number(max_context), + chatMetadata: chat_metadata, + streamingProcessor, + eventSource, + eventTypes: event_types, + addOneMessage, + generate: Generate, + sendStreamingRequest, + sendGenerationRequest, + stopGeneration, + /** @deprecated Use getTokenCountAsync instead */ + getTokenCount, + getTokenCountAsync, + extensionPrompts: extension_prompts, + setExtensionPrompt, + updateChatMetadata, + saveChat: saveChatConditional, + openCharacterChat, + openGroupChat, + saveMetadata, + sendSystemMessage, + activateSendButtons, + deactivateSendButtons, + saveReply, + substituteParams, + substituteParamsExtended, + SlashCommandParser, + SlashCommand, + SlashCommandArgument, + SlashCommandNamedArgument, + ARGUMENT_TYPE, + executeSlashCommandsWithOptions, + /** @deprecated Use SlashCommandParser.addCommandObject() instead */ + registerSlashCommand, + /** @deprecated Use executeSlashCommandWithOptions instead */ + executeSlashCommands, + timestampToMoment, + /** @deprecated Handlebars for extensions are no longer supported. */ + registerHelper: () => { }, + registerMacro: MacrosParser.registerMacro.bind(MacrosParser), + unregisterMacro: MacrosParser.unregisterMacro.bind(MacrosParser), + registerFunctionTool: ToolManager.registerFunctionTool.bind(ToolManager), + unregisterFunctionTool: ToolManager.unregisterFunctionTool.bind(ToolManager), + isToolCallingSupported: ToolManager.isToolCallingSupported.bind(ToolManager), + canPerformToolCalls: ToolManager.canPerformToolCalls.bind(ToolManager), + registerDebugFunction, + /** @deprecated Use renderExtensionTemplateAsync instead. */ + renderExtensionTemplate, + renderExtensionTemplateAsync, + registerDataBankScraper: ScraperManager.registerDataBankScraper.bind(ScraperManager), + /** @deprecated Use callGenericPopup or Popup instead. */ + callPopup, + callGenericPopup, + showLoader, + hideLoader, + mainApi: main_api, + extensionSettings: extension_settings, + ModuleWorkerWrapper, + getTokenizerModel, + generateQuietPrompt, + writeExtensionField, + getThumbnailUrl, + selectCharacterById, + messageFormatting, + shouldSendOnEnter, + isMobile, + t, + translate, + tags, + tagMap: tag_map, + menuType: menu_type, + createCharacterData: create_save, + /** @deprecated Legacy snake-case naming, compatibility with old extensions */ + event_types: event_types, + Popup, + POPUP_TYPE, + POPUP_RESULT, + chatCompletionSettings: oai_settings, + textCompletionSettings: textgenerationwebui_settings, + powerUserSettings: power_user, + }; +} + +export default getContext; diff --git a/public/scripts/textgen-settings.js b/public/scripts/textgen-settings.js index 5f1c684d0..19c270e49 100644 --- a/public/scripts/textgen-settings.js +++ b/public/scripts/textgen-settings.js @@ -18,13 +18,6 @@ import { getCurrentDreamGenModelTokenizer, getCurrentOpenRouterModelTokenizer } import { ENCODE_TOKENIZERS, TEXTGEN_TOKENIZERS, getTextTokens, tokenizers } from './tokenizers.js'; import { getSortableDelay, onlyUnique } from './utils.js'; -export { - settings as textgenerationwebui_settings, - loadTextGenSettings, - generateTextGenWithStreaming, - formatTextGenURL, -}; - export const textgen_types = { OOBA: 'ooba', MANCER: 'mancer', @@ -197,6 +190,10 @@ const settings = { featherless_model: '', }; +export { + settings as textgenerationwebui_settings, +}; + export let textgenerationwebui_banned_in_macros = []; export let textgenerationwebui_presets = []; @@ -327,7 +324,7 @@ async function selectPreset(name) { saveSettingsDebounced(); } -function formatTextGenURL(value) { +export function formatTextGenURL(value) { try { const noFormatTypes = [MANCER, TOGETHERAI, INFERMATICAI, DREAMGEN, OPENROUTER]; if (noFormatTypes.includes(settings.type)) { @@ -465,7 +462,7 @@ function calculateLogitBias() { return result; } -function loadTextGenSettings(data, loadedSettings) { +export function loadTextGenSettings(data, loadedSettings) { textgenerationwebui_presets = convertPresets(data.textgenerationwebui_presets); textgenerationwebui_preset_names = data.textgenerationwebui_preset_names ?? []; Object.assign(settings, loadedSettings.textgenerationwebui_settings ?? {}); @@ -889,7 +886,7 @@ function setSettingByName(setting, value, trigger) { * @returns {Promise<(function(): AsyncGenerator<{swipes: [], text: string, toolCalls: [], logprobs: {token: string, topLogprobs: Candidate[]}|null}, void, *>)|*>} * @throws {Error} - If the response status is not OK, or from within the generator */ -async function generateTextGenWithStreaming(generate_data, signal) { +export async function generateTextGenWithStreaming(generate_data, signal) { generate_data.stream = true; const response = await fetch('/api/backends/text-completions/generate', { diff --git a/public/scripts/tokenizers.js b/public/scripts/tokenizers.js index 846442895..c25075a0f 100644 --- a/public/scripts/tokenizers.js +++ b/public/scripts/tokenizers.js @@ -8,8 +8,6 @@ import { kai_flags } from './kai-settings.js'; import { textgen_types, textgenerationwebui_settings as textgen_settings, getTextGenServer, getTextGenModel } from './textgen-settings.js'; import { getCurrentDreamGenModelTokenizer, getCurrentOpenRouterModelTokenizer, openRouterModels } from './textgen-models.js'; -const { OOBA, TABBY, KOBOLDCPP, VLLM, APHRODITE, LLAMACPP, OPENROUTER, DREAMGEN } = textgen_types; - export const CHARACTERS_PER_TOKEN_RATIO = 3.35; export const TOKENIZER_WARNING_KEY = 'tokenizationWarningShown'; export const TOKENIZER_SUPPORTED_KEY = 'tokenizationSupported'; @@ -52,8 +50,12 @@ export const ENCODE_TOKENIZERS = [ //tokenizers.NERD2, ]; -// A list of Text Completion sources that support remote tokenization. -export const TEXTGEN_TOKENIZERS = [OOBA, TABBY, KOBOLDCPP, LLAMACPP, VLLM, APHRODITE]; +/** + * A list of Text Completion sources that support remote tokenization. + * Populated in initTokenziers due to circular dependencies. + * @type {string[]} + */ +export const TEXTGEN_TOKENIZERS = []; const TOKENIZER_URLS = { [tokenizers.GPT2]: { @@ -287,7 +289,7 @@ export function getTokenizerBestMatch(forApi) { const hasTokenizerError = sessionStorage.getItem(TOKENIZER_WARNING_KEY); const hasValidEndpoint = sessionStorage.getItem(TOKENIZER_SUPPORTED_KEY); const isConnected = online_status !== 'no_connection'; - const isTokenizerSupported = TEXTGEN_TOKENIZERS.includes(textgen_settings.type) && (textgen_settings.type !== OOBA || hasValidEndpoint); + const isTokenizerSupported = TEXTGEN_TOKENIZERS.includes(textgen_settings.type) && (textgen_settings.type !== textgen_types.OOBA || hasValidEndpoint); if (!hasTokenizerError && isConnected) { if (forApi === 'kobold' && kai_flags.can_use_tokenization) { @@ -297,10 +299,10 @@ export function getTokenizerBestMatch(forApi) { if (forApi === 'textgenerationwebui' && isTokenizerSupported) { return tokenizers.API_TEXTGENERATIONWEBUI; } - if (forApi === 'textgenerationwebui' && textgen_settings.type === OPENROUTER) { + if (forApi === 'textgenerationwebui' && textgen_settings.type === textgen_types.OPENROUTER) { return getCurrentOpenRouterModelTokenizer(); } - if (forApi === 'textgenerationwebui' && textgen_settings.type === DREAMGEN) { + if (forApi === 'textgenerationwebui' && textgen_settings.type === textgen_types.DREAMGEN) { return getCurrentDreamGenModelTokenizer(); } } @@ -576,7 +578,7 @@ export function getTokenizerModel() { // And for OpenRouter (if not a site model, then it's impossible to determine the tokenizer) if (main_api == 'openai' && oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER && oai_settings.openrouter_model || - main_api == 'textgenerationwebui' && textgen_settings.type === OPENROUTER && textgen_settings.openrouter_model) { + main_api == 'textgenerationwebui' && textgen_settings.type === textgen_types.OPENROUTER && textgen_settings.openrouter_model) { const model = main_api == 'openai' ? model_list.find(x => x.id === oai_settings.openrouter_model) : openRouterModels.find(x => x.id === textgen_settings.openrouter_model); @@ -652,7 +654,7 @@ export function getTokenizerModel() { return oai_settings.custom_model; } - if (oai_settings.chat_completion_source === chat_completion_sources.PERPLEXITY) { + if (oai_settings.chat_completion_source === chat_completion_sources.PERPLEXITY) { if (oai_settings.perplexity_model.includes('llama-3') || oai_settings.perplexity_model.includes('llama3')) { return llama3Tokenizer; } @@ -680,7 +682,7 @@ export function getTokenizerModel() { return yiTokenizer; } - if (oai_settings.chat_completion_source === chat_completion_sources.BLOCKENTROPY) { + if (oai_settings.chat_completion_source === chat_completion_sources.BLOCKENTROPY) { if (oai_settings.blockentropy_model.includes('llama3')) { return llama3Tokenizer; } @@ -1121,6 +1123,14 @@ export function decodeTextTokens(tokenizerType, ids) { } export async function initTokenizers() { + TEXTGEN_TOKENIZERS.push( + textgen_types.OOBA, + textgen_types.TABBY, + textgen_types.KOBOLDCPP, + textgen_types.LLAMACPP, + textgen_types.VLLM, + textgen_types.APHRODITE, + ); await loadTokenCache(); registerDebugFunction('resetTokenCache', 'Reset token cache', 'Purges the calculated token counts. Use this if you want to force a full re-tokenization of all chats or suspect the token counts are wrong.', resetTokenCache); }