Merge pull request #2929 from SillyTavern/model-write-in /model: Better support for write-in controls
Signed| @@ -55,7 +55,7 @@ import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockStat | |||
| 55 | import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js'; | 55 | import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js'; |
| 56 | import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; | 56 | import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; |
| 57 | import { decodeTextTokens, getAvailableTokenizers, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, selectTokenizer } from './tokenizers.js'; | 57 | import { decodeTextTokens, getAvailableTokenizers, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, selectTokenizer } from './tokenizers.js'; |
| 58 | import { debounce, delay, equalsIgnoreCaseAndAccents, findChar, getCharIndex, isFalseBoolean, isTrueBoolean, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js'; | 58 | import { debounce, delay, equalsIgnoreCaseAndAccents, findChar, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js'; |
| 59 | import { registerVariableCommands, resolveVariable } from './variables.js'; | 59 | import { registerVariableCommands, resolveVariable } from './variables.js'; |
| 60 | import { background_settings } from './backgrounds.js'; | 60 | import { background_settings } from './backgrounds.js'; |
| 61 | import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; | 61 | import { 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 subtype | 3597 | * Retrieves the available model options based on the currently selected main API and its subtype |
| 3598 | * @param {boolean} quiet - Whether to suppress toasts | 3598 | * @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 supported | 3600 | * @returns {{control: HTMLSelectElement|HTMLInputElement, options: HTMLOptionElement[]}?} An array of objects representing the available model options, or null if not supported |
| 3601 | */ | 3601 | */ |
| 3602 | function getModelOptions(quiet) { | 3602 | function 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) { | |||
| 3649 | 3650 | ||
| 3650 | const modelSelectControl = document.getElementById(modelSelectItem); | 3651 | const modelSelectControl = document.getElementById(modelSelectItem); |
| 3651 | 3652 | ||
| 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 | } |
| 3656 | 3657 | ||
| 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 | } |
| 3660 | 3680 | ||
| @@ -3673,11 +3693,6 @@ function modelCallback(args, model) { | |||
| 3673 | return ''; | 3693 | return ''; |
| 3674 | } | 3694 | } |
| 3675 | 3695 | ||
| 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(); |
| 3682 | 3697 | ||
| 3683 | if (!model) { | 3698 | if (!model) { |
| @@ -3686,6 +3701,18 @@ function modelCallback(args, model) { | |||
| 3686 | 3701 | ||
| 3687 | console.log('Set model to ' + model); | 3702 | console.log('Set model to ' + model); |
| 3688 | 3703 | ||
| 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; |
| 3690 | 3717 | ||
| 3691 | const fuse = new Fuse(options, { keys: ['text', 'value'] }); | 3718 | const fuse = new Fuse(options, { keys: ['text', 'value'] }); |