/model: Better support for write-in controls
| @@ -55,7 +55,7 @@ import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockStat | ||
| 55 | 55 | import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js'; |
| 56 | 56 | import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; |
| 57 | 57 | import { decodeTextTokens, getAvailableTokenizers, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, selectTokenizer } from './tokenizers.js'; |
| 58 | 58 | import { debounce, delay, equalsIgnoreCaseAndAccents, findChar, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js'; |
| 59 | 59 | import { registerVariableCommands, resolveVariable } from './variables.js'; |
| 60 | 60 | import { background_settings } from './backgrounds.js'; |
| 61 | 61 | import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; |
| @@ -3542,11 +3542,12 @@ function setBackgroundCallback(_, bg) { | ||
| 3542 | 3542 | * Retrieves the available model options based on the currently selected main API and its subtype |
| 3543 | 3543 | * @param {boolean} quiet - Whether to suppress toasts |
| 3544 | 3544 | * |
| 3545 | 3545 | * @returns {{control: HTMLSelectElement|HTMLInputElement, options: HTMLOptionElement[]}?} An array of objects representing the available model options, or null if not supported |
| 3546 | 3546 | */ |
| 3547 | 3547 | function getModelOptions(quiet) { |
| 3548 | 3548 | const nullResult = { control: null, options: null }; |
| 3549 | 3549 | const modelSelectMap = [ |
| 3550 | + { id: 'custom_model_textgenerationwebui', api: 'textgenerationwebui', type: textgen_types.OOBA }, | |
| 3550 | 3551 | { id: 'model_togetherai_select', api: 'textgenerationwebui', type: textgen_types.TOGETHERAI }, |
| 3551 | 3552 | { id: 'openrouter_model', api: 'textgenerationwebui', type: textgen_types.OPENROUTER }, |
| 3552 | 3553 | { id: 'model_infermaticai_select', api: 'textgenerationwebui', type: textgen_types.INFERMATICAI }, |
| @@ -3563,7 +3564,7 @@ function getModelOptions(quiet) { | ||
| 3563 | 3564 | { id: 'model_ai21_select', api: 'openai', type: chat_completion_sources.AI21 }, |
| 3564 | 3565 | { id: 'model_google_select', api: 'openai', type: chat_completion_sources.MAKERSUITE }, |
| 3565 | 3566 | { id: 'model_mistralai_select', api: 'openai', type: chat_completion_sources.MISTRALAI }, |
| 3566 | 3567 | { id: 'model_custom_selectcustom_model_id', api: 'openai', type: chat_completion_sources.CUSTOM }, |
| 3567 | 3568 | { id: 'model_cohere_select', api: 'openai', type: chat_completion_sources.COHERE }, |
| 3568 | 3569 | { id: 'model_perplexity_select', api: 'openai', type: chat_completion_sources.PERPLEXITY }, |
| 3569 | 3570 | { id: 'model_groq_select', api: 'openai', type: chat_completion_sources.GROQ }, |
| @@ -3594,12 +3595,31 @@ function getModelOptions(quiet) { | ||
| 3594 | 3595 | |
| 3595 | 3596 | const modelSelectControl = document.getElementById(modelSelectItem); |
| 3596 | 3597 | |
| 3597 | 3598 | if (!(modelSelectControl instanceof HTMLSelectElement) && !(modelSelectControl instanceof HTMLInputElement)) { |
| 3598 | 3599 | !quiet && toastr.error(`Model select control not found: ${main_api}[${apiSubType}]`); |
| 3599 | 3600 | return nullResult; |
| 3600 | 3601 | } |
| 3601 | 3602 | |
| 3602 | - const options = Array.from(modelSelectControl.options).filter(x => x.value); | |
| 3603 | + /** | |
| 3604 | + * Get options from a HTMLSelectElement or HTMLInputElement with a list. | |
| 3605 | + * @param {HTMLSelectElement | HTMLInputElement} control Control containing the options | |
| 3606 | + * @returns {HTMLOptionElement[]} Array of options | |
| 3607 | + */ | |
| 3608 | + const getOptions = (control) => { | |
| 3609 | + if (control instanceof HTMLSelectElement) { | |
| 3610 | + return Array.from(control.options); | |
| 3611 | + } | |
| 3612 | + | |
| 3613 | + const valueOption = new Option(control.value, control.value); | |
| 3614 | + | |
| 3615 | + if (control instanceof HTMLInputElement && control.list instanceof HTMLDataListElement) { | |
| 3616 | + return [valueOption, ...Array.from(control.list.options)]; | |
| 3617 | + } | |
| 3618 | + | |
| 3619 | + return [valueOption]; | |
| 3620 | + }; | |
| 3621 | + | |
| 3622 | + const options = getOptions(modelSelectControl).filter(x => x.value).filter(onlyUnique); | |
| 3603 | 3623 | return { control: modelSelectControl, options }; |
| 3604 | 3624 | } |
| 3605 | 3625 | |
| @@ -3618,11 +3638,6 @@ function modelCallback(args, model) { | ||
| 3618 | 3638 | return ''; |
| 3619 | 3639 | } |
| 3620 | 3640 | |
| 3621 | - if (!options.length) { | |
| 3622 | - !quiet && toastr.warning('No model options found. Check your API settings.'); | |
| 3623 | - return ''; | |
| 3624 | - } | |
| 3625 | - | |
| 3626 | 3641 | model = String(model || '').trim(); |
| 3627 | 3642 | |
| 3628 | 3643 | if (!model) { |
| @@ -3631,6 +3646,18 @@ function modelCallback(args, model) { | ||
| 3631 | 3646 | |
| 3632 | 3647 | console.log('Set model to ' + model); |
| 3633 | 3648 | |
| 3649 | + if (modelSelectControl instanceof HTMLInputElement) { | |
| 3650 | + modelSelectControl.value = model; | |
| 3651 | + $(modelSelectControl).trigger('input'); | |
| 3652 | + !quiet && toastr.success(`Model set to "${model}"`); | |
| 3653 | + return model; | |
| 3654 | + } | |
| 3655 | + | |
| 3656 | + if (!options.length) { | |
| 3657 | + !quiet && toastr.warning('No model options found. Check your API settings.'); | |
| 3658 | + return ''; | |
| 3659 | + } | |
| 3660 | + | |
| 3634 | 3661 | let newSelectedOption = null; |
| 3635 | 3662 | |
| 3636 | 3663 | const fuse = new Fuse(options, { keys: ['text', 'value'] }); |