"Bind preset to connection" toggle (#3999) * Implement THE TOGGLE * Don't force reconnect on preset switch if toggle off * Don't clear custom models list either
Signed| @@ -498,3 +498,15 @@ label[for="trim_spaces"]:not(:has(input:checked)) small { | ||
| 498 | 498 | #banned_tokens_block_ooba:not(:has(#send_banned_tokens_textgenerationwebui:checked)) #banned_tokens_controls_ooba { |
| 499 | 499 | filter: brightness(0.5); |
| 500 | 500 | } |
| 501 | + | |
| 502 | +#bind_preset_to_connection:checked ~ .toggleOff { | |
| 503 | + display: none; | |
| 504 | +} | |
| 505 | + | |
| 506 | +#bind_preset_to_connection:not(:checked) ~ .toggleOn { | |
| 507 | + display: none; | |
| 508 | +} | |
| 509 | + | |
| 510 | +label[for="bind_preset_to_connection"]:has(input:checked) { | |
| 511 | + color: var(--active); | |
| 512 | +} | |
| @@ -176,6 +176,11 @@ | ||
| 176 | 176 | </strong> |
| 177 | 177 | |
| 178 | 178 | <div class="flex-container gap3px"> |
| 179 | + <label for="bind_preset_to_connection" class="margin0 menu_button menu_button_icon" title="Bind presets to API connections" data-i18n="[title]Bind presets to API connections"> | |
| 180 | + <input id="bind_preset_to_connection" type="checkbox" class="displayNone" /> | |
| 181 | + <i class="fa-fw fa-solid fa-link toggleOn"></i> | |
| 182 | + <i class="fa-fw fa-solid fa-link-slash toggleOff"></i> | |
| 183 | + </label> | |
| 179 | 184 | <div id="import_oai_preset" class="margin0 menu_button menu_button_icon" title="Import preset" data-i18n="[title]Import preset"> |
| 180 | 185 | <i class="fa-fw fa-solid fa-file-import"></i> |
| 181 | 186 | </div> |
| @@ -71,7 +71,7 @@ import { SlashCommand } from './slash-commands/SlashCommand.js'; | ||
| 71 | 71 | import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js'; |
| 72 | 72 | import { renderTemplateAsync } from './templates.js'; |
| 73 | 73 | import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js'; |
| 74 | 74 | import { Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js'; |
| 75 | 75 | import { t } from './i18n.js'; |
| 76 | 76 | import { ToolManager } from './tool-calling.js'; |
| 77 | 77 | import { accountStorage } from './util/AccountStorage.js'; |
| @@ -236,87 +236,87 @@ const sensitiveFields = [ | ||
| 236 | 236 | ]; |
| 237 | 237 | |
| 238 | 238 | /** |
| 239 | 239 | * preset_name -> [selector, setting_name, is_checkbox, is_connection] |
| 240 | 240 | * @type {Record<string, [string, string, boolean, boolean]>} |
| 241 | 241 | */ |
| 242 | 242 | export const settingsToUpdate = { |
| 243 | 243 | chat_completion_source: ['#chat_completion_source', 'chat_completion_source', false, true], |
| 244 | 244 | temperature: ['#temp_openai', 'temp_openai', false, false], |
| 245 | 245 | frequency_penalty: ['#freq_pen_openai', 'freq_pen_openai', false, false], |
| 246 | 246 | presence_penalty: ['#pres_pen_openai', 'pres_pen_openai', false, false], |
| 247 | 247 | top_p: ['#top_p_openai', 'top_p_openai', false, false], |
| 248 | 248 | top_k: ['#top_k_openai', 'top_k_openai', false, false], |
| 249 | 249 | top_a: ['#top_a_openai', 'top_a_openai', false, false], |
| 250 | 250 | min_p: ['#min_p_openai', 'min_p_openai', false, false], |
| 251 | 251 | repetition_penalty: ['#repetition_penalty_openai', 'repetition_penalty_openai', false, false], |
| 252 | 252 | max_context_unlocked: ['#oai_max_context_unlocked', 'max_context_unlocked', true, false], |
| 253 | 253 | openai_model: ['#model_openai_select', 'openai_model', false, true], |
| 254 | 254 | claude_model: ['#model_claude_select', 'claude_model', false, true], |
| 255 | 255 | windowai_model: ['#model_windowai_select', 'windowai_model', false, true], |
| 256 | 256 | openrouter_model: ['#model_openrouter_select', 'openrouter_model', false, true], |
| 257 | 257 | openrouter_use_fallback: ['#openrouter_use_fallback', 'openrouter_use_fallback', true, true], |
| 258 | 258 | openrouter_group_models: ['#openrouter_group_models', 'openrouter_group_models', false, true], |
| 259 | 259 | openrouter_sort_models: ['#openrouter_sort_models', 'openrouter_sort_models', false, true], |
| 260 | 260 | openrouter_providers: ['#openrouter_providers_chat', 'openrouter_providers', false, true], |
| 261 | 261 | openrouter_allow_fallbacks: ['#openrouter_allow_fallbacks', 'openrouter_allow_fallbacks', true, true], |
| 262 | 262 | openrouter_middleout: ['#openrouter_middleout', 'openrouter_middleout', false, true], |
| 263 | 263 | ai21_model: ['#model_ai21_select', 'ai21_model', false, true], |
| 264 | 264 | mistralai_model: ['#model_mistralai_select', 'mistralai_model', false, true], |
| 265 | 265 | cohere_model: ['#model_cohere_select', 'cohere_model', false, true], |
| 266 | 266 | perplexity_model: ['#model_perplexity_select', 'perplexity_model', false, true], |
| 267 | 267 | groq_model: ['#model_groq_select', 'groq_model', false, true], |
| 268 | 268 | nanogpt_model: ['#model_nanogpt_select', 'nanogpt_model', false, true], |
| 269 | 269 | deepseek_model: ['#model_deepseek_select', 'deepseek_model', false, true], |
| 270 | 270 | zerooneai_model: ['#model_01ai_select', 'zerooneai_model', false, true], |
| 271 | 271 | xai_model: ['#model_xai_select', 'xai_model', false, true], |
| 272 | 272 | pollinations_model: ['#model_pollinations_select', 'pollinations_model', false, true], |
| 273 | 273 | custom_model: ['#custom_model_id', 'custom_model', false, true], |
| 274 | 274 | custom_url: ['#custom_api_url_text', 'custom_url', false, true], |
| 275 | 275 | custom_include_body: ['#custom_include_body', 'custom_include_body', false, true], |
| 276 | 276 | custom_exclude_body: ['#custom_exclude_body', 'custom_exclude_body', false, true], |
| 277 | 277 | custom_include_headers: ['#custom_include_headers', 'custom_include_headers', false, true], |
| 278 | 278 | custom_prompt_post_processing: ['#custom_prompt_post_processing', 'custom_prompt_post_processing', false, true], |
| 279 | 279 | google_model: ['#model_google_select', 'google_model', false, true], |
| 280 | 280 | openai_max_context: ['#openai_max_context', 'openai_max_context', false, false], |
| 281 | 281 | openai_max_tokens: ['#openai_max_tokens', 'openai_max_tokens', false, false], |
| 282 | 282 | wrap_in_quotes: ['#wrap_in_quotes', 'wrap_in_quotes', true, false], |
| 283 | 283 | names_behavior: ['#names_behavior', 'names_behavior', false, false], |
| 284 | 284 | send_if_empty: ['#send_if_empty_textarea', 'send_if_empty', false, false], |
| 285 | 285 | impersonation_prompt: ['#impersonation_prompt_textarea', 'impersonation_prompt', false, false], |
| 286 | 286 | new_chat_prompt: ['#newchat_prompt_textarea', 'new_chat_prompt', false, false], |
| 287 | 287 | new_group_chat_prompt: ['#newgroupchat_prompt_textarea', 'new_group_chat_prompt', false, false], |
| 288 | 288 | new_example_chat_prompt: ['#newexamplechat_prompt_textarea', 'new_example_chat_prompt', false, false], |
| 289 | 289 | continue_nudge_prompt: ['#continue_nudge_prompt_textarea', 'continue_nudge_prompt', false, false], |
| 290 | 290 | bias_preset_selected: ['#openai_logit_bias_preset', 'bias_preset_selected', false, false], |
| 291 | 291 | reverse_proxy: ['#openai_reverse_proxy', 'reverse_proxy', false, true], |
| 292 | 292 | wi_format: ['#wi_format_textarea', 'wi_format', false, false], |
| 293 | 293 | scenario_format: ['#scenario_format_textarea', 'scenario_format', false, false], |
| 294 | 294 | personality_format: ['#personality_format_textarea', 'personality_format', false, false], |
| 295 | 295 | group_nudge_prompt: ['#group_nudge_prompt_textarea', 'group_nudge_prompt', false, false], |
| 296 | 296 | stream_openai: ['#stream_toggle', 'stream_openai', true, false], |
| 297 | 297 | prompts: ['', 'prompts', false, false], |
| 298 | 298 | prompt_order: ['', 'prompt_order', false, false], |
| 299 | 299 | api_url_scale: ['#api_url_scale', 'api_url_scale', false, true], |
| 300 | 300 | show_external_models: ['#openai_show_external_models', 'show_external_models', true, true], |
| 301 | 301 | proxy_password: ['#openai_proxy_password', 'proxy_password', false, true], |
| 302 | 302 | assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false, false], |
| 303 | 303 | assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false, false], |
| 304 | 304 | claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true, false], |
| 305 | 305 | use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true, false], |
| 306 | 306 | use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true, true], |
| 307 | 307 | squash_system_messages: ['#squash_system_messages', 'squash_system_messages', true, false], |
| 308 | 308 | image_inlining: ['#openai_image_inlining', 'image_inlining', true, false], |
| 309 | 309 | inline_image_quality: ['#openai_inline_image_quality', 'inline_image_quality', false, false], |
| 310 | 310 | continue_prefill: ['#continue_prefill', 'continue_prefill', true, false], |
| 311 | 311 | continue_postfix: ['#continue_postfix', 'continue_postfix', false, false], |
| 312 | 312 | function_calling: ['#openai_function_calling', 'function_calling', true, false], |
| 313 | 313 | show_thoughts: ['#openai_show_thoughts', 'show_thoughts', true, false], |
| 314 | 314 | reasoning_effort: ['#openai_reasoning_effort', 'reasoning_effort', false, false], |
| 315 | 315 | enable_web_search: ['#openai_enable_web_search', 'enable_web_search', true, false], |
| 316 | 316 | seed: ['#seed_openai', 'seed', false, false], |
| 317 | 317 | n: ['#n_openai', 'n', false, false], |
| 318 | 318 | bypass_status_check: ['#openai_bypass_status_check', 'bypass_status_check', true, true], |
| 319 | 319 | request_images: ['#openai_request_images', 'request_images', true, false], |
| 320 | 320 | }; |
| 321 | 321 | |
| 322 | 322 | const default_settings = { |
| @@ -399,6 +399,7 @@ const default_settings = { | ||
| 399 | 399 | request_images: false, |
| 400 | 400 | seed: -1, |
| 401 | 401 | n: 1, |
| 402 | + bind_preset_to_connection: true, | |
| 402 | 403 | }; |
| 403 | 404 | |
| 404 | 405 | const oai_settings = { |
| @@ -481,6 +482,7 @@ const oai_settings = { | ||
| 481 | 482 | request_images: false, |
| 482 | 483 | seed: -1, |
| 483 | 484 | n: 1, |
| 485 | + bind_preset_to_connection: true, | |
| 484 | 486 | }; |
| 485 | 487 | |
| 486 | 488 | export let proxies = [ |
| @@ -3395,6 +3397,7 @@ function loadOpenAISettings(data, settings) { | ||
| 3395 | 3397 | oai_settings.continue_postfix = settings.continue_postfix ?? default_settings.continue_postfix; |
| 3396 | 3398 | oai_settings.function_calling = settings.function_calling ?? default_settings.function_calling; |
| 3397 | 3399 | oai_settings.openrouter_providers = settings.openrouter_providers ?? default_settings.openrouter_providers; |
| 3400 | + oai_settings.bind_preset_to_connection = settings.bind_preset_to_connection ?? default_settings.bind_preset_to_connection; | |
| 3398 | 3401 | |
| 3399 | 3402 | // Migrate from old settings |
| 3400 | 3403 | if (settings.names_in_completion === true) { |
| @@ -3511,6 +3514,7 @@ function loadOpenAISettings(data, settings) { | ||
| 3511 | 3514 | $('#openai_show_thoughts').prop('checked', oai_settings.show_thoughts); |
| 3512 | 3515 | $('#openai_enable_web_search').prop('checked', oai_settings.enable_web_search); |
| 3513 | 3516 | $('#openai_request_images').prop('checked', oai_settings.request_images); |
| 3517 | + $('#bind_preset_to_connection').prop('checked', oai_settings.bind_preset_to_connection); | |
| 3514 | 3518 | |
| 3515 | 3519 | $('#openai_reasoning_effort').val(oai_settings.reasoning_effort); |
| 3516 | 3520 | $(`#openai_reasoning_effort option[value="${oai_settings.reasoning_effort}"]`).prop('selected', true); |
| @@ -4039,20 +4043,33 @@ async function onExportPresetClick() { | ||
| 4039 | 4043 | const preset = structuredClone(openai_settings[openai_setting_names[oai_settings.preset_settings_openai]]); |
| 4040 | 4044 | |
| 4041 | 4045 | const fieldValues = sensitiveFields.filter(field => preset[field]).map(field => `<b>${field}</b>: <code>${preset[field]}</code>`); |
| 4042 | 4046 | const shouldConfirm =if (fieldValues.length > 0;) { |
| 4043 | 4047 | const textHeader = t`Your preset contains proxy and/or custom endpoint settings.`; |
| 4044 | 4048 | const textMessage = '<div>' + t`Do you want to remove these fields before exporting?` + `</div><br>${DOMPurify.sanitize(fieldValues.join('<br>'))}`; |
| 4045 | 4049 | const cancelButton = { text: 'Cancel', result: POPUP_RESULT.CANCELLED, appendAtEnd: true }; |
| 4046 | 4050 | const popupOptions = { customButtons: [cancelButton] }; |
| 4047 | 4051 | const popupResult = await Popup.show.confirm(textHeader, textMessage, popupOptions); |
| 4048 | 4052 | |
| 4049 | 4053 | if (popupResult === POPUP_RESULT.CANCELLED) { |
| 4050 | 4054 | console.log('Export cancelled by user'); |
| 4051 | 4055 | return; |
| 4056 | + } | |
| 4057 | + | |
| 4058 | + if (popupResult === POPUP_RESULT.AFFIRMATIVE) { | |
| 4059 | + sensitiveFields.forEach(field => delete preset[field]); | |
| 4060 | + } | |
| 4052 | 4061 | } |
| 4053 | 4062 | |
| 4054 | - if (!shouldConfirm || popupResult === POPUP_RESULT.AFFIRMATIVE) { | |
| 4063 | + const exportConnectionTemplate = $(await renderTemplateAsync('exportPreset')); | |
| 4055 | - sensitiveFields.forEach(field => delete preset[field]); | |
| 4064 | + await new Popup(exportConnectionTemplate, POPUP_TYPE.TEXT).show(); | |
| 4065 | + | |
| 4066 | + const removeConnectionData = exportConnectionTemplate.find('input[name="export_connection_data"]:checked').val() === 'false'; | |
| 4067 | + if (removeConnectionData) { | |
| 4068 | + for (const [, [, settingName, , isConnection]] of Object.entries(settingsToUpdate)) { | |
| 4069 | + if (isConnection) { | |
| 4070 | + delete preset[settingName]; | |
| 4071 | + } | |
| 4072 | + } | |
| 4056 | 4073 | } |
| 4057 | 4074 | |
| 4058 | 4075 | await eventSource.emit(event_types.OAI_PRESET_EXPORT_READY, preset); |
| @@ -4200,9 +4217,15 @@ function onSettingsPresetChange() { | ||
| 4200 | 4217 | savePreset: saveOpenAIPreset, |
| 4201 | 4218 | presetNameBefore: presetNameBefore, |
| 4202 | 4219 | }).finally(r => { |
| 4203 | - $('.model_custom_select').empty(); | |
| 4220 | + if (oai_settings.bind_preset_to_connection) { | |
| 4221 | + $('.model_custom_select').empty(); | |
| 4222 | + } | |
| 4223 | + | |
| 4224 | + for (const [key, [selector, setting, isCheckbox, isConnection]] of Object.entries(settingsToUpdate)) { | |
| 4225 | + if (isConnection && !oai_settings.bind_preset_to_connection) { | |
| 4226 | + continue; | |
| 4227 | + } | |
| 4204 | 4228 | |
| 4205 | - for (const [key, [selector, setting, isCheckbox]] of Object.entries(settingsToUpdate)) { | |
| 4206 | 4229 | if (preset[key] !== undefined) { |
| 4207 | 4230 | if (isCheckbox) { |
| 4208 | 4231 | updateCheckbox(selector, preset[key]); |
| @@ -4213,9 +4236,13 @@ function onSettingsPresetChange() { | ||
| 4213 | 4236 | } |
| 4214 | 4237 | } |
| 4215 | 4238 | |
| 4216 | - $('#chat_completion_source').trigger('change'); | |
| 4239 | + // These cannot be changed via preset if unbound to connection | |
| 4240 | + if (oai_settings.bind_preset_to_connection) { | |
| 4241 | + $('#chat_completion_source').trigger('change'); | |
| 4242 | + $('#openrouter_providers_chat').trigger('change'); | |
| 4243 | + } | |
| 4244 | + | |
| 4217 | 4245 | $('#openai_logit_bias_preset').trigger('change'); |
| 4218 | - $('#openrouter_providers_chat').trigger('change'); | |
| 4219 | 4246 | |
| 4220 | 4247 | saveSettingsDebounced(); |
| 4221 | 4248 | eventSource.emit(event_types.OAI_PRESET_CHANGED_AFTER); |
| @@ -5848,6 +5875,11 @@ export function initOpenAI() { | ||
| 5848 | 5875 | saveSettingsDebounced(); |
| 5849 | 5876 | }); |
| 5850 | 5877 | |
| 5878 | + $('#bind_preset_to_connection').on('input', function () { | |
| 5879 | + oai_settings.bind_preset_to_connection = !!$(this).prop('checked'); | |
| 5880 | + saveSettingsDebounced(); | |
| 5881 | + }); | |
| 5882 | + | |
| 5851 | 5883 | $('#api_button_openai').on('click', onConnectButtonClick); |
| 5852 | 5884 | $('#openai_reverse_proxy').on('input', onReverseProxyInput); |
| 5853 | 5885 | $('#model_openai_select').on('change', onModelChange); |
| @@ -0,0 +1,27 @@ | ||
| 1 | +<div class="flex-container flexFlowColumn marginBot10"> | |
| 2 | + <h3 data-i18n="Do you want to export connection data with the preset?"> | |
| 3 | + Do you want to export connection data with the preset? | |
| 4 | + </h3> | |
| 5 | + | |
| 6 | + <div data-i18n="This includes the selected source, models, and other preferences set in the API Connections panel."> | |
| 7 | + This includes the selected source, models, and other preferences set in the API Connections panel. | |
| 8 | + </div> | |
| 9 | + | |
| 10 | + <strong data-i18n="Your stored API keys are never exported."> | |
| 11 | + Your stored API keys are never exported. | |
| 12 | + </strong> | |
| 13 | +</div> | |
| 14 | +<div class="flex-container flexFlowColumn"> | |
| 15 | + <label class="checkbox_label" for="export_connection_data_yes"> | |
| 16 | + <input type="radio" id="export_connection_data_yes" name="export_connection_data" value="true"> | |
| 17 | + <span data-i18n="Export connection data"> | |
| 18 | + Export connection data | |
| 19 | + </span> | |
| 20 | + </label> | |
| 21 | + <label class="checkbox_label" for="export_connection_data_no"> | |
| 22 | + <input type="radio" id="export_connection_data_no" name="export_connection_data" value="false" checked> | |
| 23 | + <span data-i18n="Do not export connection data"> | |
| 24 | + Do not export connection data | |
| 25 | + </span> | |
| 26 | + </label> | |
| 27 | +</div> | |