Open router provider filter (#5208) * Added filter for OpenRouter models provider selection Now if a model is selected, only available providers for that model will show. Wanted to do the same for the quants, but I think the API is not returning the quants available for each model at the moment. Used existing API that for some reason was not consumed. * Added filter for OpenRouter providers Now if a model is selected, only the providers available show. Wanted to do the same with the quants but it seems the OpenRouter API is not giving the available quants list at the moment for each model. * gua * Now it also works on chat completion and only disables options * detail * Warning added * eslint * Move inline styles to CSS --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
Signed| @@ -568,3 +568,9 @@ label[for="bind_preset_to_connection"]:has(input:checked) { | |||
| 568 | #adaptive_p_block:has([data-tg-samplers="adaptive_target"][style*="display: none"]):has([data-tg-samplers="adaptive_decay"][style*="display: none"]) { | 568 | #adaptive_p_block:has([data-tg-samplers="adaptive_target"][style*="display: none"]):has([data-tg-samplers="adaptive_decay"][style*="display: none"]) { |
| 569 | display: none; | 569 | display: none; |
| 570 | } | 570 | } |
| 571 | |||
| 572 | #openrouter_provider_warning_text, | ||
| 573 | #openrouter_provider_warning_chat { | ||
| 574 | color: var(--warning); | ||
| 575 | margin-left: 5px; | ||
| 576 | } | ||
| @@ -2460,7 +2460,10 @@ | |||
| 2460 | </select> | 2460 | </select> |
| 2461 | </div> | 2461 | </div> |
| 2462 | <div> | 2462 | <div> |
| 2463 | <h4 data-i18n="Model Providers">Model Providers</h4> | 2463 | <h4> |
| 2464 | <span data-i18n="Model Providers">Model Providers</span> | ||
| 2465 | <i id="openrouter_provider_warning_text" class="fa-solid fa-circle-exclamation displayNone" title="Deselect inapplicable provider(s) or select an applicable provider to avoid a 404 error."></i> | ||
| 2466 | </h4> | ||
| 2464 | <select id="openrouter_providers_text" class="openrouter_providers" multiple> | 2467 | <select id="openrouter_providers_text" class="openrouter_providers" multiple> |
| 2465 | </select> | 2468 | </select> |
| 2466 | <label class="checkbox_label" data-i18n="[title]Automatically chooses an alternative provider if chosen providers can't serve your request." for="openrouter_allow_fallbacks_textgenerationwebui" title="Automatically chooses an alternative provider if chosen providers can't serve your request."> | 2469 | <label class="checkbox_label" data-i18n="[title]Automatically chooses an alternative provider if chosen providers can't serve your request." for="openrouter_allow_fallbacks_textgenerationwebui" title="Automatically chooses an alternative provider if chosen providers can't serve your request."> |
| @@ -3214,7 +3217,10 @@ | |||
| 3214 | </div> | 3217 | </div> |
| 3215 | </div> | 3218 | </div> |
| 3216 | <div> | 3219 | <div> |
| 3217 | <h4 data-i18n="Model Providers">Model Providers</h4> | 3220 | <h4> |
| 3221 | <span data-i18n="Model Providers">Model Providers</span> | ||
| 3222 | <i id="openrouter_provider_warning_chat" class="fa-solid fa-circle-exclamation displayNone" title="Deselect inapplicable provider(s) or select an applicable provider to avoid a 404 error."></i> | ||
| 3223 | </h4> | ||
| 3218 | <select id="openrouter_providers_chat" class="openrouter_providers" multiple> | 3224 | <select id="openrouter_providers_chat" class="openrouter_providers" multiple> |
| 3219 | </select> | 3225 | </select> |
| 3220 | <label class="checkbox_label marginTopBot5" for="openrouter_allow_fallbacks" data-i18n="[title]Automatically chooses an alternative provider if chosen providers can't serve your request." title="Automatically chooses an alternative provider if chosen providers can't serve your request."> | 3226 | <label class="checkbox_label marginTopBot5" for="openrouter_allow_fallbacks" data-i18n="[title]Automatically chooses an alternative provider if chosen providers can't serve your request." title="Automatically chooses an alternative provider if chosen providers can't serve your request."> |
| @@ -79,6 +79,7 @@ import { t } from './i18n.js'; | |||
| 79 | import { ToolManager } from './tool-calling.js'; | 79 | import { ToolManager } from './tool-calling.js'; |
| 80 | import { accountStorage } from './util/AccountStorage.js'; | 80 | import { accountStorage } from './util/AccountStorage.js'; |
| 81 | import { COMETAPI_IGNORE_PATTERNS, IGNORE_SYMBOL, MEDIA_DISPLAY, MEDIA_TYPE } from './constants.js'; | 81 | import { COMETAPI_IGNORE_PATTERNS, IGNORE_SYMBOL, MEDIA_DISPLAY, MEDIA_TYPE } from './constants.js'; |
| 82 | import { syncOpenRouterProvidersForModel, updateOpenRouterProvidersWarning } from './textgen-models.js'; | ||
| 82 | 83 | ||
| 83 | export { | 84 | export { |
| 84 | openai_messages_count, | 85 | openai_messages_count, |
| @@ -5135,6 +5136,7 @@ async function onModelChange() { | |||
| 5135 | 5136 | ||
| 5136 | console.log('OpenRouter model changed to', value); | 5137 | console.log('OpenRouter model changed to', value); |
| 5137 | oai_settings.openrouter_model = value; | 5138 | oai_settings.openrouter_model = value; |
| 5139 | syncOpenRouterProvidersForModel(value, '#openrouter_providers_chat'); | ||
| 5138 | } | 5140 | } |
| 5139 | 5141 | ||
| 5140 | if ($(this).is('#model_ai21_select')) { | 5142 | if ($(this).is('#model_ai21_select')) { |
| @@ -6623,6 +6625,7 @@ export function initOpenAI() { | |||
| 6623 | 6625 | ||
| 6624 | $('#openrouter_allow_fallbacks').on('input', function () { | 6626 | $('#openrouter_allow_fallbacks').on('input', function () { |
| 6625 | oai_settings.openrouter_allow_fallbacks = !!$(this).prop('checked'); | 6627 | oai_settings.openrouter_allow_fallbacks = !!$(this).prop('checked'); |
| 6628 | updateOpenRouterProvidersWarning('#openrouter_providers_chat'); | ||
| 6626 | saveSettingsDebounced(); | 6629 | saveSettingsDebounced(); |
| 6627 | }); | 6630 | }); |
| 6628 | 6631 | ||
| @@ -6882,6 +6885,7 @@ export function initOpenAI() { | |||
| 6882 | 6885 | ||
| 6883 | oai_settings.openrouter_providers = selectedProviders; | 6886 | oai_settings.openrouter_providers = selectedProviders; |
| 6884 | 6887 | ||
| 6888 | updateOpenRouterProvidersWarning('#openrouter_providers_chat'); | ||
| 6885 | saveSettingsDebounced(); | 6889 | saveSettingsDebounced(); |
| 6886 | }); | 6890 | }); |
| 6887 | 6891 | ||
| @@ -101,6 +101,85 @@ const OPENROUTER_PROVIDERS = [ | |||
| 101 | 'Z.AI', | 101 | 'Z.AI', |
| 102 | ]; | 102 | ]; |
| 103 | 103 | ||
| 104 | const OPENROUTER_PROVIDER_WARNING_SELECTORS = { | ||
| 105 | '#openrouter_providers_text': { | ||
| 106 | fallbackSelector: '#openrouter_allow_fallbacks_textgenerationwebui', | ||
| 107 | warningSelector: '#openrouter_provider_warning_text', | ||
| 108 | }, | ||
| 109 | '#openrouter_providers_chat': { | ||
| 110 | fallbackSelector: '#openrouter_allow_fallbacks', | ||
| 111 | warningSelector: '#openrouter_provider_warning_chat', | ||
| 112 | }, | ||
| 113 | }; | ||
| 114 | |||
| 115 | export function updateOpenRouterProvidersWarning(providersSelector) { | ||
| 116 | const $providers = $(providersSelector); | ||
| 117 | |||
| 118 | const warningSelectors = OPENROUTER_PROVIDER_WARNING_SELECTORS[providersSelector]; | ||
| 119 | |||
| 120 | if ($providers.length === 0 || !warningSelectors) { | ||
| 121 | return; | ||
| 122 | } | ||
| 123 | |||
| 124 | const $fallback = $(warningSelectors.fallbackSelector); | ||
| 125 | const $warning = $(warningSelectors.warningSelector); | ||
| 126 | |||
| 127 | const allowFallback = !!$fallback.prop('checked'); | ||
| 128 | const selectedCount = $providers.find('option:selected').length; | ||
| 129 | const applicableSelectedCount = $providers.find('option:selected:not(:disabled)').length; | ||
| 130 | const showWarning = !allowFallback && selectedCount > 0 && applicableSelectedCount === 0; | ||
| 131 | |||
| 132 | $warning.toggleClass('displayNone', !showWarning); | ||
| 133 | } | ||
| 134 | |||
| 135 | export async function syncOpenRouterProvidersForModel(modelId, providersSelector) { | ||
| 136 | const $providers = $(providersSelector); | ||
| 137 | |||
| 138 | const refreshWarningState = () => { | ||
| 139 | updateOpenRouterProvidersWarning(providersSelector); | ||
| 140 | }; | ||
| 141 | |||
| 142 | if (!modelId || !modelId.includes('/')) { | ||
| 143 | $providers.find('option').prop('disabled', false); | ||
| 144 | $providers.trigger('change.select2'); | ||
| 145 | refreshWarningState(); | ||
| 146 | return; | ||
| 147 | } | ||
| 148 | |||
| 149 | try { | ||
| 150 | const response = await fetch('/api/openrouter/models/providers', { | ||
| 151 | method: 'POST', | ||
| 152 | headers: getRequestHeaders(), | ||
| 153 | body: JSON.stringify({ model: modelId }), | ||
| 154 | }); | ||
| 155 | |||
| 156 | if (!response.ok) { | ||
| 157 | refreshWarningState(); | ||
| 158 | return; | ||
| 159 | } | ||
| 160 | |||
| 161 | const providerNames = await response.json(); | ||
| 162 | |||
| 163 | if (!Array.isArray(providerNames) || providerNames.length === 0) { | ||
| 164 | $providers.find('option').prop('disabled', false); | ||
| 165 | $providers.trigger('change.select2'); | ||
| 166 | refreshWarningState(); | ||
| 167 | return; | ||
| 168 | } | ||
| 169 | |||
| 170 | $providers.find('option').each(function () { | ||
| 171 | const isAvailable = providerNames.includes($(this).val()); | ||
| 172 | $(this).prop('disabled', !isAvailable); | ||
| 173 | }); | ||
| 174 | |||
| 175 | $providers.trigger('change.select2'); | ||
| 176 | refreshWarningState(); | ||
| 177 | } catch (error) { | ||
| 178 | console.error('Failed to fetch OpenRouter providers for model', error); | ||
| 179 | refreshWarningState(); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 104 | export async function loadOllamaModels(data) { | 183 | export async function loadOllamaModels(data) { |
| 105 | if (!Array.isArray(data)) { | 184 | if (!Array.isArray(data)) { |
| 106 | console.error('Invalid Ollama models data', data); | 185 | console.error('Invalid Ollama models data', data); |
| @@ -315,6 +394,7 @@ export async function loadOpenRouterModels(data) { | |||
| 315 | 394 | ||
| 316 | // Calculate the cost of the selected model + update on settings change | 395 | // Calculate the cost of the selected model + update on settings change |
| 317 | calculateOpenRouterCost(); | 396 | calculateOpenRouterCost(); |
| 397 | syncOpenRouterProvidersForModel(textgen_settings.openrouter_model, '#openrouter_providers_text'); | ||
| 318 | } | 398 | } |
| 319 | 399 | ||
| 320 | export async function loadVllmModels(data) { | 400 | export async function loadVllmModels(data) { |
| @@ -675,6 +755,7 @@ function onOpenRouterModelSelect() { | |||
| 675 | textgen_settings.openrouter_model = modelId; | 755 | textgen_settings.openrouter_model = modelId; |
| 676 | $('#api_button_textgenerationwebui').trigger('click'); | 756 | $('#api_button_textgenerationwebui').trigger('click'); |
| 677 | const model = openRouterModels.find(x => x.id === modelId); | 757 | const model = openRouterModels.find(x => x.id === modelId); |
| 758 | syncOpenRouterProvidersForModel(modelId, '#openrouter_providers_text'); | ||
| 678 | setGenerationParamsFromPreset({ max_length: model.context_length }); | 759 | setGenerationParamsFromPreset({ max_length: model.context_length }); |
| 679 | } | 760 | } |
| 680 | 761 | ||
| @@ -23,7 +23,7 @@ import { power_user, registerDebugFunction } from './power-user.js'; | |||
| 23 | import { getActiveManualApiSamplers, loadApiSelectedSamplers, isSamplerManualPriorityEnabled } from './samplerSelect.js'; | 23 | import { getActiveManualApiSamplers, loadApiSelectedSamplers, isSamplerManualPriorityEnabled } from './samplerSelect.js'; |
| 24 | import { SECRET_KEYS, writeSecret } from './secrets.js'; | 24 | import { SECRET_KEYS, writeSecret } from './secrets.js'; |
| 25 | import { getEventSourceStream } from './sse-stream.js'; | 25 | import { getEventSourceStream } from './sse-stream.js'; |
| 26 | import { getCurrentDreamGenModelTokenizer, getCurrentOpenRouterModelTokenizer, loadAphroditeModels, loadDreamGenModels, loadFeatherlessModels, loadGenericModels, loadInfermaticAIModels, loadLlamaCppModels, loadMancerModels, loadOllamaModels, loadOpenRouterModels, loadTabbyModels, loadTogetherAIModels, loadVllmModels } from './textgen-models.js'; | 26 | import { getCurrentDreamGenModelTokenizer, getCurrentOpenRouterModelTokenizer, loadAphroditeModels, loadDreamGenModels, loadFeatherlessModels, loadGenericModels, loadInfermaticAIModels, loadLlamaCppModels, loadMancerModels, loadOllamaModels, loadOpenRouterModels, loadTabbyModels, loadTogetherAIModels, loadVllmModels, updateOpenRouterProvidersWarning } from './textgen-models.js'; |
| 27 | import { ENCODE_TOKENIZERS, TEXTGEN_TOKENIZERS, TOKENIZER_SUPPORTED_KEY, getTextTokens, getTokenizerBestMatch, tokenizers } from './tokenizers.js'; | 27 | import { ENCODE_TOKENIZERS, TEXTGEN_TOKENIZERS, TOKENIZER_SUPPORTED_KEY, getTextTokens, getTokenizerBestMatch, tokenizers } from './tokenizers.js'; |
| 28 | import { AbortReason } from './util/AbortReason.js'; | 28 | import { AbortReason } from './util/AbortReason.js'; |
| 29 | import { getSortableDelay, onlyUnique, arraysEqual, isObject } from './utils.js'; | 29 | import { getSortableDelay, onlyUnique, arraysEqual, isObject } from './utils.js'; |
| @@ -1069,9 +1069,14 @@ export function initTextGenSettings() { | |||
| 1069 | 1069 | ||
| 1070 | textgenerationwebui_settings.openrouter_providers = selectedProviders; | 1070 | textgenerationwebui_settings.openrouter_providers = selectedProviders; |
| 1071 | 1071 | ||
| 1072 | updateOpenRouterProvidersWarning('#openrouter_providers_text'); | ||
| 1072 | saveSettingsDebounced(); | 1073 | saveSettingsDebounced(); |
| 1073 | }); | 1074 | }); |
| 1074 | 1075 | ||
| 1076 | $('#openrouter_allow_fallbacks_textgenerationwebui').on('input', function () { | ||
| 1077 | updateOpenRouterProvidersWarning('#openrouter_providers_text'); | ||
| 1078 | }); | ||
| 1079 | |||
| 1075 | $('#openrouter_quantizations_text').on('change', function () { | 1080 | $('#openrouter_quantizations_text').on('change', function () { |
| 1076 | const selectedQuantizations = $(this).val(); | 1081 | const selectedQuantizations = $(this).val(); |
| 1077 | 1082 | ||