"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

864a733663cc7c827a303ba614d33a797a0c67ae

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

Signed
4 files changed, +160 -84Showing whitespace changes
public/css/toggle-dependent.css+12 -0
@@ -498,3 +498,15 @@ label[for="trim_spaces"]:not(:has(input:checked)) small {
498498#banned_tokens_block_ooba:not(:has(#send_banned_tokens_textgenerationwebui:checked)) #banned_tokens_controls_ooba {
499499 filter: brightness(0.5);
500500}
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+}
public/index.html+5 -0
@@ -176,6 +176,11 @@
176176 </strong>
177177
178178 <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>
179184 <div id="import_oai_preset" class="margin0 menu_button menu_button_icon" title="Import preset" data-i18n="[title]Import preset">
180185 <i class="fa-fw fa-solid fa-file-import"></i>
181186 </div>
public/scripts/openai.js+116 -84
@@ -71,7 +71,7 @@ import { SlashCommand } from './slash-commands/SlashCommand.js';
7171import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';
7272import { renderTemplateAsync } from './templates.js';
7373import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
7474import { Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
7575import { t } from './i18n.js';
7676import { ToolManager } from './tool-calling.js';
7777import { accountStorage } from './util/AccountStorage.js';
@@ -236,87 +236,87 @@ const sensitiveFields = [
236236];
237237
238238/**
239239 * preset_name -> [selector, setting_name, is_checkbox, is_connection]
240240 * @type {Record<string, [string, string, boolean, boolean]>}
241241 */
242242export const settingsToUpdate = {
243243 chat_completion_source: ['#chat_completion_source', 'chat_completion_source', false, true],
244244 temperature: ['#temp_openai', 'temp_openai', false, false],
245245 frequency_penalty: ['#freq_pen_openai', 'freq_pen_openai', false, false],
246246 presence_penalty: ['#pres_pen_openai', 'pres_pen_openai', false, false],
247247 top_p: ['#top_p_openai', 'top_p_openai', false, false],
248248 top_k: ['#top_k_openai', 'top_k_openai', false, false],
249249 top_a: ['#top_a_openai', 'top_a_openai', false, false],
250250 min_p: ['#min_p_openai', 'min_p_openai', false, false],
251251 repetition_penalty: ['#repetition_penalty_openai', 'repetition_penalty_openai', false, false],
252252 max_context_unlocked: ['#oai_max_context_unlocked', 'max_context_unlocked', true, false],
253253 openai_model: ['#model_openai_select', 'openai_model', false, true],
254254 claude_model: ['#model_claude_select', 'claude_model', false, true],
255255 windowai_model: ['#model_windowai_select', 'windowai_model', false, true],
256256 openrouter_model: ['#model_openrouter_select', 'openrouter_model', false, true],
257257 openrouter_use_fallback: ['#openrouter_use_fallback', 'openrouter_use_fallback', true, true],
258258 openrouter_group_models: ['#openrouter_group_models', 'openrouter_group_models', false, true],
259259 openrouter_sort_models: ['#openrouter_sort_models', 'openrouter_sort_models', false, true],
260260 openrouter_providers: ['#openrouter_providers_chat', 'openrouter_providers', false, true],
261261 openrouter_allow_fallbacks: ['#openrouter_allow_fallbacks', 'openrouter_allow_fallbacks', true, true],
262262 openrouter_middleout: ['#openrouter_middleout', 'openrouter_middleout', false, true],
263263 ai21_model: ['#model_ai21_select', 'ai21_model', false, true],
264264 mistralai_model: ['#model_mistralai_select', 'mistralai_model', false, true],
265265 cohere_model: ['#model_cohere_select', 'cohere_model', false, true],
266266 perplexity_model: ['#model_perplexity_select', 'perplexity_model', false, true],
267267 groq_model: ['#model_groq_select', 'groq_model', false, true],
268268 nanogpt_model: ['#model_nanogpt_select', 'nanogpt_model', false, true],
269269 deepseek_model: ['#model_deepseek_select', 'deepseek_model', false, true],
270270 zerooneai_model: ['#model_01ai_select', 'zerooneai_model', false, true],
271271 xai_model: ['#model_xai_select', 'xai_model', false, true],
272272 pollinations_model: ['#model_pollinations_select', 'pollinations_model', false, true],
273273 custom_model: ['#custom_model_id', 'custom_model', false, true],
274274 custom_url: ['#custom_api_url_text', 'custom_url', false, true],
275275 custom_include_body: ['#custom_include_body', 'custom_include_body', false, true],
276276 custom_exclude_body: ['#custom_exclude_body', 'custom_exclude_body', false, true],
277277 custom_include_headers: ['#custom_include_headers', 'custom_include_headers', false, true],
278278 custom_prompt_post_processing: ['#custom_prompt_post_processing', 'custom_prompt_post_processing', false, true],
279279 google_model: ['#model_google_select', 'google_model', false, true],
280280 openai_max_context: ['#openai_max_context', 'openai_max_context', false, false],
281281 openai_max_tokens: ['#openai_max_tokens', 'openai_max_tokens', false, false],
282282 wrap_in_quotes: ['#wrap_in_quotes', 'wrap_in_quotes', true, false],
283283 names_behavior: ['#names_behavior', 'names_behavior', false, false],
284284 send_if_empty: ['#send_if_empty_textarea', 'send_if_empty', false, false],
285285 impersonation_prompt: ['#impersonation_prompt_textarea', 'impersonation_prompt', false, false],
286286 new_chat_prompt: ['#newchat_prompt_textarea', 'new_chat_prompt', false, false],
287287 new_group_chat_prompt: ['#newgroupchat_prompt_textarea', 'new_group_chat_prompt', false, false],
288288 new_example_chat_prompt: ['#newexamplechat_prompt_textarea', 'new_example_chat_prompt', false, false],
289289 continue_nudge_prompt: ['#continue_nudge_prompt_textarea', 'continue_nudge_prompt', false, false],
290290 bias_preset_selected: ['#openai_logit_bias_preset', 'bias_preset_selected', false, false],
291291 reverse_proxy: ['#openai_reverse_proxy', 'reverse_proxy', false, true],
292292 wi_format: ['#wi_format_textarea', 'wi_format', false, false],
293293 scenario_format: ['#scenario_format_textarea', 'scenario_format', false, false],
294294 personality_format: ['#personality_format_textarea', 'personality_format', false, false],
295295 group_nudge_prompt: ['#group_nudge_prompt_textarea', 'group_nudge_prompt', false, false],
296296 stream_openai: ['#stream_toggle', 'stream_openai', true, false],
297297 prompts: ['', 'prompts', false, false],
298298 prompt_order: ['', 'prompt_order', false, false],
299299 api_url_scale: ['#api_url_scale', 'api_url_scale', false, true],
300300 show_external_models: ['#openai_show_external_models', 'show_external_models', true, true],
301301 proxy_password: ['#openai_proxy_password', 'proxy_password', false, true],
302302 assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false, false],
303303 assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false, false],
304304 claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true, false],
305305 use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true, false],
306306 use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true, true],
307307 squash_system_messages: ['#squash_system_messages', 'squash_system_messages', true, false],
308308 image_inlining: ['#openai_image_inlining', 'image_inlining', true, false],
309309 inline_image_quality: ['#openai_inline_image_quality', 'inline_image_quality', false, false],
310310 continue_prefill: ['#continue_prefill', 'continue_prefill', true, false],
311311 continue_postfix: ['#continue_postfix', 'continue_postfix', false, false],
312312 function_calling: ['#openai_function_calling', 'function_calling', true, false],
313313 show_thoughts: ['#openai_show_thoughts', 'show_thoughts', true, false],
314314 reasoning_effort: ['#openai_reasoning_effort', 'reasoning_effort', false, false],
315315 enable_web_search: ['#openai_enable_web_search', 'enable_web_search', true, false],
316316 seed: ['#seed_openai', 'seed', false, false],
317317 n: ['#n_openai', 'n', false, false],
318318 bypass_status_check: ['#openai_bypass_status_check', 'bypass_status_check', true, true],
319319 request_images: ['#openai_request_images', 'request_images', true, false],
320320};
321321
322322const default_settings = {
@@ -399,6 +399,7 @@ const default_settings = {
399399 request_images: false,
400400 seed: -1,
401401 n: 1,
402+ bind_preset_to_connection: true,
402403};
403404
404405const oai_settings = {
@@ -481,6 +482,7 @@ const oai_settings = {
481482 request_images: false,
482483 seed: -1,
483484 n: 1,
485+ bind_preset_to_connection: true,
484486};
485487
486488export let proxies = [
@@ -3395,6 +3397,7 @@ function loadOpenAISettings(data, settings) {
33953397 oai_settings.continue_postfix = settings.continue_postfix ?? default_settings.continue_postfix;
33963398 oai_settings.function_calling = settings.function_calling ?? default_settings.function_calling;
33973399 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;
33983401
33993402 // Migrate from old settings
34003403 if (settings.names_in_completion === true) {
@@ -3511,6 +3514,7 @@ function loadOpenAISettings(data, settings) {
35113514 $('#openai_show_thoughts').prop('checked', oai_settings.show_thoughts);
35123515 $('#openai_enable_web_search').prop('checked', oai_settings.enable_web_search);
35133516 $('#openai_request_images').prop('checked', oai_settings.request_images);
3517+ $('#bind_preset_to_connection').prop('checked', oai_settings.bind_preset_to_connection);
35143518
35153519 $('#openai_reasoning_effort').val(oai_settings.reasoning_effort);
35163520 $(`#openai_reasoning_effort option[value="${oai_settings.reasoning_effort}"]`).prop('selected', true);
@@ -4039,7 +4043,7 @@ async function onExportPresetClick() {
40394043 const preset = structuredClone(openai_settings[openai_setting_names[oai_settings.preset_settings_openai]]);
40404044
40414045 const fieldValues = sensitiveFields.filter(field => preset[field]).map(field => `<b>${field}</b>: <code>${preset[field]}</code>`);
40424046 const shouldConfirm =if (fieldValues.length > 0;) {
40434047 const textHeader = t`Your preset contains proxy and/or custom endpoint settings.`;
40444048 const textMessage = '<div>' + t`Do you want to remove these fields before exporting?` + `</div><br>${DOMPurify.sanitize(fieldValues.join('<br>'))}`;
40454049 const cancelButton = { text: 'Cancel', result: POPUP_RESULT.CANCELLED, appendAtEnd: true };
@@ -4051,9 +4055,22 @@ async function onExportPresetClick() {
40514055 return;
40524056 }
40534057
40544058 if (!shouldConfirm || popupResult === POPUP_RESULT.AFFIRMATIVE) {
40554059 sensitiveFields.forEach(field => delete preset[field]);
40564060 }
4061+ }
4062+
4063+ const exportConnectionTemplate = $(await renderTemplateAsync('exportPreset'));
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+ }
4073+ }
40574074
40584075 await eventSource.emit(event_types.OAI_PRESET_EXPORT_READY, preset);
40594076 const presetJsonString = JSON.stringify(preset, null, 4);
@@ -4200,9 +4217,15 @@ function onSettingsPresetChange() {
42004217 savePreset: saveOpenAIPreset,
42014218 presetNameBefore: presetNameBefore,
42024219 }).finally(r => {
4220+ if (oai_settings.bind_preset_to_connection) {
42034221 $('.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+ }
42044228
4205- for (const [key, [selector, setting, isCheckbox]] of Object.entries(settingsToUpdate)) {
42064229 if (preset[key] !== undefined) {
42074230 if (isCheckbox) {
42084231 updateCheckbox(selector, preset[key]);
@@ -4213,9 +4236,13 @@ function onSettingsPresetChange() {
42134236 }
42144237 }
42154238
4239+ // These cannot be changed via preset if unbound to connection
4240+ if (oai_settings.bind_preset_to_connection) {
42164241 $('#chat_completion_source').trigger('change');
4217- $('#openai_logit_bias_preset').trigger('change');
42184242 $('#openrouter_providers_chat').trigger('change');
4243+ }
4244+
4245+ $('#openai_logit_bias_preset').trigger('change');
42194246
42204247 saveSettingsDebounced();
42214248 eventSource.emit(event_types.OAI_PRESET_CHANGED_AFTER);
@@ -5848,6 +5875,11 @@ export function initOpenAI() {
58485875 saveSettingsDebounced();
58495876 });
58505877
5878+ $('#bind_preset_to_connection').on('input', function () {
5879+ oai_settings.bind_preset_to_connection = !!$(this).prop('checked');
5880+ saveSettingsDebounced();
5881+ });
5882+
58515883 $('#api_button_openai').on('click', onConnectButtonClick);
58525884 $('#openai_reverse_proxy').on('input', onReverseProxyInput);
58535885 $('#model_openai_select').on('change', onModelChange);
public/scripts/templates/exportPreset.html+27 -0
@@ -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>