mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge branch 'staging' into persona-lorebook
This commit is contained in:
@ -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',
|
||||
|
@ -1,6 +1,8 @@
|
||||
# -- DATA CONFIGURATION --
|
||||
# Root directory for user data storage
|
||||
dataRoot: ./data
|
||||
# The maximum amount of memory that parsed character cards can use in MB
|
||||
cardsCacheCapacity: 100
|
||||
# -- SERVER CONFIGURATION --
|
||||
# Listen for incoming connections
|
||||
listen: false
|
||||
|
22
package-lock.json
generated
22
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "sillytavern",
|
||||
"version": "1.12.8",
|
||||
"version": "1.12.9",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sillytavern",
|
||||
"version": "1.12.8",
|
||||
"version": "1.12.9",
|
||||
"hasInstallScript": true,
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
@ -3591,9 +3591,9 @@
|
||||
"integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw=="
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.21.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
||||
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
|
||||
"version": "4.21.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
|
||||
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
@ -3615,7 +3615,7 @@
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.10",
|
||||
"path-to-regexp": "0.1.12",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.13.0",
|
||||
"range-parser": "~1.2.1",
|
||||
@ -3630,6 +3630,10 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/cookie": {
|
||||
@ -5725,9 +5729,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.10",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
|
||||
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/peek-readable": {
|
||||
|
@ -84,7 +84,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/SillyTavern/SillyTavern.git"
|
||||
},
|
||||
"version": "1.12.8",
|
||||
"version": "1.12.9",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"start:deno": "deno run --allow-run --allow-net --allow-read --allow-write --allow-sys --allow-env server.js",
|
||||
|
3
public/global.d.ts
vendored
3
public/global.d.ts
vendored
@ -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;
|
||||
};
|
||||
|
@ -2987,6 +2987,7 @@
|
||||
<optgroup label="Subversions">
|
||||
<option value="gemini-exp-1121">Gemini Experimental 2024-11-21</option>
|
||||
<option value="gemini-exp-1114">Gemini Experimental 2024-11-14</option>
|
||||
<option value="gemini-exp-1206">Gemini Experimental 2024-12-06</option>
|
||||
<option value="gemini-1.5-pro-exp-0801">Gemini 1.5 Pro Experiment 2024-08-01</option>
|
||||
<option value="gemini-1.5-pro-exp-0827">Gemini 1.5 Pro Experiment 2024-08-27</option>
|
||||
<option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option>
|
||||
@ -4183,7 +4184,7 @@
|
||||
</label>
|
||||
<label for="show_swipe_num_all_messages" class="checkbox_label" title="Display swipe numbers for all messages, not just the last." data-i18n="[title]Display swipe numbers for all messages, not just the last.">
|
||||
<input id="show_swipe_num_all_messages" type="checkbox" />
|
||||
<small data-i18n="Swipe # for All Messages">Swipe # for All Messages</small><i class="fa-solid fa-mobile-screen-button"></i>
|
||||
<small data-i18n="Swipe # for All Messages">Swipe # for All Messages</small>
|
||||
</label>
|
||||
<label for="hotswapEnabled" class="checkbox_label" title="In the Character Management panel, show quick selection buttons for favorited characters." data-i18n="[title]In the Character Management panel, show quick selection buttons for favorited characters">
|
||||
<input id="hotswapEnabled" type="checkbox" />
|
||||
|
@ -5,7 +5,8 @@
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"allowUmdGlobalAccess": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strictBindCallApply": true
|
||||
},
|
||||
"exclude": [
|
||||
"**/node_modules/**",
|
||||
|
180
public/script.js
180
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,
|
||||
@ -98,7 +94,6 @@ import {
|
||||
resetMovableStyles,
|
||||
forceCharacterEditorTokenize,
|
||||
applyPowerUserSettings,
|
||||
switchSwipeNumAllMessages,
|
||||
} from './scripts/power-user.js';
|
||||
|
||||
import {
|
||||
@ -174,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,
|
||||
@ -225,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,
|
||||
@ -242,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';
|
||||
@ -268,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 {
|
||||
@ -427,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',
|
||||
@ -536,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;
|
||||
@ -812,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: '',
|
||||
@ -864,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
|
||||
@ -1175,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();
|
||||
}
|
||||
@ -1193,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 {
|
||||
@ -1637,7 +1635,7 @@ export function getEntitiesList({ doFilter = false, doSort = true } = {}) {
|
||||
subEntities = entitiesFilter.applyFilters(subEntities, { clearScoreCache: false, tempOverrides: { [FILTER_TYPES.FOLDER]: FILTER_STATES.UNDEFINED }, clearFuzzySearchCaches: false });
|
||||
}
|
||||
if (doSort) {
|
||||
sortEntitiesList(subEntities);
|
||||
sortEntitiesList(subEntities, false);
|
||||
}
|
||||
entity.entities = subEntities;
|
||||
entity.hidden = subCount - subEntities.length;
|
||||
@ -1665,7 +1663,7 @@ export function getEntitiesList({ doFilter = false, doSort = true } = {}) {
|
||||
|
||||
// Sort before returning if requested
|
||||
if (doSort) {
|
||||
sortEntitiesList(entities);
|
||||
sortEntitiesList(entities, false);
|
||||
}
|
||||
entitiesFilter.clearFuzzySearchCaches();
|
||||
return entities;
|
||||
@ -2607,15 +2605,18 @@ export function substituteParams(content, _name1, _name2, _original, _group, _re
|
||||
};
|
||||
}
|
||||
|
||||
const getGroupValue = () => {
|
||||
const getGroupValue = (includeMuted) => {
|
||||
if (typeof _group === 'string') {
|
||||
return _group;
|
||||
}
|
||||
|
||||
if (selected_group) {
|
||||
const members = groups.find(x => x.id === selected_group)?.members;
|
||||
/** @type {string[]} */
|
||||
const disabledMembers = groups.find(x => x.id === selected_group)?.disabled_members ?? [];
|
||||
const isMuted = x => includeMuted ? true : !disabledMembers.includes(x);
|
||||
const names = Array.isArray(members)
|
||||
? members.map(m => characters.find(c => c.avatar === m)?.name).filter(Boolean).join(', ')
|
||||
? members.filter(isMuted).map(m => characters.find(c => c.avatar === m)?.name).filter(Boolean).join(', ')
|
||||
: '';
|
||||
return names;
|
||||
} else {
|
||||
@ -2639,7 +2640,8 @@ export function substituteParams(content, _name1, _name2, _original, _group, _re
|
||||
// Must be substituted last so that they're replaced inside {{description}}
|
||||
environment.user = _name1 ?? name1;
|
||||
environment.char = _name2 ?? name2;
|
||||
environment.group = environment.charIfNotGroup = getGroupValue();
|
||||
environment.group = environment.charIfNotGroup = getGroupValue(true);
|
||||
environment.groupNotMuted = getGroupValue(false);
|
||||
environment.model = getGeneratingModel();
|
||||
|
||||
if (additionalMacro && typeof additionalMacro === 'object') {
|
||||
@ -5538,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;
|
||||
}
|
||||
@ -5701,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';
|
||||
@ -7660,14 +7662,12 @@ export function showSwipeButtons() {
|
||||
//allows for writing individual swipe counters for past messages
|
||||
const lastSwipeCounter = $('.last_mes .swipes-counter');
|
||||
lastSwipeCounter.text(swipeCounterText).show();
|
||||
|
||||
switchSwipeNumAllMessages();
|
||||
}
|
||||
|
||||
export function hideSwipeButtons() {
|
||||
$('#chat').find('.swipe_right').hide();
|
||||
$('#chat').find('.last_mes .swipes-counter').hide();
|
||||
$('#chat').find('.swipe_left').hide();
|
||||
chatElement.find('.swipe_right').hide();
|
||||
chatElement.find('.last_mes .swipes-counter').hide();
|
||||
chatElement.find('.swipe_left').hide();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -8233,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.
|
||||
|
@ -75,6 +75,7 @@
|
||||
* @property {string} [source_url] - The source URL associated with the character.
|
||||
* @property {{full_path: string}} [chub] - The Chub-specific data associated with the character.
|
||||
* @property {{source: string[]}} [risuai] - The RisuAI-specific data associated with the character.
|
||||
* @property {{positive: string, negative: string}} [sd_character_prompt] - SD-specific data associated with the character.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
||||
|
@ -62,6 +62,7 @@
|
||||
<option data-type="google" value="gemini-1.5-flash-8b-exp-0924">gemini-1.5-flash-8b-exp-0924</option>
|
||||
<option data-type="google" value="gemini-exp-1114">gemini-exp-1114</option>
|
||||
<option data-type="google" value="gemini-exp-1121">gemini-exp-1121</option>
|
||||
<option data-type="google" value="gemini-exp-1206">gemini-exp-1206</option>
|
||||
<option data-type="google" value="gemini-1.5-pro">gemini-1.5-pro</option>
|
||||
<option data-type="google" value="gemini-1.5-pro-latest">gemini-1.5-pro-latest</option>
|
||||
<option data-type="google" value="gemini-1.5-pro-001">gemini-1.5-pro-001</option>
|
||||
|
@ -60,7 +60,6 @@ import { ToolManager } from '../../tool-calling.js';
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = 'sd';
|
||||
const UPDATE_INTERVAL = 1000;
|
||||
// This is a 1x1 transparent PNG
|
||||
const PNG_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
|
||||
const CUSTOM_STOP_EVENT = 'sd_stop_generation';
|
||||
@ -3687,8 +3686,6 @@ async function addSDGenButtons() {
|
||||
const button = $('#sd_gen');
|
||||
const dropdown = $('#sd_dropdown');
|
||||
dropdown.hide();
|
||||
button.hide();
|
||||
messageButton.hide();
|
||||
|
||||
let popper = Popper.createPopper(button.get(0), dropdown.get(0), {
|
||||
placement: 'top',
|
||||
@ -3770,18 +3767,6 @@ function isValidState() {
|
||||
}
|
||||
}
|
||||
|
||||
async function moduleWorker() {
|
||||
if (isValidState()) {
|
||||
$('#sd_gen').show();
|
||||
$('.sd_message_gen').show();
|
||||
}
|
||||
else {
|
||||
$('#sd_gen').hide();
|
||||
$('.sd_message_gen').hide();
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(moduleWorker, UPDATE_INTERVAL);
|
||||
let buttonAbortController = null;
|
||||
|
||||
async function sdMessageButton(e) {
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#sd_dropdown {
|
||||
z-index: 30000;
|
||||
backdrop-filter: blur(--SmartThemeBlurStrength);
|
||||
backdrop-filter: blur(var(--SmartThemeBlurStrength));
|
||||
}
|
||||
|
||||
#sd_comfy_open_workflow_editor {
|
||||
|
@ -93,7 +93,7 @@
|
||||
.at-settings-separator {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 18x;
|
||||
padding: 18px;
|
||||
font-weight: bold;
|
||||
border-top: 1px solid #e1e1e1; /* Grey line */
|
||||
border-bottom: 1px solid #e1e1e1; /* Grey line */
|
||||
|
@ -1265,18 +1265,20 @@ function getGroupCharacters({ doFilter, onlyMembers } = {}) {
|
||||
const thisGroup = openGroupId && groups.find((x) => x.id == openGroupId);
|
||||
let candidates = characters
|
||||
.filter((x) => isGroupMember(thisGroup, x.avatar) == onlyMembers)
|
||||
.map((x, index) => ({ item: x, id: index, type: 'character' }));
|
||||
|
||||
if (onlyMembers) {
|
||||
candidates.sort(sortMembersFn);
|
||||
} else {
|
||||
sortEntitiesList(candidates);
|
||||
}
|
||||
.map((x) => ({ item: x, id: characters.indexOf(x), type: 'character' }));
|
||||
|
||||
if (doFilter) {
|
||||
candidates = groupCandidatesFilter.applyFilters(candidates);
|
||||
}
|
||||
|
||||
if (onlyMembers) {
|
||||
candidates.sort(sortMembersFn);
|
||||
} else {
|
||||
const useFilterOrder = doFilter && !!$('#rm_group_filter').val();
|
||||
sortEntitiesList(candidates, useFilterOrder, groupCandidatesFilter);
|
||||
}
|
||||
|
||||
groupCandidatesFilter.clearFuzzySearchCaches();
|
||||
return candidates;
|
||||
}
|
||||
|
||||
|
@ -379,8 +379,8 @@ export let selected_proxy = proxies[0];
|
||||
let openai_setting_names;
|
||||
let openai_settings;
|
||||
|
||||
|
||||
let promptManager = null;
|
||||
/** @type {import('./PromptManager.js').PromptManager} */
|
||||
export let promptManager = null;
|
||||
|
||||
async function validateReverseProxy() {
|
||||
if (!oai_settings.reverse_proxy) {
|
||||
@ -4077,7 +4077,7 @@ async function onModelChange() {
|
||||
$('#openai_max_context').attr('max', max_2mil);
|
||||
} else if (value.includes('gemini-exp-1114') || value.includes('gemini-exp-1121')) {
|
||||
$('#openai_max_context').attr('max', max_32k);
|
||||
} else if (value.includes('gemini-1.5-pro')) {
|
||||
} else if (value.includes('gemini-1.5-pro') || value.includes('gemini-exp-1206')) {
|
||||
$('#openai_max_context').attr('max', max_2mil);
|
||||
} else if (value.includes('gemini-1.5-flash')) {
|
||||
$('#openai_max_context').attr('max', max_1mil);
|
||||
@ -4761,6 +4761,7 @@ export function isImageInliningSupported() {
|
||||
'gemini-1.5-flash-8b-exp-0924',
|
||||
'gemini-exp-1114',
|
||||
'gemini-exp-1121',
|
||||
'gemini-exp-1206',
|
||||
'gemini-1.0-pro-vision-latest',
|
||||
'gemini-1.5-pro',
|
||||
'gemini-1.5-pro-latest',
|
||||
|
@ -60,7 +60,6 @@ export {
|
||||
loadMovingUIState,
|
||||
collapseNewlines,
|
||||
playMessageSound,
|
||||
sortEntitiesList,
|
||||
fixMarkdown,
|
||||
power_user,
|
||||
send_on_enter_options,
|
||||
@ -474,9 +473,9 @@ function switchCompactInputArea() {
|
||||
$('#compact_input_area').prop('checked', power_user.compact_input_area);
|
||||
}
|
||||
|
||||
export function switchSwipeNumAllMessages() {
|
||||
function switchSwipeNumAllMessages() {
|
||||
$('#show_swipe_num_all_messages').prop('checked', power_user.show_swipe_num_all_messages);
|
||||
$('.mes:not(.last_mes) .swipes-counter').css('opacity', '').toggle(power_user.show_swipe_num_all_messages);
|
||||
$('body').toggleClass('swipeAllMessages', !!power_user.show_swipe_num_all_messages);
|
||||
}
|
||||
|
||||
var originalSliderValues = [];
|
||||
@ -1838,7 +1837,7 @@ async function loadContextSettings() {
|
||||
* Common function to perform fuzzy search with optional caching
|
||||
* @param {string} type - Type of search from fuzzySearchCategories
|
||||
* @param {any[]} data - Data array to search in
|
||||
* @param {Array<{name: string, weight: number, getFn?: Function}>} keys - Fuse.js keys configuration
|
||||
* @param {Array<{name: string, weight: number, getFn?: (obj: any) => string}>} keys - Fuse.js keys configuration
|
||||
* @param {string} searchValue - The search term
|
||||
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
|
||||
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
|
||||
@ -1852,7 +1851,6 @@ function performFuzzySearch(type, data, keys, searchValue, fuzzySearchCaches = n
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const fuse = new Fuse(data, {
|
||||
keys: keys,
|
||||
includeScore: true,
|
||||
@ -2081,19 +2079,22 @@ const compareFunc = (first, second) => {
|
||||
/**
|
||||
* Sorts an array of entities based on the current sort settings
|
||||
* @param {any[]} entities An array of objects with an `item` property
|
||||
* @param {boolean} forceSearch Whether to force search sorting
|
||||
* @param {import('./filters.js').FilterHelper} [filterHelper=null] Filter helper to use
|
||||
*/
|
||||
function sortEntitiesList(entities) {
|
||||
export function sortEntitiesList(entities, forceSearch, filterHelper = null) {
|
||||
filterHelper = filterHelper ?? entitiesFilter;
|
||||
if (power_user.sort_field == undefined || entities.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (power_user.sort_order === 'random') {
|
||||
const isSearch = forceSearch || $('#character_sort_order option[data-field="search"]').is(':selected');
|
||||
|
||||
if (!isSearch && power_user.sort_order === 'random') {
|
||||
shuffle(entities);
|
||||
return;
|
||||
}
|
||||
|
||||
const isSearch = $('#character_sort_order option[data-field="search"]').is(':selected');
|
||||
|
||||
entities.sort((a, b) => {
|
||||
// Sort tags/folders will always be at the top
|
||||
if (a.type === 'tag' && b.type !== 'tag') {
|
||||
@ -2105,8 +2106,8 @@ function sortEntitiesList(entities) {
|
||||
|
||||
// If we have search sorting, we take scores and use those
|
||||
if (isSearch) {
|
||||
const aScore = entitiesFilter.getScore(FILTER_TYPES.SEARCH, `${a.type}.${a.id}`);
|
||||
const bScore = entitiesFilter.getScore(FILTER_TYPES.SEARCH, `${b.type}.${b.id}`);
|
||||
const aScore = filterHelper.getScore(FILTER_TYPES.SEARCH, `${a.type}.${a.id}`);
|
||||
const bScore = filterHelper.getScore(FILTER_TYPES.SEARCH, `${b.type}.${b.id}`);
|
||||
return (aScore - bScore);
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ import { hideChatMessageRange } from './chats.js';
|
||||
import { getContext, saveMetadataDebounced } from './extensions.js';
|
||||
import { getRegexedString, regex_placement } from './extensions/regex/engine.js';
|
||||
import { findGroupMemberId, groups, is_group_generating, openGroupById, resetSelectedGroup, saveGroupChat, selected_group } from './group-chats.js';
|
||||
import { chat_completion_sources, oai_settings, setupChatCompletionPromptManager } from './openai.js';
|
||||
import { chat_completion_sources, oai_settings, promptManager } from './openai.js';
|
||||
import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockState, togglePersonaLock, user_avatar } from './personas.js';
|
||||
import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js';
|
||||
import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js';
|
||||
@ -1698,6 +1698,49 @@ export function initDefaultSlashCommands() {
|
||||
],
|
||||
helpString: 'Sets the model for the current API. Gets the current model name if no argument is provided.',
|
||||
}));
|
||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: 'getpromptentry',
|
||||
aliases: ['getpromptentries'],
|
||||
callback: getPromptEntryCallback,
|
||||
returns: 'true/false state of prompt(s)',
|
||||
namedArgumentList: [
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'identifier',
|
||||
description: 'Prompt entry identifier(s) to retrieve',
|
||||
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
|
||||
acceptsMultiple: true,
|
||||
enumProvider: () =>
|
||||
promptManager.serviceSettings.prompts
|
||||
.map(prompt => prompt.identifier)
|
||||
.map(identifier => new SlashCommandEnumValue(identifier)),
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'name',
|
||||
description: 'Prompt entry name(s) to retrieve',
|
||||
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
|
||||
acceptsMultiple: true,
|
||||
enumProvider: () =>
|
||||
promptManager.serviceSettings.prompts
|
||||
.map(prompt => prompt.name)
|
||||
.map(name => new SlashCommandEnumValue(name)),
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'return',
|
||||
description: 'Whether the return will be simple, a list, or a dict.',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
defaultValue: 'simple',
|
||||
enumList: ['simple', 'list', 'dict'],
|
||||
}),
|
||||
],
|
||||
helpString: `
|
||||
<div>
|
||||
Gets the state of the specified prompt entries.
|
||||
</div>
|
||||
<div>
|
||||
If <code>return</code> is <code>simple</code> (default) then the return will be a single value if only one value was retrieved; otherwise uses a dict (if the identifier parameter was used) or a list.
|
||||
</div>
|
||||
`,
|
||||
}));
|
||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: 'setpromptentry',
|
||||
aliases: ['setpromptentries'],
|
||||
@ -1709,7 +1752,6 @@ export function initDefaultSlashCommands() {
|
||||
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
|
||||
acceptsMultiple: true,
|
||||
enumProvider: () => {
|
||||
const promptManager = setupChatCompletionPromptManager(oai_settings);
|
||||
const prompts = promptManager.serviceSettings.prompts;
|
||||
return prompts.map(prompt => new SlashCommandEnumValue(prompt.identifier, prompt.name, enumTypes.enum));
|
||||
},
|
||||
@ -1720,7 +1762,6 @@ export function initDefaultSlashCommands() {
|
||||
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
|
||||
acceptsMultiple: true,
|
||||
enumProvider: () => {
|
||||
const promptManager = setupChatCompletionPromptManager(oai_settings);
|
||||
const prompts = promptManager.serviceSettings.prompts;
|
||||
return prompts.map(prompt => new SlashCommandEnumValue(prompt.name, prompt.identifier, enumTypes.enum));
|
||||
},
|
||||
@ -3786,6 +3827,75 @@ function modelCallback(args, model) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state of prompt entries (toggles) either via identifier/uuid or name.
|
||||
* @param {object} args Object containing arguments
|
||||
* @param {string} args.identifier Select prompt entry using an identifier (uuid)
|
||||
* @param {string} args.name Select prompt entry using name
|
||||
* @param {string} args.return The type of return value to use (simple, list, dict)
|
||||
* @returns {Object} An object containing the states of the requested prompt entries
|
||||
*/
|
||||
function getPromptEntryCallback(args) {
|
||||
const prompts = promptManager.serviceSettings.prompts;
|
||||
let returnType = args.return ?? 'simple';
|
||||
|
||||
function parseArgs(arg) {
|
||||
// Arg is already an array
|
||||
if (Array.isArray(arg)) {
|
||||
return arg;
|
||||
}
|
||||
const list = [];
|
||||
try {
|
||||
// Arg is a JSON-stringified array
|
||||
const parsedArg = JSON.parse(arg);
|
||||
list.push(...Array.isArray(parsedArg) ? parsedArg : [arg]);
|
||||
} catch {
|
||||
// Arg is a string
|
||||
list.push(arg);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
let identifiersList = parseArgs(args.identifier);
|
||||
let nameList = parseArgs(args.name);
|
||||
|
||||
// Check if identifiers exists in prompt, else remove from list
|
||||
if (identifiersList.length !== 0) {
|
||||
identifiersList = identifiersList.filter(identifier => prompts.some(prompt => prompt.identifier === identifier));
|
||||
}
|
||||
|
||||
if (nameList.length !== 0) {
|
||||
nameList.forEach(name => {
|
||||
let identifiers = prompts
|
||||
.filter(entry => entry.name === name)
|
||||
.map(entry => entry.identifier);
|
||||
identifiersList = identifiersList.concat(identifiers);
|
||||
});
|
||||
}
|
||||
|
||||
// Get the state for each prompt entry
|
||||
let promptStates = new Map();
|
||||
identifiersList.forEach(identifier => {
|
||||
const promptOrderEntry = promptManager.getPromptOrderEntry(promptManager.activeCharacter, identifier);
|
||||
if (promptOrderEntry) {
|
||||
promptStates.set(identifier, promptOrderEntry.enabled);
|
||||
}
|
||||
});
|
||||
|
||||
// If return is simple (default) but more than one prompt state was retrieved, then change return type
|
||||
if (returnType === 'simple' && promptStates.size > 1) {
|
||||
returnType = args.identifier ? 'dict' : 'list';
|
||||
}
|
||||
|
||||
const result = (() => {
|
||||
if (returnType === 'list') return [...promptStates.values()];
|
||||
if (returnType === 'dict') return Object.fromEntries(promptStates);
|
||||
return [...promptStates.values()][0];
|
||||
})();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets state of prompt entries (toggles) either via identifier/uuid or name.
|
||||
* @param {object} args Object containing arguments
|
||||
@ -3796,7 +3906,6 @@ function modelCallback(args, model) {
|
||||
*/
|
||||
function setPromptEntryCallback(args, targetState) {
|
||||
// needs promptManager to manipulate prompt entries
|
||||
const promptManager = setupChatCompletionPromptManager(oai_settings);
|
||||
const prompts = promptManager.serviceSettings.prompts;
|
||||
|
||||
function parseArgs(arg) {
|
||||
|
171
public/scripts/st-context.js
Normal file
171
public/scripts/st-context.js
Normal file
@ -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;
|
@ -21,7 +21,8 @@
|
||||
<li><tt>{{user}}</tt> – <span data-i18n="help_macros_15">your current Persona username</span></li>
|
||||
<li><tt>{{char}}</tt> – <span data-i18n="help_macros_16">the Character's name</span></li>
|
||||
<li><tt>{{char_version}}</tt> – <span data-i18n="help_macros_17">the Character's version number</span></li>
|
||||
<li><tt>{{group}}</tt> – <span data-i18n="help_macros_18">a comma-separated list of group member names or the character name in solo chats. Alias: {{charIfNotGroup}}</span></li>
|
||||
<li><tt>{{group}}</tt> – <span data-i18n="help_macros_18">a comma-separated list of group member names (including muted) or the character name in solo chats. Alias: {{charIfNotGroup}}</span></li>
|
||||
<li><tt>{{groupNotMuted}}</tt> – <span data-i18n="help_groupNotMuted">the same as {{group}}, but excludes muted members</span></li>
|
||||
<li><tt>{{model}}</tt> – <span data-i18n="help_macros_19">a text generation model name for the currently selected API. </span><b data-i18n="Can be inaccurate!">Can be inaccurate!</b></li>
|
||||
<li><tt>{{lastMessage}}</tt> – <span data-i18n="help_macros_20">the text of the latest chat message.</span></li>
|
||||
<li><tt>{{lastUserMessage}}</tt> – <span data-i18n="help_macros_lastUser">the text of the latest user chat message.</span></li>
|
||||
|
@ -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', {
|
||||
|
@ -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);
|
||||
@ -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);
|
||||
}
|
||||
|
@ -947,7 +947,7 @@ export class ToolManager {
|
||||
enumProvider: toolsEnumProvider,
|
||||
}),
|
||||
],
|
||||
callback: async (name) => {
|
||||
callback: async (_, name) => {
|
||||
if (typeof name !== 'string' || !name) {
|
||||
throw new Error('The unnamed argument must be a non-empty string.');
|
||||
}
|
||||
|
@ -1040,8 +1040,14 @@ body .panelControlBar {
|
||||
height: var(--swipeCounterHeight);
|
||||
}
|
||||
|
||||
.mes:not(.last_mes) .swipes-counter {
|
||||
opacity: 0.3;
|
||||
body:not(.swipeAllMessages) .mes:not(.last_mes) .swipes-counter {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
body.swipeAllMessages .mes:not(.last_mes) .swipes-counter {
|
||||
/* Avoid expensive DOM queries */
|
||||
opacity: 0.3 !important;
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.swipe_left {
|
||||
|
@ -280,7 +280,7 @@ async function sendMakerSuiteRequest(request, response) {
|
||||
delete generationConfig.stopSequences;
|
||||
}
|
||||
|
||||
const should_use_system_prompt = (model.includes('gemini-1.5-flash') || model.includes('gemini-1.5-pro') || model.includes('gemini-exp-1114') || model.includes('gemini-exp-1121')) && request.body.use_makersuite_sysprompt;
|
||||
const should_use_system_prompt = (model.includes('gemini-1.5-flash') || model.includes('gemini-1.5-pro') || model.startsWith('gemini-exp')) && request.body.use_makersuite_sysprompt;
|
||||
const prompt = convertGooglePrompt(request.body.messages, model, should_use_system_prompt, request.body.char_name, request.body.user_name);
|
||||
let body = {
|
||||
contents: prompt.contents,
|
||||
|
@ -14,7 +14,7 @@ import jimp from 'jimp';
|
||||
|
||||
import { AVATAR_WIDTH, AVATAR_HEIGHT } from '../constants.js';
|
||||
import { jsonParser, urlencodedParser } from '../express-common.js';
|
||||
import { deepMerge, humanizedISO8601DateTime, tryParse, extractFileFromZipBuffer, MemoryLimitedMap } from '../util.js';
|
||||
import { deepMerge, humanizedISO8601DateTime, tryParse, extractFileFromZipBuffer, MemoryLimitedMap, getConfigValue } from '../util.js';
|
||||
import { TavernCardValidator } from '../validator/TavernCardValidator.js';
|
||||
import { parse, write } from '../character-card-parser.js';
|
||||
import { readWorldInfoFile } from './worldinfo.js';
|
||||
@ -23,8 +23,9 @@ import { importRisuSprites } from './sprites.js';
|
||||
const defaultAvatarPath = './public/img/ai4.png';
|
||||
|
||||
// KV-store for parsed character data
|
||||
// 100 MB limit. Would take roughly 3000 characters to reach this limit
|
||||
const characterDataCache = new MemoryLimitedMap(1024 * 1024 * 100);
|
||||
const cacheCapacity = Number(getConfigValue('cardsCacheCapacity', 100)); // MB
|
||||
// With 100 MB limit it would take roughly 3000 characters to reach this limit
|
||||
const characterDataCache = new MemoryLimitedMap(1024 * 1024 * cacheCapacity);
|
||||
// Some Android devices require tighter memory management
|
||||
const isAndroid = process.platform === 'android';
|
||||
|
||||
|
@ -203,19 +203,19 @@ function importKoboldLiteChat(_userName, _characterName, data) {
|
||||
|
||||
/** @type {function(string): object} */
|
||||
function processKoboldMessage(msg) {
|
||||
const isUser = msg.includes(inputToken) || msg.includes(outputToken);
|
||||
const isUser = msg.includes(inputToken);
|
||||
return {
|
||||
name: isUser ? header.user_name : header.character_name,
|
||||
is_user: isUser,
|
||||
mes: msg.replace(inputToken, '').replace(outputToken, '').trim(),
|
||||
mes: msg.replaceAll(inputToken, '').replaceAll(outputToken, '').trim(),
|
||||
send_date: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
// Create the header
|
||||
const header = {
|
||||
user_name: data.savedsettings.chatname,
|
||||
character_name: data.savedsettings.chatopponent,
|
||||
user_name: String(data.savedsettings.chatname),
|
||||
character_name: String(data.savedsettings.chatopponent).split('||$||')[0],
|
||||
};
|
||||
// Format messages
|
||||
const formattedMessages = data.actions.map(processKoboldMessage);
|
||||
|
@ -67,17 +67,17 @@ router.post('/login', jsonParser, async (request, response) => {
|
||||
const user = await storage.getItem(toKey(request.body.handle));
|
||||
|
||||
if (!user) {
|
||||
console.log('Login failed: User not found');
|
||||
console.log('Login failed: User', request.body.handle, 'not found');
|
||||
return response.status(403).json({ error: 'Incorrect credentials' });
|
||||
}
|
||||
|
||||
if (!user.enabled) {
|
||||
console.log('Login failed: User is disabled');
|
||||
console.log('Login failed: User', user.handle, 'is disabled');
|
||||
return response.status(403).json({ error: 'User is disabled' });
|
||||
}
|
||||
|
||||
if (user.password && user.password !== getPasswordHash(request.body.password, user.salt)) {
|
||||
console.log('Login failed: Incorrect password');
|
||||
console.log('Login failed: Incorrect password for', user.handle);
|
||||
return response.status(403).json({ error: 'Incorrect credentials' });
|
||||
}
|
||||
|
||||
@ -88,7 +88,7 @@ router.post('/login', jsonParser, async (request, response) => {
|
||||
|
||||
await loginLimiter.delete(ip);
|
||||
request.session.handle = user.handle;
|
||||
console.log('Login successful:', user.handle, request.session);
|
||||
console.log('Login successful:', user.handle, 'from', ip, 'at', new Date().toLocaleString());
|
||||
return response.json({ handle: user.handle });
|
||||
} catch (error) {
|
||||
if (error instanceof RateLimiterRes) {
|
||||
@ -115,12 +115,12 @@ router.post('/recover-step1', jsonParser, async (request, response) => {
|
||||
const user = await storage.getItem(toKey(request.body.handle));
|
||||
|
||||
if (!user) {
|
||||
console.log('Recover step 1 failed: User not found');
|
||||
console.log('Recover step 1 failed: User', request.body.handle, 'not found');
|
||||
return response.status(404).json({ error: 'User not found' });
|
||||
}
|
||||
|
||||
if (!user.enabled) {
|
||||
console.log('Recover step 1 failed: User is disabled');
|
||||
console.log('Recover step 1 failed: User', user.handle, 'is disabled');
|
||||
return response.status(403).json({ error: 'User is disabled' });
|
||||
}
|
||||
|
||||
@ -153,12 +153,12 @@ router.post('/recover-step2', jsonParser, async (request, response) => {
|
||||
const ip = getIpFromRequest(request);
|
||||
|
||||
if (!user) {
|
||||
console.log('Recover step 2 failed: User not found');
|
||||
console.log('Recover step 2 failed: User', request.body.handle, 'not found');
|
||||
return response.status(404).json({ error: 'User not found' });
|
||||
}
|
||||
|
||||
if (!user.enabled) {
|
||||
console.log('Recover step 2 failed: User is disabled');
|
||||
console.log('Recover step 2 failed: User', user.handle, 'is disabled');
|
||||
return response.status(403).json({ error: 'User is disabled' });
|
||||
}
|
||||
|
||||
|
@ -347,6 +347,7 @@ export function convertGooglePrompt(messages, model, useSysPrompt = false, charN
|
||||
'gemini-1.5-flash-8b-exp-0924',
|
||||
'gemini-exp-1114',
|
||||
'gemini-exp-1121',
|
||||
'gemini-exp-1206',
|
||||
'gemini-1.5-pro',
|
||||
'gemini-1.5-pro-latest',
|
||||
'gemini-1.5-pro-001',
|
||||
|
@ -680,8 +680,9 @@ export class MemoryLimitedMap {
|
||||
* @param {number} maxMemoryInBytes - The maximum allowed memory in bytes for string values.
|
||||
*/
|
||||
constructor(maxMemoryInBytes) {
|
||||
if (typeof maxMemoryInBytes !== 'number' || maxMemoryInBytes <= 0) {
|
||||
throw new Error('maxMemoryInBytes must be a positive number');
|
||||
if (typeof maxMemoryInBytes !== 'number' || maxMemoryInBytes <= 0 || isNaN(maxMemoryInBytes)) {
|
||||
console.warn('Invalid maxMemoryInBytes, using a fallback value of 1 GB.');
|
||||
maxMemoryInBytes = 1024 * 1024 * 1024; // 1 GB
|
||||
}
|
||||
this.maxMemory = maxMemoryInBytes;
|
||||
this.currentMemory = 0;
|
||||
|
Reference in New Issue
Block a user