mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-02-03 12:47:35 +01:00
Merge branch 'staging' into pr/2204
This commit is contained in:
commit
78cf6e9086
1338
public/global.d.ts
vendored
1338
public/global.d.ts
vendored
File diff suppressed because it is too large
Load Diff
@ -1239,7 +1239,7 @@
|
||||
<input class="neo-range-slider" type="range" id="no_repeat_ngram_size_textgenerationwebui" name="volume" min="0" max="20" step="1">
|
||||
<input class="neo-range-input" type="number" min="0" max="20" step="1" data-for="no_repeat_ngram_size_textgenerationwebui" id="no_repeat_ngram_size_counter_textgenerationwebui">
|
||||
</div>
|
||||
<div data-newbie-hidden data-tg-type="mancer, ooba, dreamgen" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||
<div data-newbie-hidden data-tg-type="mancer, ooba, tabby, dreamgen" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||
<small data-i18n="Min Length">Min Length</small>
|
||||
<input class="neo-range-slider" type="range" id="min_length_textgenerationwebui" name="volume" min="0" max="2000" step="1" />
|
||||
<input class="neo-range-input" type="number" min="0" max="2000" step="1" data-for="min_length_textgenerationwebui" id="min_length_counter_textgenerationwebui">
|
||||
|
@ -288,6 +288,11 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace line breaks with <br> in unknown elements
|
||||
if (node instanceof HTMLUnknownElement) {
|
||||
node.innerHTML = node.innerHTML.replaceAll('\n', '<br>');
|
||||
}
|
||||
|
||||
const isMediaAllowed = isExternalMediaAllowed();
|
||||
if (isMediaAllowed) {
|
||||
return;
|
||||
@ -458,6 +463,7 @@ let currentVersion = '0.0.0';
|
||||
export const default_ch_mes = 'Hello';
|
||||
let generatedPromptCache = '';
|
||||
let generation_started = new Date();
|
||||
/** @type {import('scripts/char-data.js').v1CharData[]} */
|
||||
export let characters = [];
|
||||
export let this_chid;
|
||||
let saveCharactersPage = 0;
|
||||
@ -783,7 +789,6 @@ export let novelai_setting_names;
|
||||
let abortController;
|
||||
|
||||
//css
|
||||
var css_mes_bg = $('<div class="mes"></div>').css('background');
|
||||
var css_send_form_display = $('<div id=send_form></div>').css('display');
|
||||
const MAX_GENERATION_LOOPS = 5;
|
||||
|
||||
@ -811,6 +816,28 @@ $.ajaxPrefilter((options, originalOptions, xhr) => {
|
||||
xhr.setRequestHeader('X-CSRF-Token', token);
|
||||
});
|
||||
|
||||
/**
|
||||
* Pings the STserver to check if it is reachable.
|
||||
* @returns {Promise<boolean>} True if the server is reachable, false otherwise.
|
||||
*/
|
||||
export async function pingServer() {
|
||||
try {
|
||||
const result = await fetch('api/ping', {
|
||||
method: 'GET',
|
||||
headers: getRequestHeaders(),
|
||||
});
|
||||
|
||||
if (!result.ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Error pinging server', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function firstLoadInit() {
|
||||
try {
|
||||
const tokenResponse = await fetch('/csrf-token');
|
||||
@ -3077,6 +3104,15 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
|
||||
}
|
||||
|
||||
if (!dryRun) {
|
||||
// Ping server to make sure it is still alive
|
||||
const pingResult = await pingServer();
|
||||
|
||||
if (!pingResult) {
|
||||
unblockGeneration(type);
|
||||
toastr.error('Verify that the server is running and accessible.', 'ST Server cannot be reached' );
|
||||
throw new Error('Server unreachable');
|
||||
}
|
||||
|
||||
// Hide swipes if not in a dry run.
|
||||
hideSwipeButtons();
|
||||
// If generated any message, set the flag to indicate it can't be recreated again.
|
||||
@ -8580,19 +8616,17 @@ jQuery(async function () {
|
||||
}
|
||||
$('.mes').children('.del_checkbox').each(function () {
|
||||
$(this).prop('checked', false);
|
||||
$(this).parent().css('background', css_mes_bg);
|
||||
$(this).parent().removeClass('selected');
|
||||
});
|
||||
$(this).css('background', '#600'); //sets the bg of the mes selected for deletion
|
||||
$(this).addClass('selected'); //sets the bg of the mes selected for deletion
|
||||
var i = Number($(this).attr('mesid')); //checks the message ID in the chat
|
||||
this_del_mes = i;
|
||||
//as long as the current message ID is less than the total chat length
|
||||
while (i < chat.length) {
|
||||
//as long as the current message ID is less than the total chat length
|
||||
$('.mes[mesid=\'' + i + '\']').css('background', '#600'); //sets the bg of the all msgs BELOW the selected .mes
|
||||
$('.mes[mesid=\'' + i + '\']')
|
||||
.children('.del_checkbox')
|
||||
.prop('checked', true);
|
||||
//sets the bg of the all msgs BELOW the selected .mes
|
||||
$(`.mes[mesid="${i}"]`).addClass('selected');
|
||||
$(`.mes[mesid="${i}"]`).children('.del_checkbox').prop('checked', true);
|
||||
i++;
|
||||
//console.log(i);
|
||||
}
|
||||
});
|
||||
|
||||
@ -9196,7 +9230,7 @@ jQuery(async function () {
|
||||
$('.del_checkbox').each(function () {
|
||||
$(this).css('display', 'none');
|
||||
$(this).parent().children('.for_checkbox').css('display', 'block');
|
||||
$(this).parent().css('background', css_mes_bg);
|
||||
$(this).parent().removeClass('selected');
|
||||
$(this).prop('checked', false);
|
||||
});
|
||||
showSwipeButtons();
|
||||
@ -9211,22 +9245,19 @@ jQuery(async function () {
|
||||
$('.del_checkbox').each(function () {
|
||||
$(this).css('display', 'none');
|
||||
$(this).parent().children('.for_checkbox').css('display', 'block');
|
||||
$(this).parent().css('background', css_mes_bg);
|
||||
$(this).parent().removeClass('selected');
|
||||
$(this).prop('checked', false);
|
||||
});
|
||||
|
||||
if (this_del_mes >= 0) {
|
||||
$('.mes[mesid=\'' + this_del_mes + '\']')
|
||||
.nextAll('div')
|
||||
.remove();
|
||||
$('.mes[mesid=\'' + this_del_mes + '\']').remove();
|
||||
$(`.mes[mesid="${this_del_mes}"]`).nextAll('div').remove();
|
||||
$(`.mes[mesid="${this_del_mes}"]`).remove();
|
||||
chat.length = this_del_mes;
|
||||
await saveChatConditional();
|
||||
var $textchat = $('#chat');
|
||||
$textchat.scrollTop($textchat[0].scrollHeight);
|
||||
chatElement.scrollTop(chatElement[0].scrollHeight);
|
||||
eventSource.emit(event_types.MESSAGE_DELETED, chat.length);
|
||||
$('#chat .mes').removeClass('last_mes');
|
||||
$('#chat .mes').last().addClass('last_mes');
|
||||
$('#chat .mes').eq(-2).removeClass('last_mes');
|
||||
} else {
|
||||
console.log('this_del_mes is not >= 0, not deleting');
|
||||
}
|
||||
|
94
public/scripts/char-data.js
Normal file
94
public/scripts/char-data.js
Normal file
@ -0,0 +1,94 @@
|
||||
/**
|
||||
* @typedef {object} v2DataWorldInfoEntry
|
||||
* @property {string[]} keys - An array of primary keys associated with the entry.
|
||||
* @property {string[]} secondary_keys - An array of secondary keys associated with the entry (optional).
|
||||
* @property {string} comment - A human-readable description or explanation for the entry.
|
||||
* @property {string} content - The main content or data associated with the entry.
|
||||
* @property {boolean} constant - Indicates if the entry's content is fixed and unchangeable.
|
||||
* @property {boolean} selective - Indicates if the entry's inclusion is controlled by specific conditions.
|
||||
* @property {number} insertion_order - Defines the order in which the entry is inserted during processing.
|
||||
* @property {boolean} enabled - Controls whether the entry is currently active and used.
|
||||
* @property {string} position - Specifies the location or context where the entry applies.
|
||||
* @property {v2DataWorldInfoEntryExtensionInfos} extensions - An object containing additional details for extensions associated with the entry.
|
||||
* @property {number} id - A unique identifier assigned to the entry.
|
||||
*/
|
||||
/**
|
||||
* @typedef {object} v2DataWorldInfoEntryExtensionInfos
|
||||
* @property {number} position - The order in which the extension is applied relative to other extensions.
|
||||
* @property {boolean} exclude_recursion - Prevents the extension from being applied recursively.
|
||||
* @property {number} probability - The chance (between 0 and 1) of the extension being applied.
|
||||
* @property {boolean} useProbability - Determines if the `probability` property is used.
|
||||
* @property {number} depth - The maximum level of nesting allowed for recursive application of the extension.
|
||||
* @property {number} selectiveLogic - Defines the logic used to determine if the extension is applied selectively.
|
||||
* @property {string} group - A category or grouping for the extension.
|
||||
* @property {boolean} group_override - Overrides any existing group assignment for the extension.
|
||||
* @property {number} group_weight - A value used for prioritizing extensions within the same group.
|
||||
* @property {boolean} prevent_recursion - Completely disallows recursive application of the extension.
|
||||
* @property {number} scan_depth - The maximum depth to search for matches when applying the extension.
|
||||
* @property {boolean} match_whole_words - Specifies if only entire words should be matched during extension application.
|
||||
* @property {boolean} use_group_scoring - Indicates if group weight is considered when selecting extensions.
|
||||
* @property {boolean} case_sensitive - Controls whether case sensitivity is applied during matching for the extension.
|
||||
* @property {string} automation_id - An identifier used for automation purposes related to the extension.
|
||||
* @property {number} role - The specific function or purpose of the extension.
|
||||
* @property {boolean} vectorized - Indicates if the extension is optimized for vectorized processing.
|
||||
* @property {number} display_index - The order in which the extension should be displayed for user interfaces.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} v2WorldInfoBook
|
||||
* @property {string} name - the name of the book
|
||||
* @property {v2DataWorldInfoEntry[]} entries - the entries of the book
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} v2CharData
|
||||
* @property {string} name - The character's name.
|
||||
* @property {string} description - A brief description of the character.
|
||||
* @property {string} character_version - The character's data version.
|
||||
* @property {string} personality - A short summary of the character's personality traits.
|
||||
* @property {string} scenario - A description of the character's background or setting.
|
||||
* @property {string} first_mes - The character's opening message in a conversation.
|
||||
* @property {string} mes_example - An example message demonstrating the character's conversation style.
|
||||
* @property {string} creator_notes - Internal notes or comments left by the character's creator.
|
||||
* @property {string[]} tags - A list of keywords or labels associated with the character.
|
||||
* @property {string} system_prompt - The system prompt used to interact with the character.
|
||||
* @property {string} post_history_instructions - Instructions for handling the character's conversation history.
|
||||
* @property {string} creator - The name of the person who created the character.
|
||||
* @property {string[]} alternate_greetings - Additional greeting messages the character can use.
|
||||
* @property {v2WorldInfoBook} character_book - Data about the character's world or story (if applicable).
|
||||
* @property {v2CharDataExtensionInfos} extensions - Additional details specific to the character.
|
||||
*/
|
||||
/**
|
||||
* @typedef {object} v2CharDataExtensionInfos
|
||||
* @property {number} talkativeness - A numerical value indicating the character's propensity to talk.
|
||||
* @property {boolean} fav - A flag indicating whether the character is a favorite.
|
||||
* @property {string} world - The fictional world or setting where the character exists (if applicable).
|
||||
* @property {object} depth_prompt - Prompts used to explore the character's depth and complexity.
|
||||
* @property {number} depth_prompt.depth - The level of detail or nuance targeted by the prompt.
|
||||
* @property {string} depth_prompt.prompt - The actual prompt text used for deeper character interaction.
|
||||
* @property {"system" | "user" | "assistant"} depth_prompt.role - The role the character takes on during the prompted interaction (system, user, or assistant).
|
||||
* // Non-standard extensions added by external tools
|
||||
* @property {string} [pygmalion_id] - The unique identifier assigned to the character by the Pygmalion.chat.
|
||||
* @property {{full_path: string}} [chub] - The Chub-specific data associated with the character.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} v1CharData
|
||||
* @property {string} name - the name of the character
|
||||
* @property {string} description - the description of the character
|
||||
* @property {string} personality - a short personality description of the character
|
||||
* @property {string} scenario - a scenario description of the character
|
||||
* @property {string} first_mes - the first message in the conversation
|
||||
* @property {string} mes_example - the example message in the conversation
|
||||
* @property {string} creatorcomment - creator's notes of the character
|
||||
* @property {string[]} tags - the tags of the character
|
||||
* @property {number} talkativeness - talkativeness
|
||||
* @property {boolean|string} fav - fav
|
||||
* @property {string} create_date - create_date
|
||||
* @property {v2CharData} data - v2 data extension
|
||||
* // Non-standard extensions added by the ST server (not part of the original data)
|
||||
* @property {string} chat - name of the current chat file chat
|
||||
* @property {string} avatar - file name of the avatar image (acts as a unique identifier)
|
||||
* @property {string} json_data - the full raw JSON data of the character
|
||||
*/
|
||||
export default 0;// now this file is a module
|
@ -64,10 +64,6 @@
|
||||
<input id="expression_override" type="text" class="text_pole" placeholder="Override folder name" />
|
||||
<input id="expression_override_button" class="menu_button" type="submit" value="Submit" />
|
||||
</div>
|
||||
<h3 id="image_list_header">
|
||||
<strong>Sprite set:</strong> <span id="image_list_header_name"></span>
|
||||
</h3>
|
||||
<div id="image_list"></div>
|
||||
<div class="expression_buttons flex-container spaceEvenly">
|
||||
<div id="expression_upload_pack_button" class="menu_button">
|
||||
<i class="fa-solid fa-file-zipper"></i>
|
||||
@ -80,6 +76,11 @@
|
||||
</div>
|
||||
<p class="hint"><b>Hint:</b> <i>Create new folder in the <b>/characters/</b> folder of your user data directory and name it as the name of the character.
|
||||
Put images with expressions there. File names should follow the pattern: <tt>[expression_label].[image_format]</tt></i></p>
|
||||
<h3 id="image_list_header">
|
||||
<strong>Sprite set:</strong> <span id="image_list_header_name"></span>
|
||||
</h3>
|
||||
<div id="image_list"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -259,6 +259,26 @@ function diceRollReplace(input, invalidRollPlaceholder = '') {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the difference between two times. Works with any time format acceptable by moment().
|
||||
* Can work with {{date}} {{time}} macros
|
||||
* @param {string} input - The string to replace time difference macros in.
|
||||
* @returns {string} The string with replaced time difference macros.
|
||||
*/
|
||||
function timeDiffReplace(input) {
|
||||
const timeDiffPattern = /{{timeDiff::(.*?)::(.*?)}}/gi;
|
||||
|
||||
const output = input.replace(timeDiffPattern, (_match, matchPart1, matchPart2) => {
|
||||
const time1 = moment(matchPart1);
|
||||
const time2 = moment(matchPart2);
|
||||
|
||||
const timeDifference = moment.duration(time1.diff(time2));
|
||||
return timeDifference.humanize();
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes {{macro}} parameters in a string.
|
||||
* @param {string} content - The string to substitute parameters in.
|
||||
@ -330,6 +350,7 @@ export function evaluateMacros(content, env) {
|
||||
const utcTime = moment().utc().utcOffset(utcOffset).format('LT');
|
||||
return utcTime;
|
||||
});
|
||||
content = timeDiffReplace(content);
|
||||
content = bannedWordsReplace(content);
|
||||
content = randomReplace(content);
|
||||
content = pickReplace(content, rawContent);
|
||||
|
@ -1865,7 +1865,7 @@ function highlightDefaultContext() {
|
||||
/**
|
||||
* Fuzzy search characters by a search term
|
||||
* @param {string} searchValue - The search term
|
||||
* @returns {{item?: *, refIndex: number, score: number}[]} Results as items with their score
|
||||
* @returns {FuseResult[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchCharacters(searchValue) {
|
||||
// @ts-ignore
|
||||
@ -1898,7 +1898,7 @@ export function fuzzySearchCharacters(searchValue) {
|
||||
* Fuzzy search world info entries by a search term
|
||||
* @param {*[]} data - WI items data array
|
||||
* @param {string} searchValue - The search term
|
||||
* @returns {{item?: *, refIndex: number, score: number}[]} Results as items with their score
|
||||
* @returns {FuseResult[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchWorldInfo(data, searchValue) {
|
||||
// @ts-ignore
|
||||
@ -1927,7 +1927,7 @@ export function fuzzySearchWorldInfo(data, searchValue) {
|
||||
* Fuzzy search persona entries by a search term
|
||||
* @param {*[]} data - persona data array
|
||||
* @param {string} searchValue - The search term
|
||||
* @returns {{item?: *, refIndex: number, score: number}[]} Results as items with their score
|
||||
* @returns {FuseResult[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchPersonas(data, searchValue) {
|
||||
data = data.map(x => ({ key: x, name: power_user.personas[x] ?? '', description: power_user.persona_descriptions[x]?.description ?? '' }));
|
||||
@ -1951,7 +1951,7 @@ export function fuzzySearchPersonas(data, searchValue) {
|
||||
/**
|
||||
* Fuzzy search tags by a search term
|
||||
* @param {string} searchValue - The search term
|
||||
* @returns {{item?: *, refIndex: number, score: number}[]} Results as items with their score
|
||||
* @returns {FuseResult[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchTags(searchValue) {
|
||||
// @ts-ignore
|
||||
@ -1973,7 +1973,7 @@ export function fuzzySearchTags(searchValue) {
|
||||
/**
|
||||
* Fuzzy search groups by a search term
|
||||
* @param {string} searchValue - The search term
|
||||
* @returns {{item?: *, refIndex: number, score: number}[]} Results as items with their score
|
||||
* @returns {FuseResult[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchGroups(searchValue) {
|
||||
// @ts-ignore
|
||||
|
@ -33,6 +33,7 @@
|
||||
<li><tt>{{isodate}}</tt> – the current ISO date (YYYY-MM-DD)</li>
|
||||
<li><tt>{{datetimeformat …}}</tt> – the current date/time in the specified format, e. g. for German date/time: <tt>{{datetimeformat DD.MM.YYYY HH:mm}}</tt></li>
|
||||
<li><tt>{{time_UTC±#}}</tt> – the current time in the specified UTC time zone offset, e.g. UTC-4 or UTC+2</li>
|
||||
<li><tt>{{timeDiff::(time1)::(time2)}}</tt> – the time difference between time1 and time2. Accepts time and date macros. (Ex: {{timeDiff::{{isodate}} {{time}}::2024/5/11 12:30:00}})</li>
|
||||
<li><tt>{{idle_duration}}</tt> – the time since the last user message was sent</li>
|
||||
<li><tt>{{bias "text here"}}</tt> – sets a behavioral bias for the AI until the next user input. Quotes around the text are important.</li>
|
||||
<li><tt>{{roll:(formula)}}</tt> – rolls a dice. (ex: <tt>>{{roll:1d6}}</tt> will roll a 6-sided dice and return a number between 1 and 6)</li>
|
||||
|
@ -328,15 +328,20 @@ function getTokenizerForTokenIds() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} String with comma-separated banned token IDs
|
||||
* @typedef {{banned_tokens: string, banned_strings: string[]}} TokenBanResult
|
||||
* @returns {TokenBanResult} String with comma-separated banned token IDs
|
||||
*/
|
||||
function getCustomTokenBans() {
|
||||
if (!settings.banned_tokens && !textgenerationwebui_banned_in_macros.length) {
|
||||
return '';
|
||||
return {
|
||||
banned_tokens: '',
|
||||
banned_strings: [],
|
||||
};
|
||||
}
|
||||
|
||||
const tokenizer = getTokenizerForTokenIds();
|
||||
const result = [];
|
||||
const banned_tokens = [];
|
||||
const banned_strings = [];
|
||||
const sequences = settings.banned_tokens
|
||||
.split('\n')
|
||||
.concat(textgenerationwebui_banned_in_macros)
|
||||
@ -358,24 +363,31 @@ function getCustomTokenBans() {
|
||||
const tokens = JSON.parse(line);
|
||||
|
||||
if (Array.isArray(tokens) && tokens.every(t => Number.isInteger(t))) {
|
||||
result.push(...tokens);
|
||||
banned_tokens.push(...tokens);
|
||||
} else {
|
||||
throw new Error('Not an array of integers');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(`Failed to parse bad word token list: ${line}`, err);
|
||||
}
|
||||
} else if (line.startsWith('"') && line.endsWith('"')) {
|
||||
// Remove the enclosing quotes
|
||||
|
||||
banned_strings.push(line.slice(1, -1))
|
||||
} else {
|
||||
try {
|
||||
const tokens = getTextTokens(tokenizer, line);
|
||||
result.push(...tokens);
|
||||
banned_tokens.push(...tokens);
|
||||
} catch {
|
||||
console.log(`Could not tokenize raw text: ${line}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.filter(onlyUnique).map(x => String(x)).join(',');
|
||||
return {
|
||||
banned_tokens: banned_tokens.filter(onlyUnique).map(x => String(x)).join(','),
|
||||
banned_strings: banned_strings,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -697,10 +709,7 @@ jQuery(function () {
|
||||
$(`#${id}_counter_textgenerationwebui`).val(value);
|
||||
settings[id] = value;
|
||||
//special handling for vLLM/Aphrodite using -1 as disabled instead of 0
|
||||
if ($(this).attr('id') === 'top_k_textgenerationwebui' &&
|
||||
(settings.type === textgen_types.VLLM ||
|
||||
settings.type === textgen_types.APHRODITE) &&
|
||||
value === 0) {
|
||||
if ($(this).attr('id') === 'top_k_textgenerationwebui' && [INFERMATICAI, APHRODITE, VLLM].includes(settings.type) && value === 0) {
|
||||
settings[id] = -1;
|
||||
$(this).val(-1);
|
||||
}
|
||||
@ -987,6 +996,8 @@ export function isJsonSchemaSupported() {
|
||||
|
||||
export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, isContinue, cfgValues, type) {
|
||||
const canMultiSwipe = !isContinue && !isImpersonate && type !== 'quiet';
|
||||
const { banned_tokens, banned_strings } = getCustomTokenBans();
|
||||
|
||||
let params = {
|
||||
'prompt': finalPrompt,
|
||||
'model': getTextGenModel(),
|
||||
@ -1033,8 +1044,9 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
|
||||
'mirostat_tau': settings.mirostat_tau,
|
||||
'mirostat_eta': settings.mirostat_eta,
|
||||
'custom_token_bans': [APHRODITE, MANCER].includes(settings.type) ?
|
||||
toIntArray(getCustomTokenBans()) :
|
||||
getCustomTokenBans(),
|
||||
toIntArray(banned_tokens) :
|
||||
banned_tokens,
|
||||
'banned_strings': banned_strings,
|
||||
'api_type': settings.type,
|
||||
'api_server': getTextGenServer(),
|
||||
'legacy_api': settings.legacy_api && (settings.type === OOBA || settings.type === APHRODITE),
|
||||
|
@ -612,8 +612,8 @@ const dateCache = new Map();
|
||||
/**
|
||||
* Cached version of moment() to avoid re-parsing the same date strings.
|
||||
* Important: Moment objects are mutable, so use clone() before modifying them!
|
||||
* @param {any} timestamp String or number representing a date.
|
||||
* @returns {*} Moment object
|
||||
* @param {string|number} timestamp String or number representing a date.
|
||||
* @returns {moment.Moment} Moment object
|
||||
*/
|
||||
export function timestampToMoment(timestamp) {
|
||||
if (dateCache.has(timestamp)) {
|
||||
@ -663,8 +663,8 @@ function parseTimestamp(timestamp) {
|
||||
|
||||
/**
|
||||
* Compare two moment objects for sorting.
|
||||
* @param {*} a The first moment object.
|
||||
* @param {*} b The second moment object.
|
||||
* @param {moment.Moment} a The first moment object.
|
||||
* @param {moment.Moment} b The second moment object.
|
||||
* @returns {number} A negative number if a is before b, a positive number if a is after b, or 0 if they are equal.
|
||||
*/
|
||||
export function sortMoments(a, b) {
|
||||
|
@ -813,6 +813,10 @@ body .panelControlBar {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#chat .mes.selected{
|
||||
background-color: rgb(from var(--SmartThemeQuoteColor) r g b / .5);
|
||||
}
|
||||
|
||||
.mes q:before,
|
||||
.mes q:after {
|
||||
content: '';
|
||||
|
@ -3,7 +3,7 @@ const path = require('path');
|
||||
const express = require('express');
|
||||
const fetch = require('node-fetch').default;
|
||||
const sanitize = require('sanitize-filename');
|
||||
const { getConfigValue } = require('../util');
|
||||
const { getConfigValue, color } = require('../util');
|
||||
const { jsonParser } = require('../express-common');
|
||||
const writeFileAtomicSync = require('write-file-atomic').sync;
|
||||
const contentDirectory = path.join(process.cwd(), 'default/content');
|
||||
@ -94,8 +94,11 @@ function getDefaultPresetFile(filename) {
|
||||
* @param {ContentItem[]} contentIndex Content index
|
||||
* @param {import('../users').UserDirectoryList} directories User directories
|
||||
* @param {string[]} forceCategories List of categories to force check (even if content check is skipped)
|
||||
* @returns {Promise<boolean>} Whether any content was added
|
||||
*/
|
||||
async function seedContentForUser(contentIndex, directories, forceCategories) {
|
||||
let anyContentAdded = false;
|
||||
|
||||
if (!fs.existsSync(directories.root)) {
|
||||
fs.mkdirSync(directories.root, { recursive: true });
|
||||
}
|
||||
@ -134,9 +137,11 @@ async function seedContentForUser(contentIndex, directories, forceCategories) {
|
||||
|
||||
fs.cpSync(contentPath, targetPath, { recursive: true, force: false });
|
||||
console.log(`Content file ${contentItem.filename} copied to ${contentTarget}`);
|
||||
anyContentAdded = true;
|
||||
}
|
||||
|
||||
writeFileAtomicSync(contentLogPath, contentLog.join('\n'));
|
||||
return anyContentAdded;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -147,15 +152,27 @@ async function seedContentForUser(contentIndex, directories, forceCategories) {
|
||||
*/
|
||||
async function checkForNewContent(directoriesList, forceCategories = []) {
|
||||
try {
|
||||
if (getConfigValue('skipContentCheck', false) && forceCategories?.length === 0) {
|
||||
const contentCheckSkip = getConfigValue('skipContentCheck', false);
|
||||
if (contentCheckSkip && forceCategories?.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentIndexText = fs.readFileSync(contentIndexPath, 'utf8');
|
||||
const contentIndex = JSON.parse(contentIndexText);
|
||||
let anyContentAdded = false;
|
||||
|
||||
for (const directories of directoriesList) {
|
||||
await seedContentForUser(contentIndex, directories, forceCategories);
|
||||
const seedResult = await seedContentForUser(contentIndex, directories, forceCategories);
|
||||
|
||||
if (seedResult) {
|
||||
anyContentAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (anyContentAdded && !contentCheckSkip && forceCategories?.length === 0) {
|
||||
console.log();
|
||||
console.log(`${color.blue('If you don\'t want to receive content updates in the future, set')} ${color.yellow('skipContentCheck')} ${color.blue('to true in the config.yaml file.')}`);
|
||||
console.log();
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Content check failed', err);
|
||||
|
Loading…
x
Reference in New Issue
Block a user