mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge branch 'staging' into inject-filter
This commit is contained in:
@@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -53,6 +53,7 @@
|
||||
<option data-type="anthropic" value="claude-3-opus-20240229">claude-3-opus-20240229</option>
|
||||
<option data-type="anthropic" value="claude-3-sonnet-20240229">claude-3-sonnet-20240229</option>
|
||||
<option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-exp">gemini-2.0-flash-exp</option>
|
||||
<option data-type="google" value="gemini-1.5-flash">gemini-1.5-flash</option>
|
||||
<option data-type="google" value="gemini-1.5-flash-latest">gemini-1.5-flash-latest</option>
|
||||
<option data-type="google" value="gemini-1.5-flash-001">gemini-1.5-flash-001</option>
|
||||
@@ -62,6 +63,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>
|
||||
|
@@ -697,6 +697,11 @@ async function moduleWorker() {
|
||||
return;
|
||||
}
|
||||
|
||||
// If using LLM api then check if streamingProcessor is finished to avoid sending multiple requests to the API
|
||||
if (extension_settings.expressions.api === EXPRESSION_API.llm && context.streamingProcessor && !context.streamingProcessor.isFinished) {
|
||||
return;
|
||||
}
|
||||
|
||||
// API is busy
|
||||
if (inApiCall) {
|
||||
console.debug('Classification API is busy');
|
||||
@@ -995,6 +1000,11 @@ function sampleClassifyText(text) {
|
||||
// Replace macros, remove asterisks and quotes
|
||||
let result = substituteParams(text).replace(/[*"]/g, '');
|
||||
|
||||
// If using LLM api there is no need to check length of characters
|
||||
if (extension_settings.expressions.api === EXPRESSION_API.llm) {
|
||||
return result.trim();
|
||||
}
|
||||
|
||||
const SAMPLE_THRESHOLD = 500;
|
||||
const HALF_SAMPLE_THRESHOLD = SAMPLE_THRESHOLD / 2;
|
||||
|
||||
|
@@ -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) {
|
||||
|
0
public/scripts/extensions/third-party/.gitkeep
vendored
Normal file
0
public/scripts/extensions/third-party/.gitkeep
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -448,6 +448,5 @@ export class FilterHelper {
|
||||
for (const cache of Object.values(this.fuzzySearchCaches)) {
|
||||
cache.resultMap.clear();
|
||||
}
|
||||
console.log('All fuzzy search caches cleared');
|
||||
}
|
||||
}
|
||||
|
@@ -1265,7 +1265,7 @@ 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' }));
|
||||
.map((x) => ({ item: x, id: characters.indexOf(x), type: 'character' }));
|
||||
|
||||
if (doFilter) {
|
||||
candidates = groupCandidatesFilter.applyFilters(candidates);
|
||||
@@ -1275,9 +1275,10 @@ function getGroupCharacters({ doFilter, onlyMembers } = {}) {
|
||||
candidates.sort(sortMembersFn);
|
||||
} else {
|
||||
const useFilterOrder = doFilter && !!$('#rm_group_filter').val();
|
||||
sortEntitiesList(candidates, useFilterOrder);
|
||||
sortEntitiesList(candidates, useFilterOrder, groupCandidatesFilter);
|
||||
}
|
||||
|
||||
groupCandidatesFilter.clearFuzzySearchCaches();
|
||||
return candidates;
|
||||
}
|
||||
|
||||
|
@@ -1182,7 +1182,8 @@ async function preparePromptsForChatCompletion({ Scenario, charPersonality, name
|
||||
|
||||
// Apply character-specific main prompt
|
||||
const systemPrompt = prompts.get('main') ?? null;
|
||||
if (systemPromptOverride && systemPrompt && systemPrompt.forbid_overrides !== true) {
|
||||
const isSystemPromptDisabled = promptManager.isPromptDisabledForActiveCharacter('main');
|
||||
if (systemPromptOverride && systemPrompt && systemPrompt.forbid_overrides !== true && !isSystemPromptDisabled) {
|
||||
const mainOriginalContent = systemPrompt.content;
|
||||
systemPrompt.content = systemPromptOverride;
|
||||
const mainReplacement = promptManager.preparePrompt(systemPrompt, mainOriginalContent);
|
||||
@@ -1191,7 +1192,8 @@ async function preparePromptsForChatCompletion({ Scenario, charPersonality, name
|
||||
|
||||
// Apply character-specific jailbreak
|
||||
const jailbreakPrompt = prompts.get('jailbreak') ?? null;
|
||||
if (jailbreakPromptOverride && jailbreakPrompt && jailbreakPrompt.forbid_overrides !== true) {
|
||||
const isJailbreakPromptDisabled = promptManager.isPromptDisabledForActiveCharacter('jailbreak');
|
||||
if (jailbreakPromptOverride && jailbreakPrompt && jailbreakPrompt.forbid_overrides !== true && !isJailbreakPromptDisabled) {
|
||||
const jbOriginalContent = jailbreakPrompt.content;
|
||||
jailbreakPrompt.content = jailbreakPromptOverride;
|
||||
const jbReplacement = promptManager.preparePrompt(jailbreakPrompt, jbOriginalContent);
|
||||
@@ -4081,9 +4083,9 @@ 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')) {
|
||||
} else if (value.includes('gemini-1.5-flash') || value.includes('gemini-2.0-flash-exp')) {
|
||||
$('#openai_max_context').attr('max', max_1mil);
|
||||
} else if (value.includes('gemini-1.0-pro-vision') || value === 'gemini-pro-vision') {
|
||||
$('#openai_max_context').attr('max', max_16k);
|
||||
@@ -4273,7 +4275,7 @@ async function onModelChange() {
|
||||
else if (oai_settings.groq_model.includes('llama-3.2') && oai_settings.groq_model.includes('-preview')) {
|
||||
$('#openai_max_context').attr('max', max_8k);
|
||||
}
|
||||
else if (oai_settings.groq_model.includes('llama-3.2') || oai_settings.groq_model.includes('llama-3.1')) {
|
||||
else if (oai_settings.groq_model.includes('llama-3.3') || oai_settings.groq_model.includes('llama-3.2') || oai_settings.groq_model.includes('llama-3.1')) {
|
||||
$('#openai_max_context').attr('max', max_128k);
|
||||
}
|
||||
else if (oai_settings.groq_model.includes('llama3-groq')) {
|
||||
@@ -4755,6 +4757,7 @@ export function isImageInliningSupported() {
|
||||
// gultra just isn't being offered as multimodal, thanks google.
|
||||
const visionSupportedModels = [
|
||||
'gpt-4-vision',
|
||||
'gemini-2.0-flash-exp',
|
||||
'gemini-1.5-flash',
|
||||
'gemini-1.5-flash-latest',
|
||||
'gemini-1.5-flash-001',
|
||||
@@ -4765,6 +4768,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',
|
||||
|
@@ -21,8 +21,10 @@ import { PAGINATION_TEMPLATE, debounce, delay, download, ensureImageFormatSuppor
|
||||
import { debounce_timeout } from './constants.js';
|
||||
import { FILTER_TYPES, FilterHelper } from './filters.js';
|
||||
import { selected_group } from './group-chats.js';
|
||||
import { POPUP_RESULT, POPUP_TYPE, Popup } from './popup.js';
|
||||
import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
|
||||
import { t } from './i18n.js';
|
||||
import { openWorldInfoEditor, world_names } from './world-info.js';
|
||||
import { renderTemplateAsync } from './templates.js';
|
||||
|
||||
let savePersonasPage = 0;
|
||||
const GRID_STORAGE_KEY = 'Personas_GridView';
|
||||
@@ -375,6 +377,7 @@ export function initPersona(avatarId, personaName, personaDescription) {
|
||||
position: persona_description_positions.IN_PROMPT,
|
||||
depth: DEFAULT_DEPTH,
|
||||
role: DEFAULT_ROLE,
|
||||
lorebook: '',
|
||||
};
|
||||
|
||||
saveSettingsDebounced();
|
||||
@@ -418,6 +421,7 @@ export async function convertCharacterToPersona(characterId = null) {
|
||||
position: persona_description_positions.IN_PROMPT,
|
||||
depth: DEFAULT_DEPTH,
|
||||
role: DEFAULT_ROLE,
|
||||
lorebook: '',
|
||||
};
|
||||
|
||||
// If the user is currently using this persona, update the description
|
||||
@@ -461,6 +465,7 @@ export function setPersonaDescription() {
|
||||
.val(power_user.persona_description_role)
|
||||
.find(`option[value="${power_user.persona_description_role}"]`)
|
||||
.prop('selected', String(true));
|
||||
$('#persona_lore_button').toggleClass('world_set', !!power_user.persona_description_lorebook);
|
||||
countPersonaDescriptionTokens();
|
||||
}
|
||||
|
||||
@@ -490,6 +495,7 @@ async function updatePersonaNameIfExists(avatarId, newName) {
|
||||
position: persona_description_positions.IN_PROMPT,
|
||||
depth: DEFAULT_DEPTH,
|
||||
role: DEFAULT_ROLE,
|
||||
lorebook: '',
|
||||
};
|
||||
console.log(`Created persona name for ${avatarId} as ${newName}`);
|
||||
}
|
||||
@@ -535,6 +541,7 @@ async function bindUserNameToPersona(e) {
|
||||
position: isCurrentPersona ? power_user.persona_description_position : persona_description_positions.IN_PROMPT,
|
||||
depth: isCurrentPersona ? power_user.persona_description_depth : DEFAULT_DEPTH,
|
||||
role: isCurrentPersona ? power_user.persona_description_role : DEFAULT_ROLE,
|
||||
lorebook: isCurrentPersona ? power_user.persona_description_lorebook : '',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -579,12 +586,20 @@ function selectCurrentPersona() {
|
||||
power_user.persona_description_position = descriptor.position ?? persona_description_positions.IN_PROMPT;
|
||||
power_user.persona_description_depth = descriptor.depth ?? DEFAULT_DEPTH;
|
||||
power_user.persona_description_role = descriptor.role ?? DEFAULT_ROLE;
|
||||
power_user.persona_description_lorebook = descriptor.lorebook ?? '';
|
||||
} else {
|
||||
power_user.persona_description = '';
|
||||
power_user.persona_description_position = persona_description_positions.IN_PROMPT;
|
||||
power_user.persona_description_depth = DEFAULT_DEPTH;
|
||||
power_user.persona_description_role = DEFAULT_ROLE;
|
||||
power_user.persona_descriptions[user_avatar] = { description: '', position: persona_description_positions.IN_PROMPT, depth: DEFAULT_DEPTH, role: DEFAULT_ROLE };
|
||||
power_user.persona_description_lorebook = '';
|
||||
power_user.persona_descriptions[user_avatar] = {
|
||||
description: '',
|
||||
position: persona_description_positions.IN_PROMPT,
|
||||
depth: DEFAULT_DEPTH,
|
||||
role: DEFAULT_ROLE,
|
||||
lorebook: '',
|
||||
};
|
||||
}
|
||||
|
||||
setPersonaDescription();
|
||||
@@ -652,6 +667,7 @@ async function lockPersona() {
|
||||
position: persona_description_positions.IN_PROMPT,
|
||||
depth: DEFAULT_DEPTH,
|
||||
role: DEFAULT_ROLE,
|
||||
lorebook: '',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -731,6 +747,7 @@ function onPersonaDescriptionInput() {
|
||||
position: Number($('#persona_description_position').find(':selected').val()),
|
||||
depth: Number($('#persona_depth_value').val()),
|
||||
role: Number($('#persona_depth_role').find(':selected').val()),
|
||||
lorebook: '',
|
||||
};
|
||||
power_user.persona_descriptions[user_avatar] = object;
|
||||
}
|
||||
@@ -766,6 +783,52 @@ function onPersonaDescriptionDepthRoleInput() {
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a popup to set the lorebook for the current persona.
|
||||
* @param {PointerEvent} event Click event
|
||||
*/
|
||||
async function onPersonaLoreButtonClick(event) {
|
||||
const personaName = power_user.personas[user_avatar];
|
||||
const selectedLorebook = power_user.persona_description_lorebook;
|
||||
|
||||
if (!personaName) {
|
||||
toastr.warning(t`You must bind a name to this persona before you can set a lorebook.`, t`Persona name not set`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.altKey && selectedLorebook) {
|
||||
openWorldInfoEditor(selectedLorebook);
|
||||
return;
|
||||
}
|
||||
|
||||
const template = $(await renderTemplateAsync('personaLorebook'));
|
||||
|
||||
const worldSelect = template.find('select');
|
||||
template.find('.persona_name').text(personaName);
|
||||
|
||||
for (const worldName of world_names) {
|
||||
const option = document.createElement('option');
|
||||
option.value = worldName;
|
||||
option.innerText = worldName;
|
||||
option.selected = selectedLorebook === worldName;
|
||||
worldSelect.append(option);
|
||||
}
|
||||
|
||||
worldSelect.on('change', function () {
|
||||
power_user.persona_description_lorebook = String($(this).val());
|
||||
|
||||
if (power_user.personas[user_avatar]) {
|
||||
const object = getOrCreatePersonaDescriptor();
|
||||
object.lorebook = power_user.persona_description_lorebook;
|
||||
}
|
||||
|
||||
$('#persona_lore_button').toggleClass('world_set', !!power_user.persona_description_lorebook);
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
await callGenericPopup(template, POPUP_TYPE.TEXT);
|
||||
}
|
||||
|
||||
function onPersonaDescriptionPositionInput() {
|
||||
power_user.persona_description_position = Number(
|
||||
$('#persona_description_position').find(':selected').val(),
|
||||
@@ -789,6 +852,7 @@ function getOrCreatePersonaDescriptor() {
|
||||
position: power_user.persona_description_position,
|
||||
depth: power_user.persona_description_depth,
|
||||
role: power_user.persona_description_role,
|
||||
lorebook: power_user.persona_description_lorebook,
|
||||
};
|
||||
power_user.persona_descriptions[user_avatar] = object;
|
||||
}
|
||||
@@ -1038,6 +1102,7 @@ async function duplicatePersona(avatarId) {
|
||||
position: descriptor?.position ?? persona_description_positions.IN_PROMPT,
|
||||
depth: descriptor?.depth ?? DEFAULT_DEPTH,
|
||||
role: descriptor?.role ?? DEFAULT_ROLE,
|
||||
lorebook: descriptor?.lorebook ?? '',
|
||||
};
|
||||
|
||||
await uploadUserAvatar(getUserAvatar(avatarId), newAvatarId);
|
||||
@@ -1055,6 +1120,7 @@ export function initPersonas() {
|
||||
$('#persona_description_position').on('input', onPersonaDescriptionPositionInput);
|
||||
$('#persona_depth_value').on('input', onPersonaDescriptionDepthValueInput);
|
||||
$('#persona_depth_role').on('input', onPersonaDescriptionDepthRoleInput);
|
||||
$('#persona_lore_button').on('click', onPersonaLoreButtonClick);
|
||||
$('#personas_backup').on('click', onBackupPersonas);
|
||||
$('#personas_restore').on('click', () => $('#personas_restore_input').trigger('click'));
|
||||
$('#personas_restore_input').on('change', onPersonasRestoreInput);
|
||||
|
@@ -261,6 +261,7 @@ let power_user = {
|
||||
persona_description_position: persona_description_positions.IN_PROMPT,
|
||||
persona_description_role: 0,
|
||||
persona_description_depth: 2,
|
||||
persona_description_lorebook: '',
|
||||
persona_show_notifications: true,
|
||||
persona_sort_order: 'asc',
|
||||
|
||||
@@ -472,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 = [];
|
||||
@@ -1756,7 +1757,7 @@ async function loadContextSettings() {
|
||||
} else {
|
||||
$element.val(power_user.context[control.property]);
|
||||
}
|
||||
console.log(`Setting ${$element.prop('id')} to ${power_user.context[control.property]}`);
|
||||
console.debug(`Setting ${$element.prop('id')} to ${power_user.context[control.property]}`);
|
||||
|
||||
// If the setting already exists, no need to duplicate it
|
||||
// TODO: Maybe check the power_user object for the setting instead of a flag?
|
||||
@@ -1767,7 +1768,7 @@ async function loadContextSettings() {
|
||||
} else {
|
||||
power_user.context[control.property] = value;
|
||||
}
|
||||
console.log(`Setting ${$element.prop('id')} to ${value}`);
|
||||
console.debug(`Setting ${$element.prop('id')} to ${value}`);
|
||||
if (!CSS.supports('field-sizing', 'content') && $(this).is('textarea')) {
|
||||
await resetScrollHeight($(this));
|
||||
}
|
||||
@@ -1836,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
|
||||
@@ -1850,7 +1851,6 @@ function performFuzzySearch(type, data, keys, searchValue, fuzzySearchCaches = n
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const fuse = new Fuse(data, {
|
||||
keys: keys,
|
||||
includeScore: true,
|
||||
@@ -1924,7 +1924,7 @@ export function fuzzySearchPersonas(data, searchValue, fuzzySearchCaches = null)
|
||||
const mappedData = data.map(x => ({
|
||||
key: x,
|
||||
name: power_user.personas[x] ?? '',
|
||||
description: power_user.persona_descriptions[x]?.description ?? ''
|
||||
description: power_user.persona_descriptions[x]?.description ?? '',
|
||||
}));
|
||||
|
||||
const keys = [
|
||||
@@ -2080,19 +2080,21 @@ 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
|
||||
*/
|
||||
export function sortEntitiesList(entities, forceSearch) {
|
||||
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 = forceSearch || $('#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') {
|
||||
@@ -2104,8 +2106,8 @@ export function sortEntitiesList(entities, forceSearch) {
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@@ -129,6 +129,10 @@ function setSamplerListListeners() {
|
||||
relatedDOMElement = $('#sampler_priority_block_ooba');
|
||||
}
|
||||
|
||||
if (samplerName === 'samplers_priorities') { //this is for aphrodite's sampler priority
|
||||
relatedDOMElement = $('#sampler_priority_block_aphrodite');
|
||||
}
|
||||
|
||||
if (samplerName === 'penalty_alpha') { //contrastive search only has one sampler, does it need its own block?
|
||||
relatedDOMElement = $('#contrastiveSearchBlock');
|
||||
}
|
||||
@@ -237,6 +241,11 @@ async function listSamplers(main_api, arrayOnly = false) {
|
||||
displayname = 'Ooba Sampler Priority Block';
|
||||
}
|
||||
|
||||
if (sampler === 'samplers_priorities') { //this is for aphrodite's sampler priority
|
||||
targetDOMelement = $('#sampler_priority_block_aphrodite');
|
||||
displayname = 'Aphrodite Sampler Priority Block';
|
||||
}
|
||||
|
||||
if (sampler === 'penalty_alpha') { //contrastive search only has one sampler, does it need its own block?
|
||||
targetDOMelement = $('#contrastiveSearchBlock');
|
||||
displayname = 'Contrast Search Block';
|
||||
@@ -373,6 +382,10 @@ export async function validateDisabledSamplers(redraw = false) {
|
||||
relatedDOMElement = $('#sampler_priority_block_ooba');
|
||||
}
|
||||
|
||||
if (sampler === 'samplers_priorities') { //this is for aphrodite's sampler priority
|
||||
relatedDOMElement = $('#sampler_priority_block_aphrodite');
|
||||
}
|
||||
|
||||
if (sampler === 'dry_multiplier') {
|
||||
relatedDOMElement = $('#dryBlock');
|
||||
targetDisplayType = 'block';
|
||||
|
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;
|
18
public/scripts/templates/chatLorebook.html
Normal file
18
public/scripts/templates/chatLorebook.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<div class="chat_world range-block flexFlowColumn flex-container">
|
||||
<div class="range-block-title">
|
||||
<h4 data-i18n="Chat Lorebook"><!-- This data-i18n attribute is kept for backward compatibility, use the ones below when translating -->
|
||||
<span data-i18n="Chat Lorebook for">Chat Lorebook for</span> <span class="chat_name"></span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="range-block-counter justifyLeft flex-container flexFlowColumn margin-bot-10px">
|
||||
<span data-i18n="chat_world_template_txt">
|
||||
A selected World Info will be bound to this chat. When generating an AI reply,
|
||||
it will be combined with the entries from global and character lorebooks.
|
||||
</span>
|
||||
</div>
|
||||
<div class="range-block-range wide100p">
|
||||
<select class="chat_world_info_selector wide100p">
|
||||
<option value="">--- None ---</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
18
public/scripts/templates/personaLorebook.html
Normal file
18
public/scripts/templates/personaLorebook.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<div class="persona_world range-block flexFlowColumn flex-container">
|
||||
<div class="range-block-title">
|
||||
<h4>
|
||||
<span data-i18n="PErsona Lorebook for">Persona Lorebook for</span> <span class="persona_name"></span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="range-block-counter justifyLeft flex-container flexFlowColumn margin-bot-10px">
|
||||
<span data-i18n="persona_world_template_txt">
|
||||
A selected World Info will be bound to this persona. When generating an AI reply,
|
||||
it will be combined with the entries from global, character and chat lorebooks.
|
||||
</span>
|
||||
</div>
|
||||
<div class="range-block-range wide100p">
|
||||
<select class="persona_world_info_selector wide100p">
|
||||
<option value="">--- None ---</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
@@ -20,14 +20,14 @@
|
||||
<small>
|
||||
<span data-i18n="Drag handle to reorder. Click name to rename. Click color to change display.">Drag handle to reorder. Click name to rename. Click color to change display.</span><br>
|
||||
{{#if bogus_folders}}<span data-i18n="Click on the folder icon to use this tag as a folder.">Click on the folder icon to use this tag as a folder.</span><br>{{/if}}
|
||||
<label class="checkbox flex-container alignitemscenter flexNoGap m-t-1" for="auto_sort_tags">
|
||||
<label class="checkbox_label flex-container alignItemsCenter m-t-1" for="auto_sort_tags">
|
||||
<input type="checkbox" id="auto_sort_tags" name="auto_sort_tags" {{#if auto_sort_tags}} checked{{/if}} />
|
||||
<span data-i18n="Use alphabetical sorting">
|
||||
Use alphabetical sorting
|
||||
</span>
|
||||
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]tags_sorting_desc"
|
||||
title="If enabled, tags will automatically be sorted alphabetically on creation or rename.\nIf disabled, new tags will be appended at the end.\n\nIf a tag is manually reordered by dragging, automatic sorting will be disabled.">
|
||||
</div>
|
||||
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]tags_sorting_desc"
|
||||
title="If enabled, tags will automatically be sorted alphabetically on creation or rename. If disabled, new tags will be appended at the end. If a tag is manually reordered by dragging, automatic sorting will be disabled.">
|
||||
</div>
|
||||
</label>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -16,14 +16,7 @@ import { power_user, registerDebugFunction } from './power-user.js';
|
||||
import { getEventSourceStream } from './sse-stream.js';
|
||||
import { getCurrentDreamGenModelTokenizer, getCurrentOpenRouterModelTokenizer } from './textgen-models.js';
|
||||
import { ENCODE_TOKENIZERS, TEXTGEN_TOKENIZERS, getTextTokens, tokenizers } from './tokenizers.js';
|
||||
import { getSortableDelay, onlyUnique } from './utils.js';
|
||||
|
||||
export {
|
||||
settings as textgenerationwebui_settings,
|
||||
loadTextGenSettings,
|
||||
generateTextGenWithStreaming,
|
||||
formatTextGenURL,
|
||||
};
|
||||
import { getSortableDelay, onlyUnique, arraysEqual } from './utils.js';
|
||||
|
||||
export const textgen_types = {
|
||||
OOBA: 'ooba',
|
||||
@@ -89,6 +82,22 @@ const OOBA_DEFAULT_ORDER = [
|
||||
'encoder_repetition_penalty',
|
||||
'no_repeat_ngram',
|
||||
];
|
||||
const APHRODITE_DEFAULT_ORDER = [
|
||||
'dry',
|
||||
'penalties',
|
||||
'no_repeat_ngram',
|
||||
'temperature',
|
||||
'top_nsigma',
|
||||
'top_p_top_k',
|
||||
'top_a',
|
||||
'min_p',
|
||||
'tfs',
|
||||
'eta_cutoff',
|
||||
'epsilon_cutoff',
|
||||
'typical_p',
|
||||
'quadratic',
|
||||
'xtc',
|
||||
];
|
||||
const BIAS_KEY = '#textgenerationwebui_api-settings';
|
||||
|
||||
// Maybe let it be configurable in the future?
|
||||
@@ -170,6 +179,7 @@ const settings = {
|
||||
banned_tokens: '',
|
||||
sampler_priority: OOBA_DEFAULT_ORDER,
|
||||
samplers: LLAMACPP_DEFAULT_ORDER,
|
||||
samplers_priorities: APHRODITE_DEFAULT_ORDER,
|
||||
ignore_eos_token: false,
|
||||
spaces_between_special_tokens: true,
|
||||
speculative_ngram: false,
|
||||
@@ -197,6 +207,10 @@ const settings = {
|
||||
featherless_model: '',
|
||||
};
|
||||
|
||||
export {
|
||||
settings as textgenerationwebui_settings,
|
||||
};
|
||||
|
||||
export let textgenerationwebui_banned_in_macros = [];
|
||||
|
||||
export let textgenerationwebui_presets = [];
|
||||
@@ -259,6 +273,7 @@ export const setting_names = [
|
||||
'sampler_order',
|
||||
'sampler_priority',
|
||||
'samplers',
|
||||
'samplers_priorities',
|
||||
'n',
|
||||
'logit_bias',
|
||||
'custom_model',
|
||||
@@ -327,7 +342,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 +480,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 ?? {});
|
||||
@@ -556,6 +571,20 @@ function sortOobaItemsByOrder(orderArray) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the Aphrodite sampler items by the given order.
|
||||
* @param {string[]} orderArray Sampler order array.
|
||||
*/
|
||||
function sortAphroditeItemsByOrder(orderArray) {
|
||||
console.debug('Preset samplers order: ', orderArray);
|
||||
const $container = $('#sampler_priority_container_aphrodite');
|
||||
|
||||
orderArray.forEach((name) => {
|
||||
const $item = $container.find(`[data-name="${name}"]`).detach();
|
||||
$container.append($item);
|
||||
});
|
||||
}
|
||||
|
||||
jQuery(function () {
|
||||
$('#koboldcpp_order').sortable({
|
||||
delay: getSortableDelay(),
|
||||
@@ -609,6 +638,19 @@ jQuery(function () {
|
||||
},
|
||||
});
|
||||
|
||||
$('#sampler_priority_container_aphrodite').sortable({
|
||||
delay: getSortableDelay(),
|
||||
stop: function () {
|
||||
const order = [];
|
||||
$('#sampler_priority_container_aphrodite').children().each(function () {
|
||||
order.push($(this).data('name'));
|
||||
});
|
||||
settings.samplers_priorities = order;
|
||||
console.log('Samplers reordered:', settings.samplers_priorities);
|
||||
saveSettingsDebounced();
|
||||
},
|
||||
});
|
||||
|
||||
$('#tabby_json_schema').on('input', function () {
|
||||
const json_schema_string = String($(this).val());
|
||||
|
||||
@@ -627,6 +669,13 @@ jQuery(function () {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#aphrodite_default_order').on('click', function () {
|
||||
sortAphroditeItemsByOrder(APHRODITE_DEFAULT_ORDER);
|
||||
settings.samplers_priorities = APHRODITE_DEFAULT_ORDER;
|
||||
console.log('Default samplers order loaded:', settings.samplers_priorities);
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#textgen_type').on('change', function () {
|
||||
const type = String($(this).val());
|
||||
settings.type = type;
|
||||
@@ -835,6 +884,14 @@ function setSettingByName(setting, value, trigger) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ('samplers_priorities' === setting) {
|
||||
value = Array.isArray(value) ? value : APHRODITE_DEFAULT_ORDER;
|
||||
insertMissingArrayItems(APHRODITE_DEFAULT_ORDER, value);
|
||||
sortAphroditeItemsByOrder(value);
|
||||
settings.samplers_priorities = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if ('samplers' === setting) {
|
||||
value = Array.isArray(value) ? value : LLAMACPP_DEFAULT_ORDER;
|
||||
insertMissingArrayItems(LLAMACPP_DEFAULT_ORDER, value);
|
||||
@@ -889,7 +946,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', {
|
||||
@@ -1259,6 +1316,11 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
|
||||
'nsigma': settings.nsigma,
|
||||
'custom_token_bans': toIntArray(banned_tokens),
|
||||
'no_repeat_ngram_size': settings.no_repeat_ngram_size,
|
||||
'sampler_priority': settings.type === APHRODITE && !arraysEqual(
|
||||
settings.samplers_priorities,
|
||||
APHRODITE_DEFAULT_ORDER)
|
||||
? settings.samplers_priorities
|
||||
: undefined,
|
||||
};
|
||||
|
||||
if (settings.type === OPENROUTER) {
|
||||
|
@@ -8,8 +8,6 @@ import { kai_flags } from './kai-settings.js';
|
||||
import { textgen_types, textgenerationwebui_settings as textgen_settings, getTextGenServer, getTextGenModel } from './textgen-settings.js';
|
||||
import { getCurrentDreamGenModelTokenizer, getCurrentOpenRouterModelTokenizer, openRouterModels } from './textgen-models.js';
|
||||
|
||||
const { OOBA, TABBY, KOBOLDCPP, VLLM, APHRODITE, LLAMACPP, OPENROUTER, DREAMGEN } = textgen_types;
|
||||
|
||||
export const CHARACTERS_PER_TOKEN_RATIO = 3.35;
|
||||
export const TOKENIZER_WARNING_KEY = 'tokenizationWarningShown';
|
||||
export const TOKENIZER_SUPPORTED_KEY = 'tokenizationSupported';
|
||||
@@ -52,8 +50,12 @@ export const ENCODE_TOKENIZERS = [
|
||||
//tokenizers.NERD2,
|
||||
];
|
||||
|
||||
// A list of Text Completion sources that support remote tokenization.
|
||||
export const TEXTGEN_TOKENIZERS = [OOBA, TABBY, KOBOLDCPP, LLAMACPP, VLLM, APHRODITE];
|
||||
/**
|
||||
* A list of Text Completion sources that support remote tokenization.
|
||||
* Populated in initTokenziers due to circular dependencies.
|
||||
* @type {string[]}
|
||||
*/
|
||||
export const TEXTGEN_TOKENIZERS = [];
|
||||
|
||||
const TOKENIZER_URLS = {
|
||||
[tokenizers.GPT2]: {
|
||||
@@ -287,7 +289,7 @@ export function getTokenizerBestMatch(forApi) {
|
||||
const hasTokenizerError = sessionStorage.getItem(TOKENIZER_WARNING_KEY);
|
||||
const hasValidEndpoint = sessionStorage.getItem(TOKENIZER_SUPPORTED_KEY);
|
||||
const isConnected = online_status !== 'no_connection';
|
||||
const isTokenizerSupported = TEXTGEN_TOKENIZERS.includes(textgen_settings.type) && (textgen_settings.type !== OOBA || hasValidEndpoint);
|
||||
const isTokenizerSupported = TEXTGEN_TOKENIZERS.includes(textgen_settings.type) && (textgen_settings.type !== textgen_types.OOBA || hasValidEndpoint);
|
||||
|
||||
if (!hasTokenizerError && isConnected) {
|
||||
if (forApi === 'kobold' && kai_flags.can_use_tokenization) {
|
||||
@@ -297,10 +299,10 @@ export function getTokenizerBestMatch(forApi) {
|
||||
if (forApi === 'textgenerationwebui' && isTokenizerSupported) {
|
||||
return tokenizers.API_TEXTGENERATIONWEBUI;
|
||||
}
|
||||
if (forApi === 'textgenerationwebui' && textgen_settings.type === OPENROUTER) {
|
||||
if (forApi === 'textgenerationwebui' && textgen_settings.type === textgen_types.OPENROUTER) {
|
||||
return getCurrentOpenRouterModelTokenizer();
|
||||
}
|
||||
if (forApi === 'textgenerationwebui' && textgen_settings.type === DREAMGEN) {
|
||||
if (forApi === 'textgenerationwebui' && textgen_settings.type === textgen_types.DREAMGEN) {
|
||||
return getCurrentDreamGenModelTokenizer();
|
||||
}
|
||||
}
|
||||
@@ -576,7 +578,7 @@ export function getTokenizerModel() {
|
||||
|
||||
// And for OpenRouter (if not a site model, then it's impossible to determine the tokenizer)
|
||||
if (main_api == 'openai' && oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER && oai_settings.openrouter_model ||
|
||||
main_api == 'textgenerationwebui' && textgen_settings.type === OPENROUTER && textgen_settings.openrouter_model) {
|
||||
main_api == 'textgenerationwebui' && textgen_settings.type === textgen_types.OPENROUTER && textgen_settings.openrouter_model) {
|
||||
const model = main_api == 'openai'
|
||||
? model_list.find(x => x.id === oai_settings.openrouter_model)
|
||||
: openRouterModels.find(x => x.id === textgen_settings.openrouter_model);
|
||||
@@ -652,7 +654,7 @@ export function getTokenizerModel() {
|
||||
return oai_settings.custom_model;
|
||||
}
|
||||
|
||||
if (oai_settings.chat_completion_source === chat_completion_sources.PERPLEXITY) {
|
||||
if (oai_settings.chat_completion_source === chat_completion_sources.PERPLEXITY) {
|
||||
if (oai_settings.perplexity_model.includes('llama-3') || oai_settings.perplexity_model.includes('llama3')) {
|
||||
return llama3Tokenizer;
|
||||
}
|
||||
@@ -680,7 +682,7 @@ export function getTokenizerModel() {
|
||||
return yiTokenizer;
|
||||
}
|
||||
|
||||
if (oai_settings.chat_completion_source === chat_completion_sources.BLOCKENTROPY) {
|
||||
if (oai_settings.chat_completion_source === chat_completion_sources.BLOCKENTROPY) {
|
||||
if (oai_settings.blockentropy_model.includes('llama3')) {
|
||||
return llama3Tokenizer;
|
||||
}
|
||||
@@ -1121,6 +1123,14 @@ export function decodeTextTokens(tokenizerType, ids) {
|
||||
}
|
||||
|
||||
export async function initTokenizers() {
|
||||
TEXTGEN_TOKENIZERS.push(
|
||||
textgen_types.OOBA,
|
||||
textgen_types.TABBY,
|
||||
textgen_types.KOBOLDCPP,
|
||||
textgen_types.LLAMACPP,
|
||||
textgen_types.VLLM,
|
||||
textgen_types.APHRODITE,
|
||||
);
|
||||
await loadTokenCache();
|
||||
registerDebugFunction('resetTokenCache', 'Reset token cache', 'Purges the calculated token counts. Use this if you want to force a full re-tokenization of all chats or suspect the token counts are wrong.', resetTokenCache);
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ export async function setUserControls(isEnabled) {
|
||||
* Check if the current user is an admin.
|
||||
* @returns {boolean} True if the current user is an admin
|
||||
*/
|
||||
function isAdmin() {
|
||||
export function isAdmin() {
|
||||
if (!currentUser) {
|
||||
return false;
|
||||
}
|
||||
|
@@ -2173,3 +2173,20 @@ export function getCharIndex(char) {
|
||||
if (index === -1) throw new Error(`Character not found: ${char.avatar}`);
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two arrays for equality
|
||||
* @param {any[]} a - The first array
|
||||
* @param {any[]} b - The second array
|
||||
* @returns {boolean} True if the arrays are equal, false otherwise
|
||||
*/
|
||||
export function arraysEqual(a, b) {
|
||||
if (a === b) return true;
|
||||
if (a == null || b == null) return false;
|
||||
if (a.length !== b.length) return false;
|
||||
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@@ -3548,6 +3548,11 @@ async function getCharacterLore() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (power_user.persona_description_lorebook === worldName) {
|
||||
console.debug(`[WI] Character ${name}'s world ${worldName} is already activated in persona lore! Skipping...`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const data = await loadWorldInfo(worldName);
|
||||
const newEntries = data ? Object.keys(data.entries).map((x) => data.entries[x]).map(({ uid, ...rest }) => ({ uid, world: worldName, ...rest })) : [];
|
||||
entries = entries.concat(newEntries);
|
||||
@@ -3598,11 +3603,45 @@ async function getChatLore() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
async function getPersonaLore() {
|
||||
const chatWorld = chat_metadata[METADATA_KEY];
|
||||
const personaWorld = power_user.persona_description_lorebook;
|
||||
|
||||
if (!personaWorld) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (chatWorld === personaWorld) {
|
||||
console.debug(`[WI] Persona world ${personaWorld} is already activated in chat world! Skipping...`);
|
||||
return [];
|
||||
}
|
||||
|
||||
if (selected_world_info.includes(personaWorld)) {
|
||||
console.debug(`[WI] Persona world ${personaWorld} is already activated in global world info! Skipping...`);
|
||||
return [];
|
||||
}
|
||||
|
||||
const data = await loadWorldInfo(personaWorld);
|
||||
const entries = data ? Object.keys(data.entries).map((x) => data.entries[x]).map(({ uid, ...rest }) => ({ uid, world: personaWorld, ...rest })) : [];
|
||||
|
||||
console.debug(`[WI] Persona lore has ${entries.length} entries`, [personaWorld]);
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
export async function getSortedEntries() {
|
||||
try {
|
||||
const globalLore = await getGlobalLore();
|
||||
const characterLore = await getCharacterLore();
|
||||
const chatLore = await getChatLore();
|
||||
const [
|
||||
globalLore,
|
||||
characterLore,
|
||||
chatLore,
|
||||
personaLore,
|
||||
] = await Promise.all([
|
||||
getGlobalLore(),
|
||||
getCharacterLore(),
|
||||
getChatLore(),
|
||||
getPersonaLore(),
|
||||
]);
|
||||
|
||||
let entries;
|
||||
|
||||
@@ -3622,8 +3661,8 @@ export async function getSortedEntries() {
|
||||
break;
|
||||
}
|
||||
|
||||
// Chat lore always goes first
|
||||
entries = [...chatLore.sort(sortFn), ...entries];
|
||||
// Chat lore always goes first, then persona lore, then the rest
|
||||
entries = [...chatLore.sort(sortFn), ...personaLore.sort(sortFn), ...entries];
|
||||
|
||||
// Calculate hash and parse decorators. Split maps to preserve old hashes.
|
||||
entries = entries.map((entry) => {
|
||||
@@ -4816,9 +4855,33 @@ export async function importWorldInfo(file) {
|
||||
});
|
||||
}
|
||||
|
||||
export function assignLorebookToChat() {
|
||||
/**
|
||||
* Forces the world info editor to open on a specific world.
|
||||
* @param {string} worldName The name of the world to open
|
||||
*/
|
||||
export function openWorldInfoEditor(worldName) {
|
||||
console.log(`Opening lorebook for ${worldName}`);
|
||||
if (!$('#WorldInfo').is(':visible')) {
|
||||
$('#WIDrawerIcon').trigger('click');
|
||||
}
|
||||
const index = world_names.indexOf(worldName);
|
||||
$('#world_editor_select').val(index).trigger('change');
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a lorebook to the current chat.
|
||||
* @param {PointerEvent} event Pointer event
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function assignLorebookToChat(event) {
|
||||
const selectedName = chat_metadata[METADATA_KEY];
|
||||
const template = $('#chat_world_template .chat_world').clone();
|
||||
|
||||
if (selectedName && event.altKey) {
|
||||
openWorldInfoEditor(selectedName);
|
||||
return;
|
||||
}
|
||||
|
||||
const template = $(await renderTemplateAsync('chatLorebook'));
|
||||
|
||||
const worldSelect = template.find('select');
|
||||
const chatName = template.find('.chat_name');
|
||||
@@ -4846,7 +4909,7 @@ export function assignLorebookToChat() {
|
||||
saveMetadata();
|
||||
});
|
||||
|
||||
callPopup(template, 'text');
|
||||
return callGenericPopup(template, POPUP_TYPE.TEXT);
|
||||
}
|
||||
|
||||
jQuery(() => {
|
||||
@@ -4997,11 +5060,7 @@ jQuery(() => {
|
||||
const worldName = characters[chid]?.data?.extensions?.world;
|
||||
const hasEmbed = checkEmbeddedWorld(chid);
|
||||
if (worldName && world_names.includes(worldName) && !event.shiftKey) {
|
||||
if (!$('#WorldInfo').is(':visible')) {
|
||||
$('#WIDrawerIcon').trigger('click');
|
||||
}
|
||||
const index = world_names.indexOf(worldName);
|
||||
$('#world_editor_select').val(index).trigger('change');
|
||||
openWorldInfoEditor(worldName);
|
||||
} else if (hasEmbed && !event.shiftKey) {
|
||||
await importEmbeddedWorldInfo();
|
||||
saveCharacterDebounced();
|
||||
|
Reference in New Issue
Block a user