Merge pull request #2929 from SillyTavern/model-write-in /model: Better support for write-in controls

b3e88c82b83566dc632137c81955d07d15933a18

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
1 files changed, +37 -10Showing whitespace changes
public/scripts/slash-commands.js+37 -10
@@ -55,7 +55,7 @@ import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockStat
55import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js';55import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js';
56import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js';56import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js';
57import { decodeTextTokens, getAvailableTokenizers, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, selectTokenizer } from './tokenizers.js';57import { decodeTextTokens, getAvailableTokenizers, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, selectTokenizer } from './tokenizers.js';
58import { debounce, delay, equalsIgnoreCaseAndAccents, findChar, getCharIndex, isFalseBoolean, isTrueBoolean, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js';58import { debounce, delay, equalsIgnoreCaseAndAccents, findChar, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js';
59import { registerVariableCommands, resolveVariable } from './variables.js';59import { registerVariableCommands, resolveVariable } from './variables.js';
60import { background_settings } from './backgrounds.js';60import { background_settings } from './backgrounds.js';
61import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js';61import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js';
@@ -3597,11 +3597,12 @@ function setBackgroundCallback(_, bg) {
3597 * Retrieves the available model options based on the currently selected main API and its subtype3597 * Retrieves the available model options based on the currently selected main API and its subtype
3598 * @param {boolean} quiet - Whether to suppress toasts3598 * @param {boolean} quiet - Whether to suppress toasts
3599 *3599 *
3600 * @returns {{control: HTMLSelectElement, options: HTMLOptionElement[]}?} An array of objects representing the available model options, or null if not supported3600 * @returns {{control: HTMLSelectElement|HTMLInputElement, options: HTMLOptionElement[]}?} An array of objects representing the available model options, or null if not supported
3601 */3601 */
3602function getModelOptions(quiet) {3602function getModelOptions(quiet) {
3603 const nullResult = { control: null, options: null };3603 const nullResult = { control: null, options: null };
3604 const modelSelectMap = [3604 const modelSelectMap = [
3605 { id: 'custom_model_textgenerationwebui', api: 'textgenerationwebui', type: textgen_types.OOBA },
3605 { id: 'model_togetherai_select', api: 'textgenerationwebui', type: textgen_types.TOGETHERAI },3606 { id: 'model_togetherai_select', api: 'textgenerationwebui', type: textgen_types.TOGETHERAI },
3606 { id: 'openrouter_model', api: 'textgenerationwebui', type: textgen_types.OPENROUTER },3607 { id: 'openrouter_model', api: 'textgenerationwebui', type: textgen_types.OPENROUTER },
3607 { id: 'model_infermaticai_select', api: 'textgenerationwebui', type: textgen_types.INFERMATICAI },3608 { id: 'model_infermaticai_select', api: 'textgenerationwebui', type: textgen_types.INFERMATICAI },
@@ -3618,7 +3619,7 @@ function getModelOptions(quiet) {
3618 { id: 'model_ai21_select', api: 'openai', type: chat_completion_sources.AI21 },3619 { id: 'model_ai21_select', api: 'openai', type: chat_completion_sources.AI21 },
3619 { id: 'model_google_select', api: 'openai', type: chat_completion_sources.MAKERSUITE },3620 { id: 'model_google_select', api: 'openai', type: chat_completion_sources.MAKERSUITE },
3620 { id: 'model_mistralai_select', api: 'openai', type: chat_completion_sources.MISTRALAI },3621 { id: 'model_mistralai_select', api: 'openai', type: chat_completion_sources.MISTRALAI },
3621 { id: 'model_custom_select', api: 'openai', type: chat_completion_sources.CUSTOM },3622 { id: 'custom_model_id', api: 'openai', type: chat_completion_sources.CUSTOM },
3622 { id: 'model_cohere_select', api: 'openai', type: chat_completion_sources.COHERE },3623 { id: 'model_cohere_select', api: 'openai', type: chat_completion_sources.COHERE },
3623 { id: 'model_perplexity_select', api: 'openai', type: chat_completion_sources.PERPLEXITY },3624 { id: 'model_perplexity_select', api: 'openai', type: chat_completion_sources.PERPLEXITY },
3624 { id: 'model_groq_select', api: 'openai', type: chat_completion_sources.GROQ },3625 { id: 'model_groq_select', api: 'openai', type: chat_completion_sources.GROQ },
@@ -3649,12 +3650,31 @@ function getModelOptions(quiet) {
36493650
3650 const modelSelectControl = document.getElementById(modelSelectItem);3651 const modelSelectControl = document.getElementById(modelSelectItem);
36513652
3652 if (!(modelSelectControl instanceof HTMLSelectElement)) {3653 if (!(modelSelectControl instanceof HTMLSelectElement) && !(modelSelectControl instanceof HTMLInputElement)) {
3653 !quiet && toastr.error(`Model select control not found: ${main_api}[${apiSubType}]`);3654 !quiet && toastr.error(`Model select control not found: ${main_api}[${apiSubType}]`);
3654 return nullResult;3655 return nullResult;
3655 }3656 }
36563657
3657 const options = Array.from(modelSelectControl.options).filter(x => x.value);3658 /**
3659 * Get options from a HTMLSelectElement or HTMLInputElement with a list.
3660 * @param {HTMLSelectElement | HTMLInputElement} control Control containing the options
3661 * @returns {HTMLOptionElement[]} Array of options
3662 */
3663 const getOptions = (control) => {
3664 if (control instanceof HTMLSelectElement) {
3665 return Array.from(control.options);
3666 }
3667
3668 const valueOption = new Option(control.value, control.value);
3669
3670 if (control instanceof HTMLInputElement && control.list instanceof HTMLDataListElement) {
3671 return [valueOption, ...Array.from(control.list.options)];
3672 }
3673
3674 return [valueOption];
3675 };
3676
3677 const options = getOptions(modelSelectControl).filter(x => x.value).filter(onlyUnique);
3658 return { control: modelSelectControl, options };3678 return { control: modelSelectControl, options };
3659}3679}
36603680
@@ -3673,11 +3693,6 @@ function modelCallback(args, model) {
3673 return '';3693 return '';
3674 }3694 }
36753695
3676 if (!options.length) {
3677 !quiet && toastr.warning('No model options found. Check your API settings.');
3678 return '';
3679 }
3680
3681 model = String(model || '').trim();3696 model = String(model || '').trim();
36823697
3683 if (!model) {3698 if (!model) {
@@ -3686,6 +3701,18 @@ function modelCallback(args, model) {
36863701
3687 console.log('Set model to ' + model);3702 console.log('Set model to ' + model);
36883703
3704 if (modelSelectControl instanceof HTMLInputElement) {
3705 modelSelectControl.value = model;
3706 $(modelSelectControl).trigger('input');
3707 !quiet && toastr.success(`Model set to "${model}"`);
3708 return model;
3709 }
3710
3711 if (!options.length) {
3712 !quiet && toastr.warning('No model options found. Check your API settings.');
3713 return '';
3714 }
3715
3689 let newSelectedOption = null;3716 let newSelectedOption = null;
36903717
3691 const fuse = new Fuse(options, { keys: ['text', 'value'] });3718 const fuse = new Fuse(options, { keys: ['text', 'value'] });