Merge branch 'SillyTavern:staging' into staging
This commit is contained in:
commit
5009416f94
|
@ -135,7 +135,7 @@
|
||||||
"instruct": {
|
"instruct": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"preset": "🧙 Roleplay",
|
"preset": "🧙 Roleplay",
|
||||||
"system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.\n\nAvoid repetition, don't loop. Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions.\n",
|
"system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.\n\nAvoid repetition, don't loop. Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions.\n\n### Input:",
|
||||||
"input_sequence": "\n### Instruction:",
|
"input_sequence": "\n### Instruction:",
|
||||||
"output_sequence": "\n### Response:",
|
"output_sequence": "\n### Response:",
|
||||||
"last_output_sequence": "\n### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):",
|
"last_output_sequence": "\n### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):",
|
||||||
|
|
|
@ -53,8 +53,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"pkg": "^5.8.1",
|
"pkg": "^5.8.1",
|
||||||
"pkg-fetch": "^3.5.2",
|
"pkg-fetch": "^3.5.2"
|
||||||
"toastr": "^2.1.4"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@agnai/sentencepiece-js": {
|
"node_modules/@agnai/sentencepiece-js": {
|
||||||
|
@ -3366,15 +3365,6 @@
|
||||||
"node": ">=8.0"
|
"node": ">=8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/toastr": {
|
|
||||||
"version": "2.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/toastr/-/toastr-2.1.4.tgz",
|
|
||||||
"integrity": "sha512-LIy77F5n+sz4tefMmFOntcJ6HL0Fv3k1TDnNmFZ0bU/GcvIIfy6eG2v7zQmMiYgaalAiUv75ttFrPn5s0gyqlA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"jquery": ">=1.12.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/toidentifier": {
|
"node_modules/toidentifier": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||||
|
|
|
@ -81,7 +81,6 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"pkg": "^5.8.1",
|
"pkg": "^5.8.1",
|
||||||
"pkg-fetch": "^3.5.2",
|
"pkg-fetch": "^3.5.2"
|
||||||
"toastr": "^2.1.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,42 +63,8 @@
|
||||||
|
|
||||||
<link rel="stylesheet" href="css/bg_load.css">
|
<link rel="stylesheet" href="css/bg_load.css">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
<script>
|
<script type="module" src="scripts/i18n.js"></script>
|
||||||
function applyLocale() {
|
<script type="module" src="script.js"></script>
|
||||||
const overrideLanguage = localStorage.getItem("language");
|
|
||||||
var language = overrideLanguage || navigator.language || navigator.userLanguage;
|
|
||||||
language = language.toLowerCase();
|
|
||||||
console.log(language)
|
|
||||||
//load the appropriate language file
|
|
||||||
$.getJSON("i18n.json", function (data) {
|
|
||||||
console.log(data)
|
|
||||||
if (data.lang.indexOf(language) < 0) language = "en";
|
|
||||||
console.log(language)
|
|
||||||
//find all the elements with `data-i18n` attribute
|
|
||||||
$("[data-i18n]").each(function () {
|
|
||||||
//read the translation from the language data
|
|
||||||
const keys = $(this).data("i18n").split(';'); // Multi-key entries are ; delimited
|
|
||||||
for (const key of keys) {
|
|
||||||
const attrmatch = key.match(/\[(\S+)\](.+)/); // [attribute]key
|
|
||||||
if (attrmatch) { // attribute-tagged key
|
|
||||||
const locval = data?.[language]?.[attrmatch[2]];
|
|
||||||
if (locval) {
|
|
||||||
$(this).attr(attrmatch[1], locval);
|
|
||||||
}
|
|
||||||
} else { // No attribute tag, treat as 'text'
|
|
||||||
const locval = data?.[language]?.[key];
|
|
||||||
if (locval) {
|
|
||||||
$(this).text(locval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
$(document).ready(applyLocale);
|
|
||||||
window["applyLocale"] = applyLocale;
|
|
||||||
</script>
|
|
||||||
<script type=module src="script.js"></script>
|
|
||||||
|
|
||||||
<script type="module" src="scripts/world-info.js"></script>
|
<script type="module" src="scripts/world-info.js"></script>
|
||||||
<script type="module" src="scripts/group-chats.js"></script>
|
<script type="module" src="scripts/group-chats.js"></script>
|
||||||
|
@ -669,7 +635,7 @@
|
||||||
Max prompt cost: <span id="openrouter_max_prompt_cost">Unknown</span>
|
Max prompt cost: <span id="openrouter_max_prompt_cost">Unknown</span>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="range-block" data-source="openai,claude,windowai,openrouter,ai21">
|
<div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale">
|
||||||
<div class="range-block-title" data-i18n="Temperature">
|
<div class="range-block-title" data-i18n="Temperature">
|
||||||
Temperature
|
Temperature
|
||||||
</div>
|
</div>
|
||||||
|
@ -744,7 +710,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="range-block" data-source="openai,claude,openrouter,ai21">
|
<div class="range-block" data-source="openai,claude,openrouter,ai21,scale">
|
||||||
<div class="range-block-title" data-i18n="Top-p">
|
<div class="range-block-title" data-i18n="Top-p">
|
||||||
Top P
|
Top P
|
||||||
</div>
|
</div>
|
||||||
|
@ -1470,9 +1436,22 @@
|
||||||
<div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
|
<div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="inline-drawer-content">
|
<div class="inline-drawer-content">
|
||||||
<div id="quick-edit-container">
|
<div class="range-block m-t-1">
|
||||||
<div class="range-block">
|
<div class="justifyLeft" data-i18n="Main">Main</div>
|
||||||
<span data-i18n="Select a character to show quick edit options.">Select a character to show quick edit options.</span>
|
<div class="wide100p">
|
||||||
|
<textarea id="main_prompt_quick_edit_textarea" class="text_pole textarea_compact" rows="6" placeholder="" data-pm-prompt="main"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="range-block m-t-1">
|
||||||
|
<div class="justifyLeft" data-i18n="NSFW">NSFW</div>
|
||||||
|
<div class="wide100p">
|
||||||
|
<textarea id="nsfw_prompt_quick_edit_textarea" class="text_pole textarea_compact" rows="6" placeholder="" data-pm-prompt="nsfw"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="range-block m-t-1">
|
||||||
|
<div class="justifyLeft" data-i18n="Jailbreak">Jailbreak</div>
|
||||||
|
<div class="wide100p">
|
||||||
|
<textarea id="jailbreak_prompt_quick_edit_textarea" class="text_pole textarea_compact" rows="6" placeholder="" data-pm-prompt="jailbreak"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="claude_assistant_prefill_block" data-source="claude" class="range-block">
|
<div id="claude_assistant_prefill_block" data-source="claude" class="range-block">
|
||||||
|
@ -1612,7 +1591,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="range-block m-t-1" data-source="openai,openrouter">
|
<div class="range-block m-t-1" data-source="openai,openrouter,scale">
|
||||||
<div class="range-block-title openai_restorable" data-i18n="Logit Bias">
|
<div class="range-block-title openai_restorable" data-i18n="Logit Bias">
|
||||||
Logit Bias
|
Logit Bias
|
||||||
</div>
|
</div>
|
||||||
|
@ -2010,6 +1989,7 @@
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form id="scale_form" data-source="scale" action="javascript:void(null);" method="post" enctype="multipart/form-data">
|
<form id="scale_form" data-source="scale" action="javascript:void(null);" method="post" enctype="multipart/form-data">
|
||||||
|
<div id="normal_scale_form">
|
||||||
<h4>Scale API Key</h4>
|
<h4>Scale API Key</h4>
|
||||||
<div class="flex-container">
|
<div class="flex-container">
|
||||||
<input id="api_key_scale" name="api_key_scale" class="text_pole flex1" maxlength="500" value="" autocomplete="off">
|
<input id="api_key_scale" name="api_key_scale" class="text_pole flex1" maxlength="500" value="" autocomplete="off">
|
||||||
|
@ -2020,8 +2000,23 @@
|
||||||
</div>
|
</div>
|
||||||
<h4>Scale API URL</h4>
|
<h4>Scale API URL</h4>
|
||||||
<input id="api_url_scale" name="api_url_scale" class="text_pole" maxlength="500" value="" autocomplete="off" placeholder="https://dashboard.scale.com/spellbook/api/v2/deploy/xxxxxxx">
|
<input id="api_url_scale" name="api_url_scale" class="text_pole" maxlength="500" value="" autocomplete="off" placeholder="https://dashboard.scale.com/spellbook/api/v2/deploy/xxxxxxx">
|
||||||
|
</div>
|
||||||
|
<div id="alt_scale_form">
|
||||||
|
<h4>Scale Cookie (_jwt)</h4>
|
||||||
|
<div class="flex-container">
|
||||||
|
<input id="scale_cookie" name="scale_cookie" class="text_pole flex1" maxlength="500" value="" autocomplete="off">
|
||||||
|
<div title="Clear your cookie" data-i18n="[title]Clear your cookie" class="menu_button fa-solid fa-circle-xmark clear-api-key" data-key="scale_cookie"></div>
|
||||||
|
</div>
|
||||||
|
<div data-for="scale_cookie" class="neutral_warning">
|
||||||
|
For privacy reasons, your cookie will be hidden after you reload the page.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- Its only purpose is to trigger max context size check -->
|
<!-- Its only purpose is to trigger max context size check -->
|
||||||
<select id="model_scale_select" class="displayNone"></select>
|
<select id="model_scale_select" class="displayNone"></select>
|
||||||
|
<label for="scale-alt" class="checkbox_label">
|
||||||
|
<input id="scale-alt" type="checkbox" checked>
|
||||||
|
<span data-i18n="Alt Method">Alt Method</span>
|
||||||
|
</label>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form id="ai21_form" data-source="ai21" action="javascript:void(null);" method="post" enctype="multipart/form-data">
|
<form id="ai21_form" data-source="ai21" action="javascript:void(null);" method="post" enctype="multipart/form-data">
|
||||||
|
@ -2999,7 +2994,10 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h4 data-i18n="Persona Description">Persona Description</h4>
|
<h4 data-i18n="Persona Description">Persona Description</h4>
|
||||||
<textarea id="persona_description" name="persona_description" placeholder="Example: [{{user}} is a 28-year-old Romanian cat girl.]" class="text_pole textarea_compact" maxlength="5000" value="" autocomplete="off" rows="4"></textarea>
|
<textarea id="persona_description" name="persona_description" placeholder="Example: [{{user}} is a 28-year-old Romanian cat girl.]" class="text_pole textarea_compact" maxlength="5000" value="" autocomplete="off" rows="8"></textarea>
|
||||||
|
<div class="extension_token_counter">
|
||||||
|
Tokens: <span id="persona_description_token_count">0</span>
|
||||||
|
</div>
|
||||||
<label for="persona_description_position" data-i18n="Position:">Position:</label>
|
<label for="persona_description_position" data-i18n="Position:">Position:</label>
|
||||||
<select id="persona_description_position">
|
<select id="persona_description_position">
|
||||||
<option value="0" data-i18n="In Story String / Chat Completion: Before Character Card">In Story String / Chat Completion: Before Character Card</option>
|
<option value="0" data-i18n="In Story String / Chat Completion: Before Character Card">In Story String / Chat Completion: Before Character Card</option>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "🧙 Roleplay",
|
"name": "🧙 Roleplay",
|
||||||
"system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.\n\nAvoid repetition, don't loop. Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions.\n",
|
"system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.\n\nAvoid repetition, don't loop. Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions.\n\n### Input:",
|
||||||
"input_sequence": "\n### Instruction:",
|
"input_sequence": "\n### Instruction:",
|
||||||
"output_sequence": "\n### Response:",
|
"output_sequence": "\n### Response:",
|
||||||
"last_output_sequence": "\n### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):",
|
"last_output_sequence": "\n### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):",
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"checkJs": true,
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "commonjs",
|
||||||
|
"allowUmdGlobalAccess": true,
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
],
|
||||||
|
"typeAcquisition": {
|
||||||
|
"include": [
|
||||||
|
"jquery",
|
||||||
|
"@popperjs/core",
|
||||||
|
"toastr",
|
||||||
|
"showdown",
|
||||||
|
"dompurify",
|
||||||
|
"moment",
|
||||||
|
"seedrandom",
|
||||||
|
"showdown-katex",
|
||||||
|
"droll",
|
||||||
|
"handlebars",
|
||||||
|
"highlight.js",
|
||||||
|
"localforage"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
916
public/script.js
916
public/script.js
File diff suppressed because it is too large
Load Diff
|
@ -513,6 +513,38 @@ PromptManagerModule.prototype.init = function (moduleConfiguration, serviceSetti
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fill quick edit fields for the first time
|
||||||
|
if ('global' === this.configuration.promptOrder.strategy) {
|
||||||
|
const handleQuickEditSave = (event) => {
|
||||||
|
const promptId = event.target.dataset.pmPrompt;
|
||||||
|
const prompt = this.getPromptById(promptId);
|
||||||
|
|
||||||
|
prompt.content = event.target.value;
|
||||||
|
|
||||||
|
// Update edit form if present
|
||||||
|
// @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent
|
||||||
|
const popupEditFormPrompt = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt');
|
||||||
|
if (popupEditFormPrompt.offsetParent) {
|
||||||
|
popupEditFormPrompt.value = prompt.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.log('Saved prompt: ' + promptId);
|
||||||
|
this.saveServiceSettings().then(() => this.render());
|
||||||
|
};
|
||||||
|
|
||||||
|
const mainPrompt = this.getPromptById('main');
|
||||||
|
const mainElementId = this.updateQuickEdit('main', mainPrompt);
|
||||||
|
document.getElementById(mainElementId).addEventListener('blur', handleQuickEditSave);
|
||||||
|
|
||||||
|
const nsfwPrompt = this.getPromptById('nsfw');
|
||||||
|
const nsfwElementId = this.updateQuickEdit('nsfw', nsfwPrompt);
|
||||||
|
document.getElementById(nsfwElementId).addEventListener('blur', handleQuickEditSave);
|
||||||
|
|
||||||
|
const jailbreakPrompt = this.getPromptById('jailbreak');
|
||||||
|
const jailbreakElementId = this.updateQuickEdit('jailbreak', jailbreakPrompt);
|
||||||
|
document.getElementById(jailbreakElementId).addEventListener('blur', handleQuickEditSave);
|
||||||
|
}
|
||||||
|
|
||||||
// Re-render when chat history changes.
|
// Re-render when chat history changes.
|
||||||
eventSource.on(event_types.MESSAGE_DELETED, () => this.renderDebounced());
|
eventSource.on(event_types.MESSAGE_DELETED, () => this.renderDebounced());
|
||||||
eventSource.on(event_types.MESSAGE_EDITED, () => this.renderDebounced());
|
eventSource.on(event_types.MESSAGE_EDITED, () => this.renderDebounced());
|
||||||
|
@ -520,6 +552,7 @@ PromptManagerModule.prototype.init = function (moduleConfiguration, serviceSetti
|
||||||
|
|
||||||
// Re-render when chatcompletion settings change
|
// Re-render when chatcompletion settings change
|
||||||
eventSource.on(event_types.CHATCOMPLETION_SOURCE_CHANGED, () => this.renderDebounced());
|
eventSource.on(event_types.CHATCOMPLETION_SOURCE_CHANGED, () => this.renderDebounced());
|
||||||
|
|
||||||
eventSource.on(event_types.CHATCOMPLETION_MODEL_CHANGED, () => this.renderDebounced());
|
eventSource.on(event_types.CHATCOMPLETION_MODEL_CHANGED, () => this.renderDebounced());
|
||||||
|
|
||||||
// Re-render when the character changes.
|
// Re-render when the character changes.
|
||||||
|
@ -577,6 +610,15 @@ PromptManagerModule.prototype.init = function (moduleConfiguration, serviceSetti
|
||||||
this.hidePopup();
|
this.hidePopup();
|
||||||
this.clearEditForm();
|
this.clearEditForm();
|
||||||
this.renderDebounced();
|
this.renderDebounced();
|
||||||
|
|
||||||
|
const mainPrompt = this.getPromptById('main');
|
||||||
|
this.updateQuickEdit('main', mainPrompt);
|
||||||
|
|
||||||
|
const nsfwPrompt = this.getPromptById('nsfw');
|
||||||
|
this.updateQuickEdit('nsfw', nsfwPrompt);
|
||||||
|
|
||||||
|
const jailbreakPrompt = this.getPromptById('jailbreak');
|
||||||
|
this.updateQuickEdit('jailbreak', jailbreakPrompt);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -609,6 +651,7 @@ PromptManagerModule.prototype.render = function (afterTryGenerate = true) {
|
||||||
this.makeDraggable();
|
this.makeDraggable();
|
||||||
this.profileEnd('render');
|
this.profileEnd('render');
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
this.profileEnd('filling context');
|
||||||
this.log('Error caught during render: ' + error);
|
this.log('Error caught during render: ' + error);
|
||||||
this.renderPromptManager();
|
this.renderPromptManager();
|
||||||
this.renderPromptManagerListItems()
|
this.renderPromptManagerListItems()
|
||||||
|
@ -1016,8 +1059,11 @@ PromptManagerModule.prototype.createQuickEdit = function (identifier, title) {
|
||||||
}
|
}
|
||||||
|
|
||||||
PromptManagerModule.prototype.updateQuickEdit = function (identifier, prompt) {
|
PromptManagerModule.prototype.updateQuickEdit = function (identifier, prompt) {
|
||||||
const textarea = document.getElementById(`${identifier}_prompt_quick_edit_textarea`);
|
const elementId = `${identifier}_prompt_quick_edit_textarea`;
|
||||||
|
const textarea = document.getElementById(elementId);
|
||||||
textarea.value = prompt.content;
|
textarea.value = prompt.content;
|
||||||
|
|
||||||
|
return elementId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1312,48 +1358,9 @@ PromptManagerModule.prototype.renderPromptManager = function () {
|
||||||
footerDiv.querySelector('#prompt-manager-export').addEventListener('click', showExportSelection);
|
footerDiv.querySelector('#prompt-manager-export').addEventListener('click', showExportSelection);
|
||||||
rangeBlockDiv.querySelector('.export-promptmanager-prompts-full').addEventListener('click', this.handleFullExport);
|
rangeBlockDiv.querySelector('.export-promptmanager-prompts-full').addEventListener('click', this.handleFullExport);
|
||||||
rangeBlockDiv.querySelector('.export-promptmanager-prompts-character')?.addEventListener('click', this.handleCharacterExport);
|
rangeBlockDiv.querySelector('.export-promptmanager-prompts-character')?.addEventListener('click', this.handleCharacterExport);
|
||||||
|
|
||||||
const quickEditContainer = document.getElementById('quick-edit-container');
|
|
||||||
const heights = this.saveTextAreaHeights(quickEditContainer);
|
|
||||||
quickEditContainer.innerHTML = '';
|
|
||||||
|
|
||||||
this.createQuickEdit('jailbreak', 'Jailbreak');
|
|
||||||
this.createQuickEdit('nsfw', 'NSFW');
|
|
||||||
this.createQuickEdit('main', 'Main');
|
|
||||||
|
|
||||||
this.restoreTextAreaHeights(quickEditContainer, heights);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Restores the height of each textarea in the container
|
|
||||||
* @param container The container to search for textareas
|
|
||||||
* @param heights An object with textarea ids as keys and heights as values
|
|
||||||
*/
|
|
||||||
PromptManagerModule.prototype.restoreTextAreaHeights = function(container, heights) {
|
|
||||||
if (Object.keys(heights).length === 0) return;
|
|
||||||
|
|
||||||
$(container).find('textarea').each(function () {
|
|
||||||
const height = heights[this.id];
|
|
||||||
if (height > 0) $(this).height(height);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the current height of each textarea in the container
|
|
||||||
* @param container The container to search for textareas
|
|
||||||
* @returns {{}} An object with textarea ids as keys and heights as values
|
|
||||||
*/
|
|
||||||
PromptManagerModule.prototype.saveTextAreaHeights = function(container) {
|
|
||||||
const heights = {};
|
|
||||||
|
|
||||||
$(container).find('textarea').each(function () {
|
|
||||||
heights[this.id] = $(this).height();
|
|
||||||
});
|
|
||||||
|
|
||||||
return heights;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Empties, then re-assembles the prompt list
|
* Empties, then re-assembles the prompt list
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -2,15 +2,12 @@ esversion: 6
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Generate,
|
Generate,
|
||||||
this_chid,
|
|
||||||
characters,
|
characters,
|
||||||
online_status,
|
online_status,
|
||||||
main_api,
|
main_api,
|
||||||
api_server,
|
api_server,
|
||||||
api_server_textgenerationwebui,
|
api_server_textgenerationwebui,
|
||||||
is_send_press,
|
is_send_press,
|
||||||
getTokenCount,
|
|
||||||
menu_type,
|
|
||||||
max_context,
|
max_context,
|
||||||
saveSettingsDebounced,
|
saveSettingsDebounced,
|
||||||
active_group,
|
active_group,
|
||||||
|
@ -33,10 +30,9 @@ import {
|
||||||
SECRET_KEYS,
|
SECRET_KEYS,
|
||||||
secret_state,
|
secret_state,
|
||||||
} from "./secrets.js";
|
} from "./secrets.js";
|
||||||
import { debounce, delay, getStringHash } from "./utils.js";
|
import { debounce, delay, getStringHash, waitUntilCondition } from "./utils.js";
|
||||||
import { chat_completion_sources, oai_settings } from "./openai.js";
|
import { chat_completion_sources, oai_settings } from "./openai.js";
|
||||||
|
import { getTokenCount } from "./tokenizers.js";
|
||||||
var NavToggle = document.getElementById("nav-toggle");
|
|
||||||
|
|
||||||
var RPanelPin = document.getElementById("rm_button_panel_pin");
|
var RPanelPin = document.getElementById("rm_button_panel_pin");
|
||||||
var LPanelPin = document.getElementById("lm_button_panel_pin");
|
var LPanelPin = document.getElementById("lm_button_panel_pin");
|
||||||
|
@ -47,20 +43,8 @@ var LeftNavPanel = document.getElementById("left-nav-panel");
|
||||||
var WorldInfo = document.getElementById("WorldInfo");
|
var WorldInfo = document.getElementById("WorldInfo");
|
||||||
|
|
||||||
var SelectedCharacterTab = document.getElementById("rm_button_selected_ch");
|
var SelectedCharacterTab = document.getElementById("rm_button_selected_ch");
|
||||||
var AdvancedCharDefsPopup = document.getElementById("character_popup");
|
|
||||||
var ConfirmationPopup = document.getElementById("dialogue_popup");
|
|
||||||
var AutoConnectCheckbox = document.getElementById("auto-connect-checkbox");
|
var AutoConnectCheckbox = document.getElementById("auto-connect-checkbox");
|
||||||
var AutoLoadChatCheckbox = document.getElementById("auto-load-chat-checkbox");
|
var AutoLoadChatCheckbox = document.getElementById("auto-load-chat-checkbox");
|
||||||
var SelectedNavTab = ("#" + LoadLocal('SelectedNavTab'));
|
|
||||||
|
|
||||||
var create_save_name;
|
|
||||||
var create_save_description;
|
|
||||||
var create_save_personality;
|
|
||||||
var create_save_first_message;
|
|
||||||
var create_save_scenario;
|
|
||||||
var create_save_mes_example;
|
|
||||||
var count_tokens;
|
|
||||||
var perm_tokens;
|
|
||||||
|
|
||||||
var connection_made = false;
|
var connection_made = false;
|
||||||
var retry_delay = 500;
|
var retry_delay = 500;
|
||||||
|
@ -83,32 +67,6 @@ const observer = new MutationObserver(function (mutations) {
|
||||||
|
|
||||||
observer.observe(document.documentElement, observerConfig);
|
observer.observe(document.documentElement, observerConfig);
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for an element before resolving a promise
|
|
||||||
* @param {String} querySelector - Selector of element to wait for
|
|
||||||
* @param {Integer} timeout - Milliseconds to wait before timing out, or 0 for no timeout
|
|
||||||
*/
|
|
||||||
function waitForElement(querySelector, timeout) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
var timer = false;
|
|
||||||
if (document.querySelectorAll(querySelector).length) return resolve();
|
|
||||||
const observer = new MutationObserver(() => {
|
|
||||||
if (document.querySelectorAll(querySelector).length) {
|
|
||||||
observer.disconnect();
|
|
||||||
if (timer !== false) clearTimeout(timer);
|
|
||||||
return resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
observer.observe(document.body, {
|
|
||||||
childList: true,
|
|
||||||
subtree: true
|
|
||||||
});
|
|
||||||
if (timeout) timer = setTimeout(() => {
|
|
||||||
observer.disconnect();
|
|
||||||
reject();
|
|
||||||
}, timeout);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts generation time from milliseconds to a human-readable format.
|
* Converts generation time from milliseconds to a human-readable format.
|
||||||
|
@ -225,14 +183,6 @@ export function getMessageTimeStamp() {
|
||||||
// triggers:
|
// triggers:
|
||||||
$("#rm_button_create").on("click", function () { //when "+New Character" is clicked
|
$("#rm_button_create").on("click", function () { //when "+New Character" is clicked
|
||||||
$(SelectedCharacterTab).children("h2").html(''); // empty nav's 3rd panel tab
|
$(SelectedCharacterTab).children("h2").html(''); // empty nav's 3rd panel tab
|
||||||
|
|
||||||
//empty temp vars to store new char data for counting
|
|
||||||
create_save_name = "";
|
|
||||||
create_save_description = "";
|
|
||||||
create_save_personality = "";
|
|
||||||
create_save_first_message = "";
|
|
||||||
create_save_scenario = "";
|
|
||||||
create_save_mes_example = "";
|
|
||||||
});
|
});
|
||||||
//when any input is made to the create/edit character form textareas
|
//when any input is made to the create/edit character form textareas
|
||||||
$("#rm_ch_create_block").on("input", function () { countTokensDebounced(); });
|
$("#rm_ch_create_block").on("input", function () { countTokensDebounced(); });
|
||||||
|
@ -245,7 +195,7 @@ export function RA_CountCharTokens() {
|
||||||
$('[data-token-counter]').each(function () {
|
$('[data-token-counter]').each(function () {
|
||||||
const counter = $(this);
|
const counter = $(this);
|
||||||
const input = $(document.getElementById(counter.data('token-counter')));
|
const input = $(document.getElementById(counter.data('token-counter')));
|
||||||
const value = input.val();
|
const value = String(input.val());
|
||||||
|
|
||||||
if (input.length === 0) {
|
if (input.length === 0) {
|
||||||
counter.text('Invalid input reference');
|
counter.text('Invalid input reference');
|
||||||
|
@ -413,7 +363,7 @@ function RA_autoconnect(PrevApi) {
|
||||||
case 'openai':
|
case 'openai':
|
||||||
if (((secret_state[SECRET_KEYS.OPENAI] || oai_settings.reverse_proxy) && oai_settings.chat_completion_source == chat_completion_sources.OPENAI)
|
if (((secret_state[SECRET_KEYS.OPENAI] || oai_settings.reverse_proxy) && oai_settings.chat_completion_source == chat_completion_sources.OPENAI)
|
||||||
|| ((secret_state[SECRET_KEYS.CLAUDE] || oai_settings.reverse_proxy) && oai_settings.chat_completion_source == chat_completion_sources.CLAUDE)
|
|| ((secret_state[SECRET_KEYS.CLAUDE] || oai_settings.reverse_proxy) && oai_settings.chat_completion_source == chat_completion_sources.CLAUDE)
|
||||||
|| (secret_state[SECRET_KEYS.SCALE] && oai_settings.chat_completion_source == chat_completion_sources.SCALE)
|
|| ((secret_state[SECRET_KEYS.SCALE] || secret_state[SECRET_KEYS.SCALE_COOKIE]) && oai_settings.chat_completion_source == chat_completion_sources.SCALE)
|
||||||
|| (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI)
|
|| (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI)
|
||||||
|| (secret_state[SECRET_KEYS.OPENROUTER] && oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER)
|
|| (secret_state[SECRET_KEYS.OPENROUTER] && oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER)
|
||||||
|| (secret_state[SECRET_KEYS.AI21] && oai_settings.chat_completion_source == chat_completion_sources.AI21)
|
|| (secret_state[SECRET_KEYS.AI21] && oai_settings.chat_completion_source == chat_completion_sources.AI21)
|
||||||
|
@ -717,7 +667,12 @@ export async function initMovingUI() {
|
||||||
|
|
||||||
// ---------------------------------------------------
|
// ---------------------------------------------------
|
||||||
|
|
||||||
$("document").ready(function () {
|
jQuery(async function () {
|
||||||
|
try {
|
||||||
|
await waitUntilCondition(() => online_status !== undefined, 1000, 10);
|
||||||
|
} catch {
|
||||||
|
console.log('Timeout waiting for online_status');
|
||||||
|
}
|
||||||
|
|
||||||
// initial status check
|
// initial status check
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -799,7 +754,7 @@ $("document").ready(function () {
|
||||||
//console.log('setting pin class via local var');
|
//console.log('setting pin class via local var');
|
||||||
$(RightNavPanel).addClass('pinnedOpen');
|
$(RightNavPanel).addClass('pinnedOpen');
|
||||||
}
|
}
|
||||||
if ($(RPanelPin).prop('checked' == true)) {
|
if (!!$(RPanelPin).prop('checked')) {
|
||||||
console.debug('setting pin class via checkbox state');
|
console.debug('setting pin class via checkbox state');
|
||||||
$(RightNavPanel).addClass('pinnedOpen');
|
$(RightNavPanel).addClass('pinnedOpen');
|
||||||
}
|
}
|
||||||
|
@ -809,7 +764,7 @@ $("document").ready(function () {
|
||||||
//console.log('setting pin class via local var');
|
//console.log('setting pin class via local var');
|
||||||
$(LeftNavPanel).addClass('pinnedOpen');
|
$(LeftNavPanel).addClass('pinnedOpen');
|
||||||
}
|
}
|
||||||
if ($(LPanelPin).prop('checked' == true)) {
|
if (!!$(LPanelPin).prop('checked')) {
|
||||||
console.debug('setting pin class via checkbox state');
|
console.debug('setting pin class via checkbox state');
|
||||||
$(LeftNavPanel).addClass('pinnedOpen');
|
$(LeftNavPanel).addClass('pinnedOpen');
|
||||||
}
|
}
|
||||||
|
@ -821,7 +776,7 @@ $("document").ready(function () {
|
||||||
$(WorldInfo).addClass('pinnedOpen');
|
$(WorldInfo).addClass('pinnedOpen');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($(WIPanelPin).prop('checked' == true)) {
|
if (!!$(WIPanelPin).prop('checked')) {
|
||||||
console.debug('setting pin class via checkbox state');
|
console.debug('setting pin class via checkbox state');
|
||||||
$(WorldInfo).addClass('pinnedOpen');
|
$(WorldInfo).addClass('pinnedOpen');
|
||||||
}
|
}
|
||||||
|
@ -884,8 +839,6 @@ $("document").ready(function () {
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//this makes the chat input text area resize vertically to match the text size (limited by CSS at 50% window height)
|
//this makes the chat input text area resize vertically to match the text size (limited by CSS at 50% window height)
|
||||||
$('#send_textarea').on('input', function () {
|
$('#send_textarea').on('input', function () {
|
||||||
this.style.height = '40px';
|
this.style.height = '40px';
|
||||||
|
@ -896,7 +849,7 @@ $("document").ready(function () {
|
||||||
|
|
||||||
document.addEventListener('swiped-left', function (e) {
|
document.addEventListener('swiped-left', function (e) {
|
||||||
var SwipeButR = $('.swipe_right:last');
|
var SwipeButR = $('.swipe_right:last');
|
||||||
var SwipeTargetMesClassParent = e.target.closest('.last_mes');
|
var SwipeTargetMesClassParent = $(e.target).closest('.last_mes');
|
||||||
if (SwipeTargetMesClassParent !== null) {
|
if (SwipeTargetMesClassParent !== null) {
|
||||||
if (SwipeButR.css('display') === 'flex') {
|
if (SwipeButR.css('display') === 'flex') {
|
||||||
SwipeButR.click();
|
SwipeButR.click();
|
||||||
|
@ -905,7 +858,7 @@ $("document").ready(function () {
|
||||||
});
|
});
|
||||||
document.addEventListener('swiped-right', function (e) {
|
document.addEventListener('swiped-right', function (e) {
|
||||||
var SwipeButL = $('.swipe_left:last');
|
var SwipeButL = $('.swipe_left:last');
|
||||||
var SwipeTargetMesClassParent = e.target.closest('.last_mes');
|
var SwipeTargetMesClassParent = $(e.target).closest('.last_mes');
|
||||||
if (SwipeTargetMesClassParent !== null) {
|
if (SwipeTargetMesClassParent !== null) {
|
||||||
if (SwipeButL.css('display') === 'flex') {
|
if (SwipeButL.css('display') === 'flex') {
|
||||||
SwipeButL.click();
|
SwipeButL.click();
|
||||||
|
|
|
@ -2,7 +2,6 @@ import {
|
||||||
chat_metadata,
|
chat_metadata,
|
||||||
eventSource,
|
eventSource,
|
||||||
event_types,
|
event_types,
|
||||||
getTokenCount,
|
|
||||||
saveSettingsDebounced,
|
saveSettingsDebounced,
|
||||||
this_chid,
|
this_chid,
|
||||||
} from "../script.js";
|
} from "../script.js";
|
||||||
|
@ -10,6 +9,7 @@ import { selected_group } from "./group-chats.js";
|
||||||
import { extension_settings, getContext, saveMetadataDebounced } from "./extensions.js";
|
import { extension_settings, getContext, saveMetadataDebounced } from "./extensions.js";
|
||||||
import { registerSlashCommand } from "./slash-commands.js";
|
import { registerSlashCommand } from "./slash-commands.js";
|
||||||
import { getCharaFilename, debounce, waitUntilCondition, delay } from "./utils.js";
|
import { getCharaFilename, debounce, waitUntilCondition, delay } from "./utils.js";
|
||||||
|
import { getTokenCount } from "./tokenizers.js";
|
||||||
export { MODULE_NAME as NOTE_MODULE_NAME };
|
export { MODULE_NAME as NOTE_MODULE_NAME };
|
||||||
|
|
||||||
const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
|
const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { callPopup, eventSource, event_types, saveSettings, saveSettingsDebounced, getRequestHeaders } from "../script.js";
|
import { callPopup, eventSource, event_types, saveSettings, saveSettingsDebounced, getRequestHeaders, substituteParams } from "../script.js";
|
||||||
import { isSubsetOf, debounce } from "./utils.js";
|
import { isSubsetOf, debounce, waitUntilCondition } from "./utils.js";
|
||||||
export {
|
export {
|
||||||
getContext,
|
getContext,
|
||||||
getApiUrl,
|
getApiUrl,
|
||||||
|
@ -12,10 +12,46 @@ export {
|
||||||
};
|
};
|
||||||
|
|
||||||
let extensionNames = [];
|
let extensionNames = [];
|
||||||
let manifests = [];
|
let manifests = {};
|
||||||
const defaultUrl = "http://localhost:5100";
|
const defaultUrl = "http://localhost:5100";
|
||||||
export const saveMetadataDebounced = debounce(async () => await getContext().saveMetadata(), 1000);
|
export const saveMetadataDebounced = debounce(async () => await getContext().saveMetadata(), 1000);
|
||||||
|
|
||||||
|
export const extensionsHandlebars = Handlebars.create();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a Handlebars helper for use in extensions.
|
||||||
|
* @param {string} name Handlebars helper name
|
||||||
|
* @param {function} helper Handlebars helper function
|
||||||
|
*/
|
||||||
|
export function registerExtensionHelper(name, helper) {
|
||||||
|
extensionsHandlebars.registerHelper(name, helper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies handlebars extension helpers to a message.
|
||||||
|
* @param {number} messageId Message index in the chat.
|
||||||
|
*/
|
||||||
|
export function processExtensionHelpers(messageId) {
|
||||||
|
const context = getContext();
|
||||||
|
const message = context.chat[messageId];
|
||||||
|
|
||||||
|
if (!message?.mes || typeof message.mes !== 'string') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't waste time if there are no mustaches
|
||||||
|
if (!substituteParams(message.mes).includes('{{')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const template = extensionsHandlebars.compile(substituteParams(message.mes), { noEscape: true });
|
||||||
|
message.mes = template({});
|
||||||
|
} catch {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Disables parallel updates
|
// Disables parallel updates
|
||||||
class ModuleWorkerWrapper {
|
class ModuleWorkerWrapper {
|
||||||
constructor(callback) {
|
constructor(callback) {
|
||||||
|
@ -175,7 +211,10 @@ async function getManifests(names) {
|
||||||
} else {
|
} else {
|
||||||
reject();
|
reject();
|
||||||
}
|
}
|
||||||
}).catch(err => reject() && console.log('Could not load manifest.json for ' + name, err));
|
}).catch(err => {
|
||||||
|
reject();
|
||||||
|
console.log('Could not load manifest.json for ' + name, err);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
promises.push(promise);
|
promises.push(promise);
|
||||||
|
@ -232,9 +271,9 @@ async function activateExtensions() {
|
||||||
|
|
||||||
async function connectClickHandler() {
|
async function connectClickHandler() {
|
||||||
const baseUrl = $("#extensions_url").val();
|
const baseUrl = $("#extensions_url").val();
|
||||||
extension_settings.apiUrl = baseUrl;
|
extension_settings.apiUrl = String(baseUrl);
|
||||||
const testApiKey = $("#extensions_api_key").val();
|
const testApiKey = $("#extensions_api_key").val();
|
||||||
extension_settings.apiKey = testApiKey;
|
extension_settings.apiKey = String(testApiKey);
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
await connectToApi(baseUrl);
|
await connectToApi(baseUrl);
|
||||||
}
|
}
|
||||||
|
@ -459,7 +498,7 @@ async function generateExtensionHtml(name, manifest, isActive, isDisabled, isExt
|
||||||
* Gets extension data and generates the corresponding HTML for displaying the extension.
|
* Gets extension data and generates the corresponding HTML for displaying the extension.
|
||||||
*
|
*
|
||||||
* @param {Array} extension - An array where the first element is the extension name and the second element is the extension manifest.
|
* @param {Array} extension - An array where the first element is the extension name and the second element is the extension manifest.
|
||||||
* @return {object} - An object with 'isExternal' indicating whether the extension is external, and 'extensionHtml' for the extension's HTML string.
|
* @return {Promise<object>} - An object with 'isExternal' indicating whether the extension is external, and 'extensionHtml' for the extension's HTML string.
|
||||||
*/
|
*/
|
||||||
async function getExtensionData(extension) {
|
async function getExtensionData(extension) {
|
||||||
const name = extension[0];
|
const name = extension[0];
|
||||||
|
@ -576,7 +615,7 @@ async function onDeleteClick() {
|
||||||
* Fetches the version details of a specific extension.
|
* Fetches the version details of a specific extension.
|
||||||
*
|
*
|
||||||
* @param {string} extensionName - The name of the extension.
|
* @param {string} extensionName - The name of the extension.
|
||||||
* @return {object} - An object containing the extension's version details.
|
* @return {Promise<object>} - An object containing the extension's version details.
|
||||||
* This object includes the currentBranchName, currentCommitHash, isUpToDate, and remoteUrl.
|
* This object includes the currentBranchName, currentCommitHash, isUpToDate, and remoteUrl.
|
||||||
* @throws {error} - If there is an error during the fetch operation, it logs the error to the console.
|
* @throws {error} - If there is an error during the fetch operation, it logs the error to the console.
|
||||||
*/
|
*/
|
||||||
|
@ -629,8 +668,8 @@ async function runGenerationInterceptors(chat, contextSize) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(async function () {
|
jQuery(function () {
|
||||||
setTimeout(function () {
|
setTimeout(async function () {
|
||||||
addExtensionsButtonAndMenu();
|
addExtensionsButtonAndMenu();
|
||||||
$("#extensionsMenuButton").css("display", "flex");
|
$("#extensionsMenuButton").css("display", "flex");
|
||||||
}, 100)
|
}, 100)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { getBase64Async } from "../../utils.js";
|
import { getBase64Async } from "../../utils.js";
|
||||||
import { getContext, getApiUrl, doExtrasFetch, extension_settings } from "../../extensions.js";
|
import { getContext, getApiUrl, doExtrasFetch, extension_settings } from "../../extensions.js";
|
||||||
import { callPopup, saveSettingsDebounced } from "../../../script.js";
|
import { callPopup, saveSettingsDebounced } from "../../../script.js";
|
||||||
|
import { getMessageTimeStamp } from "../../RossAscends-mods.js";
|
||||||
export { MODULE_NAME };
|
export { MODULE_NAME };
|
||||||
|
|
||||||
const MODULE_NAME = 'caption';
|
const MODULE_NAME = 'caption';
|
||||||
|
@ -52,7 +53,7 @@ async function sendCaptionedMessage(caption, image) {
|
||||||
name: context.name1,
|
name: context.name1,
|
||||||
is_user: true,
|
is_user: true,
|
||||||
is_name: true,
|
is_name: true,
|
||||||
send_date: Date.now(),
|
send_date: getMessageTimeStamp(),
|
||||||
mes: messageText,
|
mes: messageText,
|
||||||
extra: {
|
extra: {
|
||||||
image: image,
|
image: image,
|
||||||
|
|
|
@ -23,7 +23,8 @@ const defaultSettings = {
|
||||||
};
|
};
|
||||||
const settingType = {
|
const settingType = {
|
||||||
guidance_scale: 0,
|
guidance_scale: 0,
|
||||||
negative_prompt: 1
|
negative_prompt: 1,
|
||||||
|
positive_prompt: 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used for character and chat CFG values
|
// Used for character and chat CFG values
|
||||||
|
@ -36,19 +37,19 @@ function setCharCfg(tempValue, setting) {
|
||||||
const avatarName = getCharaFilename();
|
const avatarName = getCharaFilename();
|
||||||
|
|
||||||
// Assign temp object
|
// Assign temp object
|
||||||
let tempCharaCfg;
|
let tempCharaCfg = {
|
||||||
|
name: avatarName
|
||||||
|
};
|
||||||
|
|
||||||
switch(setting) {
|
switch(setting) {
|
||||||
case settingType.guidance_scale:
|
case settingType.guidance_scale:
|
||||||
tempCharaCfg = {
|
tempCharaCfg["guidance_scale"] = Number(tempValue);
|
||||||
"name": avatarName,
|
|
||||||
"guidance_scale": Number(tempValue)
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case settingType.negative_prompt:
|
case settingType.negative_prompt:
|
||||||
tempCharaCfg = {
|
tempCharaCfg["negative_prompt"] = tempValue;
|
||||||
"name": avatarName,
|
break;
|
||||||
"negative_prompt": tempValue
|
case settingType.positive_prompt:
|
||||||
}
|
tempCharaCfg["positive_prompt"] = tempValue;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -66,7 +67,11 @@ function setCharCfg(tempValue, setting) {
|
||||||
const tempAssign = Object.assign(existingCharaCfg, tempCharaCfg);
|
const tempAssign = Object.assign(existingCharaCfg, tempCharaCfg);
|
||||||
|
|
||||||
// If both values are default, remove the entry
|
// If both values are default, remove the entry
|
||||||
if (!existingCharaCfg.useChara && (tempAssign.guidance_scale ?? 1.00) === 1.00 && (tempAssign.negative_prompt?.length ?? 0) === 0) {
|
if (!existingCharaCfg.useChara &&
|
||||||
|
(tempAssign.guidance_scale ?? 1.00) === 1.00 &&
|
||||||
|
(tempAssign.negative_prompt?.length ?? 0) === 0 &&
|
||||||
|
(tempAssign.positive_prompt?.length ?? 0) === 0)
|
||||||
|
{
|
||||||
extension_settings.cfg.chara.splice(existingCharaCfgIndex, 1);
|
extension_settings.cfg.chara.splice(existingCharaCfgIndex, 1);
|
||||||
}
|
}
|
||||||
} else if (avatarName && tempValue.length > 0) {
|
} else if (avatarName && tempValue.length > 0) {
|
||||||
|
@ -95,6 +100,9 @@ function setChatCfg(tempValue, setting) {
|
||||||
case settingType.negative_prompt:
|
case settingType.negative_prompt:
|
||||||
chat_metadata[metadataKeys.negative_prompt] = tempValue;
|
chat_metadata[metadataKeys.negative_prompt] = tempValue;
|
||||||
break;
|
break;
|
||||||
|
case settingType.positive_prompt:
|
||||||
|
chat_metadata[metadataKeys.positive_prompt] = tempValue;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -174,20 +182,40 @@ function loadSettings() {
|
||||||
$('#chat_cfg_guidance_scale').val(chat_metadata[metadataKeys.guidance_scale] ?? 1.0.toFixed(2));
|
$('#chat_cfg_guidance_scale').val(chat_metadata[metadataKeys.guidance_scale] ?? 1.0.toFixed(2));
|
||||||
$('#chat_cfg_guidance_scale_counter').text(chat_metadata[metadataKeys.guidance_scale]?.toFixed(2) ?? 1.0.toFixed(2));
|
$('#chat_cfg_guidance_scale_counter').text(chat_metadata[metadataKeys.guidance_scale]?.toFixed(2) ?? 1.0.toFixed(2));
|
||||||
$('#chat_cfg_negative_prompt').val(chat_metadata[metadataKeys.negative_prompt] ?? '');
|
$('#chat_cfg_negative_prompt').val(chat_metadata[metadataKeys.negative_prompt] ?? '');
|
||||||
|
$('#chat_cfg_positive_prompt').val(chat_metadata[metadataKeys.positive_prompt] ?? '');
|
||||||
$('#groupchat_cfg_use_chara').prop('checked', chat_metadata[metadataKeys.groupchat_individual_chars] ?? false);
|
$('#groupchat_cfg_use_chara').prop('checked', chat_metadata[metadataKeys.groupchat_individual_chars] ?? false);
|
||||||
if (chat_metadata[metadataKeys.negative_combine]?.length > 0) {
|
if (chat_metadata[metadataKeys.prompt_combine]?.length > 0) {
|
||||||
chat_metadata[metadataKeys.negative_combine].forEach((element) => {
|
chat_metadata[metadataKeys.prompt_combine].forEach((element) => {
|
||||||
$(`input[name="cfg_negative_combine"][value="${element}"]`)
|
$(`input[name="cfg_prompt_combine"][value="${element}"]`)
|
||||||
.prop("checked", true);
|
.prop("checked", true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Display the negative separator in quotes if not quoted already
|
||||||
|
let promptSeparatorDisplay = [];
|
||||||
|
const promptSeparator = chat_metadata[metadataKeys.prompt_separator];
|
||||||
|
if (promptSeparator) {
|
||||||
|
promptSeparatorDisplay.push(promptSeparator);
|
||||||
|
if (!promptSeparator.startsWith(`"`)) {
|
||||||
|
promptSeparatorDisplay.unshift(`"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!promptSeparator.endsWith(`"`)) {
|
||||||
|
promptSeparatorDisplay.push(`"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#cfg_prompt_separator').val(promptSeparatorDisplay.length === 0 ? '' : promptSeparatorDisplay.join(''));
|
||||||
|
|
||||||
|
$('#cfg_prompt_insertion_depth').val(chat_metadata[metadataKeys.prompt_insertion_depth] ?? 1);
|
||||||
|
|
||||||
// Set character CFG if it exists
|
// Set character CFG if it exists
|
||||||
if (!selected_group) {
|
if (!selected_group) {
|
||||||
const charaCfg = extension_settings.cfg.chara.find((e) => e.name === getCharaFilename());
|
const charaCfg = extension_settings.cfg.chara.find((e) => e.name === getCharaFilename());
|
||||||
$('#chara_cfg_guidance_scale').val(charaCfg?.guidance_scale ?? 1.00);
|
$('#chara_cfg_guidance_scale').val(charaCfg?.guidance_scale ?? 1.00);
|
||||||
$('#chara_cfg_guidance_scale_counter').text(charaCfg?.guidance_scale?.toFixed(2) ?? 1.0.toFixed(2));
|
$('#chara_cfg_guidance_scale_counter').text(charaCfg?.guidance_scale?.toFixed(2) ?? 1.0.toFixed(2));
|
||||||
$('#chara_cfg_negative_prompt').val(charaCfg?.negative_prompt ?? '');
|
$('#chara_cfg_negative_prompt').val(charaCfg?.negative_prompt ?? '');
|
||||||
|
$('#chara_cfg_positive_prompt').val(charaCfg?.positive_prompt ?? '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,26 +232,50 @@ async function initialLoadSettings() {
|
||||||
$('#global_cfg_guidance_scale').val(extension_settings.cfg.global.guidance_scale);
|
$('#global_cfg_guidance_scale').val(extension_settings.cfg.global.guidance_scale);
|
||||||
$('#global_cfg_guidance_scale_counter').text(extension_settings.cfg.global.guidance_scale.toFixed(2));
|
$('#global_cfg_guidance_scale_counter').text(extension_settings.cfg.global.guidance_scale.toFixed(2));
|
||||||
$('#global_cfg_negative_prompt').val(extension_settings.cfg.global.negative_prompt);
|
$('#global_cfg_negative_prompt').val(extension_settings.cfg.global.negative_prompt);
|
||||||
|
$('#global_cfg_positive_prompt').val(extension_settings.cfg.global.positive_prompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
function migrateSettings() {
|
function migrateSettings() {
|
||||||
let performSave = false;
|
let performSettingsSave = false;
|
||||||
|
let performMetaSave = false;
|
||||||
|
|
||||||
if (power_user.guidance_scale) {
|
if (power_user.guidance_scale) {
|
||||||
extension_settings.cfg.global.guidance_scale = power_user.guidance_scale;
|
extension_settings.cfg.global.guidance_scale = power_user.guidance_scale;
|
||||||
delete power_user['guidance_scale'];
|
delete power_user['guidance_scale'];
|
||||||
performSave = true;
|
performSettingsSave = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (power_user.negative_prompt) {
|
if (power_user.negative_prompt) {
|
||||||
extension_settings.cfg.global.negative_prompt = power_user.negative_prompt;
|
extension_settings.cfg.global.negative_prompt = power_user.negative_prompt;
|
||||||
delete power_user['negative_prompt'];
|
delete power_user['negative_prompt'];
|
||||||
performSave = true;
|
performSettingsSave = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (performSave) {
|
if (chat_metadata["cfg_negative_combine"]) {
|
||||||
|
chat_metadata[metadataKeys.prompt_combine] = chat_metadata["cfg_negative_combine"];
|
||||||
|
chat_metadata["cfg_negative_combine"] = undefined;
|
||||||
|
performMetaSave = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chat_metadata["cfg_negative_insertion_depth"]) {
|
||||||
|
chat_metadata[metadataKeys.prompt_insertion_depth] = chat_metadata["cfg_negative_insertion_depth"];
|
||||||
|
chat_metadata["cfg_negative_insertion_depth"] = undefined;
|
||||||
|
performMetaSave = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chat_metadata["cfg_negative_separator"]) {
|
||||||
|
chat_metadata[metadataKeys.prompt_separator] = chat_metadata["cfg_negative_separator"];
|
||||||
|
chat_metadata["cfg_negative_separator"] = undefined;
|
||||||
|
performMetaSave = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (performSettingsSave) {
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (performMetaSave) {
|
||||||
|
saveMetadataDebounced();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is called when the extension is loaded
|
// This function is called when the extension is loaded
|
||||||
|
@ -255,6 +307,10 @@ jQuery(async () => {
|
||||||
setChatCfg($(this).val(), settingType.negative_prompt);
|
setChatCfg($(this).val(), settingType.negative_prompt);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
windowHtml.find('#chat_cfg_positive_prompt').on('input', function() {
|
||||||
|
setChatCfg($(this).val(), settingType.positive_prompt);
|
||||||
|
});
|
||||||
|
|
||||||
windowHtml.find('#chara_cfg_guidance_scale').on('input', function() {
|
windowHtml.find('#chara_cfg_guidance_scale').on('input', function() {
|
||||||
const value = $(this).val();
|
const value = $(this).val();
|
||||||
const success = setCharCfg(value, settingType.guidance_scale);
|
const success = setCharCfg(value, settingType.guidance_scale);
|
||||||
|
@ -267,6 +323,10 @@ jQuery(async () => {
|
||||||
setCharCfg($(this).val(), settingType.negative_prompt);
|
setCharCfg($(this).val(), settingType.negative_prompt);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
windowHtml.find('#chara_cfg_positive_prompt').on('input', function() {
|
||||||
|
setCharCfg($(this).val(), settingType.positive_prompt);
|
||||||
|
});
|
||||||
|
|
||||||
windowHtml.find('#global_cfg_guidance_scale').on('input', function() {
|
windowHtml.find('#global_cfg_guidance_scale').on('input', function() {
|
||||||
extension_settings.cfg.global.guidance_scale = Number($(this).val());
|
extension_settings.cfg.global.guidance_scale = Number($(this).val());
|
||||||
$('#global_cfg_guidance_scale_counter').text(extension_settings.cfg.global.guidance_scale.toFixed(2));
|
$('#global_cfg_guidance_scale_counter').text(extension_settings.cfg.global.guidance_scale.toFixed(2));
|
||||||
|
@ -278,14 +338,29 @@ jQuery(async () => {
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
windowHtml.find(`input[name="cfg_negative_combine"]`).on('input', function() {
|
windowHtml.find('#global_cfg_positive_prompt').on('input', function() {
|
||||||
const values = windowHtml.find(`input[name="cfg_negative_combine"]`)
|
extension_settings.cfg.global.positive_prompt = $(this).val();
|
||||||
|
saveSettingsDebounced();
|
||||||
|
});
|
||||||
|
|
||||||
|
windowHtml.find(`input[name="cfg_prompt_combine"]`).on('input', function() {
|
||||||
|
const values = windowHtml.find(`input[name="cfg_prompt_combine"]`)
|
||||||
.filter(":checked")
|
.filter(":checked")
|
||||||
.map(function() { return parseInt($(this).val()) })
|
.map(function() { return parseInt($(this).val()) })
|
||||||
.get()
|
.get()
|
||||||
.filter((e) => e !== NaN) || [];
|
.filter((e) => e !== NaN) || [];
|
||||||
|
|
||||||
chat_metadata[metadataKeys.negative_combine] = values;
|
chat_metadata[metadataKeys.prompt_combine] = values;
|
||||||
|
saveMetadataDebounced();
|
||||||
|
});
|
||||||
|
|
||||||
|
windowHtml.find(`#cfg_prompt_insertion_depth`).on('input', function() {
|
||||||
|
chat_metadata[metadataKeys.prompt_insertion_depth] = Number($(this).val());
|
||||||
|
saveMetadataDebounced();
|
||||||
|
});
|
||||||
|
|
||||||
|
windowHtml.find(`#cfg_prompt_separator`).on('input', function() {
|
||||||
|
chat_metadata[metadataKeys.prompt_separator] = $(this).val();
|
||||||
saveMetadataDebounced();
|
saveMetadataDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { chat_metadata, this_chid } from "../../../script.js";
|
import { chat_metadata, substituteParams, this_chid } from "../../../script.js";
|
||||||
import { extension_settings, getContext } from "../../extensions.js"
|
import { extension_settings, getContext } from "../../extensions.js"
|
||||||
import { selected_group } from "../../group-chats.js";
|
import { selected_group } from "../../group-chats.js";
|
||||||
import { getCharaFilename } from "../../utils.js";
|
import { getCharaFilename } from "../../utils.js";
|
||||||
|
@ -11,46 +11,20 @@ export const cfgType = {
|
||||||
export const metadataKeys = {
|
export const metadataKeys = {
|
||||||
guidance_scale: "cfg_guidance_scale",
|
guidance_scale: "cfg_guidance_scale",
|
||||||
negative_prompt: "cfg_negative_prompt",
|
negative_prompt: "cfg_negative_prompt",
|
||||||
negative_combine: "cfg_negative_combine",
|
positive_prompt: "cfg_positive_prompt",
|
||||||
groupchat_individual_chars: "cfg_groupchat_individual_chars"
|
prompt_combine: "cfg_prompt_combine",
|
||||||
|
groupchat_individual_chars: "cfg_groupchat_individual_chars",
|
||||||
|
prompt_insertion_depth: "cfg_prompt_insertion_depth",
|
||||||
|
prompt_separator: "cfg_prompt_separator"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the CFG value from hierarchy of chat -> character -> global
|
// Gets the CFG guidance scale
|
||||||
// Returns undefined values which should be handled in the respective backend APIs
|
// If the guidance scale is 1, ignore the CFG prompt(s) since it won't be used anyways
|
||||||
export function getCfg() {
|
export function getGuidanceScale() {
|
||||||
let splitNegativePrompt = [];
|
|
||||||
const charaCfg = extension_settings.cfg.chara?.find((e) => e.name === getCharaFilename(this_chid));
|
const charaCfg = extension_settings.cfg.chara?.find((e) => e.name === getCharaFilename(this_chid));
|
||||||
const guidanceScale = getGuidanceScale(charaCfg);
|
|
||||||
const chatNegativeCombine = chat_metadata[metadataKeys.negative_combine] ?? [];
|
|
||||||
|
|
||||||
// If there's a guidance scale, continue. Otherwise assume undefined
|
|
||||||
if (guidanceScale?.value && guidanceScale?.value !== 1) {
|
|
||||||
if (guidanceScale.type === cfgType.chat || chatNegativeCombine.includes(cfgType.chat)) {
|
|
||||||
splitNegativePrompt.push(chat_metadata[metadataKeys.negative_prompt]?.trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (guidanceScale.type === cfgType.chara || chatNegativeCombine.includes(cfgType.chara)) {
|
|
||||||
splitNegativePrompt.push(charaCfg.negative_prompt?.trim())
|
|
||||||
}
|
|
||||||
|
|
||||||
if (guidanceScale.type === cfgType.global || chatNegativeCombine.includes(cfgType.global)) {
|
|
||||||
splitNegativePrompt.push(extension_settings.cfg.global.negative_prompt?.trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
const combinedNegatives = splitNegativePrompt.filter((e) => e.length > 0).join(", ");
|
|
||||||
console.debug(`Setting CFG with guidance scale: ${guidanceScale.value}, negatives: ${combinedNegatives}`)
|
|
||||||
|
|
||||||
return {
|
|
||||||
guidanceScale: guidanceScale.value,
|
|
||||||
negativePrompt: combinedNegatives
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the guidance scale is 1, ignore the CFG negative prompt since it won't be used anyways
|
|
||||||
function getGuidanceScale(charaCfg) {
|
|
||||||
const chatGuidanceScale = chat_metadata[metadataKeys.guidance_scale];
|
const chatGuidanceScale = chat_metadata[metadataKeys.guidance_scale];
|
||||||
const groupchatCharOverride = chat_metadata[metadataKeys.groupchat_individual_chars] ?? false;
|
const groupchatCharOverride = chat_metadata[metadataKeys.groupchat_individual_chars] ?? false;
|
||||||
|
|
||||||
if (chatGuidanceScale && chatGuidanceScale !== 1 && !groupchatCharOverride) {
|
if (chatGuidanceScale && chatGuidanceScale !== 1 && !groupchatCharOverride) {
|
||||||
return {
|
return {
|
||||||
type: cfgType.chat,
|
type: cfgType.chat,
|
||||||
|
@ -70,3 +44,48 @@ function getGuidanceScale(charaCfg) {
|
||||||
value: extension_settings.cfg.global.guidance_scale
|
value: extension_settings.cfg.global.guidance_scale
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gets the CFG prompt
|
||||||
|
export function getCfgPrompt(guidanceScale, isNegative) {
|
||||||
|
let splitCfgPrompt = [];
|
||||||
|
|
||||||
|
const cfgPromptCombine = chat_metadata[metadataKeys.prompt_combine] ?? [];
|
||||||
|
if (guidanceScale.type === cfgType.chat || cfgPromptCombine.includes(cfgType.chat)) {
|
||||||
|
splitCfgPrompt.unshift(
|
||||||
|
substituteParams(
|
||||||
|
chat_metadata[isNegative ? metadataKeys.negative_prompt : metadataKeys.positive_prompt]
|
||||||
|
)
|
||||||
|
?.trim()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const charaCfg = extension_settings.cfg.chara?.find((e) => e.name === getCharaFilename(this_chid));
|
||||||
|
if (guidanceScale.type === cfgType.chara || cfgPromptCombine.includes(cfgType.chara)) {
|
||||||
|
splitCfgPrompt.unshift(
|
||||||
|
substituteParams(
|
||||||
|
isNegative ? charaCfg.negative_prompt : charaCfg.positive_prompt
|
||||||
|
)
|
||||||
|
?.trim()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (guidanceScale.type === cfgType.global || cfgPromptCombine.includes(cfgType.global)) {
|
||||||
|
splitCfgPrompt.unshift(
|
||||||
|
substituteParams(
|
||||||
|
isNegative ? extension_settings.cfg.global.negative_prompt : extension_settings.cfg.global.positive_prompt
|
||||||
|
)
|
||||||
|
?.trim()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This line is a bit hacky with a JSON.stringify and JSON.parse. Fix this if possible.
|
||||||
|
const customSeparator = JSON.parse(chat_metadata[metadataKeys.prompt_separator] || JSON.stringify("\n")) ?? "\n";
|
||||||
|
const combinedCfgPrompt = splitCfgPrompt.filter((e) => e.length > 0).join(customSeparator);
|
||||||
|
const insertionDepth = chat_metadata[metadataKeys.prompt_insertion_depth] ?? 1;
|
||||||
|
console.log(`Setting CFG with guidance scale: ${guidanceScale.value}, negatives: ${combinedCfgPrompt}`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
value: combinedCfgPrompt,
|
||||||
|
depth: insertionDepth
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<small>
|
<small>
|
||||||
<b>Unique to this chat.</b><br>
|
<b>Unique to this chat.</b><br>
|
||||||
</small>
|
</small>
|
||||||
<label for="chat_cfg_negative_prompt">
|
<label for="chat_cfg_guidance_scale">
|
||||||
<span data-i18n="Scale">Scale</span>
|
<span data-i18n="Scale">Scale</span>
|
||||||
<small data-i18n="1 = disabled">1 = disabled</small>
|
<small data-i18n="1 = disabled">1 = disabled</small>
|
||||||
</label>
|
</label>
|
||||||
|
@ -33,6 +33,11 @@
|
||||||
<span data-i18n="Negative Prompt">Negative Prompt</span>
|
<span data-i18n="Negative Prompt">Negative Prompt</span>
|
||||||
</label>
|
</label>
|
||||||
<textarea id="chat_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
<textarea id="chat_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||||
|
|
||||||
|
<label for="chat_cfg_positive_prompt">
|
||||||
|
<span data-i18n="Positive Prompt">Positive Prompt</span>
|
||||||
|
</label>
|
||||||
|
<textarea id="chat_cfg_positive_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div id="groupchat_cfg_use_chara_container">
|
<div id="groupchat_cfg_use_chara_container">
|
||||||
<label class="checkbox_label" for="groupchat_cfg_use_chara">
|
<label class="checkbox_label" for="groupchat_cfg_use_chara">
|
||||||
|
@ -53,7 +58,7 @@
|
||||||
<div class="inline-drawer-content">
|
<div class="inline-drawer-content">
|
||||||
<small><b>Will be automatically added as the CFG for this character.</b></small>
|
<small><b>Will be automatically added as the CFG for this character.</b></small>
|
||||||
<br />
|
<br />
|
||||||
<label for="chara_cfg_negative_prompt">
|
<label for="chara_cfg_guidance_scale">
|
||||||
<span data-i18n="Scale">Scale</span>
|
<span data-i18n="Scale">Scale</span>
|
||||||
<small data-i18n="1 = disabled">1 = disabled</small>
|
<small data-i18n="1 = disabled">1 = disabled</small>
|
||||||
</label>
|
</label>
|
||||||
|
@ -72,6 +77,11 @@
|
||||||
<span data-i18n="Negative Prompt">Negative Prompt</span>
|
<span data-i18n="Negative Prompt">Negative Prompt</span>
|
||||||
</label>
|
</label>
|
||||||
<textarea id="chara_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
<textarea id="chara_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||||
|
|
||||||
|
<label for="chara_cfg_positive_prompt">
|
||||||
|
<span data-i18n="Positive Prompt">Positive Prompt</span>
|
||||||
|
</label>
|
||||||
|
<textarea id="chara_cfg_positive_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -86,7 +96,7 @@
|
||||||
<div class="inline-drawer-content">
|
<div class="inline-drawer-content">
|
||||||
<small><b>Will be used as the default CFG options for every chat unless overridden.</b></small>
|
<small><b>Will be used as the default CFG options for every chat unless overridden.</b></small>
|
||||||
<br />
|
<br />
|
||||||
<label for="global_cfg_negative_prompt">
|
<label for="global_cfg_guidance_scale">
|
||||||
<span data-i18n="Scale">Scale</span>
|
<span data-i18n="Scale">Scale</span>
|
||||||
<small data-i18n="1 = disabled">1 = disabled</small>
|
<small data-i18n="1 = disabled">1 = disabled</small>
|
||||||
</label>
|
</label>
|
||||||
|
@ -105,40 +115,57 @@
|
||||||
<span data-i18n="Negative Prompt">Negative Prompt</span>
|
<span data-i18n="Negative Prompt">Negative Prompt</span>
|
||||||
</label>
|
</label>
|
||||||
<textarea id="global_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
<textarea id="global_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||||
|
|
||||||
|
<label for="global_cfg_positive_prompt">
|
||||||
|
<span data-i18n="Positive Prompt">Positive Prompt</span>
|
||||||
|
</label>
|
||||||
|
<textarea id="global_cfg_positive_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="cfg_negative_combine_container">
|
<div id="cfg_prompt_combine_container">
|
||||||
<hr class="sysHR">
|
<hr class="sysHR">
|
||||||
<div class="inline-drawer">
|
<div class="inline-drawer">
|
||||||
<div id="defaultANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
|
<div id="defaultANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
|
||||||
<b>Negative Cascading</b>
|
<b>CFG Prompt Cascading</b>
|
||||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="inline-drawer-content">
|
<div class="inline-drawer-content">
|
||||||
|
<div class="flex-container flexFlowColumn">
|
||||||
<small>
|
<small>
|
||||||
<b>Combine negative prompts from other boxes.</b>
|
<b>Combine positive/negative prompts from other boxes.</b>
|
||||||
<br />
|
<br />
|
||||||
For example, ticking the chat, global, and character boxes combine all negative prompts into a comma-separated string.
|
For example, ticking the chat, global, and character boxes combine all negative prompts into a comma-separated string.
|
||||||
</small>
|
</small>
|
||||||
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<label for="cfg_negative_combine">
|
<div class="flex-container flexFlowColumn">
|
||||||
|
<label for="cfg_prompt_combine">
|
||||||
<span data-i18n="Scale">Always Include</span>
|
<span data-i18n="Scale">Always Include</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="checkbox_label">
|
<label class="checkbox_label">
|
||||||
<input type="checkbox" name="cfg_negative_combine" value="0" />
|
<input type="checkbox" name="cfg_prompt_combine" value="0" />
|
||||||
<span data-i18n="Chat Negatives">Chat Negatives</span>
|
<span data-i18n="Chat Negatives">Chat Negatives</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="checkbox_label">
|
<label class="checkbox_label">
|
||||||
<input type="checkbox" name="cfg_negative_combine" value="1" />
|
<input type="checkbox" name="cfg_prompt_combine" value="1" />
|
||||||
<span data-i18n="Character Negatives">Character Negatives</span>
|
<span data-i18n="Character Negatives">Character Negatives</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="checkbox_label">
|
<label class="checkbox_label">
|
||||||
<input type="checkbox" name="cfg_negative_combine" value="2" />
|
<input type="checkbox" name="cfg_prompt_combine" value="2" />
|
||||||
<span data-i18n="Global Negatives">Global Negatives</span>
|
<span data-i18n="Global Negatives">Global Negatives</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex-container flexFlowColumn">
|
||||||
|
<label>
|
||||||
|
Custom Separator: <input id="cfg_prompt_separator" class="text_pole textarea_compact widthUnset" placeholder=""\n"" type="text" />
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Insertion Depth: <input id="cfg_prompt_insertion_depth" class="text_pole widthUnset" type="number" min="0" max="99" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { saveSettingsDebounced, getCurrentChatId, system_message_types, extension_prompt_types, eventSource, event_types, getRequestHeaders, CHARACTERS_PER_TOKEN_RATIO, substituteParams, max_context, } from "../../../script.js";
|
import { saveSettingsDebounced, getCurrentChatId, system_message_types, extension_prompt_types, eventSource, event_types, getRequestHeaders, substituteParams, } from "../../../script.js";
|
||||||
import { humanizedDateTime } from "../../RossAscends-mods.js";
|
import { humanizedDateTime } from "../../RossAscends-mods.js";
|
||||||
import { getApiUrl, extension_settings, getContext, doExtrasFetch } from "../../extensions.js";
|
import { getApiUrl, extension_settings, getContext, doExtrasFetch } from "../../extensions.js";
|
||||||
|
import { CHARACTERS_PER_TOKEN_RATIO } from "../../tokenizers.js";
|
||||||
import { getFileText, onlyUnique, splitRecursive } from "../../utils.js";
|
import { getFileText, onlyUnique, splitRecursive } from "../../utils.js";
|
||||||
export { MODULE_NAME };
|
export { MODULE_NAME };
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ async function updateQuickReplyPresetList() {
|
||||||
if (result.ok) {
|
if (result.ok) {
|
||||||
var data = await result.json();
|
var data = await result.json();
|
||||||
presets = data.quickReplyPresets?.length ? data.quickReplyPresets : [];
|
presets = data.quickReplyPresets?.length ? data.quickReplyPresets : [];
|
||||||
console.log(presets)
|
console.debug('Quick Reply presets', presets);
|
||||||
$("#quickReplyPresets").find('option[value!=""]').remove();
|
$("#quickReplyPresets").find('option[value!=""]').remove();
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,12 @@ TODO:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { saveSettingsDebounced } from "../../../script.js";
|
import { saveSettingsDebounced } from "../../../script.js";
|
||||||
import { getContext, getApiUrl, modules, extension_settings, ModuleWorkerWrapper, doExtrasFetch } from "../../extensions.js";
|
import { getContext, extension_settings, ModuleWorkerWrapper } from "../../extensions.js";
|
||||||
import { VoskSttProvider } from './vosk.js'
|
import { VoskSttProvider } from './vosk.js'
|
||||||
import { WhisperSttProvider } from './whisper.js'
|
import { WhisperSttProvider } from './whisper.js'
|
||||||
import { BrowserSttProvider } from './browser.js'
|
import { BrowserSttProvider } from './browser.js'
|
||||||
import { StreamingSttProvider } from './streaming.js'
|
import { StreamingSttProvider } from './streaming.js'
|
||||||
|
import { getMessageTimeStamp } from "../../RossAscends-mods.js";
|
||||||
export { MODULE_NAME };
|
export { MODULE_NAME };
|
||||||
|
|
||||||
const MODULE_NAME = 'Speech Recognition';
|
const MODULE_NAME = 'Speech Recognition';
|
||||||
|
@ -152,7 +153,7 @@ async function processTranscript(transcript) {
|
||||||
name: context.name1,
|
name: context.name1,
|
||||||
is_user: true,
|
is_user: true,
|
||||||
is_name: true,
|
is_name: true,
|
||||||
send_date: Date.now(),
|
send_date: getMessageTimeStamp(),
|
||||||
mes: messageText,
|
mes: messageText,
|
||||||
};
|
};
|
||||||
context.chat.push(message);
|
context.chat.push(message);
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
import { getApiUrl, getContext, extension_settings, doExtrasFetch, modules } from "../../extensions.js";
|
import { getApiUrl, getContext, extension_settings, doExtrasFetch, modules } from "../../extensions.js";
|
||||||
import { selected_group } from "../../group-chats.js";
|
import { selected_group } from "../../group-chats.js";
|
||||||
import { stringFormat, initScrollHeight, resetScrollHeight, timestampToMoment, getCharaFilename, saveBase64AsFile } from "../../utils.js";
|
import { stringFormat, initScrollHeight, resetScrollHeight, timestampToMoment, getCharaFilename, saveBase64AsFile } from "../../utils.js";
|
||||||
import { humanizedDateTime } from "../../RossAscends-mods.js";
|
import { getMessageTimeStamp, humanizedDateTime } from "../../RossAscends-mods.js";
|
||||||
export { MODULE_NAME };
|
export { MODULE_NAME };
|
||||||
|
|
||||||
// Wraps a string into monospace font-face span
|
// Wraps a string into monospace font-face span
|
||||||
|
@ -755,11 +755,10 @@ async function sendMessage(prompt, image) {
|
||||||
const messageText = `[${context.name2} sends a picture that contains: ${prompt}]`;
|
const messageText = `[${context.name2} sends a picture that contains: ${prompt}]`;
|
||||||
const message = {
|
const message = {
|
||||||
name: context.groupId ? systemUserName : context.name2,
|
name: context.groupId ? systemUserName : context.name2,
|
||||||
is_system: context.groupId ? true : false,
|
|
||||||
is_user: false,
|
is_user: false,
|
||||||
is_system: true,
|
is_system: true,
|
||||||
is_name: true,
|
is_name: true,
|
||||||
send_date: timestampToMoment(Date.now()).format('LL LT'),
|
send_date: getMessageTimeStamp(),
|
||||||
mes: context.groupId ? p(messageText) : messageText,
|
mes: context.groupId ? p(messageText) : messageText,
|
||||||
extra: {
|
extra: {
|
||||||
image: image,
|
image: image,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { callPopup, main_api } from "../../../script.js";
|
import { callPopup, main_api } from "../../../script.js";
|
||||||
import { getContext } from "../../extensions.js";
|
import { getContext } from "../../extensions.js";
|
||||||
import { getTokenizerModel } from "../../openai.js";
|
import { getTokenizerModel } from "../../tokenizers.js";
|
||||||
|
|
||||||
async function doTokenCounter() {
|
async function doTokenCounter() {
|
||||||
const selectedTokenizer = main_api == 'openai'
|
const selectedTokenizer = main_api == 'openai'
|
||||||
|
|
|
@ -421,9 +421,9 @@ jQuery(() => {
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
|
||||||
eventSource.on(event_types.MESSAGE_RECEIVED, handleIncomingMessage);
|
eventSource.on(event_types.CHARACTER_MESSAGE_RENDERED, handleIncomingMessage);
|
||||||
eventSource.on(event_types.MESSAGE_SWIPED, handleIncomingMessage);
|
eventSource.on(event_types.MESSAGE_SWIPED, handleIncomingMessage);
|
||||||
eventSource.on(event_types.MESSAGE_SENT, handleOutgoingMessage);
|
eventSource.on(event_types.USER_MESSAGE_RENDERED, handleOutgoingMessage);
|
||||||
eventSource.on(event_types.IMPERSONATE_READY, handleImpersonateReady);
|
eventSource.on(event_types.IMPERSONATE_READY, handleImpersonateReady);
|
||||||
eventSource.on(event_types.MESSAGE_EDITED, handleMessageEdit);
|
eventSource.on(event_types.MESSAGE_EDITED, handleMessageEdit);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { getContext } from "../../extensions.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a chat variable from the current chat metadata.
|
||||||
|
* @param {string} name The name of the variable to get.
|
||||||
|
* @returns {string} The value of the variable.
|
||||||
|
*/
|
||||||
|
function getChatVariable(name) {
|
||||||
|
const metadata = getContext().chatMetadata;
|
||||||
|
|
||||||
|
if (!metadata) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!metadata.variables) {
|
||||||
|
metadata.variables = {};
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.variables[name] || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a chat variable in the current chat metadata.
|
||||||
|
* @param {string} name The name of the variable to set.
|
||||||
|
* @param {any} value The value of the variable to set.
|
||||||
|
*/
|
||||||
|
function setChatVariable(name, value) {
|
||||||
|
if (name === undefined || value === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadata = getContext().chatMetadata;
|
||||||
|
|
||||||
|
if (!metadata) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!metadata.variables) {
|
||||||
|
metadata.variables = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata.variables[name] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function listChatVariables() {
|
||||||
|
const metadata = getContext().chatMetadata;
|
||||||
|
|
||||||
|
if (!metadata) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!metadata.variables) {
|
||||||
|
metadata.variables = {};
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.keys(metadata.variables).map(key => `${key}=${metadata.variables[key]}`).join(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
jQuery(() => {
|
||||||
|
const context = getContext();
|
||||||
|
context.registerHelper('getvar', getChatVariable);
|
||||||
|
context.registerHelper('setvar', setChatVariable);
|
||||||
|
context.registerHelper('listvar', listChatVariables);
|
||||||
|
});
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"display_name": "Chat Variables",
|
||||||
|
"loading_order": 100,
|
||||||
|
"requires": [],
|
||||||
|
"optional": [],
|
||||||
|
"js": "index.js",
|
||||||
|
"css": "",
|
||||||
|
"author": "Cohee#1207",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"homePage": "https://github.com/SillyTavern/SillyTavern"
|
||||||
|
}
|
|
@ -1,6 +1,10 @@
|
||||||
import { fuzzySearchCharacters, fuzzySearchGroups, fuzzySearchWorldInfo, power_user } from "./power-user.js";
|
import { fuzzySearchCharacters, fuzzySearchGroups, fuzzySearchWorldInfo, power_user } from "./power-user.js";
|
||||||
import { tag_map } from "./tags.js";
|
import { tag_map } from "./tags.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The filter types.
|
||||||
|
* @type {Object.<string, string>}
|
||||||
|
*/
|
||||||
export const FILTER_TYPES = {
|
export const FILTER_TYPES = {
|
||||||
SEARCH: 'search',
|
SEARCH: 'search',
|
||||||
TAG: 'tag',
|
TAG: 'tag',
|
||||||
|
@ -9,11 +13,26 @@ export const FILTER_TYPES = {
|
||||||
WORLD_INFO_SEARCH: 'world_info_search',
|
WORLD_INFO_SEARCH: 'world_info_search',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for filtering data.
|
||||||
|
* @example
|
||||||
|
* const filterHelper = new FilterHelper(() => console.log('data changed'));
|
||||||
|
* filterHelper.setFilterData(FILTER_TYPES.SEARCH, 'test');
|
||||||
|
* data = filterHelper.applyFilters(data);
|
||||||
|
*/
|
||||||
export class FilterHelper {
|
export class FilterHelper {
|
||||||
|
/**
|
||||||
|
* Creates a new FilterHelper
|
||||||
|
* @param {Function} onDataChanged Callback to trigger when the filter data changes
|
||||||
|
*/
|
||||||
constructor(onDataChanged) {
|
constructor(onDataChanged) {
|
||||||
this.onDataChanged = onDataChanged;
|
this.onDataChanged = onDataChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The filter functions.
|
||||||
|
* @type {Object.<string, Function>}
|
||||||
|
*/
|
||||||
filterFunctions = {
|
filterFunctions = {
|
||||||
[FILTER_TYPES.SEARCH]: this.searchFilter.bind(this),
|
[FILTER_TYPES.SEARCH]: this.searchFilter.bind(this),
|
||||||
[FILTER_TYPES.GROUP]: this.groupFilter.bind(this),
|
[FILTER_TYPES.GROUP]: this.groupFilter.bind(this),
|
||||||
|
@ -22,6 +41,10 @@ export class FilterHelper {
|
||||||
[FILTER_TYPES.WORLD_INFO_SEARCH]: this.wiSearchFilter.bind(this),
|
[FILTER_TYPES.WORLD_INFO_SEARCH]: this.wiSearchFilter.bind(this),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The filter data.
|
||||||
|
* @type {Object.<string, any>}
|
||||||
|
*/
|
||||||
filterData = {
|
filterData = {
|
||||||
[FILTER_TYPES.SEARCH]: '',
|
[FILTER_TYPES.SEARCH]: '',
|
||||||
[FILTER_TYPES.GROUP]: false,
|
[FILTER_TYPES.GROUP]: false,
|
||||||
|
@ -30,6 +53,11 @@ export class FilterHelper {
|
||||||
[FILTER_TYPES.WORLD_INFO_SEARCH]: '',
|
[FILTER_TYPES.WORLD_INFO_SEARCH]: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a fuzzy search filter to the World Info data.
|
||||||
|
* @param {any[]} data The data to filter. Must have a uid property.
|
||||||
|
* @returns {any[]} The filtered data.
|
||||||
|
*/
|
||||||
wiSearchFilter(data) {
|
wiSearchFilter(data) {
|
||||||
const term = this.filterData[FILTER_TYPES.WORLD_INFO_SEARCH];
|
const term = this.filterData[FILTER_TYPES.WORLD_INFO_SEARCH];
|
||||||
|
|
||||||
|
@ -41,6 +69,11 @@ export class FilterHelper {
|
||||||
return data.filter(entity => fuzzySearchResults.includes(entity.uid));
|
return data.filter(entity => fuzzySearchResults.includes(entity.uid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a tag filter to the data.
|
||||||
|
* @param {any[]} data The data to filter.
|
||||||
|
* @returns {any[]} The filtered data.
|
||||||
|
*/
|
||||||
tagFilter(data) {
|
tagFilter(data) {
|
||||||
const TAG_LOGIC_AND = true; // switch to false to use OR logic for combining tags
|
const TAG_LOGIC_AND = true; // switch to false to use OR logic for combining tags
|
||||||
const { selected, excluded } = this.filterData[FILTER_TYPES.TAG];
|
const { selected, excluded } = this.filterData[FILTER_TYPES.TAG];
|
||||||
|
@ -76,6 +109,11 @@ export class FilterHelper {
|
||||||
return data.filter(entity => getIsTagged(entity));
|
return data.filter(entity => getIsTagged(entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a favorite filter to the data.
|
||||||
|
* @param {any[]} data The data to filter.
|
||||||
|
* @returns {any[]} The filtered data.
|
||||||
|
*/
|
||||||
favFilter(data) {
|
favFilter(data) {
|
||||||
if (!this.filterData[FILTER_TYPES.FAV]) {
|
if (!this.filterData[FILTER_TYPES.FAV]) {
|
||||||
return data;
|
return data;
|
||||||
|
@ -84,6 +122,11 @@ export class FilterHelper {
|
||||||
return data.filter(entity => entity.item.fav || entity.item.fav == "true");
|
return data.filter(entity => entity.item.fav || entity.item.fav == "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a group type filter to the data.
|
||||||
|
* @param {any[]} data The data to filter.
|
||||||
|
* @returns {any[]} The filtered data.
|
||||||
|
*/
|
||||||
groupFilter(data) {
|
groupFilter(data) {
|
||||||
if (!this.filterData[FILTER_TYPES.GROUP]) {
|
if (!this.filterData[FILTER_TYPES.GROUP]) {
|
||||||
return data;
|
return data;
|
||||||
|
@ -92,6 +135,11 @@ export class FilterHelper {
|
||||||
return data.filter(entity => entity.type === 'group');
|
return data.filter(entity => entity.type === 'group');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a search filter to the data. Uses fuzzy search if enabled.
|
||||||
|
* @param {any[]} data The data to filter.
|
||||||
|
* @returns {any[]} The filtered data.
|
||||||
|
*/
|
||||||
searchFilter(data) {
|
searchFilter(data) {
|
||||||
if (!this.filterData[FILTER_TYPES.SEARCH]) {
|
if (!this.filterData[FILTER_TYPES.SEARCH]) {
|
||||||
return data;
|
return data;
|
||||||
|
@ -122,6 +170,12 @@ export class FilterHelper {
|
||||||
return data.filter(entity => getIsValidSearch(entity));
|
return data.filter(entity => getIsValidSearch(entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the filter data for the given filter type.
|
||||||
|
* @param {string} filterType The filter type to set data for.
|
||||||
|
* @param {any} data The data to set.
|
||||||
|
* @param {boolean} suppressDataChanged Whether to suppress the data changed callback.
|
||||||
|
*/
|
||||||
setFilterData(filterType, data, suppressDataChanged = false) {
|
setFilterData(filterType, data, suppressDataChanged = false) {
|
||||||
const oldData = this.filterData[filterType];
|
const oldData = this.filterData[filterType];
|
||||||
this.filterData[filterType] = data;
|
this.filterData[filterType] = data;
|
||||||
|
@ -132,10 +186,19 @@ export class FilterHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the filter data for the given filter type.
|
||||||
|
* @param {string} filterType The filter type to get data for.
|
||||||
|
*/
|
||||||
getFilterData(filterType) {
|
getFilterData(filterType) {
|
||||||
return this.filterData[filterType];
|
return this.filterData[filterType];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies all filters to the given data.
|
||||||
|
* @param {any[]} data The data to filter.
|
||||||
|
* @returns {any[]} The filtered data.
|
||||||
|
*/
|
||||||
applyFilters(data) {
|
applyFilters(data) {
|
||||||
return Object.values(this.filterFunctions)
|
return Object.values(this.filterFunctions)
|
||||||
.reduce((data, fn) => fn(data), data);
|
.reduce((data, fn) => fn(data), data);
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
saveBase64AsFile,
|
saveBase64AsFile,
|
||||||
PAGINATION_TEMPLATE,
|
PAGINATION_TEMPLATE,
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { RA_CountCharTokens, humanizedDateTime, dragElement, favsToHotswap } from "./RossAscends-mods.js";
|
import { RA_CountCharTokens, humanizedDateTime, dragElement, favsToHotswap, getMessageTimeStamp } from "./RossAscends-mods.js";
|
||||||
import { loadMovingUIState, sortEntitiesList } from './power-user.js';
|
import { loadMovingUIState, sortEntitiesList } from './power-user.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -202,7 +202,7 @@ function getFirstCharacterMessage(character) {
|
||||||
mes["is_system"] = false;
|
mes["is_system"] = false;
|
||||||
mes["name"] = character.name;
|
mes["name"] = character.name;
|
||||||
mes["is_name"] = true;
|
mes["is_name"] = true;
|
||||||
mes["send_date"] = humanizedDateTime();
|
mes["send_date"] = getMessageTimeStamp();
|
||||||
mes["original_avatar"] = character.avatar;
|
mes["original_avatar"] = character.avatar;
|
||||||
mes["extra"] = { "gen_id": Date.now() * Math.random() * 1000000 };
|
mes["extra"] = { "gen_id": Date.now() * Math.random() * 1000000 };
|
||||||
mes["mes"] = messageText
|
mes["mes"] = messageText
|
||||||
|
@ -463,7 +463,7 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
|
||||||
is_group_generating = true;
|
is_group_generating = true;
|
||||||
setCharacterName('');
|
setCharacterName('');
|
||||||
setCharacterId(undefined);
|
setCharacterId(undefined);
|
||||||
const userInput = $("#send_textarea").val();
|
const userInput = String($("#send_textarea").val());
|
||||||
|
|
||||||
if (typingIndicator.length === 0 && !isStreamingEnabled()) {
|
if (typingIndicator.length === 0 && !isStreamingEnabled()) {
|
||||||
typingIndicator = $(
|
typingIndicator = $(
|
||||||
|
@ -983,11 +983,9 @@ function printGroupCandidates() {
|
||||||
const storageKey = 'GroupCandidates_PerPage';
|
const storageKey = 'GroupCandidates_PerPage';
|
||||||
$("#rm_group_add_members_pagination").pagination({
|
$("#rm_group_add_members_pagination").pagination({
|
||||||
dataSource: getGroupCharacters({ doFilter: true, onlyMembers: false }),
|
dataSource: getGroupCharacters({ doFilter: true, onlyMembers: false }),
|
||||||
pageSize: 5,
|
|
||||||
pageRange: 1,
|
pageRange: 1,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
showPageNumbers: false,
|
showPageNumbers: false,
|
||||||
showSizeChanger: false,
|
|
||||||
prevText: '<',
|
prevText: '<',
|
||||||
nextText: '>',
|
nextText: '>',
|
||||||
formatNavigator: PAGINATION_TEMPLATE,
|
formatNavigator: PAGINATION_TEMPLATE,
|
||||||
|
@ -1011,11 +1009,9 @@ function printGroupMembers() {
|
||||||
const storageKey = 'GroupMembers_PerPage';
|
const storageKey = 'GroupMembers_PerPage';
|
||||||
$("#rm_group_members_pagination").pagination({
|
$("#rm_group_members_pagination").pagination({
|
||||||
dataSource: getGroupCharacters({ doFilter: false, onlyMembers: true }),
|
dataSource: getGroupCharacters({ doFilter: false, onlyMembers: true }),
|
||||||
pageSize: 5,
|
|
||||||
pageRange: 1,
|
pageRange: 1,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
showPageNumbers: false,
|
showPageNumbers: false,
|
||||||
showSizeChanger: false,
|
|
||||||
prevText: '<',
|
prevText: '<',
|
||||||
nextText: '>',
|
nextText: '>',
|
||||||
formatNavigator: PAGINATION_TEMPLATE,
|
formatNavigator: PAGINATION_TEMPLATE,
|
||||||
|
@ -1320,7 +1316,7 @@ function openCharacterDefinition(characterSelect) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterGroupMembers() {
|
function filterGroupMembers() {
|
||||||
const searchValue = $(this).val().toLowerCase();
|
const searchValue = String($(this).val()).toLowerCase();
|
||||||
groupCandidatesFilter.setFilterData(FILTER_TYPES.SEARCH, searchValue);
|
groupCandidatesFilter.setFilterData(FILTER_TYPES.SEARCH, searchValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1390,7 +1386,7 @@ export async function createNewGroupChat(groupId) {
|
||||||
group.chat_metadata = {};
|
group.chat_metadata = {};
|
||||||
updateChatMetadata(group.chat_metadata, true);
|
updateChatMetadata(group.chat_metadata, true);
|
||||||
|
|
||||||
await editGroup(group.id, true);
|
await editGroup(group.id, true, false);
|
||||||
await getGroupChat(group.id);
|
await getGroupChat(group.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { waitUntilCondition } from "./utils.js";
|
||||||
|
|
||||||
|
const storageKey = "language";
|
||||||
|
export const localeData = await fetch("i18n.json").then(response => response.json());
|
||||||
|
|
||||||
|
export function applyLocale(root = document) {
|
||||||
|
const overrideLanguage = localStorage.getItem("language");
|
||||||
|
var language = overrideLanguage || navigator.language || navigator.userLanguage;
|
||||||
|
language = language.toLowerCase();
|
||||||
|
//load the appropriate language file
|
||||||
|
if (localeData.lang.indexOf(language) < 0) language = "en";
|
||||||
|
|
||||||
|
const $root = root instanceof Document ? $(root) : $(new DOMParser().parseFromString(root, "text/html"));
|
||||||
|
|
||||||
|
//find all the elements with `data-i18n` attribute
|
||||||
|
$root.find("[data-i18n]").each(function () {
|
||||||
|
//read the translation from the language data
|
||||||
|
const keys = $(this).data("i18n").split(';'); // Multi-key entries are ; delimited
|
||||||
|
for (const key of keys) {
|
||||||
|
const attributeMatch = key.match(/\[(\S+)\](.+)/); // [attribute]key
|
||||||
|
if (attributeMatch) { // attribute-tagged key
|
||||||
|
const localizedValue = localeData?.[language]?.[attributeMatch[2]];
|
||||||
|
if (localizedValue) {
|
||||||
|
$(this).attr(attributeMatch[1], localizedValue);
|
||||||
|
}
|
||||||
|
} else { // No attribute tag, treat as 'text'
|
||||||
|
const localizedValue = localeData?.[language]?.[key];
|
||||||
|
if (localizedValue) {
|
||||||
|
$(this).text(localizedValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (root !== document) {
|
||||||
|
return $root.get(0).body.innerHTML;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addLanguagesToDropdown() {
|
||||||
|
if (!Array.isArray(localeData?.lang)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const lang of localeData.lang) {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = lang;
|
||||||
|
option.innerText = lang;
|
||||||
|
$('#ui_language_select').append(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedLanguage = localStorage.getItem(storageKey);
|
||||||
|
if (selectedLanguage) {
|
||||||
|
$('#ui_language_select').val(selectedLanguage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jQuery(async () => {
|
||||||
|
waitUntilCondition(() => !!localeData);
|
||||||
|
window["applyLocale"] = applyLocale;
|
||||||
|
applyLocale();
|
||||||
|
addLanguagesToDropdown();
|
||||||
|
|
||||||
|
$('#ui_language_select').on('change', async function () {
|
||||||
|
const language = String($(this).val());
|
||||||
|
|
||||||
|
if (language) {
|
||||||
|
localStorage.setItem(storageKey, language);
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem(storageKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
});
|
|
@ -4,6 +4,9 @@ import { saveSettingsDebounced, substituteParams } from "../script.js";
|
||||||
import { selected_group } from "./group-chats.js";
|
import { selected_group } from "./group-chats.js";
|
||||||
import { power_user } from "./power-user.js";
|
import { power_user } from "./power-user.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {any[]} Instruct mode presets.
|
||||||
|
*/
|
||||||
export let instruct_presets = [];
|
export let instruct_presets = [];
|
||||||
|
|
||||||
const controls = [
|
const controls = [
|
||||||
|
@ -116,6 +119,11 @@ export function autoSelectInstructPreset(modelId) {
|
||||||
* @returns {string[]} Array of instruct mode stopping strings.
|
* @returns {string[]} Array of instruct mode stopping strings.
|
||||||
*/
|
*/
|
||||||
export function getInstructStoppingSequences() {
|
export function getInstructStoppingSequences() {
|
||||||
|
/**
|
||||||
|
* Adds instruct mode sequence to the result array.
|
||||||
|
* @param {string} sequence Sequence string.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
function addInstructSequence(sequence) {
|
function addInstructSequence(sequence) {
|
||||||
// Cohee: oobabooga's textgen always appends newline before the sequence as a stopping string
|
// Cohee: oobabooga's textgen always appends newline before the sequence as a stopping string
|
||||||
// But it's a problem for Metharme which doesn't use newlines to separate them.
|
// But it's a problem for Metharme which doesn't use newlines to separate them.
|
||||||
|
@ -215,6 +223,7 @@ export function formatInstructModeExamples(mesExamples, name1, name2) {
|
||||||
* @param {string} promptBias Prompt bias string.
|
* @param {string} promptBias Prompt bias string.
|
||||||
* @param {string} name1 User name.
|
* @param {string} name1 User name.
|
||||||
* @param {string} name2 Character name.
|
* @param {string} name2 Character name.
|
||||||
|
* @returns {string} Formatted instruct mode last prompt line.
|
||||||
*/
|
*/
|
||||||
export function formatInstructModePrompt(name, isImpersonate, promptBias, name1, name2) {
|
export function formatInstructModePrompt(name, isImpersonate, promptBias, name1, name2) {
|
||||||
const includeNames = power_user.instruct.names || (!!selected_group && power_user.instruct.names_force_groups);
|
const includeNames = power_user.instruct.names || (!!selected_group && power_user.instruct.names_force_groups);
|
||||||
|
@ -258,7 +267,7 @@ jQuery(() => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
power_user.instruct.preset = name;
|
power_user.instruct.preset = String(name);
|
||||||
controls.forEach(control => {
|
controls.forEach(control => {
|
||||||
if (preset[control.property] !== undefined) {
|
if (preset[control.property] !== undefined) {
|
||||||
power_user.instruct[control.property] = preset[control.property];
|
power_user.instruct[control.property] = preset[control.property];
|
||||||
|
|
|
@ -75,18 +75,18 @@ function loadKoboldSettings(preset) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getKoboldGenerationData(finalPromt, this_settings, this_amount_gen, this_max_context, isImpersonate, type) {
|
function getKoboldGenerationData(finalPrompt, this_settings, this_amount_gen, this_max_context, isImpersonate, type) {
|
||||||
const sampler_order = kai_settings.sampler_order || this_settings.sampler_order;
|
const sampler_order = kai_settings.sampler_order || this_settings.sampler_order;
|
||||||
let generate_data = {
|
let generate_data = {
|
||||||
prompt: finalPromt,
|
prompt: finalPrompt,
|
||||||
gui_settings: false,
|
gui_settings: false,
|
||||||
sampler_order: sampler_order,
|
sampler_order: sampler_order,
|
||||||
max_context_length: parseInt(this_max_context),
|
max_context_length: Number(this_max_context),
|
||||||
max_length: this_amount_gen,
|
max_length: this_amount_gen,
|
||||||
rep_pen: parseFloat(kai_settings.rep_pen),
|
rep_pen: Number(kai_settings.rep_pen),
|
||||||
rep_pen_range: parseInt(kai_settings.rep_pen_range),
|
rep_pen_range: Number(kai_settings.rep_pen_range),
|
||||||
rep_pen_slope: kai_settings.rep_pen_slope,
|
rep_pen_slope: kai_settings.rep_pen_slope,
|
||||||
temperature: parseFloat(kai_settings.temp),
|
temperature: Number(kai_settings.temp),
|
||||||
tfs: kai_settings.tfs,
|
tfs: kai_settings.tfs,
|
||||||
top_a: kai_settings.top_a,
|
top_a: kai_settings.top_a,
|
||||||
top_k: kai_settings.top_k,
|
top_k: kai_settings.top_k,
|
||||||
|
@ -223,16 +223,30 @@ const sliders = [
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the Kobold stop sequence can be used with the given version.
|
||||||
|
* @param {string} version KoboldAI version to check.
|
||||||
|
* @returns {boolean} True if the Kobold stop sequence can be used, false otherwise.
|
||||||
|
*/
|
||||||
function canUseKoboldStopSequence(version) {
|
function canUseKoboldStopSequence(version) {
|
||||||
return (version || '0.0.0').localeCompare(MIN_STOP_SEQUENCE_VERSION, undefined, { numeric: true, sensitivity: 'base' }) > -1;
|
return (version || '0.0.0').localeCompare(MIN_STOP_SEQUENCE_VERSION, undefined, { numeric: true, sensitivity: 'base' }) > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the Kobold streaming API can be used with the given version.
|
||||||
|
* @param {{ result: string; version: string; }} koboldVersion KoboldAI version object.
|
||||||
|
* @returns {boolean} True if the Kobold streaming API can be used, false otherwise.
|
||||||
|
*/
|
||||||
function canUseKoboldStreaming(koboldVersion) {
|
function canUseKoboldStreaming(koboldVersion) {
|
||||||
if (koboldVersion && koboldVersion.result == 'KoboldCpp') {
|
if (koboldVersion && koboldVersion.result == 'KoboldCpp') {
|
||||||
return (koboldVersion.version || '0.0').localeCompare(MIN_STREAMING_KCPPVERSION, undefined, { numeric: true, sensitivity: 'base' }) > -1;
|
return (koboldVersion.version || '0.0').localeCompare(MIN_STREAMING_KCPPVERSION, undefined, { numeric: true, sensitivity: 'base' }) > -1;
|
||||||
} else return false;
|
} else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the sampler items by the given order.
|
||||||
|
* @param {any[]} orderArray Sampler order array.
|
||||||
|
*/
|
||||||
function sortItemsByOrder(orderArray) {
|
function sortItemsByOrder(orderArray) {
|
||||||
console.debug('Preset samplers order: ' + orderArray);
|
console.debug('Preset samplers order: ' + orderArray);
|
||||||
const $draggableItems = $("#kobold_order");
|
const $draggableItems = $("#kobold_order");
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import {
|
import {
|
||||||
getRequestHeaders,
|
getRequestHeaders,
|
||||||
getStoppingStrings,
|
getStoppingStrings,
|
||||||
getTextTokens,
|
|
||||||
max_context,
|
max_context,
|
||||||
novelai_setting_names,
|
novelai_setting_names,
|
||||||
saveSettingsDebounced,
|
saveSettingsDebounced,
|
||||||
setGenerationParamsFromPreset
|
setGenerationParamsFromPreset
|
||||||
} from "../script.js";
|
} from "../script.js";
|
||||||
import { getCfg } from "./extensions/cfg/util.js";
|
import { getCfgPrompt } from "./extensions/cfg/util.js";
|
||||||
import { MAX_CONTEXT_DEFAULT, tokenizers } from "./power-user.js";
|
import { MAX_CONTEXT_DEFAULT } from "./power-user.js";
|
||||||
|
import { getTextTokens, tokenizers } from "./tokenizers.js";
|
||||||
import {
|
import {
|
||||||
getSortableDelay,
|
getSortableDelay,
|
||||||
getStringHash,
|
getStringHash,
|
||||||
|
@ -395,7 +395,11 @@ function getBadWordPermutations(text) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNovelGenerationData(finalPrompt, this_settings, this_amount_gen, isImpersonate) {
|
export function getNovelGenerationData(finalPrompt, this_settings, this_amount_gen, isImpersonate, cfgValues) {
|
||||||
|
if (cfgValues.guidanceScale && cfgValues.guidanceScale?.value !== 1) {
|
||||||
|
cfgValues.negativePrompt = (getCfgPrompt(cfgValues.guidanceScale, true))?.value;
|
||||||
|
}
|
||||||
|
|
||||||
const clio = nai_settings.model_novel.includes('clio');
|
const clio = nai_settings.model_novel.includes('clio');
|
||||||
const kayra = nai_settings.model_novel.includes('kayra');
|
const kayra = nai_settings.model_novel.includes('kayra');
|
||||||
|
|
||||||
|
@ -410,7 +414,6 @@ export function getNovelGenerationData(finalPrompt, this_settings, this_amount_g
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const prefix = selectPrefix(nai_settings.prefix, finalPrompt);
|
const prefix = selectPrefix(nai_settings.prefix, finalPrompt);
|
||||||
const cfgSettings = getCfg();
|
|
||||||
|
|
||||||
let logitBias = [];
|
let logitBias = [];
|
||||||
if (tokenizerType !== tokenizers.NONE && Array.isArray(nai_settings.logit_bias) && nai_settings.logit_bias.length) {
|
if (tokenizerType !== tokenizers.NONE && Array.isArray(nai_settings.logit_bias) && nai_settings.logit_bias.length) {
|
||||||
|
@ -437,8 +440,8 @@ export function getNovelGenerationData(finalPrompt, this_settings, this_amount_g
|
||||||
"typical_p": parseFloat(nai_settings.typical_p),
|
"typical_p": parseFloat(nai_settings.typical_p),
|
||||||
"mirostat_lr": parseFloat(nai_settings.mirostat_lr),
|
"mirostat_lr": parseFloat(nai_settings.mirostat_lr),
|
||||||
"mirostat_tau": parseFloat(nai_settings.mirostat_tau),
|
"mirostat_tau": parseFloat(nai_settings.mirostat_tau),
|
||||||
"cfg_scale": cfgSettings?.guidanceScale ?? parseFloat(nai_settings.cfg_scale),
|
"cfg_scale": cfgValues?.guidanceScale?.value ?? parseFloat(nai_settings.cfg_scale),
|
||||||
"cfg_uc": cfgSettings?.negativePrompt ?? nai_settings.cfg_uc ?? "",
|
"cfg_uc": cfgValues?.negativePrompt ?? nai_settings.cfg_uc ?? "",
|
||||||
"phrase_rep_pen": nai_settings.phrase_rep_pen,
|
"phrase_rep_pen": nai_settings.phrase_rep_pen,
|
||||||
"stop_sequences": stopSequences,
|
"stop_sequences": stopSequences,
|
||||||
"bad_words_ids": badWordIds,
|
"bad_words_ids": badWordIds,
|
||||||
|
|
|
@ -48,10 +48,10 @@ import {
|
||||||
delay,
|
delay,
|
||||||
download,
|
download,
|
||||||
getFileText, getSortableDelay,
|
getFileText, getSortableDelay,
|
||||||
getStringHash,
|
|
||||||
parseJsonFile,
|
parseJsonFile,
|
||||||
stringFormat,
|
stringFormat,
|
||||||
} from "./utils.js";
|
} from "./utils.js";
|
||||||
|
import { countTokensOpenAI } from "./tokenizers.js";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
is_get_status_openai,
|
is_get_status_openai,
|
||||||
|
@ -67,7 +67,6 @@ export {
|
||||||
sendOpenAIRequest,
|
sendOpenAIRequest,
|
||||||
setOpenAIOnlineStatus,
|
setOpenAIOnlineStatus,
|
||||||
getChatCompletionModel,
|
getChatCompletionModel,
|
||||||
countTokens,
|
|
||||||
TokenHandler,
|
TokenHandler,
|
||||||
IdentifierNotFoundError,
|
IdentifierNotFoundError,
|
||||||
Message,
|
Message,
|
||||||
|
@ -109,8 +108,8 @@ const max_4k = 4095;
|
||||||
const max_8k = 8191;
|
const max_8k = 8191;
|
||||||
const max_16k = 16383;
|
const max_16k = 16383;
|
||||||
const max_32k = 32767;
|
const max_32k = 32767;
|
||||||
const scale_max = 7900; // Probably more. Save some for the system prompt defined on Scale site.
|
const scale_max = 8191;
|
||||||
const claude_max = 8000; // We have a proper tokenizer, so theoretically could be larger (up to 9k)
|
const claude_max = 9000; // We have a proper tokenizer, so theoretically could be larger (up to 9k)
|
||||||
const palm2_max = 7500; // The real context window is 8192, spare some for padding due to using turbo tokenizer
|
const palm2_max = 7500; // The real context window is 8192, spare some for padding due to using turbo tokenizer
|
||||||
const claude_100k_max = 99000;
|
const claude_100k_max = 99000;
|
||||||
let ai21_max = 9200; //can easily fit 9k gpt tokens because j2's tokenizer is efficient af
|
let ai21_max = 9200; //can easily fit 9k gpt tokens because j2's tokenizer is efficient af
|
||||||
|
@ -124,40 +123,6 @@ const openrouter_website_model = 'OR_Website';
|
||||||
|
|
||||||
let biasCache = undefined;
|
let biasCache = undefined;
|
||||||
let model_list = [];
|
let model_list = [];
|
||||||
const objectStore = new localforage.createInstance({ name: "SillyTavern_ChatCompletions" });
|
|
||||||
|
|
||||||
let tokenCache = {};
|
|
||||||
|
|
||||||
async function loadTokenCache() {
|
|
||||||
try {
|
|
||||||
console.debug('Chat Completions: loading token cache')
|
|
||||||
tokenCache = await objectStore.getItem('tokenCache') || {};
|
|
||||||
} catch (e) {
|
|
||||||
console.log('Chat Completions: unable to load token cache, using default value', e);
|
|
||||||
tokenCache = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveTokenCache() {
|
|
||||||
try {
|
|
||||||
console.debug('Chat Completions: saving token cache')
|
|
||||||
await objectStore.setItem('tokenCache', tokenCache);
|
|
||||||
} catch (e) {
|
|
||||||
console.log('Chat Completions: unable to save token cache', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function resetTokenCache() {
|
|
||||||
try {
|
|
||||||
console.debug('Chat Completions: resetting token cache');
|
|
||||||
Object.keys(tokenCache).forEach(key => delete tokenCache[key]);
|
|
||||||
await objectStore.removeItem('tokenCache');
|
|
||||||
} catch (e) {
|
|
||||||
console.log('Chat Completions: unable to reset token cache', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window['resetTokenCache'] = resetTokenCache;
|
|
||||||
|
|
||||||
export const chat_completion_sources = {
|
export const chat_completion_sources = {
|
||||||
OPENAI: 'openai',
|
OPENAI: 'openai',
|
||||||
|
@ -219,6 +184,7 @@ const default_settings = {
|
||||||
assistant_prefill: '',
|
assistant_prefill: '',
|
||||||
use_ai21_tokenizer: false,
|
use_ai21_tokenizer: false,
|
||||||
exclude_assistant: false,
|
exclude_assistant: false,
|
||||||
|
use_alt_scale: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const oai_settings = {
|
const oai_settings = {
|
||||||
|
@ -261,15 +227,12 @@ const oai_settings = {
|
||||||
assistant_prefill: '',
|
assistant_prefill: '',
|
||||||
use_ai21_tokenizer: false,
|
use_ai21_tokenizer: false,
|
||||||
exclude_assistant: false,
|
exclude_assistant: false,
|
||||||
|
use_alt_scale: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let openai_setting_names;
|
let openai_setting_names;
|
||||||
let openai_settings;
|
let openai_settings;
|
||||||
|
|
||||||
export function getTokenCountOpenAI(text) {
|
|
||||||
const message = { role: 'system', content: text };
|
|
||||||
return countTokens(message, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
let promptManager = null;
|
let promptManager = null;
|
||||||
|
|
||||||
|
@ -869,8 +832,6 @@ function prepareOpenAIMessages({
|
||||||
|
|
||||||
const chat = chatCompletion.getChat();
|
const chat = chatCompletion.getChat();
|
||||||
openai_messages_count = chat.filter(x => x?.role === "user" || x?.role === "assistant")?.length || 0;
|
openai_messages_count = chat.filter(x => x?.role === "user" || x?.role === "assistant")?.length || 0;
|
||||||
// Save token cache to IndexedDB storage (async, no need to await)
|
|
||||||
saveTokenCache();
|
|
||||||
|
|
||||||
return [chat, promptManager.tokenHandler.counts];
|
return [chat, promptManager.tokenHandler.counts];
|
||||||
}
|
}
|
||||||
|
@ -1082,6 +1043,47 @@ function saveModelList(data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function sendAltScaleRequest(openai_msgs_tosend, logit_bias, signal) {
|
||||||
|
const generate_url = '/generate_altscale';
|
||||||
|
|
||||||
|
let firstSysMsgs = []
|
||||||
|
for (let msg of openai_msgs_tosend) {
|
||||||
|
if (msg.role === 'system') {
|
||||||
|
firstSysMsgs.push(substituteParams(msg.name ? msg.name + ": " + msg.content : msg.content));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let subsequentMsgs = openai_msgs_tosend.slice(firstSysMsgs.length);
|
||||||
|
|
||||||
|
const joinedSysMsgs = substituteParams(firstSysMsgs.join("\n"));
|
||||||
|
const joinedSubsequentMsgs = subsequentMsgs.reduce((acc, obj) => {
|
||||||
|
return acc + obj.role + ": " + obj.content + "\n";
|
||||||
|
}, "");
|
||||||
|
|
||||||
|
openai_msgs_tosend = substituteParams(joinedSubsequentMsgs);
|
||||||
|
|
||||||
|
const generate_data = {
|
||||||
|
sysprompt: joinedSysMsgs,
|
||||||
|
prompt: openai_msgs_tosend,
|
||||||
|
temp: parseFloat(oai_settings.temp_openai),
|
||||||
|
top_p: parseFloat(oai_settings.top_p_openai),
|
||||||
|
max_tokens: parseFloat(oai_settings.openai_max_tokens),
|
||||||
|
logit_bias: logit_bias,
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(generate_url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(generate_data),
|
||||||
|
headers: getRequestHeaders(),
|
||||||
|
signal: signal
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return data.output;
|
||||||
|
}
|
||||||
|
|
||||||
async function sendOpenAIRequest(type, openai_msgs_tosend, signal) {
|
async function sendOpenAIRequest(type, openai_msgs_tosend, signal) {
|
||||||
// Provide default abort signal
|
// Provide default abort signal
|
||||||
if (!signal) {
|
if (!signal) {
|
||||||
|
@ -1118,7 +1120,7 @@ async function sendOpenAIRequest(type, openai_msgs_tosend, signal) {
|
||||||
return sendWindowAIRequest(openai_msgs_tosend, signal, stream);
|
return sendWindowAIRequest(openai_msgs_tosend, signal, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
const logitBiasSources = [chat_completion_sources.OPENAI, chat_completion_sources.OPENROUTER];
|
const logitBiasSources = [chat_completion_sources.OPENAI, chat_completion_sources.OPENROUTER, chat_completion_sources.SCALE];
|
||||||
if (oai_settings.bias_preset_selected
|
if (oai_settings.bias_preset_selected
|
||||||
&& logitBiasSources.includes(oai_settings.chat_completion_source)
|
&& logitBiasSources.includes(oai_settings.chat_completion_source)
|
||||||
&& Array.isArray(oai_settings.bias_presets[oai_settings.bias_preset_selected])
|
&& Array.isArray(oai_settings.bias_presets[oai_settings.bias_preset_selected])
|
||||||
|
@ -1127,6 +1129,10 @@ async function sendOpenAIRequest(type, openai_msgs_tosend, signal) {
|
||||||
biasCache = logit_bias;
|
biasCache = logit_bias;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isScale && oai_settings.use_alt_scale) {
|
||||||
|
return sendAltScaleRequest(openai_msgs_tosend, logit_bias, signal)
|
||||||
|
}
|
||||||
|
|
||||||
const model = getChatCompletionModel();
|
const model = getChatCompletionModel();
|
||||||
const generate_data = {
|
const generate_data = {
|
||||||
"messages": openai_msgs_tosend,
|
"messages": openai_msgs_tosend,
|
||||||
|
@ -1363,63 +1369,8 @@ class TokenHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function countTokens(messages, full = false) {
|
|
||||||
let shouldTokenizeAI21 = oai_settings.chat_completion_source === chat_completion_sources.AI21 && oai_settings.use_ai21_tokenizer;
|
|
||||||
let chatId = 'undefined';
|
|
||||||
|
|
||||||
try {
|
const tokenHandler = new TokenHandler(countTokensOpenAI);
|
||||||
if (selected_group) {
|
|
||||||
chatId = groups.find(x => x.id == selected_group)?.chat_id;
|
|
||||||
}
|
|
||||||
else if (this_chid) {
|
|
||||||
chatId = characters[this_chid].chat;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
console.log('No character / group selected. Using default cache item');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof tokenCache[chatId] !== 'object') {
|
|
||||||
tokenCache[chatId] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Array.isArray(messages)) {
|
|
||||||
messages = [messages];
|
|
||||||
}
|
|
||||||
|
|
||||||
let token_count = -1;
|
|
||||||
|
|
||||||
for (const message of messages) {
|
|
||||||
const model = getTokenizerModel();
|
|
||||||
const hash = getStringHash(JSON.stringify(message));
|
|
||||||
const cacheKey = `${model}-${hash}`;
|
|
||||||
const cachedCount = tokenCache[chatId][cacheKey];
|
|
||||||
|
|
||||||
if (typeof cachedCount === 'number') {
|
|
||||||
token_count += cachedCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
jQuery.ajax({
|
|
||||||
async: false,
|
|
||||||
type: 'POST', //
|
|
||||||
url: shouldTokenizeAI21 ? '/tokenize_ai21' : `/tokenize_openai?model=${model}`,
|
|
||||||
data: JSON.stringify([message]),
|
|
||||||
dataType: "json",
|
|
||||||
contentType: "application/json",
|
|
||||||
success: function (data) {
|
|
||||||
token_count += Number(data.token_count);
|
|
||||||
tokenCache[chatId][cacheKey] = Number(data.token_count);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!full) token_count -= 2;
|
|
||||||
|
|
||||||
return token_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokenHandler = new TokenHandler(countTokens);
|
|
||||||
|
|
||||||
// Thrown by ChatCompletion when a requested prompt couldn't be found.
|
// Thrown by ChatCompletion when a requested prompt couldn't be found.
|
||||||
class IdentifierNotFoundError extends Error {
|
class IdentifierNotFoundError extends Error {
|
||||||
|
@ -1856,62 +1807,6 @@ class ChatCompletion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTokenizerModel() {
|
|
||||||
// OpenAI models always provide their own tokenizer
|
|
||||||
if (oai_settings.chat_completion_source == chat_completion_sources.OPENAI) {
|
|
||||||
return oai_settings.openai_model;
|
|
||||||
}
|
|
||||||
|
|
||||||
const turboTokenizer = 'gpt-3.5-turbo';
|
|
||||||
const gpt4Tokenizer = 'gpt-4';
|
|
||||||
const gpt2Tokenizer = 'gpt2';
|
|
||||||
const claudeTokenizer = 'claude';
|
|
||||||
|
|
||||||
// Assuming no one would use it for different models.. right?
|
|
||||||
if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) {
|
|
||||||
return gpt4Tokenizer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select correct tokenizer for WindowAI proxies
|
|
||||||
if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI && oai_settings.windowai_model) {
|
|
||||||
if (oai_settings.windowai_model.includes('gpt-4')) {
|
|
||||||
return gpt4Tokenizer;
|
|
||||||
}
|
|
||||||
else if (oai_settings.windowai_model.includes('gpt-3.5-turbo')) {
|
|
||||||
return turboTokenizer;
|
|
||||||
}
|
|
||||||
else if (oai_settings.windowai_model.includes('claude')) {
|
|
||||||
return claudeTokenizer;
|
|
||||||
}
|
|
||||||
else if (oai_settings.windowai_model.includes('GPT-NeoXT')) {
|
|
||||||
return gpt2Tokenizer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// And for OpenRouter (if not a site model, then it's impossible to determine the tokenizer)
|
|
||||||
if (oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER && oai_settings.openrouter_model) {
|
|
||||||
if (oai_settings.openrouter_model.includes('gpt-4')) {
|
|
||||||
return gpt4Tokenizer;
|
|
||||||
}
|
|
||||||
else if (oai_settings.openrouter_model.includes('gpt-3.5-turbo')) {
|
|
||||||
return turboTokenizer;
|
|
||||||
}
|
|
||||||
else if (oai_settings.openrouter_model.includes('claude')) {
|
|
||||||
return claudeTokenizer;
|
|
||||||
}
|
|
||||||
else if (oai_settings.openrouter_model.includes('GPT-NeoXT')) {
|
|
||||||
return gpt2Tokenizer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
|
|
||||||
return claudeTokenizer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default to Turbo 3.5
|
|
||||||
return turboTokenizer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadOpenAISettings(data, settings) {
|
function loadOpenAISettings(data, settings) {
|
||||||
openai_setting_names = data.openai_setting_names;
|
openai_setting_names = data.openai_setting_names;
|
||||||
openai_settings = data.openai_settings;
|
openai_settings = data.openai_settings;
|
||||||
|
@ -1971,6 +1866,7 @@ function loadOpenAISettings(data, settings) {
|
||||||
if (settings.openai_model !== undefined) oai_settings.openai_model = settings.openai_model;
|
if (settings.openai_model !== undefined) oai_settings.openai_model = settings.openai_model;
|
||||||
if (settings.use_ai21_tokenizer !== undefined) { oai_settings.use_ai21_tokenizer = !!settings.use_ai21_tokenizer; oai_settings.use_ai21_tokenizer ? ai21_max = 8191 : ai21_max = 9200; }
|
if (settings.use_ai21_tokenizer !== undefined) { oai_settings.use_ai21_tokenizer = !!settings.use_ai21_tokenizer; oai_settings.use_ai21_tokenizer ? ai21_max = 8191 : ai21_max = 9200; }
|
||||||
if (settings.exclude_assistant !== undefined) oai_settings.exclude_assistant = !!settings.exclude_assistant;
|
if (settings.exclude_assistant !== undefined) oai_settings.exclude_assistant = !!settings.exclude_assistant;
|
||||||
|
if (settings.use_alt_scale !== undefined) { oai_settings.use_alt_scale = !!settings.use_alt_scale; updateScaleForm(); }
|
||||||
$('#stream_toggle').prop('checked', oai_settings.stream_openai);
|
$('#stream_toggle').prop('checked', oai_settings.stream_openai);
|
||||||
$('#api_url_scale').val(oai_settings.api_url_scale);
|
$('#api_url_scale').val(oai_settings.api_url_scale);
|
||||||
$('#openai_proxy_password').val(oai_settings.proxy_password);
|
$('#openai_proxy_password').val(oai_settings.proxy_password);
|
||||||
|
@ -2001,6 +1897,7 @@ function loadOpenAISettings(data, settings) {
|
||||||
$('#openai_external_category').toggle(oai_settings.show_external_models);
|
$('#openai_external_category').toggle(oai_settings.show_external_models);
|
||||||
$('#use_ai21_tokenizer').prop('checked', oai_settings.use_ai21_tokenizer);
|
$('#use_ai21_tokenizer').prop('checked', oai_settings.use_ai21_tokenizer);
|
||||||
$('#exclude_assistant').prop('checked', oai_settings.exclude_assistant);
|
$('#exclude_assistant').prop('checked', oai_settings.exclude_assistant);
|
||||||
|
$('#scale-alt').prop('checked', oai_settings.use_alt_scale);
|
||||||
if (settings.impersonation_prompt !== undefined) oai_settings.impersonation_prompt = settings.impersonation_prompt;
|
if (settings.impersonation_prompt !== undefined) oai_settings.impersonation_prompt = settings.impersonation_prompt;
|
||||||
|
|
||||||
$('#impersonation_prompt_textarea').val(oai_settings.impersonation_prompt);
|
$('#impersonation_prompt_textarea').val(oai_settings.impersonation_prompt);
|
||||||
|
@ -2199,6 +2096,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
|
||||||
assistant_prefill: settings.assistant_prefill,
|
assistant_prefill: settings.assistant_prefill,
|
||||||
use_ai21_tokenizer: settings.use_ai21_tokenizer,
|
use_ai21_tokenizer: settings.use_ai21_tokenizer,
|
||||||
exclude_assistant: settings.exclude_assistant,
|
exclude_assistant: settings.exclude_assistant,
|
||||||
|
use_alt_scale: settings.use_alt_scale,
|
||||||
};
|
};
|
||||||
|
|
||||||
const savePresetSettings = await fetch(`/savepreset_openai?name=${name}`, {
|
const savePresetSettings = await fetch(`/savepreset_openai?name=${name}`, {
|
||||||
|
@ -2536,6 +2434,7 @@ function onSettingsPresetChange() {
|
||||||
assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],
|
assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],
|
||||||
use_ai21_tokenizer: ['#use_ai21_tokenizer', 'use_ai21_tokenizer', false],
|
use_ai21_tokenizer: ['#use_ai21_tokenizer', 'use_ai21_tokenizer', false],
|
||||||
exclude_assistant: ['#exclude_assistant', 'exclude_assistant', false],
|
exclude_assistant: ['#exclude_assistant', 'exclude_assistant', false],
|
||||||
|
use_alt_scale: ['#use_alt_scale', 'use_alt_scale', false],
|
||||||
};
|
};
|
||||||
|
|
||||||
const presetName = $('#settings_perset_openai').find(":selected").text();
|
const presetName = $('#settings_perset_openai').find(":selected").text();
|
||||||
|
@ -2831,20 +2730,31 @@ async function onConnectButtonClick(e) {
|
||||||
|
|
||||||
if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) {
|
if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) {
|
||||||
const api_key_scale = $('#api_key_scale').val().trim();
|
const api_key_scale = $('#api_key_scale').val().trim();
|
||||||
|
const scale_cookie = $('#scale_cookie').val().trim();
|
||||||
|
|
||||||
if (api_key_scale.length) {
|
if (api_key_scale.length) {
|
||||||
await writeSecret(SECRET_KEYS.SCALE, api_key_scale);
|
await writeSecret(SECRET_KEYS.SCALE, api_key_scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!oai_settings.api_url_scale) {
|
if (scale_cookie.length) {
|
||||||
|
await writeSecret(SECRET_KEYS.SCALE_COOKIE, scale_cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!oai_settings.api_url_scale && !oai_settings.use_alt_scale) {
|
||||||
console.log('No API URL saved for Scale');
|
console.log('No API URL saved for Scale');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!secret_state[SECRET_KEYS.SCALE]) {
|
if (!secret_state[SECRET_KEYS.SCALE] && !oai_settings.use_alt_scale) {
|
||||||
console.log('No secret key saved for Scale');
|
console.log('No secret key saved for Scale');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!secret_state[SECRET_KEYS.SCALE_COOKIE] && oai_settings.use_alt_scale) {
|
||||||
|
console.log("No cookie set for Scale");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
|
if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
|
||||||
|
@ -2958,11 +2868,25 @@ function onProxyPasswordShowClick() {
|
||||||
$(this).toggleClass('fa-eye-slash fa-eye');
|
$(this).toggleClass('fa-eye-slash fa-eye');
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(async function () {
|
function updateScaleForm() {
|
||||||
await loadTokenCache();
|
if (oai_settings.use_alt_scale) {
|
||||||
|
$('#normal_scale_form').css('display', 'none');
|
||||||
|
$('#alt_scale_form').css('display', '');
|
||||||
|
} else {
|
||||||
|
$('#normal_scale_form').css('display', '');
|
||||||
|
$('#alt_scale_form').css('display', 'none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(async function () {
|
||||||
$('#test_api_button').on('click', testApiConnection);
|
$('#test_api_button').on('click', testApiConnection);
|
||||||
|
|
||||||
|
$('#scale-alt').on('change', function () {
|
||||||
|
oai_settings.use_alt_scale = !!$('#scale-alt').prop('checked');
|
||||||
|
saveSettingsDebounced();
|
||||||
|
updateScaleForm();
|
||||||
|
});
|
||||||
|
|
||||||
$(document).on('input', '#temp_openai', function () {
|
$(document).on('input', '#temp_openai', function () {
|
||||||
oai_settings.temp_openai = Number($(this).val());
|
oai_settings.temp_openai = Number($(this).val());
|
||||||
$('#temp_counter_openai').text(Number($(this).val()).toFixed(2));
|
$('#temp_counter_openai').text(Number($(this).val()).toFixed(2));
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
import { loadInstructMode } from "./instruct-mode.js";
|
import { loadInstructMode } from "./instruct-mode.js";
|
||||||
|
|
||||||
import { registerSlashCommand } from "./slash-commands.js";
|
import { registerSlashCommand } from "./slash-commands.js";
|
||||||
|
import { tokenizers } from "./tokenizers.js";
|
||||||
|
|
||||||
import { delay } from "./utils.js";
|
import { delay } from "./utils.js";
|
||||||
|
|
||||||
|
@ -35,7 +36,6 @@ export {
|
||||||
fixMarkdown,
|
fixMarkdown,
|
||||||
power_user,
|
power_user,
|
||||||
pygmalion_options,
|
pygmalion_options,
|
||||||
tokenizers,
|
|
||||||
send_on_enter_options,
|
send_on_enter_options,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -63,17 +63,6 @@ const pygmalion_options = {
|
||||||
ENABLED: 1,
|
ENABLED: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokenizers = {
|
|
||||||
NONE: 0,
|
|
||||||
GPT3: 1,
|
|
||||||
CLASSIC: 2,
|
|
||||||
LLAMA: 3,
|
|
||||||
NERD: 4,
|
|
||||||
NERD2: 5,
|
|
||||||
API: 6,
|
|
||||||
BEST_MATCH: 99,
|
|
||||||
}
|
|
||||||
|
|
||||||
const send_on_enter_options = {
|
const send_on_enter_options = {
|
||||||
DISABLED: -1,
|
DISABLED: -1,
|
||||||
AUTO: 0,
|
AUTO: 0,
|
||||||
|
@ -207,7 +196,6 @@ let movingUIPresets = [];
|
||||||
let context_presets = [];
|
let context_presets = [];
|
||||||
|
|
||||||
const storage_keys = {
|
const storage_keys = {
|
||||||
ui_language: "language",
|
|
||||||
fast_ui_mode: "TavernAI_fast_ui_mode",
|
fast_ui_mode: "TavernAI_fast_ui_mode",
|
||||||
avatar_style: "TavernAI_avatar_style",
|
avatar_style: "TavernAI_avatar_style",
|
||||||
chat_display: "TavernAI_chat_display",
|
chat_display: "TavernAI_chat_display",
|
||||||
|
@ -247,29 +235,42 @@ function playMessageSound() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const audio = document.getElementById('audio_message_sound');
|
const audio = document.getElementById('audio_message_sound');
|
||||||
|
if (audio instanceof HTMLAudioElement) {
|
||||||
audio.volume = 0.8;
|
audio.volume = 0.8;
|
||||||
audio.pause();
|
audio.pause();
|
||||||
audio.currentTime = 0;
|
audio.currentTime = 0;
|
||||||
audio.play();
|
audio.play();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces consecutive newlines with a single newline.
|
||||||
|
* @param {string} x String to be processed.
|
||||||
|
* @returns {string} Processed string.
|
||||||
|
* @example
|
||||||
|
* collapseNewlines("\n\n\n"); // "\n"
|
||||||
|
*/
|
||||||
function collapseNewlines(x) {
|
function collapseNewlines(x) {
|
||||||
return x.replaceAll(/\n+/g, "\n");
|
return x.replaceAll(/\n+/g, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix formatting problems in markdown.
|
||||||
|
* @param {string} text Text to be processed.
|
||||||
|
* @returns {string} Processed text.
|
||||||
|
* @example
|
||||||
|
* "^example * text*\n" // "^example *text*\n"
|
||||||
|
* "^*example * text\n"// "^*example* text\n"
|
||||||
|
* "^example *text *\n" // "^example *text*\n"
|
||||||
|
* "^* example * text\n" // "^*example* text\n"
|
||||||
|
* // take note that the side you move the asterisk depends on where its pairing is
|
||||||
|
* // i.e. both of the following strings have the same broken asterisk ' * ',
|
||||||
|
* // but you move the first to the left and the second to the right, to match the non-broken asterisk
|
||||||
|
* "^example * text*\n" // "^*example * text\n"
|
||||||
|
* // and you HAVE to handle the cases where multiple pairs of asterisks exist in the same line
|
||||||
|
* "^example * text* * harder problem *\n" // "^example *text* *harder problem*\n"
|
||||||
|
*/
|
||||||
function fixMarkdown(text) {
|
function fixMarkdown(text) {
|
||||||
// fix formatting problems in markdown
|
|
||||||
// e.g.:
|
|
||||||
// "^example * text*\n" -> "^example *text*\n"
|
|
||||||
// "^*example * text\n" -> "^*example* text\n"
|
|
||||||
// "^example *text *\n" -> "^example *text*\n"
|
|
||||||
// "^* example * text\n" -> "^*example* text\n"
|
|
||||||
// take note that the side you move the asterisk depends on where its pairing is
|
|
||||||
// i.e. both of the following strings have the same broken asterisk ' * ',
|
|
||||||
// but you move the first to the left and the second to the right, to match the non-broken asterisk "^example * text*\n" "^*example * text\n"
|
|
||||||
// and you HAVE to handle the cases where multiple pairs of asterisks exist in the same line
|
|
||||||
// i.e. "^example * text* * harder problem *\n" -> "^example *text* *harder problem*\n"
|
|
||||||
|
|
||||||
// Find pairs of formatting characters and capture the text in between them
|
// Find pairs of formatting characters and capture the text in between them
|
||||||
const format = /([\*_]{1,2})([\s\S]*?)\1/gm;
|
const format = /([\*_]{1,2})([\s\S]*?)\1/gm;
|
||||||
let matches = [];
|
let matches = [];
|
||||||
|
@ -899,7 +900,7 @@ function loadContextSettings() {
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#context_presets').on('change', function () {
|
$('#context_presets').on('change', function () {
|
||||||
const name = $(this).find(':selected').val();
|
const name = String($(this).find(':selected').val());
|
||||||
const preset = context_presets.find(x => x.name === name);
|
const preset = context_presets.find(x => x.name === name);
|
||||||
|
|
||||||
if (!preset) {
|
if (!preset) {
|
||||||
|
@ -1020,6 +1021,10 @@ 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
|
||||||
|
*/
|
||||||
function sortEntitiesList(entities) {
|
function sortEntitiesList(entities) {
|
||||||
if (power_user.sort_field == undefined || entities.length === 0) {
|
if (power_user.sort_field == undefined || entities.length === 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -1027,6 +1032,7 @@ function sortEntitiesList(entities) {
|
||||||
|
|
||||||
entities.sort((a, b) => sortFunc(a.item, b.item));
|
entities.sort((a, b) => sortFunc(a.item, b.item));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveTheme() {
|
async function saveTheme() {
|
||||||
const name = await callPopup('Enter a theme preset name:', 'input');
|
const name = await callPopup('Enter a theme preset name:', 'input');
|
||||||
|
|
||||||
|
@ -1250,8 +1256,8 @@ async function doDelMode(_, text) {
|
||||||
if (text) {
|
if (text) {
|
||||||
await delay(300) //same as above, need event signal for 'entered del mode'
|
await delay(300) //same as above, need event signal for 'entered del mode'
|
||||||
console.debug('parsing msgs to del')
|
console.debug('parsing msgs to del')
|
||||||
let numMesToDel = Number(text).toFixed(0)
|
let numMesToDel = Number(text);
|
||||||
let lastMesID = $('.last_mes').attr('mesid')
|
let lastMesID = Number($('.last_mes').attr('mesid'));
|
||||||
let oldestMesIDToDel = lastMesID - numMesToDel + 1;
|
let oldestMesIDToDel = lastMesID - numMesToDel + 1;
|
||||||
|
|
||||||
//disallow targeting first message
|
//disallow targeting first message
|
||||||
|
@ -1277,26 +1283,6 @@ function doResetPanels() {
|
||||||
$("#movingUIreset").trigger('click');
|
$("#movingUIreset").trigger('click');
|
||||||
}
|
}
|
||||||
|
|
||||||
function addLanguagesToDropdown() {
|
|
||||||
$.getJSON('i18n.json', function (data) {
|
|
||||||
if (!Array.isArray(data?.lang)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const lang of data.lang) {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = lang;
|
|
||||||
option.innerText = lang;
|
|
||||||
$('#ui_language_select').append(option);
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectedLanguage = localStorage.getItem(storage_keys.ui_language);
|
|
||||||
if (selectedLanguage) {
|
|
||||||
$('#ui_language_select').val(selectedLanguage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAvgBG() {
|
function setAvgBG() {
|
||||||
const bgimg = new Image();
|
const bgimg = new Image();
|
||||||
bgimg.src = $('#bg1')
|
bgimg.src = $('#bg1')
|
||||||
|
@ -1348,10 +1334,6 @@ function setAvgBG() {
|
||||||
$("#user-mes-blur-tint-color-picker").attr('color', 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')');
|
$("#user-mes-blur-tint-color-picker").attr('color', 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')');
|
||||||
} */
|
} */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function getAverageRGB(imgEl) {
|
function getAverageRGB(imgEl) {
|
||||||
|
|
||||||
var blockSize = 5, // only visit every 5 pixels
|
var blockSize = 5, // only visit every 5 pixels
|
||||||
|
@ -1396,6 +1378,13 @@ function setAvgBG() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an HSL color value to RGB.
|
||||||
|
* @param {number} h Hue value
|
||||||
|
* @param {number} s Saturation value
|
||||||
|
* @param {number} l Luminance value
|
||||||
|
* @return {Array} The RGB representation
|
||||||
|
*/
|
||||||
function hslToRgb(h, s, l) {
|
function hslToRgb(h, s, l) {
|
||||||
const hueToRgb = (p, q, t) => {
|
const hueToRgb = (p, q, t) => {
|
||||||
if (t < 0) t += 1;
|
if (t < 0) t += 1;
|
||||||
|
@ -1437,7 +1426,7 @@ function setAvgBG() {
|
||||||
|
|
||||||
console.log(`rLum ${rLuminance}, gLum ${gLuminance}, bLum ${bLuminance}`)
|
console.log(`rLum ${rLuminance}, gLum ${gLuminance}, bLum ${bLuminance}`)
|
||||||
|
|
||||||
return 0.2126 * rLuminance + 0.7152 * gLuminance + 0.0722 * bLuminance;
|
return 0.2126 * Number(rLuminance) + 0.7152 * Number(gLuminance) + 0.0722 * Number(bLuminance);
|
||||||
}
|
}
|
||||||
|
|
||||||
//this version keeps BG and main text in same hue
|
//this version keeps BG and main text in same hue
|
||||||
|
@ -1620,13 +1609,13 @@ $(document).ready(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#markdown_escape_strings").on('input', function () {
|
$("#markdown_escape_strings").on('input', function () {
|
||||||
power_user.markdown_escape_strings = $(this).val();
|
power_user.markdown_escape_strings = String($(this).val());
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
reloadMarkdownProcessor(power_user.render_formulas);
|
reloadMarkdownProcessor(power_user.render_formulas);
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#start_reply_with").on('input', function () {
|
$("#start_reply_with").on('input', function () {
|
||||||
power_user.user_prompt_bias = $(this).val();
|
power_user.user_prompt_bias = String($(this).val());
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1753,7 +1742,7 @@ $(document).ready(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#themes").on('change', function () {
|
$("#themes").on('change', function () {
|
||||||
const themeSelected = $(this).find(':selected').val();
|
const themeSelected = String($(this).find(':selected').val());
|
||||||
power_user.theme = themeSelected;
|
power_user.theme = themeSelected;
|
||||||
applyTheme(themeSelected);
|
applyTheme(themeSelected);
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
|
@ -1761,7 +1750,7 @@ $(document).ready(() => {
|
||||||
|
|
||||||
$("#movingUIPresets").on('change', async function () {
|
$("#movingUIPresets").on('change', async function () {
|
||||||
console.log('saw MUI preset change')
|
console.log('saw MUI preset change')
|
||||||
const movingUIPresetSelected = $(this).find(':selected').val();
|
const movingUIPresetSelected = String($(this).find(':selected').val());
|
||||||
power_user.movingUIPreset = movingUIPresetSelected;
|
power_user.movingUIPreset = movingUIPresetSelected;
|
||||||
applyMovingUIPreset(movingUIPresetSelected);
|
applyMovingUIPreset(movingUIPresetSelected);
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
|
@ -1821,7 +1810,7 @@ $(document).ready(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#auto_swipe_blacklist').on('input', function () {
|
$('#auto_swipe_blacklist').on('input', function () {
|
||||||
power_user.auto_swipe_blacklist = $(this).val()
|
power_user.auto_swipe_blacklist = String($(this).val())
|
||||||
.split(",")
|
.split(",")
|
||||||
.map(str => str.trim())
|
.map(str => str.trim())
|
||||||
.filter(str => str);
|
.filter(str => str);
|
||||||
|
@ -1830,7 +1819,7 @@ $(document).ready(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#auto_swipe_minimum_length').on('input', function () {
|
$('#auto_swipe_minimum_length').on('input', function () {
|
||||||
const number = parseInt($(this).val());
|
const number = Number($(this).val());
|
||||||
if (!isNaN(number)) {
|
if (!isNaN(number)) {
|
||||||
power_user.auto_swipe_minimum_length = number;
|
power_user.auto_swipe_minimum_length = number;
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
|
@ -1838,7 +1827,7 @@ $(document).ready(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#auto_swipe_blacklist_threshold').on('input', function () {
|
$('#auto_swipe_blacklist_threshold').on('input', function () {
|
||||||
const number = parseInt($(this).val());
|
const number = Number($(this).val());
|
||||||
if (!isNaN(number)) {
|
if (!isNaN(number)) {
|
||||||
power_user.auto_swipe_blacklist_threshold = number;
|
power_user.auto_swipe_blacklist_threshold = number;
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
|
@ -1921,35 +1910,35 @@ $(document).ready(() => {
|
||||||
$("#messageTimerEnabled").on("input", function () {
|
$("#messageTimerEnabled").on("input", function () {
|
||||||
const value = !!$(this).prop('checked');
|
const value = !!$(this).prop('checked');
|
||||||
power_user.timer_enabled = value;
|
power_user.timer_enabled = value;
|
||||||
localStorage.setItem(storage_keys.timer_enabled, power_user.timer_enabled);
|
localStorage.setItem(storage_keys.timer_enabled, String(power_user.timer_enabled));
|
||||||
switchTimer();
|
switchTimer();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#messageTimestampsEnabled").on("input", function () {
|
$("#messageTimestampsEnabled").on("input", function () {
|
||||||
const value = !!$(this).prop('checked');
|
const value = !!$(this).prop('checked');
|
||||||
power_user.timestamps_enabled = value;
|
power_user.timestamps_enabled = value;
|
||||||
localStorage.setItem(storage_keys.timestamps_enabled, power_user.timestamps_enabled);
|
localStorage.setItem(storage_keys.timestamps_enabled, String(power_user.timestamps_enabled));
|
||||||
switchTimestamps();
|
switchTimestamps();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#messageModelIconEnabled").on("input", function () {
|
$("#messageModelIconEnabled").on("input", function () {
|
||||||
const value = !!$(this).prop('checked');
|
const value = !!$(this).prop('checked');
|
||||||
power_user.timestamp_model_icon = value;
|
power_user.timestamp_model_icon = value;
|
||||||
localStorage.setItem(storage_keys.timestamp_model_icon, power_user.timestamp_model_icon);
|
localStorage.setItem(storage_keys.timestamp_model_icon, String(power_user.timestamp_model_icon));
|
||||||
switchIcons();
|
switchIcons();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#mesIDDisplayEnabled").on("input", function () {
|
$("#mesIDDisplayEnabled").on("input", function () {
|
||||||
const value = !!$(this).prop('checked');
|
const value = !!$(this).prop('checked');
|
||||||
power_user.mesIDDisplay_enabled = value;
|
power_user.mesIDDisplay_enabled = value;
|
||||||
localStorage.setItem(storage_keys.mesIDDisplay_enabled, power_user.mesIDDisplay_enabled);
|
localStorage.setItem(storage_keys.mesIDDisplay_enabled, String(power_user.mesIDDisplay_enabled));
|
||||||
switchMesIDDisplay();
|
switchMesIDDisplay();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#hotswapEnabled").on("input", function () {
|
$("#hotswapEnabled").on("input", function () {
|
||||||
const value = !!$(this).prop('checked');
|
const value = !!$(this).prop('checked');
|
||||||
power_user.hotswap_enabled = value;
|
power_user.hotswap_enabled = value;
|
||||||
localStorage.setItem(storage_keys.hotswap_enabled, power_user.hotswap_enabled);
|
localStorage.setItem(storage_keys.hotswap_enabled, String(power_user.hotswap_enabled));
|
||||||
switchHotswap();
|
switchHotswap();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1995,7 +1984,7 @@ $(document).ready(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#custom_stopping_strings').on('input', function () {
|
$('#custom_stopping_strings').on('input', function () {
|
||||||
power_user.custom_stopping_strings = $(this).val();
|
power_user.custom_stopping_strings = String($(this).val());
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2025,18 +2014,6 @@ $(document).ready(() => {
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#ui_language_select').on('change', async function () {
|
|
||||||
const language = $(this).val();
|
|
||||||
|
|
||||||
if (language) {
|
|
||||||
localStorage.setItem(storage_keys.ui_language, language);
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem(storage_keys.ui_language);
|
|
||||||
}
|
|
||||||
|
|
||||||
location.reload();
|
|
||||||
});
|
|
||||||
|
|
||||||
$(window).on('focus', function () {
|
$(window).on('focus', function () {
|
||||||
browser_has_focus = true;
|
browser_has_focus = true;
|
||||||
});
|
});
|
||||||
|
@ -2052,5 +2029,4 @@ $(document).ready(() => {
|
||||||
registerSlashCommand('cut', doMesCut, [], ' <span class="monospace">(requred number)</span> – cuts the specified message from the chat', true, true);
|
registerSlashCommand('cut', doMesCut, [], ' <span class="monospace">(requred number)</span> – cuts the specified message from the chat', true, true);
|
||||||
registerSlashCommand('resetpanels', doResetPanels, ['resetui'], ' – resets UI panels to original state.', true, true);
|
registerSlashCommand('resetpanels', doResetPanels, ['resetui'], ' – resets UI panels to original state.', true, true);
|
||||||
registerSlashCommand('bgcol', setAvgBG, [], ' – WIP test of auto-bg avg coloring', true, true);
|
registerSlashCommand('bgcol', setAvgBG, [], ' – WIP test of auto-bg avg coloring', true, true);
|
||||||
addLanguagesToDropdown();
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -118,7 +118,7 @@ class PresetManager {
|
||||||
async savePresetAs() {
|
async savePresetAs() {
|
||||||
const popupText = `
|
const popupText = `
|
||||||
<h3>Preset name:</h3>
|
<h3>Preset name:</h3>
|
||||||
<h4>Hint: Use a character/group name to bind preset to a specific chat.</h4>`;
|
${!this.isNonGenericApi() ? '<h4>Hint: Use a character/group name to bind preset to a specific chat.</h4>' : ''}`;
|
||||||
const name = await callPopup(popupText, "input");
|
const name = await callPopup(popupText, "input");
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
|
@ -131,7 +131,8 @@ class PresetManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
async savePreset(name, settings) {
|
async savePreset(name, settings) {
|
||||||
const preset = settings ?? this.getPresetSettings();
|
const preset = settings ?? this.getPresetSettings(name);
|
||||||
|
|
||||||
const res = await fetch(`/save_preset`, {
|
const res = await fetch(`/save_preset`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
|
@ -220,7 +221,7 @@ class PresetManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getPresetSettings() {
|
getPresetSettings(name) {
|
||||||
function getSettingsByApiId(apiId) {
|
function getSettingsByApiId(apiId) {
|
||||||
switch (apiId) {
|
switch (apiId) {
|
||||||
case "koboldhorde":
|
case "koboldhorde":
|
||||||
|
@ -232,7 +233,7 @@ class PresetManager {
|
||||||
return textgenerationwebui_settings;
|
return textgenerationwebui_settings;
|
||||||
case "instruct":
|
case "instruct":
|
||||||
const preset = deepClone(power_user.instruct);
|
const preset = deepClone(power_user.instruct);
|
||||||
preset['name'] = power_user.instruct.preset;
|
preset['name'] = name || power_user.instruct.preset;
|
||||||
return preset;
|
return preset;
|
||||||
default:
|
default:
|
||||||
console.warn(`Unknown API ID ${apiId}`);
|
console.warn(`Unknown API ID ${apiId}`);
|
||||||
|
@ -346,7 +347,7 @@ jQuery(async () => {
|
||||||
|
|
||||||
const selected = $(presetManager.select).find("option:selected");
|
const selected = $(presetManager.select).find("option:selected");
|
||||||
const name = selected.text();
|
const name = selected.text();
|
||||||
const preset = presetManager.getPresetSettings();
|
const preset = presetManager.getPresetSettings(name);
|
||||||
const data = JSON.stringify(preset, null, 4);
|
const data = JSON.stringify(preset, null, 4);
|
||||||
download(data, `${name}.json`, "application/json");
|
download(data, `${name}.json`, "application/json");
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,6 +9,7 @@ export const SECRET_KEYS = {
|
||||||
OPENROUTER: 'api_key_openrouter',
|
OPENROUTER: 'api_key_openrouter',
|
||||||
SCALE: 'api_key_scale',
|
SCALE: 'api_key_scale',
|
||||||
AI21: 'api_key_ai21',
|
AI21: 'api_key_ai21',
|
||||||
|
SCALE_COOKIE: 'scale_cookie',
|
||||||
}
|
}
|
||||||
|
|
||||||
const INPUT_MAP = {
|
const INPUT_MAP = {
|
||||||
|
@ -20,6 +21,7 @@ const INPUT_MAP = {
|
||||||
[SECRET_KEYS.OPENROUTER]: '#api_key_openrouter',
|
[SECRET_KEYS.OPENROUTER]: '#api_key_openrouter',
|
||||||
[SECRET_KEYS.SCALE]: '#api_key_scale',
|
[SECRET_KEYS.SCALE]: '#api_key_scale',
|
||||||
[SECRET_KEYS.AI21]: '#api_key_ai21',
|
[SECRET_KEYS.AI21]: '#api_key_ai21',
|
||||||
|
[SECRET_KEYS.SCALE_COOKIE]: '#scale_cookie',
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clearSecret() {
|
async function clearSecret() {
|
||||||
|
|
|
@ -22,7 +22,7 @@ import {
|
||||||
reloadCurrentChat,
|
reloadCurrentChat,
|
||||||
sendMessageAsUser,
|
sendMessageAsUser,
|
||||||
} from "../script.js";
|
} from "../script.js";
|
||||||
import { humanizedDateTime } from "./RossAscends-mods.js";
|
import { getMessageTimeStamp } from "./RossAscends-mods.js";
|
||||||
import { resetSelectedGroup } from "./group-chats.js";
|
import { resetSelectedGroup } from "./group-chats.js";
|
||||||
import { getRegexedString, regex_placement } from "./extensions/regex/engine.js";
|
import { getRegexedString, regex_placement } from "./extensions/regex/engine.js";
|
||||||
import { chat_styles, power_user } from "./power-user.js";
|
import { chat_styles, power_user } from "./power-user.js";
|
||||||
|
@ -327,7 +327,7 @@ async function sendMessageAs(_, text) {
|
||||||
is_user: false,
|
is_user: false,
|
||||||
is_name: true,
|
is_name: true,
|
||||||
is_system: isSystem,
|
is_system: isSystem,
|
||||||
send_date: humanizedDateTime(),
|
send_date: getMessageTimeStamp(),
|
||||||
mes: substituteParams(mesText),
|
mes: substituteParams(mesText),
|
||||||
force_avatar: force_avatar,
|
force_avatar: force_avatar,
|
||||||
original_avatar: original_avatar,
|
original_avatar: original_avatar,
|
||||||
|
@ -338,8 +338,9 @@ async function sendMessageAs(_, text) {
|
||||||
};
|
};
|
||||||
|
|
||||||
chat.push(message);
|
chat.push(message);
|
||||||
addOneMessage(message);
|
|
||||||
await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));
|
await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));
|
||||||
|
addOneMessage(message);
|
||||||
|
await eventSource.emit(event_types.USER_MESSAGE_RENDERED, (chat.length - 1));
|
||||||
saveChatConditional();
|
saveChatConditional();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,7 +359,7 @@ async function sendNarratorMessage(_, text) {
|
||||||
is_user: false,
|
is_user: false,
|
||||||
is_name: false,
|
is_name: false,
|
||||||
is_system: isSystem,
|
is_system: isSystem,
|
||||||
send_date: humanizedDateTime(),
|
send_date: getMessageTimeStamp(),
|
||||||
mes: substituteParams(text.trim()),
|
mes: substituteParams(text.trim()),
|
||||||
force_avatar: system_avatar,
|
force_avatar: system_avatar,
|
||||||
extra: {
|
extra: {
|
||||||
|
@ -369,8 +370,9 @@ async function sendNarratorMessage(_, text) {
|
||||||
};
|
};
|
||||||
|
|
||||||
chat.push(message);
|
chat.push(message);
|
||||||
addOneMessage(message);
|
|
||||||
await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));
|
await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));
|
||||||
|
addOneMessage(message);
|
||||||
|
await eventSource.emit(event_types.USER_MESSAGE_RENDERED, (chat.length - 1));
|
||||||
saveChatConditional();
|
saveChatConditional();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,7 +386,7 @@ async function sendCommentMessage(_, text) {
|
||||||
is_user: false,
|
is_user: false,
|
||||||
is_name: true,
|
is_name: true,
|
||||||
is_system: true,
|
is_system: true,
|
||||||
send_date: humanizedDateTime(),
|
send_date: getMessageTimeStamp(),
|
||||||
mes: substituteParams(text.trim()),
|
mes: substituteParams(text.trim()),
|
||||||
force_avatar: comment_avatar,
|
force_avatar: comment_avatar,
|
||||||
extra: {
|
extra: {
|
||||||
|
@ -394,8 +396,9 @@ async function sendCommentMessage(_, text) {
|
||||||
};
|
};
|
||||||
|
|
||||||
chat.push(message);
|
chat.push(message);
|
||||||
addOneMessage(message);
|
|
||||||
await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));
|
await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));
|
||||||
|
addOneMessage(message);
|
||||||
|
await eventSource.emit(event_types.USER_MESSAGE_RENDERED, (chat.length - 1));
|
||||||
saveChatConditional();
|
saveChatConditional();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,13 +25,12 @@ function createStatBlock(statName, statValue) {
|
||||||
* @returns {number} - The stat value if it is a number, otherwise 0.
|
* @returns {number} - The stat value if it is a number, otherwise 0.
|
||||||
*/
|
*/
|
||||||
function verifyStatValue(stat) {
|
function verifyStatValue(stat) {
|
||||||
return isNaN(stat) ? 0 : stat;
|
return isNaN(Number(stat)) ? 0 : Number(stat);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates total stats from character statistics.
|
* Calculates total stats from character statistics.
|
||||||
*
|
*
|
||||||
* @param {Object} charStats - Object containing character statistics.
|
|
||||||
* @returns {Object} - Object containing total statistics.
|
* @returns {Object} - Object containing total statistics.
|
||||||
*/
|
*/
|
||||||
function calculateTotalStats() {
|
function calculateTotalStats() {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
getCharacters,
|
getCharacters,
|
||||||
entitiesFilter,
|
entitiesFilter,
|
||||||
} from "../script.js";
|
} from "../script.js";
|
||||||
import { FILTER_TYPES } from "./filters.js";
|
import { FILTER_TYPES, FilterHelper } from "./filters.js";
|
||||||
|
|
||||||
import { groupCandidatesFilter, selected_group } from "./group-chats.js";
|
import { groupCandidatesFilter, selected_group } from "./group-chats.js";
|
||||||
import { uuidv4 } from "./utils.js";
|
import { uuidv4 } from "./utils.js";
|
||||||
|
@ -24,7 +24,6 @@ export {
|
||||||
importTags,
|
importTags,
|
||||||
};
|
};
|
||||||
|
|
||||||
const random_id = () => uuidv4();
|
|
||||||
const CHARACTER_FILTER_SELECTOR = '#rm_characters_block .rm_tag_filter';
|
const CHARACTER_FILTER_SELECTOR = '#rm_characters_block .rm_tag_filter';
|
||||||
const GROUP_FILTER_SELECTOR = '#rm_group_chats_block .rm_tag_filter';
|
const GROUP_FILTER_SELECTOR = '#rm_group_chats_block .rm_tag_filter';
|
||||||
|
|
||||||
|
@ -49,17 +48,21 @@ const InListActionable = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_TAGS = [
|
const DEFAULT_TAGS = [
|
||||||
{ id: random_id(), name: "Plain Text" },
|
{ id: uuidv4(), name: "Plain Text" },
|
||||||
{ id: random_id(), name: "OpenAI" },
|
{ id: uuidv4(), name: "OpenAI" },
|
||||||
{ id: random_id(), name: "W++" },
|
{ id: uuidv4(), name: "W++" },
|
||||||
{ id: random_id(), name: "Boostyle" },
|
{ id: uuidv4(), name: "Boostyle" },
|
||||||
{ id: random_id(), name: "PList" },
|
{ id: uuidv4(), name: "PList" },
|
||||||
{ id: random_id(), name: "AliChat" },
|
{ id: uuidv4(), name: "AliChat" },
|
||||||
];
|
];
|
||||||
|
|
||||||
let tags = [];
|
let tags = [];
|
||||||
let tag_map = {};
|
let tag_map = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the favorite filter to the character list.
|
||||||
|
* @param {FilterHelper} filterHelper Instance of FilterHelper class.
|
||||||
|
*/
|
||||||
function applyFavFilter(filterHelper) {
|
function applyFavFilter(filterHelper) {
|
||||||
const isSelected = $(this).hasClass('selected');
|
const isSelected = $(this).hasClass('selected');
|
||||||
const displayFavoritesOnly = !isSelected;
|
const displayFavoritesOnly = !isSelected;
|
||||||
|
@ -68,6 +71,10 @@ function applyFavFilter(filterHelper) {
|
||||||
filterHelper.setFilterData(FILTER_TYPES.FAV, displayFavoritesOnly);
|
filterHelper.setFilterData(FILTER_TYPES.FAV, displayFavoritesOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the "is group" filter to the character list.
|
||||||
|
* @param {FilterHelper} filterHelper Instance of FilterHelper class.
|
||||||
|
*/
|
||||||
function filterByGroups(filterHelper) {
|
function filterByGroups(filterHelper) {
|
||||||
const isSelected = $(this).hasClass('selected');
|
const isSelected = $(this).hasClass('selected');
|
||||||
const displayGroupsOnly = !isSelected;
|
const displayGroupsOnly = !isSelected;
|
||||||
|
@ -253,7 +260,7 @@ async function importTags(imported_char) {
|
||||||
|
|
||||||
function createNewTag(tagName) {
|
function createNewTag(tagName) {
|
||||||
const tag = {
|
const tag = {
|
||||||
id: random_id(),
|
id: uuidv4(),
|
||||||
name: tagName,
|
name: tagName,
|
||||||
color: '',
|
color: '',
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
Text formatting commands:
|
||||||
|
<ul>
|
||||||
|
<li><tt>*text*</tt> - displays as <i>italics</i></li>
|
||||||
|
<li><tt>**text**</tt> - displays as <b>bold</b></li>
|
||||||
|
<li><tt>***text***</tt> - displays as <b><i>bold italics</i></b></li>
|
||||||
|
<li><tt>```text```</tt> - displays as a code block (new lines allowed between the backticks)</li>
|
||||||
|
</ul>
|
||||||
|
<pre><code> like this</code></pre>
|
||||||
|
<ul>
|
||||||
|
<li><tt>`text`</tt> - displays as <code>inline code</code></li>
|
||||||
|
<li><tt> text</tt> - displays as a blockquote (note the space after >)</li>
|
||||||
|
<blockquote>like this</blockquote>
|
||||||
|
<li><tt># text</tt> - displays as a large header (note the space)</li>
|
||||||
|
<h1>like this</h1>
|
||||||
|
<li><tt>## text</tt> - displays as a medium header (note the space)</li>
|
||||||
|
<h2>like this</h2>
|
||||||
|
<li><tt>### text</tt> - displays as a small header (note the space)</li>
|
||||||
|
<h3>like this</h3>
|
||||||
|
<li><tt>$$ text $$</tt> - renders a LaTeX formula (if enabled)</li>
|
||||||
|
<li><tt>$ text $</tt> - renders an AsciiMath formula (if enabled)</li>
|
||||||
|
</ul>
|
|
@ -0,0 +1,11 @@
|
||||||
|
Hello there! Please select the help topic you would like to learn more about:
|
||||||
|
<ul>
|
||||||
|
<li><a href="#" data-displayHelp="1">Slash Commands</a> (or <tt>/help slash</tt>)</li>
|
||||||
|
<li><a href="#" data-displayHelp="2">Formatting</a> (or <tt>/help format</tt>)</li>
|
||||||
|
<li><a href="#" data-displayHelp="3">Hotkeys</a> (or <tt>/help hotkeys</tt>)</li>
|
||||||
|
<li><a href="#" data-displayHelp="4">{{Macros}}</a> (or <tt>/help macros</tt>)</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<b>
|
||||||
|
Still got questions left? The <a target="_blank" href="https://docs.sillytavern.app/">Official SillyTavern Documentation Website</a> has much more information!
|
||||||
|
</b>
|
|
@ -0,0 +1,13 @@
|
||||||
|
Hotkeys/Keybinds:
|
||||||
|
<ul>
|
||||||
|
<li><tt>Up</tt> = Edit last message in chat</li>
|
||||||
|
<li><tt>Ctrl+Up</tt> = Edit last USER message in chat</li>
|
||||||
|
<li><tt>Left</tt> = swipe left</li>
|
||||||
|
<li><tt>Right</tt> = swipe right (NOTE: swipe hotkeys are disabled when chatbar has something typed into it)</li>
|
||||||
|
<li><tt>Ctrl+Left</tt> = view locally stored variables (in the browser console window)</li>
|
||||||
|
<li><tt>Enter</tt> (with chat bar selected) = send your message to AI</li>
|
||||||
|
<li><tt>Ctrl+Enter</tt> = Regenerate the last AI response</li>
|
||||||
|
<li><tt>Escape</tt> = stop AI response generation</li>
|
||||||
|
<li><tt>Ctrl+Shift+Up</tt> = Scroll to context line</li>
|
||||||
|
<li><tt>Ctrl+Shift+Down</tt> = Scroll chat to bottom</li>
|
||||||
|
</ul>
|
|
@ -0,0 +1,128 @@
|
||||||
|
<h3 class="flex-container justifyCenter alignitemscenter">
|
||||||
|
Prompt Itemization
|
||||||
|
<div id="showRawPrompt" class="fa-solid fa-square-poll-horizontal menu_button"></div>
|
||||||
|
</h3>
|
||||||
|
Tokenizer: {{selectedTokenizer}}<br>
|
||||||
|
API Used: {{this_main_api}}<br>
|
||||||
|
<span class="tokenItemizingSubclass">
|
||||||
|
Only the white numbers really matter. All numbers are estimates.
|
||||||
|
Grey color items may not have been included in the context due to certain prompt format settings.
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<div class="justifyLeft">
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class="flex-container flex1 flexFlowColumns flexNoGap wide50p tokenGraph">
|
||||||
|
<div class="wide100p" style="background-color: grey; height: {{oaiSystemTokensPercentage}}%;"></div>
|
||||||
|
<div class="wide100p" style="background-color: salmon; height: {{oaiStartTokensPercentage}}%;"></div>
|
||||||
|
<div class="wide100p" style="background-color: indianred; height: {{storyStringTokensPercentage}}%;"></div>
|
||||||
|
<div class="wide100p" style="background-color: gold; height: {{worldInfoStringTokensPercentage}}%;"></div>
|
||||||
|
<div class="wide100p" style="background-color: palegreen; height: {{ActualChatHistoryTokensPercentage}}%;">
|
||||||
|
</div>
|
||||||
|
<div class="wide100p" style="background-color: cornflowerblue; height: {{allAnchorsTokensPercentage}}%;">
|
||||||
|
</div>
|
||||||
|
<div class="wide100p" style="background-color: mediumpurple; height: {{promptBiasTokensPercentage}}%;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container wide50p">
|
||||||
|
<div class="wide100p flex-container flexNoGap flexFlowColumn">
|
||||||
|
<div class="flex-container wide100p">
|
||||||
|
<div class="flex1" style="color: grey;">System Info:</div>
|
||||||
|
<div class=""> {{oaiSystemTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container ">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Chat Start: </div>
|
||||||
|
<div class="tokenItemizingSubclass"> {{oaiStartTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container ">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Main: </div>
|
||||||
|
<div class="tokenItemizingSubclass">{{oaiMainTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container ">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Jailbreak: </div>
|
||||||
|
<div class="tokenItemizingSubclass">{{oaiJailbreakTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container ">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- NSFW: </div>
|
||||||
|
<div class="tokenItemizingSubclass">{{oaiNsfwTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container ">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Nudge: </div>
|
||||||
|
<div class="tokenItemizingSubclass">{{oaiNudgeTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container ">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Impersonate: </div>
|
||||||
|
<div class="tokenItemizingSubclass">{{oaiImpersonateTokens}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="wide100p flex-container flexNoGap flexFlowColumn">
|
||||||
|
<div class="flex-container wide100p">
|
||||||
|
<div class="flex1" style="color: indianred;">Prompt Tokens:</div>
|
||||||
|
<div class=""> {{oaiPromptTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container ">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Description: </div>
|
||||||
|
<div class="tokenItemizingSubclass">{{charDescriptionTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container ">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Personality:</div>
|
||||||
|
<div class="tokenItemizingSubclass"> {{charPersonalityTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container ">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Scenario: </div>
|
||||||
|
<div class="tokenItemizingSubclass">{{scenarioTextTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container ">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Examples:</div>
|
||||||
|
<div class="tokenItemizingSubclass"> {{examplesStringTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container ">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- User Persona:</div>
|
||||||
|
<div class="tokenItemizingSubclass"> {{userPersonaStringTokens}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="wide100p flex-container">
|
||||||
|
<div class="flex1" style="color: gold;">World Info:</div>
|
||||||
|
<div class="">{{worldInfoStringTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="wide100p flex-container">
|
||||||
|
<div class="flex1" style="color: palegreen;">Chat History:</div>
|
||||||
|
<div class=""> {{ActualChatHistoryTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="wide100p flex-container flexNoGap flexFlowColumn">
|
||||||
|
<div class="wide100p flex-container">
|
||||||
|
<div class="flex1" style="color: cornflowerblue;">Extensions:</div>
|
||||||
|
<div class="">{{allAnchorsTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container ">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Summarize: </div>
|
||||||
|
<div class="tokenItemizingSubclass">{{summarizeStringTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container ">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Author's Note:</div>
|
||||||
|
<div class="tokenItemizingSubclass"> {{authorsNoteStringTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container ">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Smart Context:</div>
|
||||||
|
<div class="tokenItemizingSubclass"> {{smartContextStringTokens}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="wide100p flex-container">
|
||||||
|
<div class="flex1" style="color: mediumpurple;">{{}} Bias:</div>
|
||||||
|
<div class="">{{oaiBiasTokens}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="wide100p flex-container flexFlowColumns">
|
||||||
|
<div class="flex-container wide100p">
|
||||||
|
<div class="flex1">Total Tokens in Prompt:</div>
|
||||||
|
<div class=""> {{finalPromptTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container wide100p">
|
||||||
|
<div class="flex1">Max Context (Context Size - Response Length):</div>
|
||||||
|
<div class="">{{thisPrompt_max_context}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
|
@ -0,0 +1,108 @@
|
||||||
|
<h3 class="flex-container justifyCenter alignitemscenter">
|
||||||
|
Prompt Itemization
|
||||||
|
<div id="showRawPrompt" class="fa-solid fa-square-poll-horizontal menu_button"></div>
|
||||||
|
</h3>
|
||||||
|
Tokenizer: {{selectedTokenizer}}<br>
|
||||||
|
API Used: {{this_main_api}}<br>
|
||||||
|
<span class="tokenItemizingSubclass">
|
||||||
|
Only the white numbers really matter. All numbers are estimates.
|
||||||
|
Grey color items may not have been included in the context due to certain prompt format settings.
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<div class="justifyLeft">
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class="flex-container flex1 flexFlowColumns flexNoGap wide50p tokenGraph">
|
||||||
|
<div class="wide100p" style="background-color: indianred; height: {{storyStringTokensPercentage}}%;"></div>
|
||||||
|
<div class="wide100p" style="background-color: gold; height: {{worldInfoStringTokensPercentage}}%;"></div>
|
||||||
|
<div class="wide100p" style="background-color: palegreen; height: {{ActualChatHistoryTokensPercentage}}%;">
|
||||||
|
</div>
|
||||||
|
<div class="wide100p" style="background-color: cornflowerblue; height: {{allAnchorsTokensPercentage}}%;">
|
||||||
|
</div>
|
||||||
|
<div class="wide100p" style="background-color: mediumpurple; height: {{promptBiasTokensPercentage}}%;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container wide50p">
|
||||||
|
<div class="wide100p flex-container flexNoGap flexFlowColumn">
|
||||||
|
<div class="flex-container wide100p">
|
||||||
|
<div class="flex1" style="color: indianred;"> Character Definitions:</div>
|
||||||
|
<div class=""> {{storyStringTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Description: </div>
|
||||||
|
<div class="tokenItemizingSubclass">{{charDescriptionTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Personality:</div>
|
||||||
|
<div class="tokenItemizingSubclass"> {{charPersonalityTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Scenario: </div>
|
||||||
|
<div class="tokenItemizingSubclass">{{scenarioTextTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Examples:</div>
|
||||||
|
<div class="tokenItemizingSubclass"> {{examplesStringTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- User Persona:</div>
|
||||||
|
<div class="tokenItemizingSubclass"> {{userPersonaStringTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- System Prompt (Instruct):</div>
|
||||||
|
<div class="tokenItemizingSubclass"> {{instructionTokens}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="wide100p flex-container">
|
||||||
|
<div class="flex1" style="color: gold;">World Info:</div>
|
||||||
|
<div class="">{{worldInfoStringTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="wide100p flex-container">
|
||||||
|
<div class="flex1" style="color: palegreen;">Chat History:</div>
|
||||||
|
<div class=""> {{ActualChatHistoryTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="wide100p flex-container flexNoGap flexFlowColumn">
|
||||||
|
<div class="wide100p flex-container">
|
||||||
|
<div class="flex1" style="color: cornflowerblue;">Extensions:</div>
|
||||||
|
<div class="">{{allAnchorsTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Summarize: </div>
|
||||||
|
<div class="tokenItemizingSubclass">{{summarizeStringTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Author's Note:</div>
|
||||||
|
<div class="tokenItemizingSubclass"> {{authorsNoteStringTokens}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container">
|
||||||
|
<div class=" flex1 tokenItemizingSubclass">-- Smart Context:</div>
|
||||||
|
<div class="tokenItemizingSubclass"> {{smartContextStringTokens}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="wide100p flex-container">
|
||||||
|
<div class="flex1" style="color: mediumpurple;">{{}} Bias:</div>
|
||||||
|
<div class="">{{promptBiasTokens}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="wide100p flex-container flexFlowColumns">
|
||||||
|
<div class="flex-container wide100p">
|
||||||
|
<div class="flex1">Total Tokens in Prompt:</div>
|
||||||
|
<div class=""> {{totalTokensInPrompt}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container wide100p">
|
||||||
|
<div class="flex1">Max Context (Context Size - Response Length):</div>
|
||||||
|
<div class="">{{thisPrompt_max_context}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container wide100p">
|
||||||
|
<div class="flex1">- Padding:</div>
|
||||||
|
<div class=""> {{thisPrompt_padding}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container wide100p">
|
||||||
|
<div class="flex1">Actual Max Context Allowed:</div>
|
||||||
|
<div class="">{{thisPrompt_actual}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
|
@ -0,0 +1,11 @@
|
||||||
|
System-wide Replacement Macros:
|
||||||
|
<ul>
|
||||||
|
<li><tt>{{user}}</tt> - your current Persona username</li>
|
||||||
|
<li><tt>{{char}}</tt> - the Character's name</li>
|
||||||
|
<li><tt>{{input}}</tt> - the user input</li>
|
||||||
|
<li><tt>{{time}}</tt> - the current time</li>
|
||||||
|
<li><tt>{{date}}</tt> - the current date</li>
|
||||||
|
<li><tt>{{idle_duration}}</tt> - the time since the last user message was sent</li>
|
||||||
|
<li><tt>{{random:(args)}}</tt> - returns a random item from the list. (ex: {{random:1,2,3,4}} will return 1 of the 4 numbers at random. Works with text lists too.</li>
|
||||||
|
<li><tt>{{roll:(formula)}}</tt> - rolls a dice. (ex: {{roll:1d6}} will roll a 6-sided dice and return a number between 1 and 6)</li>
|
||||||
|
</ul>
|
|
@ -0,0 +1,72 @@
|
||||||
|
<h3>
|
||||||
|
<span id="version_display_welcome">SillyTavern</span>
|
||||||
|
<div id="version_display_welcome"></div>
|
||||||
|
</h3>
|
||||||
|
<a href="https://docs.sillytavern.app/usage/update/"" target=" _blank">
|
||||||
|
Want to update?
|
||||||
|
</a>
|
||||||
|
<hr>
|
||||||
|
<h3>How to start chatting?</h3>
|
||||||
|
<ol>
|
||||||
|
<li>Click <code><i class="fa-solid fa-plug"></i></code> and select a <a href="https://docs.sillytavern.app/usage/api-connections/" target="_blank">Chat API</a>.</li>
|
||||||
|
<li>Click <code><i class="fa-solid fa-address-card"></i></code> and pick a character</li>
|
||||||
|
</ol>
|
||||||
|
<hr>
|
||||||
|
<h3>
|
||||||
|
Want more characters?
|
||||||
|
</h3>
|
||||||
|
<i>
|
||||||
|
Not controlled by SillyTavern team.
|
||||||
|
</i>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a target="_blank" href="https://discord.gg/pygmalionai">
|
||||||
|
Pygmalion AI Discord
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a target="_blank" href="https://chub.ai/">
|
||||||
|
Chub (NSFW)
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<hr>
|
||||||
|
<h3>Confused or lost?</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<span class="note-link-span">?</span> - click these icons!
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Enter <code>/?</code> in the chat bar
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a target="_blank" href="https://docs.sillytavern.app/">
|
||||||
|
SillyTavern Documentation Site
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a target="_blank" href="https://docs.sillytavern.app/extras/installation/">
|
||||||
|
Extras Installation Guide
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<h3>Still have questions?</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a target="_blank" href="https://discord.gg/RZdyAEUPvj">
|
||||||
|
Join the SillyTavern Discord
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a target="_blank" href="https://github.com/SillyTavern/SillyTavern/issues">
|
||||||
|
Post a GitHub issue
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a target="_blank" href="https://github.com/SillyTavern/SillyTavern#questions-or-suggestions">
|
||||||
|
Contact the developers
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
|
@ -6,8 +6,6 @@ import {
|
||||||
setGenerationParamsFromPreset,
|
setGenerationParamsFromPreset,
|
||||||
} from "../script.js";
|
} from "../script.js";
|
||||||
|
|
||||||
import { getCfg } from "./extensions/cfg/util.js";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
power_user,
|
power_user,
|
||||||
} from "./power-user.js";
|
} from "./power-user.js";
|
||||||
|
@ -170,9 +168,9 @@ $(document).ready(function () {
|
||||||
textgenerationwebui_settings[id] = value;
|
textgenerationwebui_settings[id] = value;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const value = parseFloat($(this).val());
|
const value = Number($(this).val());
|
||||||
$(`#${id}_counter_textgenerationwebui`).text(value.toFixed(2));
|
$(`#${id}_counter_textgenerationwebui`).text(value.toFixed(2));
|
||||||
textgenerationwebui_settings[id] = parseFloat(value);
|
textgenerationwebui_settings[id] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
|
@ -209,7 +207,7 @@ async function generateTextGenWithStreaming(generate_data, signal) {
|
||||||
const response = await fetch('/generate_textgenerationwebui', {
|
const response = await fetch('/generate_textgenerationwebui', {
|
||||||
headers: {
|
headers: {
|
||||||
...getRequestHeaders(),
|
...getRequestHeaders(),
|
||||||
'X-Response-Streaming': true,
|
'X-Response-Streaming': String(true),
|
||||||
'X-Streaming-URL': textgenerationwebui_settings.streaming_url,
|
'X-Streaming-URL': textgenerationwebui_settings.streaming_url,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(generate_data),
|
body: JSON.stringify(generate_data),
|
||||||
|
@ -235,9 +233,7 @@ async function generateTextGenWithStreaming(generate_data, signal) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTextGenGenerationData(finalPromt, this_amount_gen, isImpersonate) {
|
export function getTextGenGenerationData(finalPromt, this_amount_gen, isImpersonate, cfgValues) {
|
||||||
const cfgValues = getCfg();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'prompt': finalPromt,
|
'prompt': finalPromt,
|
||||||
'max_new_tokens': this_amount_gen,
|
'max_new_tokens': this_amount_gen,
|
||||||
|
@ -255,7 +251,7 @@ export function getTextGenGenerationData(finalPromt, this_amount_gen, isImperson
|
||||||
'penalty_alpha': textgenerationwebui_settings.penalty_alpha,
|
'penalty_alpha': textgenerationwebui_settings.penalty_alpha,
|
||||||
'length_penalty': textgenerationwebui_settings.length_penalty,
|
'length_penalty': textgenerationwebui_settings.length_penalty,
|
||||||
'early_stopping': textgenerationwebui_settings.early_stopping,
|
'early_stopping': textgenerationwebui_settings.early_stopping,
|
||||||
'guidance_scale': cfgValues?.guidanceScale ?? textgenerationwebui_settings.guidance_scale ?? 1,
|
'guidance_scale': cfgValues?.guidanceScale?.value ?? textgenerationwebui_settings.guidance_scale ?? 1,
|
||||||
'negative_prompt': cfgValues?.negativePrompt ?? textgenerationwebui_settings.negative_prompt ?? '',
|
'negative_prompt': cfgValues?.negativePrompt ?? textgenerationwebui_settings.negative_prompt ?? '',
|
||||||
'seed': textgenerationwebui_settings.seed,
|
'seed': textgenerationwebui_settings.seed,
|
||||||
'add_bos_token': textgenerationwebui_settings.add_bos_token,
|
'add_bos_token': textgenerationwebui_settings.add_bos_token,
|
||||||
|
|
|
@ -0,0 +1,342 @@
|
||||||
|
import { characters, main_api, nai_settings, this_chid } from "../script.js";
|
||||||
|
import { power_user } from "./power-user.js";
|
||||||
|
import { encode } from "../lib/gpt-2-3-tokenizer/mod.js";
|
||||||
|
import { GPT3BrowserTokenizer } from "../lib/gpt-3-tokenizer/gpt3-tokenizer.js";
|
||||||
|
import { chat_completion_sources, oai_settings } from "./openai.js";
|
||||||
|
import { groups, selected_group } from "./group-chats.js";
|
||||||
|
import { getStringHash } from "./utils.js";
|
||||||
|
|
||||||
|
export const CHARACTERS_PER_TOKEN_RATIO = 3.35;
|
||||||
|
|
||||||
|
export const tokenizers = {
|
||||||
|
NONE: 0,
|
||||||
|
GPT3: 1,
|
||||||
|
CLASSIC: 2,
|
||||||
|
LLAMA: 3,
|
||||||
|
NERD: 4,
|
||||||
|
NERD2: 5,
|
||||||
|
API: 6,
|
||||||
|
BEST_MATCH: 99,
|
||||||
|
};
|
||||||
|
|
||||||
|
const objectStore = new localforage.createInstance({ name: "SillyTavern_ChatCompletions" });
|
||||||
|
const gpt3 = new GPT3BrowserTokenizer({ type: 'gpt3' });
|
||||||
|
|
||||||
|
let tokenCache = {};
|
||||||
|
|
||||||
|
async function loadTokenCache() {
|
||||||
|
try {
|
||||||
|
console.debug('Chat Completions: loading token cache')
|
||||||
|
tokenCache = await objectStore.getItem('tokenCache') || {};
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Chat Completions: unable to load token cache, using default value', e);
|
||||||
|
tokenCache = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveTokenCache() {
|
||||||
|
try {
|
||||||
|
console.debug('Chat Completions: saving token cache')
|
||||||
|
await objectStore.setItem('tokenCache', tokenCache);
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Chat Completions: unable to save token cache', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resetTokenCache() {
|
||||||
|
try {
|
||||||
|
console.debug('Chat Completions: resetting token cache');
|
||||||
|
Object.keys(tokenCache).forEach(key => delete tokenCache[key]);
|
||||||
|
await objectStore.removeItem('tokenCache');
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Chat Completions: unable to reset token cache', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window['resetTokenCache'] = resetTokenCache;
|
||||||
|
|
||||||
|
function getTokenizerBestMatch() {
|
||||||
|
if (main_api === 'novel') {
|
||||||
|
if (nai_settings.model_novel.includes('krake') || nai_settings.model_novel.includes('euterpe')) {
|
||||||
|
return tokenizers.CLASSIC;
|
||||||
|
}
|
||||||
|
if (nai_settings.model_novel.includes('clio')) {
|
||||||
|
return tokenizers.NERD;
|
||||||
|
}
|
||||||
|
if (nai_settings.model_novel.includes('kayra')) {
|
||||||
|
return tokenizers.NERD2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (main_api === 'kobold' || main_api === 'textgenerationwebui' || main_api === 'koboldhorde') {
|
||||||
|
return tokenizers.LLAMA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokenizers.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the token count for a string using the current model tokenizer.
|
||||||
|
* @param {string} str String to tokenize
|
||||||
|
* @param {number | undefined} padding Optional padding tokens. Defaults to 0.
|
||||||
|
* @returns {number} Token count.
|
||||||
|
*/
|
||||||
|
export function getTokenCount(str, padding = undefined) {
|
||||||
|
/**
|
||||||
|
* Calculates the token count for a string.
|
||||||
|
* @param {number} [type] Tokenizer type.
|
||||||
|
* @returns {number} Token count.
|
||||||
|
*/
|
||||||
|
function calculate(type) {
|
||||||
|
switch (type) {
|
||||||
|
case tokenizers.NONE:
|
||||||
|
return Math.ceil(str.length / CHARACTERS_PER_TOKEN_RATIO) + padding;
|
||||||
|
case tokenizers.GPT3:
|
||||||
|
return gpt3.encode(str).bpe.length + padding;
|
||||||
|
case tokenizers.CLASSIC:
|
||||||
|
return encode(str).length + padding;
|
||||||
|
case tokenizers.LLAMA:
|
||||||
|
return countTokensRemote('/tokenize_llama', str, padding);
|
||||||
|
case tokenizers.NERD:
|
||||||
|
return countTokensRemote('/tokenize_nerdstash', str, padding);
|
||||||
|
case tokenizers.NERD2:
|
||||||
|
return countTokensRemote('/tokenize_nerdstash_v2', str, padding);
|
||||||
|
case tokenizers.API:
|
||||||
|
return countTokensRemote('/tokenize_via_api', str, padding);
|
||||||
|
default:
|
||||||
|
console.warn("Unknown tokenizer type", type);
|
||||||
|
return calculate(tokenizers.NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof str !== 'string' || !str?.length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokenizerType = power_user.tokenizer;
|
||||||
|
|
||||||
|
if (main_api === 'openai') {
|
||||||
|
if (padding === power_user.token_padding) {
|
||||||
|
// For main "shadow" prompt building
|
||||||
|
tokenizerType = tokenizers.NONE;
|
||||||
|
} else {
|
||||||
|
// For extensions and WI
|
||||||
|
return counterWrapperOpenAI(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenizerType === tokenizers.BEST_MATCH) {
|
||||||
|
tokenizerType = getTokenizerBestMatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (padding === undefined) {
|
||||||
|
padding = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cacheObject = getTokenCacheObject();
|
||||||
|
const hash = getStringHash(str);
|
||||||
|
const cacheKey = `${tokenizerType}-${hash}`;
|
||||||
|
|
||||||
|
if (typeof cacheObject[cacheKey] === 'number') {
|
||||||
|
return cacheObject[cacheKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = calculate(tokenizerType);
|
||||||
|
|
||||||
|
if (isNaN(result)) {
|
||||||
|
console.warn("Token count calculation returned NaN");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheObject[cacheKey] = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the token count for a string using the OpenAI tokenizer.
|
||||||
|
* @param {string} text Text to tokenize.
|
||||||
|
* @returns {number} Token count.
|
||||||
|
*/
|
||||||
|
function counterWrapperOpenAI(text) {
|
||||||
|
const message = { role: 'system', content: text };
|
||||||
|
return countTokensOpenAI(message, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTokenizerModel() {
|
||||||
|
// OpenAI models always provide their own tokenizer
|
||||||
|
if (oai_settings.chat_completion_source == chat_completion_sources.OPENAI) {
|
||||||
|
return oai_settings.openai_model;
|
||||||
|
}
|
||||||
|
|
||||||
|
const turboTokenizer = 'gpt-3.5-turbo';
|
||||||
|
const gpt4Tokenizer = 'gpt-4';
|
||||||
|
const gpt2Tokenizer = 'gpt2';
|
||||||
|
const claudeTokenizer = 'claude';
|
||||||
|
|
||||||
|
// Assuming no one would use it for different models.. right?
|
||||||
|
if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) {
|
||||||
|
return gpt4Tokenizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select correct tokenizer for WindowAI proxies
|
||||||
|
if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI && oai_settings.windowai_model) {
|
||||||
|
if (oai_settings.windowai_model.includes('gpt-4')) {
|
||||||
|
return gpt4Tokenizer;
|
||||||
|
}
|
||||||
|
else if (oai_settings.windowai_model.includes('gpt-3.5-turbo')) {
|
||||||
|
return turboTokenizer;
|
||||||
|
}
|
||||||
|
else if (oai_settings.windowai_model.includes('claude')) {
|
||||||
|
return claudeTokenizer;
|
||||||
|
}
|
||||||
|
else if (oai_settings.windowai_model.includes('GPT-NeoXT')) {
|
||||||
|
return gpt2Tokenizer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// And for OpenRouter (if not a site model, then it's impossible to determine the tokenizer)
|
||||||
|
if (oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER && oai_settings.openrouter_model) {
|
||||||
|
if (oai_settings.openrouter_model.includes('gpt-4')) {
|
||||||
|
return gpt4Tokenizer;
|
||||||
|
}
|
||||||
|
else if (oai_settings.openrouter_model.includes('gpt-3.5-turbo')) {
|
||||||
|
return turboTokenizer;
|
||||||
|
}
|
||||||
|
else if (oai_settings.openrouter_model.includes('claude')) {
|
||||||
|
return claudeTokenizer;
|
||||||
|
}
|
||||||
|
else if (oai_settings.openrouter_model.includes('GPT-NeoXT')) {
|
||||||
|
return gpt2Tokenizer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
|
||||||
|
return claudeTokenizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to Turbo 3.5
|
||||||
|
return turboTokenizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any[] | Object} messages
|
||||||
|
*/
|
||||||
|
export function countTokensOpenAI(messages, full = false) {
|
||||||
|
const shouldTokenizeAI21 = oai_settings.chat_completion_source === chat_completion_sources.AI21 && oai_settings.use_ai21_tokenizer;
|
||||||
|
const cacheObject = getTokenCacheObject();
|
||||||
|
|
||||||
|
if (!Array.isArray(messages)) {
|
||||||
|
messages = [messages];
|
||||||
|
}
|
||||||
|
|
||||||
|
let token_count = -1;
|
||||||
|
|
||||||
|
for (const message of messages) {
|
||||||
|
const model = getTokenizerModel();
|
||||||
|
|
||||||
|
if (model === 'claude' || shouldTokenizeAI21) {
|
||||||
|
full = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hash = getStringHash(JSON.stringify(message));
|
||||||
|
const cacheKey = `${model}-${hash}`;
|
||||||
|
const cachedCount = cacheObject[cacheKey];
|
||||||
|
|
||||||
|
if (typeof cachedCount === 'number') {
|
||||||
|
token_count += cachedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
jQuery.ajax({
|
||||||
|
async: false,
|
||||||
|
type: 'POST', //
|
||||||
|
url: shouldTokenizeAI21 ? '/tokenize_ai21' : `/tokenize_openai?model=${model}`,
|
||||||
|
data: JSON.stringify([message]),
|
||||||
|
dataType: "json",
|
||||||
|
contentType: "application/json",
|
||||||
|
success: function (data) {
|
||||||
|
token_count += Number(data.token_count);
|
||||||
|
cacheObject[cacheKey] = Number(data.token_count);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!full) token_count -= 2;
|
||||||
|
|
||||||
|
return token_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the token cache object for the current chat.
|
||||||
|
* @returns {Object} Token cache object for the current chat.
|
||||||
|
*/
|
||||||
|
function getTokenCacheObject() {
|
||||||
|
let chatId = 'undefined';
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (selected_group) {
|
||||||
|
chatId = groups.find(x => x.id == selected_group)?.chat_id;
|
||||||
|
}
|
||||||
|
else if (this_chid !== undefined) {
|
||||||
|
chatId = characters[this_chid].chat;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
console.log('No character / group selected. Using default cache item');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof tokenCache[chatId] !== 'object') {
|
||||||
|
tokenCache[chatId] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokenCache[String(chatId)];
|
||||||
|
}
|
||||||
|
|
||||||
|
function countTokensRemote(endpoint, str, padding) {
|
||||||
|
let tokenCount = 0;
|
||||||
|
jQuery.ajax({
|
||||||
|
async: false,
|
||||||
|
type: 'POST',
|
||||||
|
url: endpoint,
|
||||||
|
data: JSON.stringify({ text: str }),
|
||||||
|
dataType: "json",
|
||||||
|
contentType: "application/json",
|
||||||
|
success: function (data) {
|
||||||
|
tokenCount = data.count;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return tokenCount + padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTextTokensRemote(endpoint, str) {
|
||||||
|
let ids = [];
|
||||||
|
jQuery.ajax({
|
||||||
|
async: false,
|
||||||
|
type: 'POST',
|
||||||
|
url: endpoint,
|
||||||
|
data: JSON.stringify({ text: str }),
|
||||||
|
dataType: "json",
|
||||||
|
contentType: "application/json",
|
||||||
|
success: function (data) {
|
||||||
|
ids = data.ids;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTextTokens(tokenizerType, str) {
|
||||||
|
switch (tokenizerType) {
|
||||||
|
case tokenizers.LLAMA:
|
||||||
|
return getTextTokensRemote('/tokenize_llama', str);
|
||||||
|
case tokenizers.NERD:
|
||||||
|
return getTextTokensRemote('/tokenize_nerdstash', str);
|
||||||
|
case tokenizers.NERD2:
|
||||||
|
return getTextTokensRemote('/tokenize_nerdstash_v2', str);
|
||||||
|
default:
|
||||||
|
console.warn("Calling getTextTokens with unsupported tokenizer type", tokenizerType);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jQuery(async () => {
|
||||||
|
await loadTokenCache();
|
||||||
|
});
|
|
@ -1,21 +1,56 @@
|
||||||
import { getContext } from "./extensions.js";
|
import { getContext } from "./extensions.js";
|
||||||
import { getRequestHeaders } from "../script.js";
|
import { getRequestHeaders } from "../script.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pagination status string template.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
export const PAGINATION_TEMPLATE = '<%= rangeStart %>-<%= rangeEnd %> of <%= totalNumber %>';
|
export const PAGINATION_TEMPLATE = '<%= rangeStart %>-<%= rangeEnd %> of <%= totalNumber %>';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigation options for pagination.
|
||||||
|
* @enum {number}
|
||||||
|
*/
|
||||||
|
export const navigation_option = { none: 0, previous: 1, last: 2, };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a value is unique in an array.
|
||||||
|
* @param {any} value Current value.
|
||||||
|
* @param {number} index Current index.
|
||||||
|
* @param {any} array The array being processed.
|
||||||
|
* @returns {boolean} True if the value is unique, false otherwise.
|
||||||
|
*/
|
||||||
export function onlyUnique(value, index, array) {
|
export function onlyUnique(value, index, array) {
|
||||||
return array.indexOf(value) === index;
|
return array.indexOf(value) === index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a string only contains digits.
|
||||||
|
* @param {string} str The string to check.
|
||||||
|
* @returns {boolean} True if the string only contains digits, false otherwise.
|
||||||
|
* @example
|
||||||
|
* isDigitsOnly('123'); // true
|
||||||
|
* isDigitsOnly('abc'); // false
|
||||||
|
*/
|
||||||
export function isDigitsOnly(str) {
|
export function isDigitsOnly(str) {
|
||||||
return /^\d+$/.test(str);
|
return /^\d+$/.test(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increase delay on touch screens
|
/**
|
||||||
|
* Gets a drag delay for sortable elements. This is to prevent accidental drags when scrolling.
|
||||||
|
* @returns {number} The delay in milliseconds. 100ms for desktop, 750ms for mobile.
|
||||||
|
*/
|
||||||
export function getSortableDelay() {
|
export function getSortableDelay() {
|
||||||
return navigator.maxTouchPoints > 0 ? 750 : 100;
|
return navigator.maxTouchPoints > 0 ? 750 : 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rearranges an array in a random order.
|
||||||
|
* @param {any[]} array The array to shuffle.
|
||||||
|
* @returns {any[]} The shuffled array.
|
||||||
|
* @example
|
||||||
|
* shuffle([1, 2, 3]); // [2, 3, 1]
|
||||||
|
*/
|
||||||
export function shuffle(array) {
|
export function shuffle(array) {
|
||||||
let currentIndex = array.length,
|
let currentIndex = array.length,
|
||||||
randomIndex;
|
randomIndex;
|
||||||
|
@ -31,6 +66,12 @@ export function shuffle(array) {
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads a file to the user's devices.
|
||||||
|
* @param {BlobPart} content File content to download.
|
||||||
|
* @param {string} fileName File name.
|
||||||
|
* @param {string} contentType File content type.
|
||||||
|
*/
|
||||||
export function download(content, fileName, contentType) {
|
export function download(content, fileName, contentType) {
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
const file = new Blob([content], { type: contentType });
|
const file = new Blob([content], { type: contentType });
|
||||||
|
@ -39,22 +80,38 @@ export function download(content, fileName, contentType) {
|
||||||
a.click();
|
a.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a file by URL and parses its contents as data URI.
|
||||||
|
* @param {string} url The URL to fetch.
|
||||||
|
* @param {any} params Fetch parameters.
|
||||||
|
* @returns {Promise<string>} A promise that resolves to the data URI.
|
||||||
|
*/
|
||||||
export async function urlContentToDataUri(url, params) {
|
export async function urlContentToDataUri(url, params) {
|
||||||
const response = await fetch(url, params);
|
const response = await fetch(url, params);
|
||||||
const blob = await response.blob();
|
const blob = await response.blob();
|
||||||
return await new Promise(callback => {
|
return await new Promise((resolve, reject) => {
|
||||||
let reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = function () { callback(this.result); };
|
reader.onload = function () {
|
||||||
|
resolve(String(reader.result));
|
||||||
|
};
|
||||||
|
reader.onerror = function (error) {
|
||||||
|
reject(error);
|
||||||
|
};
|
||||||
reader.readAsDataURL(blob);
|
reader.readAsDataURL(blob);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a promise that resolves to the file's text.
|
||||||
|
* @param {Blob} file The file to read.
|
||||||
|
* @returns {Promise<string>} A promise that resolves to the file's text.
|
||||||
|
*/
|
||||||
export function getFileText(file) {
|
export function getFileText(file) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
reader.onload = function () {
|
reader.onload = function () {
|
||||||
resolve(reader.result);
|
resolve(String(reader.result));
|
||||||
};
|
};
|
||||||
reader.onerror = function (error) {
|
reader.onerror = function (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
|
@ -62,6 +119,10 @@ export function getFileText(file) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a promise that resolves to the file's array buffer.
|
||||||
|
* @param {Blob} file The file to read.
|
||||||
|
*/
|
||||||
export function getFileBuffer(file) {
|
export function getFileBuffer(file) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
@ -75,12 +136,17 @@ export function getFileBuffer(file) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a promise that resolves to the base64 encoded string of a file.
|
||||||
|
* @param {Blob} file The file to read.
|
||||||
|
* @returns {Promise<string>} A promise that resolves to the base64 encoded string.
|
||||||
|
*/
|
||||||
export function getBase64Async(file) {
|
export function getBase64Async(file) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
reader.onload = function () {
|
reader.onload = function () {
|
||||||
resolve(reader.result);
|
resolve(String(reader.result));
|
||||||
};
|
};
|
||||||
reader.onerror = function (error) {
|
reader.onerror = function (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
|
@ -88,15 +154,26 @@ export function getBase64Async(file) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a file blob as a JSON object.
|
||||||
|
* @param {Blob} file The file to read.
|
||||||
|
* @returns {Promise<any>} A promise that resolves to the parsed JSON object.
|
||||||
|
*/
|
||||||
export async function parseJsonFile(file) {
|
export async function parseJsonFile(file) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const fileReader = new FileReader();
|
const fileReader = new FileReader();
|
||||||
fileReader.onload = event => resolve(JSON.parse(event.target.result));
|
|
||||||
fileReader.onerror = error => reject(error);
|
|
||||||
fileReader.readAsText(file);
|
fileReader.readAsText(file);
|
||||||
|
fileReader.onload = event => resolve(JSON.parse(String(event.target.result)));
|
||||||
|
fileReader.onerror = error => reject(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates a hash code for a string.
|
||||||
|
* @param {string} str The string to hash.
|
||||||
|
* @param {number} [seed=0] The seed to use for the hash.
|
||||||
|
* @returns {number} The hash code.
|
||||||
|
*/
|
||||||
export function getStringHash(str, seed = 0) {
|
export function getStringHash(str, seed = 0) {
|
||||||
if (typeof str !== 'string') {
|
if (typeof str !== 'string') {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -116,6 +193,12 @@ export function getStringHash(str, seed = 0) {
|
||||||
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a debounced function that delays invoking func until after wait milliseconds have elapsed since the last time the debounced function was invoked.
|
||||||
|
* @param {function} func The function to debounce.
|
||||||
|
* @param {number} [timeout=300] The timeout in milliseconds.
|
||||||
|
* @returns {function} The debounced function.
|
||||||
|
*/
|
||||||
export function debounce(func, timeout = 300) {
|
export function debounce(func, timeout = 300) {
|
||||||
let timer;
|
let timer;
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
|
@ -124,6 +207,12 @@ export function debounce(func, timeout = 300) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a throttled function that only invokes func at most once per every limit milliseconds.
|
||||||
|
* @param {function} func The function to throttle.
|
||||||
|
* @param {number} [limit=300] The limit in milliseconds.
|
||||||
|
* @returns {function} The throttled function.
|
||||||
|
*/
|
||||||
export function throttle(func, limit = 300) {
|
export function throttle(func, limit = 300) {
|
||||||
let lastCall;
|
let lastCall;
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
|
@ -135,6 +224,11 @@ export function throttle(func, limit = 300) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if an element is in the viewport.
|
||||||
|
* @param {Element} el The element to check.
|
||||||
|
* @returns {boolean} True if the element is in the viewport, false otherwise.
|
||||||
|
*/
|
||||||
export function isElementInViewport(el) {
|
export function isElementInViewport(el) {
|
||||||
if (typeof jQuery === "function" && el instanceof jQuery) {
|
if (typeof jQuery === "function" && el instanceof jQuery) {
|
||||||
el = el[0];
|
el = el[0];
|
||||||
|
@ -148,6 +242,12 @@ export function isElementInViewport(el) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a name that is unique among the names that exist.
|
||||||
|
* @param {string} name The name to check.
|
||||||
|
* @param {{ (y: any): boolean; }} exists Function to check if name exists.
|
||||||
|
* @returns {string} A unique name.
|
||||||
|
*/
|
||||||
export function getUniqueName(name, exists) {
|
export function getUniqueName(name, exists) {
|
||||||
let i = 1;
|
let i = 1;
|
||||||
let baseName = name;
|
let baseName = name;
|
||||||
|
@ -158,18 +258,48 @@ export function getUniqueName(name, exists) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const delay = (ms) => new Promise((res) => setTimeout(res, ms));
|
/**
|
||||||
export const isSubsetOf = (a, b) => (Array.isArray(a) && Array.isArray(b)) ? b.every(val => a.includes(val)) : false;
|
* Returns a promise that resolves after the specified number of milliseconds.
|
||||||
|
* @param {number} ms The number of milliseconds to wait.
|
||||||
|
* @returns {Promise<void>} A promise that resolves after the specified number of milliseconds.
|
||||||
|
*/
|
||||||
|
export function delay(ms) {
|
||||||
|
return new Promise((res) => setTimeout(res, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if an array is a subset of another array.
|
||||||
|
* @param {any[]} a Array A
|
||||||
|
* @param {any[]} b Array B
|
||||||
|
* @returns {boolean} True if B is a subset of A, false otherwise.
|
||||||
|
*/
|
||||||
|
export function isSubsetOf(a, b) {
|
||||||
|
return (Array.isArray(a) && Array.isArray(b)) ? b.every(val => a.includes(val)) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the trailing number in a string.
|
||||||
|
* @param {string} str The string to process.
|
||||||
|
* @returns {string} The string with the trailing number incremented by 1.
|
||||||
|
* @example
|
||||||
|
* incrementString('Hello, world! 1'); // 'Hello, world! 2'
|
||||||
|
*/
|
||||||
export function incrementString(str) {
|
export function incrementString(str) {
|
||||||
// Find the trailing number or it will match the empty string
|
// Find the trailing number or it will match the empty string
|
||||||
const count = str.match(/\d*$/);
|
const count = str.match(/\d*$/);
|
||||||
|
|
||||||
// Take the substring up until where the integer was matched
|
// Take the substring up until where the integer was matched
|
||||||
// Concatenate it to the matched count incremented by 1
|
// Concatenate it to the matched count incremented by 1
|
||||||
return str.substr(0, count.index) + (++count[0]);
|
return str.substring(0, count.index) + (Number(count[0]) + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a string using the specified arguments.
|
||||||
|
* @param {string} format The format string.
|
||||||
|
* @returns {string} The formatted string.
|
||||||
|
* @example
|
||||||
|
* stringFormat('Hello, {0}!', 'world'); // 'Hello, world!'
|
||||||
|
*/
|
||||||
export function stringFormat(format) {
|
export function stringFormat(format) {
|
||||||
const args = Array.prototype.slice.call(arguments, 1);
|
const args = Array.prototype.slice.call(arguments, 1);
|
||||||
return format.replace(/{(\d+)}/g, function (match, number) {
|
return format.replace(/{(\d+)}/g, function (match, number) {
|
||||||
|
@ -180,7 +310,11 @@ export function stringFormat(format) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Save the caret position in a contenteditable element
|
/**
|
||||||
|
* Save the caret position in a contenteditable element.
|
||||||
|
* @param {Element} element The element to save the caret position of.
|
||||||
|
* @returns {{ start: number, end: number }} An object with the start and end offsets of the caret.
|
||||||
|
*/
|
||||||
export function saveCaretPosition(element) {
|
export function saveCaretPosition(element) {
|
||||||
// Get the current selection
|
// Get the current selection
|
||||||
const selection = window.getSelection();
|
const selection = window.getSelection();
|
||||||
|
@ -209,7 +343,11 @@ export function saveCaretPosition(element) {
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore the caret position in a contenteditable element
|
/**
|
||||||
|
* Restore the caret position in a contenteditable element.
|
||||||
|
* @param {Element} element The element to restore the caret position of.
|
||||||
|
* @param {{ start: any; end: any; }} position An object with the start and end offsets of the caret.
|
||||||
|
*/
|
||||||
export function restoreCaretPosition(element, position) {
|
export function restoreCaretPosition(element, position) {
|
||||||
// If the position is null, do nothing
|
// If the position is null, do nothing
|
||||||
if (!position) {
|
if (!position) {
|
||||||
|
@ -236,6 +374,11 @@ export async function resetScrollHeight(element) {
|
||||||
$(element).css('height', $(element).prop('scrollHeight') + 3 + 'px');
|
$(element).css('height', $(element).prop('scrollHeight') + 3 + 'px');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the height of an element to its scroll height.
|
||||||
|
* @param {JQuery<HTMLElement>} element The element to initialize the scroll height of.
|
||||||
|
* @returns {Promise<void>} A promise that resolves when the scroll height has been initialized.
|
||||||
|
*/
|
||||||
export async function initScrollHeight(element) {
|
export async function initScrollHeight(element) {
|
||||||
await delay(1);
|
await delay(1);
|
||||||
|
|
||||||
|
@ -252,15 +395,27 @@ export async function initScrollHeight(element) {
|
||||||
//resetScrollHeight(element);
|
//resetScrollHeight(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares elements by their CSS order property. Used for sorting.
|
||||||
|
* @param {any} a The first element.
|
||||||
|
* @param {any} b The second element.
|
||||||
|
* @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 sortByCssOrder(a, b) {
|
export function sortByCssOrder(a, b) {
|
||||||
const _a = Number($(a).css('order'));
|
const _a = Number($(a).css('order'));
|
||||||
const _b = Number($(b).css('order'));
|
const _b = Number($(b).css('order'));
|
||||||
return _a - _b;
|
return _a - _b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trims a string to the end of a nearest sentence.
|
||||||
|
* @param {string} input The string to trim.
|
||||||
|
* @param {boolean} include_newline Whether to include a newline character in the trimmed string.
|
||||||
|
* @returns {string} The trimmed string.
|
||||||
|
* @example
|
||||||
|
* end_trim_to_sentence('Hello, world! I am from'); // 'Hello, world!'
|
||||||
|
*/
|
||||||
export function end_trim_to_sentence(input, include_newline = false) {
|
export function end_trim_to_sentence(input, include_newline = false) {
|
||||||
// inspired from https://github.com/kaihordewebui/kaihordewebui.github.io/blob/06b95e6b7720eb85177fbaf1a7f52955d7cdbc02/index.html#L4853-L4867
|
|
||||||
|
|
||||||
const punctuation = new Set(['.', '!', '?', '*', '"', ')', '}', '`', ']', '$', '。', '!', '?', '”', ')', '】', '】', '’', '」', '】']); // extend this as you see fit
|
const punctuation = new Set(['.', '!', '?', '*', '"', ')', '}', '`', ']', '$', '。', '!', '?', '”', ')', '】', '】', '’', '」', '】']); // extend this as you see fit
|
||||||
let last = -1;
|
let last = -1;
|
||||||
|
|
||||||
|
@ -285,6 +440,15 @@ export function end_trim_to_sentence(input, include_newline = false) {
|
||||||
return input.substring(0, last + 1).trimEnd();
|
return input.substring(0, last + 1).trimEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counts the number of occurrences of a character in a string.
|
||||||
|
* @param {string} string The string to count occurrences in.
|
||||||
|
* @param {string} character The character to count occurrences of.
|
||||||
|
* @returns {number} The number of occurrences of the character in the string.
|
||||||
|
* @example
|
||||||
|
* countOccurrences('Hello, world!', 'l'); // 3
|
||||||
|
* countOccurrences('Hello, world!', 'x'); // 0
|
||||||
|
*/
|
||||||
export function countOccurrences(string, character) {
|
export function countOccurrences(string, character) {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
|
@ -297,6 +461,14 @@ export function countOccurrences(string, character) {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a number is odd.
|
||||||
|
* @param {number} number The number to check.
|
||||||
|
* @returns {boolean} True if the number is odd, false otherwise.
|
||||||
|
* @example
|
||||||
|
* isOdd(3); // true
|
||||||
|
* isOdd(4); // false
|
||||||
|
*/
|
||||||
export function isOdd(number) {
|
export function isOdd(number) {
|
||||||
return number % 2 !== 0;
|
return number % 2 !== 0;
|
||||||
}
|
}
|
||||||
|
@ -337,6 +509,12 @@ export function timestampToMoment(timestamp) {
|
||||||
return moment.invalid();
|
return moment.invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two moment objects for sorting.
|
||||||
|
* @param {*} a The first moment object.
|
||||||
|
* @param {*} 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) {
|
export function sortMoments(a, b) {
|
||||||
if (a.isBefore(b)) {
|
if (a.isBefore(b)) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -347,14 +525,21 @@ export function sortMoments(a, b) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Split string to parts no more than length in size */
|
/** Split string to parts no more than length in size.
|
||||||
export function splitRecursive(input, length, delimitiers = ['\n\n', '\n', ' ', '']) {
|
* @param {string} input The string to split.
|
||||||
const delim = delimitiers[0] ?? '';
|
* @param {number} length The maximum length of each part.
|
||||||
|
* @param {string[]} delimiters The delimiters to use when splitting the string.
|
||||||
|
* @returns {string[]} The split string.
|
||||||
|
* @example
|
||||||
|
* splitRecursive('Hello, world!', 3); // ['Hel', 'lo,', 'wor', 'ld!']
|
||||||
|
*/
|
||||||
|
export function splitRecursive(input, length, delimiters = ['\n\n', '\n', ' ', '']) {
|
||||||
|
const delim = delimiters[0] ?? '';
|
||||||
const parts = input.split(delim);
|
const parts = input.split(delim);
|
||||||
|
|
||||||
const flatParts = parts.flatMap(p => {
|
const flatParts = parts.flatMap(p => {
|
||||||
if (p.length < length) return p;
|
if (p.length < length) return p;
|
||||||
return splitRecursive(input, length, delimitiers.slice(1));
|
return splitRecursive(input, length, delimiters.slice(1));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Merge short chunks
|
// Merge short chunks
|
||||||
|
@ -378,6 +563,13 @@ export function splitRecursive(input, length, delimitiers = ['\n\n', '\n', ' ',
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a string is a valid data URL.
|
||||||
|
* @param {string} str The string to check.
|
||||||
|
* @returns {boolean} True if the string is a valid data URL, false otherwise.
|
||||||
|
* @example
|
||||||
|
* isDataURL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA...'); // true
|
||||||
|
*/
|
||||||
export function isDataURL(str) {
|
export function isDataURL(str) {
|
||||||
const regex = /^data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)*;?)?(base64)?,([a-z0-9!$&',()*+;=\-_%.~:@\/?#]+)?$/i;
|
const regex = /^data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)*;?)?(base64)?,([a-z0-9!$&',()*+;=\-_%.~:@\/?#]+)?$/i;
|
||||||
return regex.test(str);
|
return regex.test(str);
|
||||||
|
@ -392,6 +584,13 @@ export function getCharaFilename(chid) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts words from a string.
|
||||||
|
* @param {string} value The string to extract words from.
|
||||||
|
* @returns {string[]} The extracted words.
|
||||||
|
* @example
|
||||||
|
* extractAllWords('Hello, world!'); // ['hello', 'world']
|
||||||
|
*/
|
||||||
export function extractAllWords(value) {
|
export function extractAllWords(value) {
|
||||||
const words = [];
|
const words = [];
|
||||||
|
|
||||||
|
@ -406,21 +605,45 @@ export function extractAllWords(value) {
|
||||||
return words;
|
return words;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes a string for use in a regular expression.
|
||||||
|
* @param {string} string The string to escape.
|
||||||
|
* @returns {string} The escaped string.
|
||||||
|
* @example
|
||||||
|
* escapeRegex('^Hello$'); // '\\^Hello\\$'
|
||||||
|
*/
|
||||||
export function escapeRegex(string) {
|
export function escapeRegex(string) {
|
||||||
return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&');
|
return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides an interface for rate limiting function calls.
|
||||||
|
*/
|
||||||
export class RateLimiter {
|
export class RateLimiter {
|
||||||
constructor(intervalMillis) {
|
/**
|
||||||
this._intervalMillis = intervalMillis;
|
* Creates a new RateLimiter.
|
||||||
this._lastResolveTime = 0;
|
* @param {number} interval The interval in milliseconds.
|
||||||
this._pendingResolve = Promise.resolve();
|
* @example
|
||||||
|
* const rateLimiter = new RateLimiter(1000);
|
||||||
|
* rateLimiter.waitForResolve().then(() => {
|
||||||
|
* console.log('Waited 1000ms');
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
constructor(interval) {
|
||||||
|
this.interval = interval;
|
||||||
|
this.lastResolveTime = 0;
|
||||||
|
this.pendingResolve = Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for the remaining time in the interval.
|
||||||
|
* @param {AbortSignal} abortSignal An optional AbortSignal to abort the wait.
|
||||||
|
* @returns {Promise<void>} A promise that resolves when the remaining time has elapsed.
|
||||||
|
*/
|
||||||
_waitRemainingTime(abortSignal) {
|
_waitRemainingTime(abortSignal) {
|
||||||
const currentTime = Date.now();
|
const currentTime = Date.now();
|
||||||
const elapsedTime = currentTime - this._lastResolveTime;
|
const elapsedTime = currentTime - this.lastResolveTime;
|
||||||
const remainingTime = Math.max(0, this._intervalMillis - elapsedTime);
|
const remainingTime = Math.max(0, this.interval - elapsedTime);
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const timeoutId = setTimeout(() => {
|
const timeoutId = setTimeout(() => {
|
||||||
|
@ -436,19 +659,29 @@ export class RateLimiter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for the next interval to elapse.
|
||||||
|
* @param {AbortSignal} abortSignal An optional AbortSignal to abort the wait.
|
||||||
|
* @returns {Promise<void>} A promise that resolves when the next interval has elapsed.
|
||||||
|
*/
|
||||||
async waitForResolve(abortSignal) {
|
async waitForResolve(abortSignal) {
|
||||||
await this._pendingResolve;
|
await this.pendingResolve;
|
||||||
this._pendingResolve = this._waitRemainingTime(abortSignal);
|
this.pendingResolve = this._waitRemainingTime(abortSignal);
|
||||||
|
|
||||||
// Update the last resolve time
|
// Update the last resolve time
|
||||||
this._lastResolveTime = Date.now() + this._intervalMillis;
|
this.lastResolveTime = Date.now() + this.interval;
|
||||||
console.debug(`RateLimiter.waitForResolve() ${this._lastResolveTime}`);
|
console.debug(`RateLimiter.waitForResolve() ${this.lastResolveTime}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Taken from https://github.com/LostRuins/lite.koboldai.net/blob/main/index.html
|
/**
|
||||||
//import tavern png data. adapted from png-chunks-extract under MIT license
|
* Extracts a JSON object from a PNG file.
|
||||||
//accepts png input data, and returns the extracted JSON
|
* Taken from https://github.com/LostRuins/lite.koboldai.net/blob/main/index.html
|
||||||
|
* Adapted from png-chunks-extract under MIT license
|
||||||
|
* @param {Uint8Array} data The PNG data to extract the JSON from.
|
||||||
|
* @param {string} identifier The identifier to look for in the PNG tEXT data.
|
||||||
|
* @returns {object} The extracted JSON object.
|
||||||
|
*/
|
||||||
export function extractDataFromPng(data, identifier = 'chara') {
|
export function extractDataFromPng(data, identifier = 'chara') {
|
||||||
console.log("Attempting PNG import...");
|
console.log("Attempting PNG import...");
|
||||||
let uint8 = new Uint8Array(4);
|
let uint8 = new Uint8Array(4);
|
||||||
|
@ -599,6 +832,13 @@ export async function saveBase64AsFile(base64Data, characterName, filename = "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a thumbnail from a data URL.
|
||||||
|
* @param {string} dataUrl The data URL encoded data of the image.
|
||||||
|
* @param {number} maxWidth The maximum width of the thumbnail.
|
||||||
|
* @param {number} maxHeight The maximum height of the thumbnail.
|
||||||
|
* @returns {Promise<string>} A promise that resolves to the thumbnail data URL.
|
||||||
|
*/
|
||||||
export function createThumbnail(dataUrl, maxWidth, maxHeight) {
|
export function createThumbnail(dataUrl, maxWidth, maxHeight) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
|
@ -634,6 +874,13 @@ export function createThumbnail(dataUrl, maxWidth, maxHeight) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for a condition to be true. Throws an error if the condition is not true within the timeout.
|
||||||
|
* @param {{ (): boolean; }} condition The condition to wait for.
|
||||||
|
* @param {number} [timeout=1000] The timeout in milliseconds.
|
||||||
|
* @param {number} [interval=100] The interval in milliseconds.
|
||||||
|
* @returns {Promise<void>} A promise that resolves when the condition is true.
|
||||||
|
*/
|
||||||
export async function waitUntilCondition(condition, timeout = 1000, interval = 100) {
|
export async function waitUntilCondition(condition, timeout = 1000, interval = 100) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const timeoutId = setTimeout(() => {
|
const timeoutId = setTimeout(() => {
|
||||||
|
@ -651,6 +898,12 @@ export async function waitUntilCondition(condition, timeout = 1000, interval = 1
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a UUID v4 string.
|
||||||
|
* @returns {string} A UUID v4 string.
|
||||||
|
* @example
|
||||||
|
* uuidv4(); // '3e2fd9e1-0a7a-4f6d-9aaf-8a7a4babe7eb'
|
||||||
|
*/
|
||||||
export function uuidv4() {
|
export function uuidv4() {
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||||
const r = Math.random() * 16 | 0;
|
const r = Math.random() * 16 | 0;
|
||||||
|
@ -659,6 +912,11 @@ export function uuidv4() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones an object using JSON serialization.
|
||||||
|
* @param {any} obj The object to clone.
|
||||||
|
* @returns {any} A deep clone of the object.
|
||||||
|
*/
|
||||||
export function deepClone(obj) {
|
export function deepClone(obj) {
|
||||||
return JSON.parse(JSON.stringify(obj));
|
return JSON.parse(JSON.stringify(obj));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { saveSettings, callPopup, substituteParams, getTokenCount, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types } from "../script.js";
|
import { saveSettings, callPopup, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types } from "../script.js";
|
||||||
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, deepClone, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE } from "./utils.js";
|
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, deepClone, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option } from "./utils.js";
|
||||||
import { getContext } from "./extensions.js";
|
import { getContext } from "./extensions.js";
|
||||||
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from "./authors-note.js";
|
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from "./authors-note.js";
|
||||||
import { registerSlashCommand } from "./slash-commands.js";
|
import { registerSlashCommand } from "./slash-commands.js";
|
||||||
import { deviceInfo } from "./RossAscends-mods.js";
|
import { deviceInfo } from "./RossAscends-mods.js";
|
||||||
import { FILTER_TYPES, FilterHelper } from "./filters.js";
|
import { FILTER_TYPES, FilterHelper } from "./filters.js";
|
||||||
|
import { getTokenCount } from "./tokenizers.js";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
world_info,
|
world_info,
|
||||||
|
@ -46,7 +47,6 @@ const saveSettingsDebounced = debounce(() => {
|
||||||
saveSettings()
|
saveSettings()
|
||||||
}, 1000);
|
}, 1000);
|
||||||
const sortFn = (a, b) => b.order - a.order;
|
const sortFn = (a, b) => b.order - a.order;
|
||||||
const navigation_option = { none: 0, previous: 1, last: 2, };
|
|
||||||
let updateEditor = (navigation) => { navigation; };
|
let updateEditor = (navigation) => { navigation; };
|
||||||
|
|
||||||
// Do not optimize. updateEditor is a function that is updated by the displayWorldEntries with new data.
|
// Do not optimize. updateEditor is a function that is updated by the displayWorldEntries with new data.
|
||||||
|
@ -418,7 +418,7 @@ function getWorldEntry(name, data, entry) {
|
||||||
|
|
||||||
keyInput.on("input", function () {
|
keyInput.on("input", function () {
|
||||||
const uid = $(this).data("uid");
|
const uid = $(this).data("uid");
|
||||||
const value = $(this).val();
|
const value = String($(this).val());
|
||||||
resetScrollHeight(this);
|
resetScrollHeight(this);
|
||||||
data.entries[uid].key = value
|
data.entries[uid].key = value
|
||||||
.split(",")
|
.split(",")
|
||||||
|
@ -454,7 +454,7 @@ function getWorldEntry(name, data, entry) {
|
||||||
keySecondaryInput.data("uid", entry.uid);
|
keySecondaryInput.data("uid", entry.uid);
|
||||||
keySecondaryInput.on("input", function () {
|
keySecondaryInput.on("input", function () {
|
||||||
const uid = $(this).data("uid");
|
const uid = $(this).data("uid");
|
||||||
const value = $(this).val();
|
const value = String($(this).val());
|
||||||
resetScrollHeight(this);
|
resetScrollHeight(this);
|
||||||
data.entries[uid].keysecondary = value
|
data.entries[uid].keysecondary = value
|
||||||
.split(",")
|
.split(",")
|
||||||
|
@ -1506,19 +1506,6 @@ jQuery(() => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
if (deviceInfo.device.type === 'desktop') {
|
|
||||||
let selectScrollTop = null;
|
|
||||||
e.preventDefault();
|
|
||||||
const option = $(e.target);
|
|
||||||
const selectElement = $(this)[0];
|
|
||||||
selectScrollTop = selectElement.scrollTop;
|
|
||||||
option.prop('selected', !option.prop('selected'));
|
|
||||||
await delay(1);
|
|
||||||
selectElement.scrollTop = selectScrollTop;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
onWorldInfoChange('__notSlashCommand__');
|
onWorldInfoChange('__notSlashCommand__');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
69
server.js
69
server.js
|
@ -3302,6 +3302,72 @@ async function sendScaleRequest(request, response) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.post("/generate_altscale", jsonParser, function (request, response_generate_scale) {
|
||||||
|
if (!request.body) return response_generate_scale.sendStatus(400);
|
||||||
|
|
||||||
|
fetch('https://dashboard.scale.com/spellbook/api/trpc/v2.variant.run', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'cookie': `_jwt=${readSecret(SECRET_KEYS.SCALE_COOKIE)}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
json: {
|
||||||
|
variant: {
|
||||||
|
name: 'New Variant',
|
||||||
|
appId: '',
|
||||||
|
taxonomy: null
|
||||||
|
},
|
||||||
|
prompt: {
|
||||||
|
id: '',
|
||||||
|
template: '{{input}}\n',
|
||||||
|
exampleVariables: {},
|
||||||
|
variablesSourceDataId: null,
|
||||||
|
systemMessage: request.body.sysprompt
|
||||||
|
},
|
||||||
|
modelParameters: {
|
||||||
|
id: '',
|
||||||
|
modelId: 'GPT4',
|
||||||
|
modelType: 'OpenAi',
|
||||||
|
maxTokens: request.body.max_tokens,
|
||||||
|
temperature: request.body.temp,
|
||||||
|
stop: "user:",
|
||||||
|
suffix: null,
|
||||||
|
topP: request.body.top_p,
|
||||||
|
logprobs: null,
|
||||||
|
logitBias: request.body.logit_bias
|
||||||
|
},
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
index: '-1',
|
||||||
|
valueByName: {
|
||||||
|
input: request.body.prompt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
values: {
|
||||||
|
'variant.taxonomy': ['undefined'],
|
||||||
|
'prompt.variablesSourceDataId': ['undefined'],
|
||||||
|
'modelParameters.suffix': ['undefined'],
|
||||||
|
'modelParameters.logprobs': ['undefined'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
console.log(data.result.data.json.outputs[0])
|
||||||
|
return response_generate_scale.send({ output: data.result.data.json.outputs[0] });
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Error:', error)
|
||||||
|
return response_generate_scale.send({ error: true })
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
async function sendClaudeRequest(request, response) {
|
async function sendClaudeRequest(request, response) {
|
||||||
const fetch = require('node-fetch').default;
|
const fetch = require('node-fetch').default;
|
||||||
|
|
||||||
|
@ -3990,7 +4056,8 @@ const SECRET_KEYS = {
|
||||||
DEEPL: 'deepl',
|
DEEPL: 'deepl',
|
||||||
OPENROUTER: 'api_key_openrouter',
|
OPENROUTER: 'api_key_openrouter',
|
||||||
SCALE: 'api_key_scale',
|
SCALE: 'api_key_scale',
|
||||||
AI21: 'api_key_ai21'
|
AI21: 'api_key_ai21',
|
||||||
|
SCALE_COOKIE: 'scale_cookie',
|
||||||
}
|
}
|
||||||
|
|
||||||
function migrateSecrets() {
|
function migrateSecrets() {
|
||||||
|
|
Loading…
Reference in New Issue