🎨 优化扩展模块,完成ai接入和对话功能
This commit is contained in:
448
data/st-core-scripts/scripts/samplerSelect.js
Normal file
448
data/st-core-scripts/scripts/samplerSelect.js
Normal file
@@ -0,0 +1,448 @@
|
||||
import {
|
||||
main_api,
|
||||
saveSettingsDebounced,
|
||||
} from '../script.js';
|
||||
//import { BIAS_CACHE, displayLogitBias, getLogitBiasListResult } from './logit-bias.js';
|
||||
//import { getEventSourceStream } from './sse-stream.js';
|
||||
//import { getSortableDelay, onlyUnique } from './utils.js';
|
||||
//import { getCfgPrompt } from './cfg-scale.js';
|
||||
import { setting_names as TGsamplerNames, showTGSamplerControls, textgenerationwebui_settings } from './textgen-settings.js';
|
||||
import { renderTemplateAsync } from './templates.js';
|
||||
import { Popup, POPUP_TYPE } from './popup.js';
|
||||
import { localforage } from '../lib.js';
|
||||
|
||||
const forcedOnColoring = 'color: #89db35;';
|
||||
const forcedOffColoring = 'color: #e84f62;';
|
||||
const SELECT_SAMPLER = {
|
||||
DATA: 'selectsampler',
|
||||
SHOWN: 'shown',
|
||||
HIDDEN: 'hidden',
|
||||
};
|
||||
|
||||
const textGenObjectStore = localforage.createInstance({ name: 'SillyTavern_TextCompletions' });
|
||||
let selectedSamplers = {};
|
||||
|
||||
// Goal 1: show popup with all samplers for active API
|
||||
async function showSamplerSelectPopup() {
|
||||
const html = $(document.createElement('div'));
|
||||
html.attr('id', 'sampler_view_list')
|
||||
.addClass('flex-container flexFlowColumn');
|
||||
html.append(await renderTemplateAsync('samplerSelector'));
|
||||
|
||||
const listContainer = $('<div id="apiSamplersList" class="flex-container flexNoGap"></div>');
|
||||
const APISamplers = await listSamplers(main_api);
|
||||
listContainer.append(APISamplers.toString());
|
||||
html.append(listContainer);
|
||||
|
||||
const showPromise = new Popup(html, POPUP_TYPE.TEXT, null, { wide: true, large: true, allowVerticalScrolling: true }).show();
|
||||
|
||||
setSamplerListListeners();
|
||||
|
||||
$('#resetSelectedSamplers').off('click').on('click', async function () {
|
||||
console.log('saw sampler select reset click');
|
||||
|
||||
if (main_api === 'textgenerationwebui') {
|
||||
$('#prioritizeManuallySelectedSamplers').toggleClass('toggleEnabled', false);
|
||||
await resetApiSelectedSamplers(null, true);
|
||||
}
|
||||
|
||||
await validateDisabledSamplers(true);
|
||||
});
|
||||
|
||||
if (main_api === 'textgenerationwebui') {
|
||||
$('#prioritizeManuallySelectedSamplers').show();
|
||||
$('#prioritizeManuallySelectedSamplers').toggleClass('toggleEnabled', isSamplerManualPriorityEnabled());
|
||||
$('#prioritizeManuallySelectedSamplers').off('click').on('click', function () {
|
||||
$(this).toggleClass('toggleEnabled');
|
||||
|
||||
const isActive = $(this).hasClass('toggleEnabled');
|
||||
|
||||
toggleSamplerManualPriority(isActive);
|
||||
});
|
||||
} else {
|
||||
$('#prioritizeManuallySelectedSamplers').hide();
|
||||
$('#prioritizeManuallySelectedSamplers').off('click');
|
||||
}
|
||||
|
||||
await showPromise;
|
||||
if (main_api === 'textgenerationwebui') await saveApiSelectedSamplers();
|
||||
}
|
||||
|
||||
function getRelatedDOMElement(samplerName) {
|
||||
let relatedDOMElement = $(`#${samplerName}_${main_api}`).parent();
|
||||
let targetDisplayType = 'flex';
|
||||
let displayname;
|
||||
|
||||
if (samplerName === 'json_schema') {
|
||||
relatedDOMElement = $('#json_schema_block');
|
||||
targetDisplayType = 'block';
|
||||
displayname = 'JSON Schema Block';
|
||||
}
|
||||
|
||||
if (samplerName === 'grammar_string') {
|
||||
relatedDOMElement = $('#grammar_block_ooba');
|
||||
targetDisplayType = 'block';
|
||||
displayname = 'Grammar Block';
|
||||
}
|
||||
|
||||
if (samplerName === 'guidance_scale') {
|
||||
relatedDOMElement = $('#cfg_block_ooba');
|
||||
targetDisplayType = 'block';
|
||||
displayname = 'CFG Block';
|
||||
}
|
||||
|
||||
if (samplerName === 'mirostat_mode') {
|
||||
relatedDOMElement = $('#mirostat_block_ooba');
|
||||
targetDisplayType = 'block';
|
||||
displayname = 'Mirostat Block';
|
||||
}
|
||||
|
||||
if (samplerName === 'dry_multiplier') {
|
||||
relatedDOMElement = $('#dryBlock');
|
||||
targetDisplayType = 'block';
|
||||
displayname = 'DRY Rep Pen Block';
|
||||
}
|
||||
|
||||
if (samplerName === 'xtc_probability') {
|
||||
relatedDOMElement = $('#xtc_block');
|
||||
targetDisplayType = 'block';
|
||||
displayname = 'XTC Block';
|
||||
}
|
||||
|
||||
if (samplerName === 'dynatemp') {
|
||||
relatedDOMElement = $('#dynatemp_block_ooba');
|
||||
targetDisplayType = 'block';
|
||||
displayname = 'DynaTemp Block';
|
||||
}
|
||||
|
||||
if (samplerName === 'banned_tokens') {
|
||||
relatedDOMElement = $('#banned_tokens_block_ooba');
|
||||
targetDisplayType = 'block';
|
||||
}
|
||||
|
||||
if (samplerName === 'sampler_order') { //this is for kcpp sampler order
|
||||
relatedDOMElement = $('#sampler_order_block_kcpp');
|
||||
displayname = 'KCPP Sampler Order Block';
|
||||
}
|
||||
|
||||
if (samplerName === 'samplers') { //this is for lcpp sampler order
|
||||
relatedDOMElement = $('#sampler_order_block_lcpp');
|
||||
displayname = 'LCPP Sampler Order Block';
|
||||
}
|
||||
|
||||
if (samplerName === 'sampler_priority') { //this is for ooba's sampler priority
|
||||
relatedDOMElement = $('#sampler_priority_block_ooba');
|
||||
displayname = 'Ooba Sampler Priority Block';
|
||||
}
|
||||
|
||||
if (samplerName === 'samplers_priorities') { //this is for aphrodite's sampler priority
|
||||
relatedDOMElement = $('#sampler_priority_block_aphrodite');
|
||||
displayname = 'Aphrodite Sampler Priority Block';
|
||||
}
|
||||
|
||||
if (samplerName === 'penalty_alpha') { //contrastive search only has one sampler, does it need its own block?
|
||||
relatedDOMElement = $('#contrastiveSearchBlock');
|
||||
displayname = 'Contrast Search Block';
|
||||
}
|
||||
|
||||
if (samplerName === 'num_beams') { // num_beams is the killswitch for Beam Search
|
||||
relatedDOMElement = $('#beamSearchBlock');
|
||||
targetDisplayType = 'block';
|
||||
displayname = 'Beam Search Block';
|
||||
}
|
||||
|
||||
if (samplerName === 'smoothing_factor') { // num_beams is the killswitch for Beam Search
|
||||
relatedDOMElement = $('#smoothingBlock');
|
||||
targetDisplayType = 'block';
|
||||
displayname = 'Smoothing Block';
|
||||
}
|
||||
|
||||
return { relatedDOMElement, targetDisplayType, displayname };
|
||||
}
|
||||
|
||||
function setSamplerListListeners() {
|
||||
// Goal 2: hide unchecked samplers from DOM
|
||||
let listContainer = $('#apiSamplersList');
|
||||
listContainer.find('input').off('change').on('change', async function () {
|
||||
const samplerName = this.name.replace('_checkbox', '');
|
||||
const { relatedDOMElement, targetDisplayType } = getRelatedDOMElement(samplerName);
|
||||
|
||||
// Get the current state of the custom data attribute
|
||||
const previousState = relatedDOMElement.data(SELECT_SAMPLER.DATA);
|
||||
const isChecked = $(this).prop('checked');
|
||||
const popupInputLabel = $(this).parent().find('.sampler_name');
|
||||
|
||||
if (isChecked === false) {
|
||||
if (previousState === SELECT_SAMPLER.SHOWN) {
|
||||
console.log('saw previously custom shown sampler => new state:', isChecked, samplerName);
|
||||
relatedDOMElement.removeData(SELECT_SAMPLER.DATA);
|
||||
popupInputLabel.removeAttr('style');
|
||||
} else {
|
||||
console.log('saw previous untouched sampler => new state:', isChecked, samplerName);
|
||||
relatedDOMElement.data(SELECT_SAMPLER.DATA, SELECT_SAMPLER.HIDDEN);
|
||||
popupInputLabel.attr('style', forcedOffColoring);
|
||||
}
|
||||
} else {
|
||||
if (previousState === SELECT_SAMPLER.HIDDEN) {
|
||||
console.log('saw previously custom hidden sampler => new state:', isChecked, samplerName);
|
||||
relatedDOMElement.removeData(SELECT_SAMPLER.DATA);
|
||||
popupInputLabel.removeAttr('style');
|
||||
} else {
|
||||
console.log('saw previous untouched sampler => new state:', isChecked, samplerName);
|
||||
relatedDOMElement.data(SELECT_SAMPLER.DATA, SELECT_SAMPLER.SHOWN);
|
||||
popupInputLabel.attr('style', forcedOnColoring);
|
||||
}
|
||||
}
|
||||
|
||||
await saveSettingsDebounced();
|
||||
|
||||
const shouldDisplay = isChecked ? targetDisplayType : 'none';
|
||||
relatedDOMElement.css('display', shouldDisplay);
|
||||
|
||||
if (main_api === 'textgenerationwebui') setApiSamplersState(samplerName, shouldDisplay !== 'none');
|
||||
|
||||
console.log(samplerName, relatedDOMElement.data(SELECT_SAMPLER.DATA), shouldDisplay);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function isElementVisibleInDOM(element) {
|
||||
while (element && element !== document.body) {
|
||||
if (window.getComputedStyle(element).display === 'none') {
|
||||
return false;
|
||||
}
|
||||
element = element.parentElement;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
async function listSamplers(main_api, arrayOnly = false) {
|
||||
let availableSamplers;
|
||||
if (main_api === 'textgenerationwebui') {
|
||||
availableSamplers = TGsamplerNames;
|
||||
const valuesToRemove = new Set(['streaming', 'bypass_status_check', 'custom_model', 'legacy_api']);
|
||||
availableSamplers = availableSamplers.filter(sampler => !valuesToRemove.has(sampler));
|
||||
availableSamplers.sort();
|
||||
}
|
||||
|
||||
if (arrayOnly) {
|
||||
console.debug('returning full samplers array');
|
||||
return availableSamplers;
|
||||
}
|
||||
|
||||
const samplersActivatedManually = (main_api === 'textgenerationwebui') ? getActiveManualApiSamplers() : [];
|
||||
const prioritizeManualSamplerSelect = (main_api === 'textgenerationwebui') ? isSamplerManualPriorityEnabled() : false;
|
||||
|
||||
const samplersListHTML = availableSamplers.reduce((html, sampler) => {
|
||||
let customColor;
|
||||
let { relatedDOMElement, displayname } = getRelatedDOMElement(sampler);
|
||||
|
||||
const isManuallyActivated = samplersActivatedManually.includes(sampler);
|
||||
const displayModified = relatedDOMElement.data(SELECT_SAMPLER.DATA);
|
||||
const isInDefaultState = !displayModified;
|
||||
|
||||
const shouldBeChecked = () => {
|
||||
let finalState = isElementVisibleInDOM(relatedDOMElement[0]);
|
||||
|
||||
if (prioritizeManualSamplerSelect) {
|
||||
finalState = isManuallyActivated;
|
||||
}
|
||||
|
||||
else if (!isInDefaultState) {
|
||||
finalState = displayModified === SELECT_SAMPLER.SHOWN;
|
||||
customColor = finalState ? forcedOnColoring : forcedOffColoring;
|
||||
}
|
||||
|
||||
return finalState;
|
||||
};
|
||||
|
||||
console.log(sampler, relatedDOMElement.prop('id'), isInDefaultState, shouldBeChecked());
|
||||
|
||||
if (displayname === undefined) displayname = sampler;
|
||||
if (main_api === 'textgenerationwebui') setApiSamplersState(sampler, shouldBeChecked());
|
||||
|
||||
return html + `
|
||||
<label class="sampler_view_list_item wide50p flex-container">
|
||||
<input type="checkbox" name="${sampler}_checkbox" ${shouldBeChecked() ? 'checked' : ''}>
|
||||
<small class="sampler_name" style="${customColor}">${displayname}</small>
|
||||
</label>`;
|
||||
}, '');
|
||||
|
||||
return samplersListHTML;
|
||||
}
|
||||
|
||||
// Goal 3: make "sampler is hidden/disabled" status persistent (save settings)
|
||||
// this runs on initial getSettings as well as after API changes
|
||||
|
||||
export async function validateDisabledSamplers(redraw = false) {
|
||||
const APISamplers = await listSamplers(main_api, true);
|
||||
|
||||
if (!Array.isArray(APISamplers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const samplersActivatedManually = (main_api === 'textgenerationwebui') ? getActiveManualApiSamplers() : [];
|
||||
const prioritizeManualSamplerSelect = (main_api === 'textgenerationwebui') ? isSamplerManualPriorityEnabled() : false;
|
||||
|
||||
for (const sampler of APISamplers) {
|
||||
const { relatedDOMElement, targetDisplayType } = getRelatedDOMElement(sampler);
|
||||
|
||||
if (prioritizeManualSamplerSelect) {
|
||||
const isManuallyActivated = samplersActivatedManually.includes(sampler);
|
||||
relatedDOMElement.css('display', isManuallyActivated ? targetDisplayType : 'none');
|
||||
} else {
|
||||
const selectSamplerData = relatedDOMElement.data(SELECT_SAMPLER.DATA);
|
||||
relatedDOMElement.css('display', selectSamplerData === SELECT_SAMPLER.SHOWN ? targetDisplayType : 'none');
|
||||
}
|
||||
|
||||
relatedDOMElement.removeData(SELECT_SAMPLER.DATA);
|
||||
}
|
||||
|
||||
if (!prioritizeManualSamplerSelect && main_api === 'textgenerationwebui') {
|
||||
showTGSamplerControls();
|
||||
}
|
||||
|
||||
if (redraw) {
|
||||
let samplersHTML = await listSamplers(main_api);
|
||||
$('#apiSamplersList').empty().append(samplersHTML.toString());
|
||||
setSamplerListListeners();
|
||||
}
|
||||
|
||||
await saveSettingsDebounced();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the configuration object for manually selected samplers.
|
||||
* @returns void
|
||||
*/
|
||||
export async function loadApiSelectedSamplers() {
|
||||
try {
|
||||
console.debug('Text Completions: loading selected samplers');
|
||||
selectedSamplers = await textGenObjectStore.getItem('selectedSamplers') || {};
|
||||
} catch (error) {
|
||||
console.log('Text Completions: unable to load selected samplers, using default samplers', error);
|
||||
selectedSamplers = {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronizes the local forage instance with the selected samplers configuration object.
|
||||
* @returns void
|
||||
*/
|
||||
export async function saveApiSelectedSamplers() {
|
||||
try {
|
||||
console.debug('Text Completions: saving selected samplers');
|
||||
await textGenObjectStore.setItem('selectedSamplers', selectedSamplers);
|
||||
} catch (error) {
|
||||
console.log('Text Completions: unable to save selected samplers', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the selected samplers configuration object from the local forage instance.
|
||||
* @param {string?} tcApiType Name of the target API Type - It picks the currently active TC API type name by default
|
||||
* @param {boolean} silent Suppresses the toastr message confirming that the data was deleted.
|
||||
* @returns void
|
||||
*/
|
||||
export async function resetApiSelectedSamplers(tcApiType = '', silent = false) {
|
||||
try {
|
||||
if (!textgenerationwebui_settings?.type && !tcApiType) return;
|
||||
if (!tcApiType) tcApiType = textgenerationwebui_settings.type;
|
||||
if (!selectedSamplers[tcApiType]) return;
|
||||
|
||||
console.debug('Text Completions: resetting selected samplers');
|
||||
delete selectedSamplers[tcApiType];
|
||||
await saveApiSelectedSamplers();
|
||||
if (!silent) toastr.success('Selected samplers cleared.');
|
||||
} catch (error) {
|
||||
console.log('Text Completions: unable to reset selected preset samplers', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the visibility state for selected samplers into the configuration object.
|
||||
* @param {string} samplerName Target sampler key name
|
||||
* @param {string|boolean} state Visibility state of the target sampler
|
||||
* @param {string?} tcApiType Name of the target API Type - It picks the currently active TC API type name by default
|
||||
* @returns void
|
||||
*/
|
||||
export function setApiSamplersState(samplerName, state, tcApiType = '') {
|
||||
if (!textgenerationwebui_settings?.type && !tcApiType) return;
|
||||
if (!tcApiType) tcApiType = textgenerationwebui_settings.type;
|
||||
if (!selectedSamplers[tcApiType]) selectedSamplers[tcApiType] = {};
|
||||
|
||||
const presetSamplers = selectedSamplers[tcApiType];
|
||||
presetSamplers[samplerName] = String(state) === 'true';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the local forage object belonging to the active/selected TC API Type
|
||||
* @param {string?} tcApiType Name of the target API Type - It picks the currently active TC API type name by default
|
||||
* @returns {object} Full localforage object with manual selections
|
||||
*/
|
||||
export function getAllManualApiSamplers(tcApiType = '') {
|
||||
if (!textgenerationwebui_settings?.type && !tcApiType) return {};
|
||||
if (!tcApiType) tcApiType = textgenerationwebui_settings.type;
|
||||
if (!selectedSamplers[tcApiType]) selectedSamplers[tcApiType] = {};
|
||||
|
||||
return selectedSamplers[tcApiType];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key names of all the manually activated API Type samplers.
|
||||
* @param {string?} tcApiType Name of the target API Type - It picks the currently active TC API type name by default
|
||||
* @returns {string[]} Array of sampler key names
|
||||
*/
|
||||
export function getActiveManualApiSamplers(tcApiType = '') {
|
||||
if (!textgenerationwebui_settings?.type && !tcApiType) return [];
|
||||
if (!tcApiType) tcApiType = textgenerationwebui_settings.type;
|
||||
if (!selectedSamplers[tcApiType]) selectedSamplers[tcApiType] = {};
|
||||
|
||||
try {
|
||||
const presetSamplers = Object.entries(selectedSamplers[tcApiType]);
|
||||
|
||||
return presetSamplers
|
||||
.filter(([key, val]) => val === true && key !== 'st_manual_priority')
|
||||
.map(([key, val]) => key);
|
||||
} catch (error) {
|
||||
console.log('Text Completions: unable to fetch active preset samplers', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string|boolean} state Target state of the feature
|
||||
* @param {string?} tcApiType Name of the target API Type - It picks the currently active TC API type name by default
|
||||
* @returns void
|
||||
*/
|
||||
export function toggleSamplerManualPriority(state = false, tcApiType = '') {
|
||||
if (!textgenerationwebui_settings?.type && !tcApiType) return;
|
||||
if (!tcApiType) tcApiType = textgenerationwebui_settings.type;
|
||||
if (!selectedSamplers[tcApiType]) selectedSamplers[tcApiType] = {};
|
||||
|
||||
const presetSamplers = selectedSamplers[tcApiType];
|
||||
presetSamplers.st_manual_priority = String(state) === 'true';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string?} tcApiType Name of the target API Type - It picks the currently active TC API type name by default
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isSamplerManualPriorityEnabled(tcApiType = '') {
|
||||
if (!textgenerationwebui_settings?.type && !tcApiType) return false;
|
||||
if (!tcApiType) tcApiType = textgenerationwebui_settings.type;
|
||||
if (!selectedSamplers[tcApiType]) selectedSamplers[tcApiType] = {};
|
||||
|
||||
return selectedSamplers[tcApiType]?.st_manual_priority ?? false;
|
||||
}
|
||||
|
||||
export async function initCustomSelectedSamplers() {
|
||||
await saveSettingsDebounced();
|
||||
$('#samplerSelectButton').off('click').on('click', showSamplerSelectPopup);
|
||||
}
|
||||
|
||||
// Goal 4: filter hidden samplers from API output
|
||||
|
||||
// Goal 5: allow addition of custom samplers to be displayed
|
||||
// Goal 6: send custom sampler values into prompt
|
||||
Reference in New Issue
Block a user