mirror of
				https://github.com/SillyTavern/SillyTavern.git
				synced 2025-06-05 21:59:27 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			448 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			448 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import {
 | |
|     main_api,
 | |
|     saveSettingsDebounced,
 | |
|     callPopup,
 | |
| } from '../script.js';
 | |
| import { power_user } from './power-user.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 } from './textgen-settings.js';
 | |
| import { renderTemplateAsync } from './templates.js';
 | |
| 
 | |
| 
 | |
| const TGsamplerNames = setting_names;
 | |
| 
 | |
| const forcedOnColoring = 'color: #89db35;';
 | |
| const forcedOffColoring = 'color: #e84f62;';
 | |
| 
 | |
| let userDisabledSamplers, userShownSamplers;
 | |
| 
 | |
| // Goal 1: show popup with all samplers for active API
 | |
| async function showSamplerSelectPopup() {
 | |
|     const popup = $('#dialogue_popup');
 | |
|     popup.addClass('large_dialogue_popup');
 | |
|     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);
 | |
|     html.append(listContainer);
 | |
| 
 | |
|     callPopup(html, 'text', null, { allowVerticalScrolling: true });
 | |
| 
 | |
|     setSamplerListListeners();
 | |
| 
 | |
|     $('#resetSelectedSamplers').off('click').on('click', async function () {
 | |
|         console.log('saw sampler select reset click');
 | |
|         userDisabledSamplers = [];
 | |
|         userShownSamplers = [];
 | |
|         power_user.selectSamplers.forceShown = [];
 | |
|         power_user.selectSamplers.forceHidden = [];
 | |
|         await validateDisabledSamplers(true);
 | |
|     });
 | |
| 
 | |
|     $('#textgen_type').on('change', async function () {
 | |
|         console.log('changed TG Type, resetting custom samplers'); //unfortunate, but necessary unless we save custom samplers for each TGTytpe
 | |
|         userDisabledSamplers = [];
 | |
|         userShownSamplers = [];
 | |
|         power_user.selectSamplers.forceShown = [];
 | |
|         power_user.selectSamplers.forceHidden = [];
 | |
|         await validateDisabledSamplers();
 | |
|     });
 | |
| }
 | |
| 
 | |
| 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', '');
 | |
|         let relatedDOMElement = $(`#${samplerName}_${main_api}`).parent();
 | |
|         let targetDisplayType = 'flex';
 | |
| 
 | |
|         if (samplerName === 'json_schema') {
 | |
|             relatedDOMElement = $('#json_schema_block');
 | |
|             targetDisplayType = 'block';
 | |
|         }
 | |
| 
 | |
|         if (samplerName === 'grammar_string') {
 | |
|             relatedDOMElement = $('#grammar_block_ooba');
 | |
|             targetDisplayType = 'block';
 | |
|         }
 | |
| 
 | |
|         if (samplerName === 'guidance_scale') {
 | |
|             relatedDOMElement = $('#cfg_block_ooba');
 | |
|             targetDisplayType = 'block';
 | |
|         }
 | |
| 
 | |
|         if (samplerName === 'mirostat_mode') {
 | |
|             relatedDOMElement = $('#mirostat_block_ooba');
 | |
|             targetDisplayType = 'block';
 | |
|         }
 | |
| 
 | |
|         if (samplerName === 'dry_multiplier') {
 | |
|             relatedDOMElement = $('#dryBlock');
 | |
|             targetDisplayType = 'block';
 | |
|         }
 | |
| 
 | |
|         if (samplerName === 'xtc_probability') {
 | |
|             relatedDOMElement = $('#xtc_block');
 | |
|             targetDisplayType = 'block';
 | |
|         }
 | |
| 
 | |
|         if (samplerName === 'dynatemp') {
 | |
|             relatedDOMElement = $('#dynatemp_block_ooba');
 | |
|             targetDisplayType = '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');
 | |
|         }
 | |
| 
 | |
|         if (samplerName === 'samplers') { //this is for lcpp sampler order
 | |
|             relatedDOMElement = $('#sampler_order_block_lcpp');
 | |
|         }
 | |
| 
 | |
|         if (samplerName === 'sampler_priority') { //this is for ooba's sampler priority
 | |
|             relatedDOMElement = $('#sampler_priority_block_ooba');
 | |
|         }
 | |
| 
 | |
|         if (samplerName === 'samplers_priorities') { //this is for aphrodite's sampler priority
 | |
|             relatedDOMElement = $('#sampler_priority_block_aphrodite');
 | |
|         }
 | |
| 
 | |
|         if (samplerName === 'penalty_alpha') { //contrastive search only has one sampler, does it need its own block?
 | |
|             relatedDOMElement = $('#contrastiveSearchBlock');
 | |
|         }
 | |
| 
 | |
|         if (samplerName === 'num_beams') { // num_beams is the killswitch for Beam Search
 | |
|             relatedDOMElement = $('#beamSearchBlock');
 | |
|             targetDisplayType = 'block';
 | |
|         }
 | |
| 
 | |
|         if (samplerName === 'smoothing_factor') { // num_beams is the killswitch for Beam Search
 | |
|             relatedDOMElement = $('#smoothingBlock');
 | |
|             targetDisplayType = 'block';
 | |
|         }
 | |
| 
 | |
|         // Get the current state of the custom data attribute
 | |
|         const previousState = relatedDOMElement.data('selectsampler');
 | |
| 
 | |
|         if ($(this).prop('checked') === false) {
 | |
|             //console.log('saw clicking checkbox from on to off...');
 | |
|             if (previousState === 'shown') {
 | |
|                 console.log('saw previously custom shown sampler');
 | |
|                 //console.log('removing from custom force show list');
 | |
|                 relatedDOMElement.removeData('selectsampler');
 | |
|                 $(this).parent().find('.sampler_name').removeAttr('style');
 | |
|                 power_user?.selectSamplers?.forceShown.splice(power_user?.selectSamplers?.forceShown.indexOf(samplerName), 1);
 | |
|                 console.log(power_user?.selectSamplers?.forceShown);
 | |
|             } else {
 | |
|                 console.log('saw previous untouched sampler');
 | |
|                 //console.log(`adding ${samplerName} to force hide list`);
 | |
|                 relatedDOMElement.data('selectsampler', 'hidden');
 | |
|                 console.log(relatedDOMElement.data('selectsampler'));
 | |
|                 power_user.selectSamplers.forceHidden.push(samplerName);
 | |
|                 $(this).parent().find('.sampler_name').attr('style', forcedOffColoring);
 | |
|                 console.log(power_user.selectSamplers.forceHidden);
 | |
|             }
 | |
|         } else { // going from unchecked to checked
 | |
|             //console.log('saw clicking checkbox from off to on...');
 | |
|             if (previousState === 'hidden') {
 | |
|                 console.log('saw previously custom hidden sampler');
 | |
|                 //console.log('removing from custom force hide list');
 | |
|                 relatedDOMElement.removeData('selectsampler');
 | |
|                 $(this).parent().find('.sampler_name').removeAttr('style');
 | |
|                 power_user?.selectSamplers?.forceHidden.splice(power_user?.selectSamplers?.forceHidden.indexOf(samplerName), 1);
 | |
|                 console.log(power_user?.selectSamplers?.forceHidden);
 | |
|             } else {
 | |
|                 console.log('saw previous untouched sampler');
 | |
|                 //console.log(`adding ${samplerName} to force shown list`);
 | |
|                 relatedDOMElement.data('selectsampler', 'shown');
 | |
|                 console.log(relatedDOMElement.data('selectsampler'));
 | |
|                 power_user.selectSamplers.forceShown.push(samplerName);
 | |
|                 $(this).parent().find('.sampler_name').attr('style', forcedOnColoring);
 | |
|                 console.log(power_user.selectSamplers.forceShown);
 | |
|             }
 | |
|         }
 | |
|         await saveSettingsDebounced();
 | |
| 
 | |
|         const shouldDisplay = $(this).prop('checked') ? targetDisplayType : 'none';
 | |
|         relatedDOMElement.css('display', shouldDisplay);
 | |
| 
 | |
|         console.log(samplerName, relatedDOMElement.data('selectsampler'), 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 samplersListHTML = availableSamplers.reduce((html, sampler) => {
 | |
|         let customColor, displayname;
 | |
|         let targetDOMelement = $(`#${sampler}_${main_api}`);
 | |
| 
 | |
|         if (sampler === 'sampler_order') { //this is for kcpp sampler order
 | |
|             targetDOMelement = $('#sampler_order_block_kcpp');
 | |
|             displayname = 'KCPP Sampler Order Block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'samplers') { //this is for lcpp sampler order
 | |
|             targetDOMelement = $('#sampler_order_block_lcpp');
 | |
|             displayname = 'LCPP Sampler Order Block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'sampler_priority') { //this is for ooba's sampler priority
 | |
|             targetDOMelement = $('#sampler_priority_block_ooba');
 | |
|             displayname = 'Ooba Sampler Priority Block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'samplers_priorities') { //this is for aphrodite's sampler priority
 | |
|             targetDOMelement = $('#sampler_priority_block_aphrodite');
 | |
|             displayname = 'Aphrodite Sampler Priority Block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'penalty_alpha') { //contrastive search only has one sampler, does it need its own block?
 | |
|             targetDOMelement = $('#contrastiveSearchBlock');
 | |
|             displayname = 'Contrast Search Block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'num_beams') { // num_beams is the killswitch for Beam Search
 | |
|             targetDOMelement = $('#beamSearchBlock');
 | |
|             displayname = 'Beam Search Block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'smoothing_factor') { // num_beams is the killswitch for Beam Search
 | |
|             targetDOMelement = $('#smoothingBlock');
 | |
|             displayname = 'Smoothing Block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'dry_multiplier') {
 | |
|             targetDOMelement = $('#dryBlock');
 | |
|             displayname = 'DRY Rep Pen Block';
 | |
|         }
 | |
|         if (sampler === 'xtc_probability') {
 | |
|             targetDOMelement = $('#xtc_block');
 | |
|             displayname = 'XTC Block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'dynatemp') {
 | |
|             targetDOMelement = $('#dynatemp_block_ooba');
 | |
|             displayname = 'DynaTemp Block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'json_schema') {
 | |
|             targetDOMelement = $('#json_schema_block');
 | |
|             displayname = 'JSON Schema Block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'grammar_string') {
 | |
|             targetDOMelement = $('#grammar_block_ooba');
 | |
|             displayname = 'Grammar Block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'guidance_scale') {
 | |
|             targetDOMelement = $('#cfg_block_ooba');
 | |
|             displayname = 'CFG Block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'mirostat_mode') {
 | |
|             targetDOMelement = $('#mirostat_block_ooba');
 | |
|             displayname = 'Mirostat Block';
 | |
|         }
 | |
| 
 | |
| 
 | |
| 
 | |
|         const isInForceHiddenArray = userDisabledSamplers.includes(sampler);
 | |
|         const isInForceShownArray = userShownSamplers.includes(sampler);
 | |
|         let isVisibleInDOM = isElementVisibleInDOM(targetDOMelement[0]);
 | |
|         const isInDefaultState = () => {
 | |
|             if (isVisibleInDOM && isInForceShownArray) { return false; }
 | |
|             else if (!isVisibleInDOM && isInForceHiddenArray) { return false; }
 | |
|             else { return true; }
 | |
|         };
 | |
| 
 | |
|         const shouldBeChecked = () => {
 | |
|             if (isInForceHiddenArray) {
 | |
|                 customColor = forcedOffColoring;
 | |
|                 return false;
 | |
|             }
 | |
|             else if (isInForceShownArray) {
 | |
|                 customColor = forcedOnColoring;
 | |
|                 return true;
 | |
|             }
 | |
|             else { return isVisibleInDOM; }
 | |
|         };
 | |
|         console.log(sampler, targetDOMelement.prop('id'), isInDefaultState(), isInForceShownArray, isInForceHiddenArray, shouldBeChecked());
 | |
|         if (displayname === undefined) { displayname = sampler; }
 | |
|         return html + `
 | |
|         <div class="sampler_view_list_item wide50p flex-container">
 | |
|             <input type="checkbox" name="${sampler}_checkbox" ${shouldBeChecked() ? 'checked' : ''}>
 | |
|             <small class="sampler_name" style="${customColor}">${displayname}</small>
 | |
|         </div>
 | |
|         `;
 | |
|     }, '');
 | |
| 
 | |
|     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;
 | |
|     }
 | |
| 
 | |
|     for (const sampler of APISamplers) {
 | |
|         let relatedDOMElement = $(`#${sampler}_${main_api}`).parent();
 | |
|         let targetDisplayType = 'flex';
 | |
| 
 | |
|         if (sampler === 'json_schema') {
 | |
|             relatedDOMElement = $('#json_schema_block');
 | |
|             targetDisplayType = 'block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'grammar_string') {
 | |
|             relatedDOMElement = $('#grammar_block_ooba');
 | |
|             targetDisplayType = 'block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'guidance_scale') {
 | |
|             relatedDOMElement = $('#cfg_block_ooba');
 | |
|             targetDisplayType = 'block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'mirostat_mode') {
 | |
|             relatedDOMElement = $('#mirostat_block_ooba');
 | |
|             targetDisplayType = 'block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'dynatemp') {
 | |
|             relatedDOMElement = $('#dynatemp_block_ooba');
 | |
|             targetDisplayType = 'block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'banned_tokens') {
 | |
|             relatedDOMElement = $('#banned_tokens_block_ooba');
 | |
|             targetDisplayType = 'block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'sampler_order') { //this is for kcpp sampler order
 | |
|             relatedDOMElement = $('#sampler_order_block_kcpp');
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'samplers') { //this is for lcpp sampler order
 | |
|             relatedDOMElement = $('#sampler_order_block_lcpp');
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'sampler_priority') { //this is for ooba's sampler priority
 | |
|             relatedDOMElement = $('#sampler_priority_block_ooba');
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'samplers_priorities') { //this is for aphrodite's sampler priority
 | |
|             relatedDOMElement = $('#sampler_priority_block_aphrodite');
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'dry_multiplier') {
 | |
|             relatedDOMElement = $('#dryBlock');
 | |
|             targetDisplayType = 'block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'xtc_probability') {
 | |
|             relatedDOMElement = $('#xtc_block');
 | |
|             targetDisplayType = 'block';
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'penalty_alpha') { //contrastive search only has one sampler, does it need its own block?
 | |
|             relatedDOMElement = $('#contrastiveSearchBlock');
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'num_beams') { // num_beams is the killswitch for Beam Search
 | |
|             relatedDOMElement = $('#beamSearchBlock');
 | |
|         }
 | |
| 
 | |
|         if (sampler === 'smoothing_factor') { // num_beams is the killswitch for Beam Search
 | |
|             relatedDOMElement = $('#smoothingBlock');
 | |
|         }
 | |
| 
 | |
|         if (power_user?.selectSamplers?.forceHidden.includes(sampler)) {
 | |
|             //default handling for standard sliders
 | |
|             relatedDOMElement.data('selectsampler', 'hidden');
 | |
|             relatedDOMElement.css('display', 'none');
 | |
|         } else if (power_user?.selectSamplers?.forceShown.includes(sampler)) {
 | |
|             relatedDOMElement.data('selectsampler', 'shown');
 | |
|             relatedDOMElement.css('display', targetDisplayType);
 | |
|         } else {
 | |
|             if (relatedDOMElement.data('selectsampler') === 'hidden') {
 | |
|                 relatedDOMElement.removeAttr('selectsampler');
 | |
|                 relatedDOMElement.css('display', targetDisplayType);
 | |
|             }
 | |
|             if (relatedDOMElement.data('selectsampler') === 'shown') {
 | |
|                 relatedDOMElement.removeAttr('selectsampler');
 | |
|                 relatedDOMElement.css('display', 'none');
 | |
|             }
 | |
|         }
 | |
|         if (redraw) {
 | |
|             let samplersHTML = await listSamplers(main_api);
 | |
|             $('#apiSamplersList').empty().append(samplersHTML);
 | |
|             setSamplerListListeners();
 | |
|         }
 | |
| 
 | |
|         await saveSettingsDebounced();
 | |
| 
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| export async function initCustomSelectedSamplers() {
 | |
| 
 | |
|     userDisabledSamplers = power_user?.selectSamplers?.forceHidden || [];
 | |
|     userShownSamplers = power_user?.selectSamplers?.forceShown || [];
 | |
|     power_user.selectSamplers = {};
 | |
|     power_user.selectSamplers.forceHidden = userDisabledSamplers;
 | |
|     power_user.selectSamplers.forceShown = userShownSamplers;
 | |
|     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
 |