Merge branch 'staging' into custom_request_stop_string_cleanup

68c572f2ebe10382beff62df442fcd0c525dc9c4

Cohee <18619528+Cohee1207@users.noreply.github.com>

18 files changed, +242 -282Ignore whitespace
public/index.html+6 -17
@@ -2745,7 +2745,6 @@
27452745 <optgroup>
27462746 <option value="01ai">01.AI (Yi)</option>
27472747 <option value="ai21">AI21</option>
2748- <option value="blockentropy">Block Entropy</option>
27492748 <option value="claude">Claude</option>
27502749 <option value="cohere">Cohere</option>
27512750 <option value="deepseek">DeepSeek</option>
@@ -3375,20 +3374,6 @@
33753374 </select>
33763375 </div>
33773376 </form>
3378- <form id="blockentropy_form" data-source="blockentropy">
3379- <h4 data-i18n="Block Entropy API Key">Block Entropy API Key</h4>
3380- <div class="flex-container">
3381- <input id="api_key_blockentropy" name="api_key_blockentropy" class="text_pole flex1" value="" type="text" autocomplete="off">
3382- <div title="Clear your API key" data-i18n="[title]Clear your API key" class="menu_button fa-solid fa-circle-xmark clear-api-key" data-key="api_key_blockentropy"></div>
3383- </div>
3384- <div data-for="api_key_blockentropy" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you reload the page.">
3385- For privacy reasons, your API key will be hidden after you reload the page.
3386- </div>
3387- <h4 data-i18n="Select a Model">Select a Model</h4>
3388- <div class="flex-container">
3389- <select id="model_blockentropy_select" class="text_pole"></select>
3390- </div>
3391- </form>
33923377 <form id="custom_form" data-source="custom">
33933378 <h4 data-i18n="Custom Endpoint (Base URL)">Custom Endpoint (Base URL)</h4>
33943379 <div class="flex-container">
@@ -6013,6 +5998,7 @@
60135998 </div>
60145999 </div>
60156000 </div>
6001+ <i class="menu_button move_entry_button fa-solid fa-right-left" title="Move Entry to Another Lorebook" data-i18n="[title]Move Entry to Another Lorebook"></i>
60166002 <i class="menu_button duplicate_entry_button fa-solid fa-paste" title="Duplicate world info entry" data-i18n="[title]Duplicate world info entry" type="submit" value=""></i>
60176003 <i class="menu_button delete_entry_button fa-solid fa-trash-can" title="Delete world info entry" data-i18n="[title]Delete world info entry" type="submit" value=""></i>
60186004 </div>
@@ -6101,8 +6087,11 @@
61016087 <label for="content ">
61026088 <small>
61036089 <span class="alignitemscenter flex-container flexnowrap wide100p justifySpaceBetween">
61046090 <span data-i18n="Content" class="alignitemscenter flex-container flexNoGap mdhotkey_location">
6105- Content
6091+ <span data-i18n="Content" class="mdhotkey_location">
6092+ Content
6093+ </span>
6094+ <i class="editor_maximize fa-solid fa-maximize right_menu_button" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
61066095 </span>
61076096 <span>
61086097 (<span data-i18n="extension_token_counter">Tokens:</span>&nbsp; <span class="world_entry_form_token_counter" data-first-run="true">counting...</span>)&nbsp;
public/script.js+7 -1
@@ -174,7 +174,7 @@ import {
174174 saveBase64AsFile,
175175 uuidv4,
176176} from './scripts/utils.js';
177177import { debounce_timeout, IGNORE_SYMBOL } from './scripts/constants.js';
178178
179179import { doDailyExtensionUpdatesCheck, extension_settings, initExtensions, loadExtensionSettings, runGenerationInterceptors, saveMetadataDebounced } from './scripts/extensions.js';
180180import { COMMENT_NAME_DEFAULT, executeSlashCommandsOnChatInput, getSlashCommandsHelp, initDefaultSlashCommands, isExecutingCommandsFromChatInput, pauseScriptExecution, processChatSlashCommands, stopScriptExecution } from './scripts/slash-commands.js';
@@ -5301,6 +5301,12 @@ function formatMessageHistoryItem(chatItem, isInstruct, forceOutputSequence) {
53015301 const itemName = chatItem.is_user ? chatItem['name'] : characterName;
53025302 const shouldPrependName = !isNarratorType;
53035303
5304+ // If this symbol flag is set, completely ignore the message.
5305+ // This can be used to hide messages without affecting the number of messages in the chat.
5306+ if (chatItem.extra?.[IGNORE_SYMBOL]) {
5307+ return '';
5308+ }
5309+
53045310 // Don't include a name if it's empty
53055311 let textResult = chatItem?.name && shouldPrependName ? `${itemName}: ${chatItem.mes}\n` : `${chatItem.mes}\n`;
53065312
public/scripts/RossAscends-mods.js+0 -1
@@ -407,7 +407,6 @@ function RA_autoconnect(PrevApi) {
407407 || (secret_state[SECRET_KEYS.PERPLEXITY] && oai_settings.chat_completion_source == chat_completion_sources.PERPLEXITY)
408408 || (secret_state[SECRET_KEYS.GROQ] && oai_settings.chat_completion_source == chat_completion_sources.GROQ)
409409 || (secret_state[SECRET_KEYS.ZEROONEAI] && oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI)
410- || (secret_state[SECRET_KEYS.BLOCKENTROPY] && oai_settings.chat_completion_source == chat_completion_sources.BLOCKENTROPY)
411410 || (secret_state[SECRET_KEYS.NANOGPT] && oai_settings.chat_completion_source == chat_completion_sources.NANOGPT)
412411 || (secret_state[SECRET_KEYS.DEEPSEEK] && oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK)
413412 || (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)
public/scripts/constants.js+8 -0
@@ -14,3 +14,11 @@ export const debounce_timeout = {
1414 /** [5 sec] For delayed tasks, like auto-saving or completing batch operations that need a significant pause. */
1515 extended: 5000,
1616};
17+
18+/**
19+ * Used as an ephemeral key in message extra metadata.
20+ * When set, the message will be excluded from generation
21+ * prompts without affecting the number of chat messages,
22+ * which is needed to preserve world info timed effects.
23+ */
24+export const IGNORE_SYMBOL = Symbol.for('ignore');
public/scripts/extensions.js+24 -4
@@ -794,6 +794,16 @@ async function showExtensionsDetails() {
794794 };
795795
796796 /** @type {import('./popup.js').CustomPopupButton} */
797+ const updateEnabledOnlyButton = {
798+ text: t`Update enabled only`,
799+ action: async () => {
800+ requiresReload = true;
801+ await autoUpdateExtensions(false);
802+ await popup.complete(POPUP_RESULT.AFFIRMATIVE);
803+ },
804+ };
805+
806+ /** @type {import('./popup.js').CustomPopupButton} */
797807 const sortOrderButton = {
798808 text: sortByName ? t`Sort: Display Name` : t`Sort: Loading Order`,
799809 action: async () => {
@@ -809,7 +819,7 @@ async function showExtensionsDetails() {
809819 okButton: t`Close`,
810820 wide: true,
811821 large: true,
812822 customButtons: [sortOrderButton, updateEnabledOnlyButton, updateAllButton],
813823 allowVerticalScrolling: true,
814824 onClosing: async () => {
815825 if (waitingForSave) {
@@ -1196,7 +1206,7 @@ async function checkForUpdatesManual(sortFn, abortSignal) {
11961206}
11971207
11981208/**
11991209 * Checks if there are updates available for enabled 3rd-party extensions.
12001210 * @param {boolean} force Skip nag check
12011211 * @returns {Promise<any>}
12021212 */
@@ -1218,6 +1228,11 @@ async function checkForExtensionUpdates(force) {
12181228 const promises = [];
12191229
12201230 for (const [id, manifest] of Object.entries(manifests)) {
1231+ const isDisabled = extension_settings.disabledExtensions.includes(id);
1232+ if (isDisabled) {
1233+ console.debug(`Skipping extension: ${manifest.display_name} (${id}) for non-admin user`);
1234+ continue;
1235+ }
12211236 const isGlobal = getExtensionType(id) === 'global';
12221237 if (isGlobal && !isCurrentUserAdmin) {
12231238 console.debug(`Skipping global extension: ${manifest.display_name} (${id}) for non-admin user`);
@@ -1247,8 +1262,8 @@ async function checkForExtensionUpdates(force) {
12471262}
12481263
12491264/**
12501265 * Updates all enabled 3rd-party extensions that have auto-update enabled.
12511266 * @param {boolean} forceAll Force update allInclude evendisabled ifand not auto-updating
12521267 * @returns {Promise<void>}
12531268 */
12541269async function autoUpdateExtensions(forceAll) {
@@ -1260,6 +1275,11 @@ async function autoUpdateExtensions(forceAll) {
12601275 const isCurrentUserAdmin = isAdmin();
12611276 const promises = [];
12621277 for (const [id, manifest] of Object.entries(manifests)) {
1278+ const isDisabled = extension_settings.disabledExtensions.includes(id);
1279+ if (!forceAll && isDisabled) {
1280+ console.debug(`Skipping extension: ${manifest.display_name} (${id}) for non-admin user`);
1281+ continue;
1282+ }
12631283 const isGlobal = getExtensionType(id) === 'global';
12641284 if (isGlobal && !isCurrentUserAdmin) {
12651285 console.debug(`Skipping global extension: ${manifest.display_name} (${id}) for non-admin user`);
public/scripts/extensions/stable-diffusion/index.js+0 -73
@@ -77,7 +77,6 @@ const sources = {
7777 drawthings: 'drawthings',
7878 pollinations: 'pollinations',
7979 stability: 'stability',
80- blockentropy: 'blockentropy',
8180 huggingface: 'huggingface',
8281 nanogpt: 'nanogpt',
8382 bfl: 'bfl',
@@ -1300,7 +1299,6 @@ async function onModelChange() {
13001299 sources.togetherai,
13011300 sources.pollinations,
13021301 sources.stability,
1303- sources.blockentropy,
13041302 sources.huggingface,
13051303 sources.nanogpt,
13061304 sources.bfl,
@@ -1511,9 +1509,6 @@ async function loadSamplers() {
15111509 case sources.stability:
15121510 samplers = ['N/A'];
15131511 break;
1514- case sources.blockentropy:
1515- samplers = ['N/A'];
1516- break;
15171512 case sources.huggingface:
15181513 samplers = ['N/A'];
15191514 break;
@@ -1701,9 +1696,6 @@ async function loadModels() {
17011696 case sources.stability:
17021697 models = await loadStabilityModels();
17031698 break;
1704- case sources.blockentropy:
1705- models = await loadBlockEntropyModels();
1706- break;
17071699 case sources.huggingface:
17081700 models = [{ value: '', text: '<Enter Model ID above>' }];
17091701 break;
@@ -1799,26 +1791,6 @@ async function loadTogetherAIModels() {
17991791 return [];
18001792}
18011793
1802-async function loadBlockEntropyModels() {
1803- if (!secret_state[SECRET_KEYS.BLOCKENTROPY]) {
1804- console.debug('Block Entropy API key is not set.');
1805- return [];
1806- }
1807-
1808- const result = await fetch('/api/sd/blockentropy/models', {
1809- method: 'POST',
1810- headers: getRequestHeaders(),
1811- });
1812- console.log(result);
1813- if (result.ok) {
1814- const data = await result.json();
1815- console.log(data);
1816- return data;
1817- }
1818-
1819- return [];
1820-}
1821-
18221794async function loadNanoGPTModels() {
18231795 if (!secret_state[SECRET_KEYS.NANOGPT]) {
18241796 console.debug('NanoGPT API key is not set.');
@@ -2097,9 +2069,6 @@ async function loadSchedulers() {
20972069 case sources.stability:
20982070 schedulers = ['N/A'];
20992071 break;
2100- case sources.blockentropy:
2101- schedulers = ['N/A'];
2102- break;
21032072 case sources.huggingface:
21042073 schedulers = ['N/A'];
21052074 break;
@@ -2188,9 +2157,6 @@ async function loadVaes() {
21882157 case sources.stability:
21892158 vaes = ['N/A'];
21902159 break;
2191- case sources.blockentropy:
2192- vaes = ['N/A'];
2193- break;
21942160 case sources.huggingface:
21952161 vaes = ['N/A'];
21962162 break;
@@ -2757,9 +2723,6 @@ async function sendGenerationRequest(generationType, prompt, additionalNegativeP
27572723 case sources.stability:
27582724 result = await generateStabilityImage(prefixedPrompt, negativePrompt, signal);
27592725 break;
2760- case sources.blockentropy:
2761- result = await generateBlockEntropyImage(prefixedPrompt, negativePrompt, signal);
2762- break;
27632726 case sources.huggingface:
27642727 result = await generateHuggingFaceImage(prefixedPrompt, signal);
27652728 break;
@@ -2828,40 +2791,6 @@ async function generateTogetherAIImage(prompt, negativePrompt, signal) {
28282791 }
28292792}
28302793
2831-async function generateBlockEntropyImage(prompt, negativePrompt, signal) {
2832- const result = await fetch('/api/sd/blockentropy/generate', {
2833- method: 'POST',
2834- headers: getRequestHeaders(),
2835- signal: signal,
2836- body: JSON.stringify({
2837- prompt: prompt,
2838- negative_prompt: negativePrompt,
2839- model: extension_settings.sd.model,
2840- steps: extension_settings.sd.steps,
2841- width: extension_settings.sd.width,
2842- height: extension_settings.sd.height,
2843- seed: extension_settings.sd.seed >= 0 ? extension_settings.sd.seed : undefined,
2844- }),
2845- });
2846-
2847- if (result.ok) {
2848- const data = await result.json();
2849-
2850- // Default format is 'jpg'
2851- let format = 'jpg';
2852-
2853- // Check if a format is specified in the result
2854- if (data.format) {
2855- format = data.format.toLowerCase();
2856- }
2857-
2858- return { format: format, data: data.images[0] };
2859- } else {
2860- const text = await result.text();
2861- throw new Error(text);
2862- }
2863-}
2864-
28652794/**
28662795 * Generates an image using the Pollinations API.
28672796 * @param {string} prompt - The main instruction used to guide the image generation.
@@ -3845,8 +3774,6 @@ function isValidState() {
38453774 return true;
38463775 case sources.stability:
38473776 return secret_state[SECRET_KEYS.STABILITY];
3848- case sources.blockentropy:
3849- return secret_state[SECRET_KEYS.BLOCKENTROPY];
38503777 case sources.huggingface:
38513778 return secret_state[SECRET_KEYS.HUGGINGFACE];
38523779 case sources.nanogpt:
public/scripts/extensions/stable-diffusion/settings.html+1 -2
@@ -38,7 +38,6 @@
3838 <label for="sd_source" data-i18n="Source">Source</label>
3939 <select id="sd_source">
4040 <option value="bfl">BFL (Black Forest Labs)</option>
41- <option value="blockentropy">Block Entropy</option>
4241 <option value="comfy">ComfyUI</option>
4342 <option value="drawthings">DrawThings HTTP API</option>
4443 <option value="extras">Extras API (deprecated)</option>
@@ -422,7 +421,7 @@
422421 </label>
423422 </div>
424423
425424 <div data-sd-source="novel,togetherai,pollinations,comfy,drawthings,vlad,auto,horde,extras,stability,blockentropy,bfl" class="marginTop5">
426425 <label for="sd_seed">
427426 <span data-i18n="Seed">Seed</span>
428427 <small data-i18n="(-1 for random)">(-1 for random)</small>
public/scripts/openai.js+8 -72
@@ -75,6 +75,7 @@ import { Popup, POPUP_RESULT } from './popup.js';
7575import { t } from './i18n.js';
7676import { ToolManager } from './tool-calling.js';
7777import { accountStorage } from './util/AccountStorage.js';
78+import { IGNORE_SYMBOL } from './constants.js';
7879
7980export {
8081 openai_messages_count,
@@ -119,7 +120,6 @@ const default_bias_presets = {
119120const max_2k = 2047;
120121const max_4k = 4095;
121122const max_8k = 8191;
122-const max_12k = 12287;
123123const max_16k = 16383;
124124const max_32k = 32767;
125125const max_64k = 65535;
@@ -182,7 +182,6 @@ export const chat_completion_sources = {
182182 PERPLEXITY: 'perplexity',
183183 GROQ: 'groq',
184184 ZEROONEAI: '01ai',
185- BLOCKENTROPY: 'blockentropy',
186185 NANOGPT: 'nanogpt',
187186 DEEPSEEK: 'deepseek',
188187};
@@ -258,7 +257,6 @@ export const settingsToUpdate = {
258257 nanogpt_model: ['#model_nanogpt_select', 'nanogpt_model', false],
259258 deepseek_model: ['#model_deepseek_select', 'deepseek_model', false],
260259 zerooneai_model: ['#model_01ai_select', 'zerooneai_model', false],
261- blockentropy_model: ['#model_blockentropy_select', 'blockentropy_model', false],
262260 custom_model: ['#custom_model_id', 'custom_model', false],
263261 custom_url: ['#custom_api_url_text', 'custom_url', false],
264262 custom_include_body: ['#custom_include_body', 'custom_include_body', false],
@@ -346,7 +344,6 @@ const default_settings = {
346344 groq_model: 'llama-3.3-70b-versatile',
347345 nanogpt_model: 'gpt-4o-mini',
348346 zerooneai_model: 'yi-large',
349- blockentropy_model: 'be-70b-base-llama3.1',
350347 deepseek_model: 'deepseek-chat',
351348 custom_model: '',
352349 custom_url: '',
@@ -427,7 +424,6 @@ const oai_settings = {
427424 groq_model: 'llama-3.1-70b-versatile',
428425 nanogpt_model: 'gpt-4o-mini',
429426 zerooneai_model: 'yi-large',
430- blockentropy_model: 'be-70b-base-llama3.1',
431427 deepseek_model: 'deepseek-chat',
432428 custom_model: '',
433429 custom_url: '',
@@ -527,6 +523,13 @@ function setOpenAIMessages(chat) {
527523 let role = chat[j]['is_user'] ? 'user' : 'assistant';
528524 let content = chat[j]['mes'];
529525
526+ // If this symbol flag is set, completely ignore the message.
527+ // This can be used to hide messages without affecting the number of messages in the chat.
528+ if (chat[j].extra?.[IGNORE_SYMBOL]) {
529+ j++;
530+ continue;
531+ }
532+
530533 // 100% legal way to send a message as system
531534 if (chat[j].extra?.type === system_message_types.NARRATOR) {
532535 role = 'system';
@@ -1637,8 +1640,6 @@ export function getChatCompletionModel(source = null) {
16371640 return oai_settings.groq_model;
16381641 case chat_completion_sources.ZEROONEAI:
16391642 return oai_settings.zerooneai_model;
1640- case chat_completion_sources.BLOCKENTROPY:
1641- return oai_settings.blockentropy_model;
16421643 case chat_completion_sources.NANOGPT:
16431644 return oai_settings.nanogpt_model;
16441645 case chat_completion_sources.DEEPSEEK:
@@ -1757,23 +1758,6 @@ function saveModelList(data) {
17571758 $('#model_01ai_select').val(oai_settings.zerooneai_model).trigger('change');
17581759 }
17591760
1760- if (oai_settings.chat_completion_source == chat_completion_sources.BLOCKENTROPY) {
1761- $('#model_blockentropy_select').empty();
1762- model_list.forEach((model) => {
1763- $('#model_blockentropy_select').append(
1764- $('<option>', {
1765- value: model.id,
1766- text: model.id,
1767- }));
1768- });
1769-
1770- if (!oai_settings.blockentropy_model && model_list.length > 0) {
1771- oai_settings.blockentropy_model = model_list[0].id;
1772- }
1773-
1774- $('#model_blockentropy_select').val(oai_settings.blockentropy_model).trigger('change');
1775- }
1776-
17771761 if (oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI) {
17781762 /** @type {HTMLSelectElement} */
17791763 const mistralModelSelect = document.querySelector('#model_mistralai_select');
@@ -3246,7 +3230,6 @@ function loadOpenAISettings(data, settings) {
32463230 oai_settings.groq_model = settings.groq_model ?? default_settings.groq_model;
32473231 oai_settings.nanogpt_model = settings.nanogpt_model ?? default_settings.nanogpt_model;
32483232 oai_settings.deepseek_model = settings.deepseek_model ?? default_settings.deepseek_model;
3249- oai_settings.blockentropy_model = settings.blockentropy_model ?? default_settings.blockentropy_model;
32503233 oai_settings.zerooneai_model = settings.zerooneai_model ?? default_settings.zerooneai_model;
32513234 oai_settings.custom_model = settings.custom_model ?? default_settings.custom_model;
32523235 oai_settings.custom_url = settings.custom_url ?? default_settings.custom_url;
@@ -3333,7 +3316,6 @@ function loadOpenAISettings(data, settings) {
33333316 $('#model_deepseek_select').val(oai_settings.deepseek_model);
33343317 $(`#model_deepseek_select option[value="${oai_settings.deepseek_model}"`).prop('selected', true);
33353318 $('#model_01ai_select').val(oai_settings.zerooneai_model);
3336- $('#model_blockentropy_select').val(oai_settings.blockentropy_model);
33373319 $('#custom_model_id').val(oai_settings.custom_model);
33383320 $('#custom_api_url_text').val(oai_settings.custom_url);
33393321 $('#openai_max_context').val(oai_settings.openai_max_context);
@@ -3613,7 +3595,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
36133595 perplexity_model: settings.perplexity_model,
36143596 groq_model: settings.groq_model,
36153597 zerooneai_model: settings.zerooneai_model,
3616- blockentropy_model: settings.blockentropy_model,
36173598 custom_model: settings.custom_model,
36183599 custom_url: settings.custom_url,
36193600 custom_include_body: settings.custom_include_body,
@@ -4322,12 +4303,6 @@ async function onModelChange() {
43224303 oai_settings.zerooneai_model = value;
43234304 }
43244305
4325- if (value && $(this).is('#model_blockentropy_select')) {
4326- console.log('Block Entropy model changed to', value);
4327- oai_settings.blockentropy_model = value;
4328- $('#blockentropy_model_id').val(value).trigger('input');
4329- }
4330-
43314306 if (value && $(this).is('#model_custom_select')) {
43324307 console.log('Custom model changed to', value);
43334308 oai_settings.custom_model = value;
@@ -4577,29 +4552,6 @@ async function onModelChange() {
45774552 oai_settings.temp_openai = Math.min(oai_max_temp, oai_settings.temp_openai);
45784553 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
45794554 }
4580- if (oai_settings.chat_completion_source === chat_completion_sources.BLOCKENTROPY) {
4581- if (oai_settings.max_context_unlocked) {
4582- $('#openai_max_context').attr('max', unlocked_max);
4583- }
4584- else if (oai_settings.blockentropy_model.includes('llama3.1')) {
4585- $('#openai_max_context').attr('max', max_16k);
4586- }
4587- else if (oai_settings.blockentropy_model.includes('72b')) {
4588- $('#openai_max_context').attr('max', max_16k);
4589- }
4590- else if (oai_settings.blockentropy_model.includes('120b')) {
4591- $('#openai_max_context').attr('max', max_12k);
4592- }
4593- else {
4594- $('#openai_max_context').attr('max', max_8k);
4595- }
4596-
4597- oai_settings.openai_max_context = Math.min(oai_settings.openai_max_context, Number($('#openai_max_context').attr('max')));
4598- $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
4599-
4600- oai_settings.temp_openai = Math.min(oai_max_temp, oai_settings.temp_openai);
4601- $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
4602- }
46034555
46044556 if (oai_settings.chat_completion_source === chat_completion_sources.NANOGPT) {
46054557 if (oai_settings.max_context_unlocked) {
@@ -4866,18 +4818,6 @@ async function onConnectButtonClick(e) {
48664818 return;
48674819 }
48684820 }
4869- if (oai_settings.chat_completion_source == chat_completion_sources.BLOCKENTROPY) {
4870- const api_key_blockentropy = String($('#api_key_blockentropy').val()).trim();
4871-
4872- if (api_key_blockentropy.length) {
4873- await writeSecret(SECRET_KEYS.BLOCKENTROPY, api_key_blockentropy);
4874- }
4875-
4876- if (!secret_state[SECRET_KEYS.BLOCKENTROPY]) {
4877- console.log('No secret key saved for Block Entropy');
4878- return;
4879- }
4880- }
48814821
48824822 startStatusLoading();
48834823 saveSettingsDebounced();
@@ -4932,9 +4872,6 @@ function toggleChatCompletionForms() {
49324872 else if (oai_settings.chat_completion_source == chat_completion_sources.CUSTOM) {
49334873 $('#model_custom_select').trigger('change');
49344874 }
4935- else if (oai_settings.chat_completion_source == chat_completion_sources.BLOCKENTROPY) {
4936- $('#model_blockentropy_select').trigger('change');
4937- }
49384875 else if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) {
49394876 $('#model_deepseek_select').trigger('change');
49404877 }
@@ -5687,7 +5624,6 @@ export function initOpenAI() {
56875624 $('#model_nanogpt_select').on('change', onModelChange);
56885625 $('#model_deepseek_select').on('change', onModelChange);
56895626 $('#model_01ai_select').on('change', onModelChange);
5690- $('#model_blockentropy_select').on('change', onModelChange);
56915627 $('#model_custom_select').on('change', onModelChange);
56925628 $('#settings_preset_openai').on('change', onSettingsPresetChange);
56935629 $('#new_oai_preset').on('click', onNewPresetClick);
public/scripts/secrets.js+0 -2
@@ -34,7 +34,6 @@ export const SECRET_KEYS = {
3434 ZEROONEAI: 'api_key_01ai',
3535 HUGGINGFACE: 'api_key_huggingface',
3636 STABILITY: 'api_key_stability',
37- BLOCKENTROPY: 'api_key_blockentropy',
3837 CUSTOM_OPENAI_TTS: 'api_key_custom_openai_tts',
3938 NANOGPT: 'api_key_nanogpt',
4039 TAVILY: 'api_key_tavily',
@@ -74,7 +73,6 @@ const INPUT_MAP = {
7473 [SECRET_KEYS.FEATHERLESS]: '#api_key_featherless',
7574 [SECRET_KEYS.ZEROONEAI]: '#api_key_01ai',
7675 [SECRET_KEYS.HUGGINGFACE]: '#api_key_huggingface',
77- [SECRET_KEYS.BLOCKENTROPY]: '#api_key_blockentropy',
7876 [SECRET_KEYS.NANOGPT]: '#api_key_nanogpt',
7977 [SECRET_KEYS.GENERIC]: '#api_key_generic',
8078 [SECRET_KEYS.DEEPSEEK]: '#api_key_deepseek',
public/scripts/slash-commands.js+0 -1
@@ -3936,7 +3936,6 @@ function getModelOptions(quiet) {
39363936 { id: 'model_groq_select', api: 'openai', type: chat_completion_sources.GROQ },
39373937 { id: 'model_nanogpt_select', api: 'openai', type: chat_completion_sources.NANOGPT },
39383938 { id: 'model_01ai_select', api: 'openai', type: chat_completion_sources.ZEROONEAI },
3939- { id: 'model_blockentropy_select', api: 'openai', type: chat_completion_sources.BLOCKENTROPY },
39403939 { id: 'model_deepseek_select', api: 'openai', type: chat_completion_sources.DEEPSEEK },
39413940 { id: 'model_novel_select', api: 'novel', type: null },
39423941 { id: 'horde_model', api: 'koboldhorde', type: null },
public/scripts/st-context.js+4 -0
@@ -83,6 +83,7 @@ import { convertCharacterBook, getWorldInfoPrompt, loadWorldInfo, reloadEditor,
8383import { ChatCompletionService, TextCompletionService } from './custom-request.js';
8484import { ConnectionManagerRequestService } from './extensions/shared.js';
8585import { updateReasoningUI, parseReasoningFromString } from './reasoning.js';
86+import { IGNORE_SYMBOL } from './constants.js';
8687
8788export function getContext() {
8889 return {
@@ -225,6 +226,9 @@ export function getContext() {
225226 parseReasoningFromString,
226227 unshallowCharacter,
227228 unshallowGroupMembers,
229+ symbols: {
230+ ignore: IGNORE_SYMBOL,
231+ },
228232 };
229233}
230234
public/scripts/templates/worldInfoKeywordHeaders.html+1 -1
@@ -1,4 +1,4 @@
11<div id="WIEntryHeaderTitlesPC" class="flex-container wide100p spaceBetween justifyCenter textAlignCenter" style="padding:0 47.5em0em;">
22 <small class="flex1" data-i18n="Title/Memo">Title/Memo</small>
33 <small style="width: calc(3.5em + 10px)" data-i18n="Strategy">Strategy</small>
44 <small style="width: calc(3.5em + 20px)" data-i18n="Position">Position</small>
public/scripts/tokenizers.js+0 -9
@@ -729,15 +729,6 @@ export function getTokenizerModel() {
729729 return yiTokenizer;
730730 }
731731
732- if (oai_settings.chat_completion_source === chat_completion_sources.BLOCKENTROPY) {
733- if (oai_settings.blockentropy_model.includes('llama3')) {
734- return llama3Tokenizer;
735- }
736- if (oai_settings.blockentropy_model.includes('miqu') || oai_settings.blockentropy_model.includes('mixtral')) {
737- return mistralTokenizer;
738- }
739- }
740-
741732 // Default to Turbo 3.5
742733 return turboTokenizer;
743734}
public/scripts/world-info.js+183 -3
@@ -2208,7 +2208,7 @@ function verifyWorldInfoSearchSortRule() {
22082208 * Use `originalWIDataKeyMap` to find the correct value to be set.
22092209 *
22102210 * @param {object} data - The data object containing the original data entries.
22112211 * @param {stringnumber} uid - The unique identifier of the data entry.
22122212 * @param {string} key - The key of the value to be set.
22132213 * @param {any} value - The value to be set.
22142214 */
@@ -2232,7 +2232,9 @@ export function setWIOriginalDataValue(data, uid, key, value) {
22322232 */
22332233export function deleteWIOriginalDataValue(data, uid) {
22342234 if (data.originalData && Array.isArray(data.originalData.entries)) {
2235- const originalIndex = data.originalData.entries.findIndex(x => x.uid === uid);
2235+ // Non-strict equality is used here to allow for both string and number comparisons
2236+ // @eslint-disable-next-line eqeqeq
2237+ const originalIndex = data.originalData.entries.findIndex(x => x.uid == uid);
22362238
22372239 if (originalIndex >= 0) {
22382240 data.originalData.entries.splice(originalIndex, 1);
@@ -2680,8 +2682,10 @@ export async function getWorldEntry(name, data, entry) {
26802682 $(counter).text(numberOfTokens);
26812683 }, debounce_timeout.relaxed);
26822684
2685+ const contentInputId = `world_entry_content_${entry.uid}`;
26832686 const contentInput = template.find('textarea[name="content"]');
26842687 contentInput.data('uid', entry.uid);
2688+ contentInput.attr('id', contentInputId);
26852689 contentInput.on('input', async function (_, { skipCount } = {}) {
26862690 const uid = $(this).data('uid');
26872691 const value = $(this).val();
@@ -2698,7 +2702,9 @@ export async function getWorldEntry(name, data, entry) {
26982702 countTokensDebounced(counter, value);
26992703 });
27002704 contentInput.val(entry.content).trigger('input', { skipCount: true });
2701- //initScrollHeight(contentInput);
2705+
2706+ const contentExpandButton = template.find('.editor_maximize');
2707+ contentExpandButton.attr('data-for', contentInputId);
27022708
27032709 template.find('.inline-drawer-toggle').on('click', function () {
27042710 if (counter.data('first-run')) {
@@ -3139,6 +3145,84 @@ export async function getWorldEntry(name, data, entry) {
31393145 updateEditor(navigation_option.previous);
31403146 });
31413147
3148+ // move button
3149+ const moveButton = template.find('.move_entry_button');
3150+ moveButton.attr('data-uid', entry.uid);
3151+ moveButton.attr('data-current-world', name);
3152+ moveButton.on('click', async function (e) {
3153+ e.stopPropagation();
3154+ const sourceUid = $(this).attr('data-uid');
3155+ const sourceWorld = $(this).attr('data-current-world');
3156+ const sourceWorldInfo = await loadWorldInfo(sourceWorld);
3157+ if (!sourceWorldInfo) {
3158+ return;
3159+ }
3160+ const sourceName = sourceWorldInfo.entries[sourceUid]?.comment;
3161+ if (sourceName === undefined) {
3162+ return;
3163+ }
3164+
3165+ const select = document.createElement('select');
3166+ select.id = 'move_entry_target_select';
3167+ select.classList.add('text_pole', 'wide100p', 'marginTop10');
3168+
3169+ const defaultOption = document.createElement('option');
3170+ defaultOption.value = '';
3171+ defaultOption.textContent = `-- ${t`Select Target Lorebook`} --`;
3172+ select.appendChild(defaultOption);
3173+
3174+ let selectableWorldCount = 0;
3175+ world_names.forEach(worldName => {
3176+ if (worldName !== sourceWorld) { // Exclude current world
3177+ const option = document.createElement('option');
3178+ option.value = world_names.indexOf(worldName).toString();
3179+ option.textContent = worldName;
3180+ select.appendChild(option);
3181+ selectableWorldCount++;
3182+ }
3183+ });
3184+
3185+ if (selectableWorldCount === 0) {
3186+ toastr.warning(t`There are no other lorebooks to move to.`);
3187+ return;
3188+ }
3189+
3190+ // Create wrapper div
3191+ const wrapper = document.createElement('div');
3192+ wrapper.textContent = t`Move "${sourceName}" to:`;
3193+
3194+ // Create container and append elements
3195+ const container = document.createElement('div');
3196+ container.appendChild(wrapper);
3197+ container.appendChild(select);
3198+
3199+ let selectedWorldIndex = -1;
3200+ select.addEventListener('change', function() {
3201+ selectedWorldIndex = this.value === '' ? -1 : Number(this.value);
3202+ });
3203+
3204+ const popupConfirm = await callGenericPopup(container, POPUP_TYPE.CONFIRM, '', {
3205+ okButton: t`Move`,
3206+ cancelButton: t`Cancel`,
3207+ });
3208+ if (!popupConfirm) {
3209+ return;
3210+ }
3211+
3212+ if (selectedWorldIndex === -1) {
3213+ return;
3214+ }
3215+
3216+ const selectedValue = world_names[selectedWorldIndex];
3217+
3218+ if (!selectedValue) {
3219+ toastr.warning(t`Please select a target lorebook.`);
3220+ return;
3221+ }
3222+
3223+ await moveWorldInfoEntry(sourceWorld, selectedValue, sourceUid);
3224+ });
3225+
31423226 // scan depth
31433227 const scanDepthInput = template.find('input[name="scanDepth"]');
31443228 scanDepthInput.data('uid', entry.uid);
@@ -5267,3 +5351,99 @@ jQuery(() => {
52675351 });
52685352 });
52695353});
5354+
5355+/**
5356+ * Moves a World Info entry from a source lorebook to a target lorebook.
5357+ *
5358+ * @param {string} sourceName - The name of the source lorebook file.
5359+ * @param {string} targetName - The name of the target lorebook file.
5360+ * @param {string|number} uid - The UID of the entry to move from the source lorebook.
5361+ * @returns {Promise<boolean>} True if the move was successful, false otherwise.
5362+ */
5363+export async function moveWorldInfoEntry(sourceName, targetName, uid) {
5364+ if (sourceName === targetName) {
5365+ return false;
5366+ }
5367+
5368+ if (!world_names.includes(sourceName)) {
5369+ toastr.error(t`Source lorebook '${sourceName}' not found.`);
5370+ console.error(`[WI Move] Source lorebook '${sourceName}' does not exist.`);
5371+ return false;
5372+ }
5373+
5374+ if (!world_names.includes(targetName)) {
5375+ toastr.error(t`Target lorebook '${targetName}' not found.`);
5376+ console.error(`[WI Move] Target lorebook '${targetName}' does not exist.`);
5377+ return false;
5378+ }
5379+
5380+ const entryUidString = String(uid);
5381+
5382+ try {
5383+ const sourceData = await loadWorldInfo(sourceName);
5384+ const targetData = await loadWorldInfo(targetName);
5385+
5386+ if (!sourceData || !sourceData.entries) {
5387+ toastr.error(t`Failed to load data for source lorebook '${sourceName}'.`);
5388+ console.error(`[WI Move] Could not load source data for '${sourceName}'.`);
5389+ return false;
5390+ }
5391+ if (!targetData || !targetData.entries) {
5392+ toastr.error(t`Failed to load data for target lorebook '${targetName}'.`);
5393+ console.error(`[WI Move] Could not load target data for '${targetName}'.`);
5394+ return false;
5395+ }
5396+
5397+ if (!sourceData.entries[entryUidString]) {
5398+ toastr.error(t`Entry not found in source lorebook '${sourceName}'.`);
5399+ console.error(`[WI Move] Entry UID ${entryUidString} not found in '${sourceName}'.`);
5400+ return false;
5401+ }
5402+
5403+ const entryToMove = structuredClone(sourceData.entries[entryUidString]);
5404+
5405+
5406+ const newUid = getFreeWorldEntryUid(targetData);
5407+ if (newUid === null) {
5408+ console.error(`[WI Move] Failed to get a free UID in '${targetName}'.`);
5409+ return false;
5410+ }
5411+
5412+ entryToMove.uid = newUid;
5413+ // Place the entry at the end of the target lorebook
5414+ const maxDisplayIndex = Object.values(targetData.entries).reduce((max, entry) => Math.max(max, entry.displayIndex ?? -1), -1);
5415+ entryToMove.displayIndex = maxDisplayIndex + 1;
5416+
5417+ targetData.entries[newUid] = entryToMove;
5418+
5419+ delete sourceData.entries[entryUidString];
5420+ // Remove from originalData if it exists
5421+ deleteWIOriginalDataValue(sourceData, entryUidString);
5422+ // TODO: setWIOriginalDataValue
5423+ console.debug(`[WI Move] Removed entry UID ${entryUidString} from source '${sourceName}'.`);
5424+
5425+
5426+ await saveWorldInfo(targetName, targetData, true);
5427+ console.debug(`[WI Move] Saved target lorebook '${targetName}'.`);
5428+ await saveWorldInfo(sourceName, sourceData, true);
5429+ console.debug(`[WI Move] Saved source lorebook '${sourceName}'.`);
5430+
5431+
5432+ console.log(`[WI Move] ${entryToMove.comment} moved successfully to '${targetName}'.`);
5433+
5434+ // Check if the currently viewed book in the editor is the source or target and reload it
5435+ const currentEditorBookIndex = Number($('#world_editor_select').val());
5436+ if (!isNaN(currentEditorBookIndex)) {
5437+ const currentEditorBookName = world_names[currentEditorBookIndex];
5438+ if (currentEditorBookName === sourceName || currentEditorBookName === targetName) {
5439+ reloadEditor(currentEditorBookName);
5440+ }
5441+ }
5442+
5443+ return true;
5444+ } catch (error) {
5445+ toastr.error(t`An unexpected error occurred while moving the entry: ${error.message}`);
5446+ console.error('[WI Move] Unexpected error:', error);
5447+ return false;
5448+ }
5449+}
src/constants.js+0 -1
@@ -174,7 +174,6 @@ export const CHAT_COMPLETION_SOURCES = {
174174 PERPLEXITY: 'perplexity',
175175 GROQ: 'groq',
176176 ZEROONEAI: '01ai',
177- BLOCKENTROPY: 'blockentropy',
178177 NANOGPT: 'nanogpt',
179178 DEEPSEEK: 'deepseek',
180179};
src/endpoints/backends/chat-completions.js+0 -10
@@ -50,7 +50,6 @@ const API_PERPLEXITY = 'https://api.perplexity.ai';
5050const API_GROQ = 'https://api.groq.com/openai/v1';
5151const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';
5252const API_01AI = 'https://api.lingyiwanwu.com/v1';
53-const API_BLOCKENTROPY = 'https://api.blockentropy.ai/v1';
5453const API_AI21 = 'https://api.ai21.com/studio/v1';
5554const API_NANOGPT = 'https://nano-gpt.com/api/v1';
5655const API_DEEPSEEK = 'https://api.deepseek.com/beta';
@@ -865,10 +864,6 @@ router.post('/status', async function (request, response_getstatus_openai) {
865864 api_url = API_01AI;
866865 api_key_openai = readSecret(request.user.directories, SECRET_KEYS.ZEROONEAI);
867866 headers = {};
868- } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.BLOCKENTROPY) {
869- api_url = API_BLOCKENTROPY;
870- api_key_openai = readSecret(request.user.directories, SECRET_KEYS.BLOCKENTROPY);
871- headers = {};
872867 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.NANOGPT) {
873868 api_url = API_NANOGPT;
874869 api_key_openai = readSecret(request.user.directories, SECRET_KEYS.NANOGPT);
@@ -1155,11 +1150,6 @@ router.post('/generate', function (request, response) {
11551150 apiKey = readSecret(request.user.directories, SECRET_KEYS.ZEROONEAI);
11561151 headers = {};
11571152 bodyParams = {};
1158- } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.BLOCKENTROPY) {
1159- apiUrl = API_BLOCKENTROPY;
1160- apiKey = readSecret(request.user.directories, SECRET_KEYS.BLOCKENTROPY);
1161- headers = {};
1162- bodyParams = {};
11631153 } else {
11641154 console.warn('This chat completion source is not supported yet.');
11651155 return response.status(400).send({ error: true });
src/endpoints/secrets.js+0 -1
@@ -44,7 +44,6 @@ export const SECRET_KEYS = {
4444 ZEROONEAI: 'api_key_01ai',
4545 HUGGINGFACE: 'api_key_huggingface',
4646 STABILITY: 'api_key_stability',
47- BLOCKENTROPY: 'api_key_blockentropy',
4847 CUSTOM_OPENAI_TTS: 'api_key_custom_openai_tts',
4948 TAVILY: 'api_key_tavily',
5049 NANOGPT: 'api_key_nanogpt',
src/endpoints/stable-diffusion.js+0 -84
@@ -907,89 +907,6 @@ stability.post('/generate', async (request, response) => {
907907 }
908908});
909909
910-const blockentropy = express.Router();
911-
912-blockentropy.post('/models', async (request, response) => {
913- try {
914- const key = readSecret(request.user.directories, SECRET_KEYS.BLOCKENTROPY);
915-
916- if (!key) {
917- console.warn('Block Entropy key not found.');
918- return response.sendStatus(400);
919- }
920-
921- const modelsResponse = await fetch('https://api.blockentropy.ai/sdapi/v1/sd-models', {
922- method: 'GET',
923- headers: {
924- 'Authorization': `Bearer ${key}`,
925- },
926- });
927-
928- if (!modelsResponse.ok) {
929- console.warn('Block Entropy returned an error.');
930- return response.sendStatus(500);
931- }
932-
933- const data = await modelsResponse.json();
934-
935- if (!Array.isArray(data)) {
936- console.warn('Block Entropy returned invalid data.');
937- return response.sendStatus(500);
938- }
939- const models = data.map(x => ({ value: x.name, text: x.name }));
940- return response.send(models);
941-
942- } catch (error) {
943- console.error(error);
944- return response.sendStatus(500);
945- }
946-});
947-
948-blockentropy.post('/generate', async (request, response) => {
949- try {
950- const key = readSecret(request.user.directories, SECRET_KEYS.BLOCKENTROPY);
951-
952- if (!key) {
953- console.warn('Block Entropy key not found.');
954- return response.sendStatus(400);
955- }
956-
957- console.debug('Block Entropy request:', request.body);
958-
959- const result = await fetch('https://api.blockentropy.ai/sdapi/v1/txt2img', {
960- method: 'POST',
961- body: JSON.stringify({
962- prompt: request.body.prompt,
963- negative_prompt: request.body.negative_prompt,
964- model: request.body.model,
965- steps: request.body.steps,
966- width: request.body.width,
967- height: request.body.height,
968- // Random seed if negative.
969- seed: request.body.seed >= 0 ? request.body.seed : Math.floor(Math.random() * 10_000_000),
970- }),
971- headers: {
972- 'Content-Type': 'application/json',
973- 'Authorization': `Bearer ${key}`,
974- },
975- });
976-
977- if (!result.ok) {
978- console.warn('Block Entropy returned an error.');
979- return response.sendStatus(500);
980- }
981-
982- const data = await result.json();
983- console.debug('Block Entropy response:', data);
984-
985- return response.send(data);
986- } catch (error) {
987- console.error(error);
988- return response.sendStatus(500);
989- }
990-});
991-
992-
993910const huggingface = express.Router();
994911
995912huggingface.post('/generate', async (request, response) => {
@@ -1358,7 +1275,6 @@ router.use('/together', together);
13581275router.use('/drawthings', drawthings);
13591276router.use('/pollinations', pollinations);
13601277router.use('/stability', stability);
1361-router.use('/blockentropy', blockentropy);
13621278router.use('/huggingface', huggingface);
13631279router.use('/nanogpt', nanogpt);
13641280router.use('/bfl', bfl);