diff --git a/public/css/toggle-dependent.css b/public/css/toggle-dependent.css
index d582cbbd8..86e00acb2 100644
--- a/public/css/toggle-dependent.css
+++ b/public/css/toggle-dependent.css
@@ -498,3 +498,15 @@ label[for="trim_spaces"]:not(:has(input:checked)) small {
#banned_tokens_block_ooba:not(:has(#send_banned_tokens_textgenerationwebui:checked)) #banned_tokens_controls_ooba {
filter: brightness(0.5);
}
+
+#bind_preset_to_connection:checked ~ .toggleOff {
+ display: none;
+}
+
+#bind_preset_to_connection:not(:checked) ~ .toggleOn {
+ display: none;
+}
+
+label[for="bind_preset_to_connection"]:has(input:checked) {
+ color: var(--active);
+}
diff --git a/public/index.html b/public/index.html
index 4deb5fbdd..f7359f742 100644
--- a/public/index.html
+++ b/public/index.html
@@ -176,6 +176,11 @@
+
diff --git a/public/scripts/openai.js b/public/scripts/openai.js
index fccfd7846..1fdd0d9d5 100644
--- a/public/scripts/openai.js
+++ b/public/scripts/openai.js
@@ -71,7 +71,7 @@ import { SlashCommand } from './slash-commands/SlashCommand.js';
import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';
import { renderTemplateAsync } from './templates.js';
import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
-import { Popup, POPUP_RESULT } from './popup.js';
+import { Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
import { t } from './i18n.js';
import { ToolManager } from './tool-calling.js';
import { accountStorage } from './util/AccountStorage.js';
@@ -236,87 +236,87 @@ const sensitiveFields = [
];
/**
- * preset_name -> [selector, setting_name, is_checkbox]
- * @type {Record
}
+ * preset_name -> [selector, setting_name, is_checkbox, is_connection]
+ * @type {Record}
*/
export const settingsToUpdate = {
- chat_completion_source: ['#chat_completion_source', 'chat_completion_source', false],
- temperature: ['#temp_openai', 'temp_openai', false],
- frequency_penalty: ['#freq_pen_openai', 'freq_pen_openai', false],
- presence_penalty: ['#pres_pen_openai', 'pres_pen_openai', false],
- top_p: ['#top_p_openai', 'top_p_openai', false],
- top_k: ['#top_k_openai', 'top_k_openai', false],
- top_a: ['#top_a_openai', 'top_a_openai', false],
- min_p: ['#min_p_openai', 'min_p_openai', false],
- repetition_penalty: ['#repetition_penalty_openai', 'repetition_penalty_openai', false],
- max_context_unlocked: ['#oai_max_context_unlocked', 'max_context_unlocked', true],
- openai_model: ['#model_openai_select', 'openai_model', false],
- claude_model: ['#model_claude_select', 'claude_model', false],
- windowai_model: ['#model_windowai_select', 'windowai_model', false],
- openrouter_model: ['#model_openrouter_select', 'openrouter_model', false],
- openrouter_use_fallback: ['#openrouter_use_fallback', 'openrouter_use_fallback', true],
- openrouter_group_models: ['#openrouter_group_models', 'openrouter_group_models', false],
- openrouter_sort_models: ['#openrouter_sort_models', 'openrouter_sort_models', false],
- openrouter_providers: ['#openrouter_providers_chat', 'openrouter_providers', false],
- openrouter_allow_fallbacks: ['#openrouter_allow_fallbacks', 'openrouter_allow_fallbacks', true],
- openrouter_middleout: ['#openrouter_middleout', 'openrouter_middleout', false],
- ai21_model: ['#model_ai21_select', 'ai21_model', false],
- mistralai_model: ['#model_mistralai_select', 'mistralai_model', false],
- cohere_model: ['#model_cohere_select', 'cohere_model', false],
- perplexity_model: ['#model_perplexity_select', 'perplexity_model', false],
- groq_model: ['#model_groq_select', 'groq_model', false],
- nanogpt_model: ['#model_nanogpt_select', 'nanogpt_model', false],
- deepseek_model: ['#model_deepseek_select', 'deepseek_model', false],
- zerooneai_model: ['#model_01ai_select', 'zerooneai_model', false],
- xai_model: ['#model_xai_select', 'xai_model', false],
- pollinations_model: ['#model_pollinations_select', 'pollinations_model', false],
- custom_model: ['#custom_model_id', 'custom_model', false],
- custom_url: ['#custom_api_url_text', 'custom_url', false],
- custom_include_body: ['#custom_include_body', 'custom_include_body', false],
- custom_exclude_body: ['#custom_exclude_body', 'custom_exclude_body', false],
- custom_include_headers: ['#custom_include_headers', 'custom_include_headers', false],
- custom_prompt_post_processing: ['#custom_prompt_post_processing', 'custom_prompt_post_processing', false],
- google_model: ['#model_google_select', 'google_model', false],
- openai_max_context: ['#openai_max_context', 'openai_max_context', false],
- openai_max_tokens: ['#openai_max_tokens', 'openai_max_tokens', false],
- wrap_in_quotes: ['#wrap_in_quotes', 'wrap_in_quotes', true],
- names_behavior: ['#names_behavior', 'names_behavior', false],
- send_if_empty: ['#send_if_empty_textarea', 'send_if_empty', false],
- impersonation_prompt: ['#impersonation_prompt_textarea', 'impersonation_prompt', false],
- new_chat_prompt: ['#newchat_prompt_textarea', 'new_chat_prompt', false],
- new_group_chat_prompt: ['#newgroupchat_prompt_textarea', 'new_group_chat_prompt', false],
- new_example_chat_prompt: ['#newexamplechat_prompt_textarea', 'new_example_chat_prompt', false],
- continue_nudge_prompt: ['#continue_nudge_prompt_textarea', 'continue_nudge_prompt', false],
- bias_preset_selected: ['#openai_logit_bias_preset', 'bias_preset_selected', false],
- reverse_proxy: ['#openai_reverse_proxy', 'reverse_proxy', false],
- wi_format: ['#wi_format_textarea', 'wi_format', false],
- scenario_format: ['#scenario_format_textarea', 'scenario_format', false],
- personality_format: ['#personality_format_textarea', 'personality_format', false],
- group_nudge_prompt: ['#group_nudge_prompt_textarea', 'group_nudge_prompt', false],
- stream_openai: ['#stream_toggle', 'stream_openai', true],
- prompts: ['', 'prompts', false],
- prompt_order: ['', 'prompt_order', false],
- api_url_scale: ['#api_url_scale', 'api_url_scale', false],
- show_external_models: ['#openai_show_external_models', 'show_external_models', true],
- proxy_password: ['#openai_proxy_password', 'proxy_password', false],
- assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],
- assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false],
- claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true],
- use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true],
- use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],
- squash_system_messages: ['#squash_system_messages', 'squash_system_messages', true],
- image_inlining: ['#openai_image_inlining', 'image_inlining', true],
- inline_image_quality: ['#openai_inline_image_quality', 'inline_image_quality', false],
- continue_prefill: ['#continue_prefill', 'continue_prefill', true],
- continue_postfix: ['#continue_postfix', 'continue_postfix', false],
- function_calling: ['#openai_function_calling', 'function_calling', true],
- show_thoughts: ['#openai_show_thoughts', 'show_thoughts', true],
- reasoning_effort: ['#openai_reasoning_effort', 'reasoning_effort', false],
- enable_web_search: ['#openai_enable_web_search', 'enable_web_search', true],
- seed: ['#seed_openai', 'seed', false],
- n: ['#n_openai', 'n', false],
- bypass_status_check: ['#openai_bypass_status_check', 'bypass_status_check', true],
- request_images: ['#openai_request_images', 'request_images', true],
+ chat_completion_source: ['#chat_completion_source', 'chat_completion_source', false, true],
+ temperature: ['#temp_openai', 'temp_openai', false, false],
+ frequency_penalty: ['#freq_pen_openai', 'freq_pen_openai', false, false],
+ presence_penalty: ['#pres_pen_openai', 'pres_pen_openai', false, false],
+ top_p: ['#top_p_openai', 'top_p_openai', false, false],
+ top_k: ['#top_k_openai', 'top_k_openai', false, false],
+ top_a: ['#top_a_openai', 'top_a_openai', false, false],
+ min_p: ['#min_p_openai', 'min_p_openai', false, false],
+ repetition_penalty: ['#repetition_penalty_openai', 'repetition_penalty_openai', false, false],
+ max_context_unlocked: ['#oai_max_context_unlocked', 'max_context_unlocked', true, false],
+ openai_model: ['#model_openai_select', 'openai_model', false, true],
+ claude_model: ['#model_claude_select', 'claude_model', false, true],
+ windowai_model: ['#model_windowai_select', 'windowai_model', false, true],
+ openrouter_model: ['#model_openrouter_select', 'openrouter_model', false, true],
+ openrouter_use_fallback: ['#openrouter_use_fallback', 'openrouter_use_fallback', true, true],
+ openrouter_group_models: ['#openrouter_group_models', 'openrouter_group_models', false, true],
+ openrouter_sort_models: ['#openrouter_sort_models', 'openrouter_sort_models', false, true],
+ openrouter_providers: ['#openrouter_providers_chat', 'openrouter_providers', false, true],
+ openrouter_allow_fallbacks: ['#openrouter_allow_fallbacks', 'openrouter_allow_fallbacks', true, true],
+ openrouter_middleout: ['#openrouter_middleout', 'openrouter_middleout', false, true],
+ ai21_model: ['#model_ai21_select', 'ai21_model', false, true],
+ mistralai_model: ['#model_mistralai_select', 'mistralai_model', false, true],
+ cohere_model: ['#model_cohere_select', 'cohere_model', false, true],
+ perplexity_model: ['#model_perplexity_select', 'perplexity_model', false, true],
+ groq_model: ['#model_groq_select', 'groq_model', false, true],
+ nanogpt_model: ['#model_nanogpt_select', 'nanogpt_model', false, true],
+ deepseek_model: ['#model_deepseek_select', 'deepseek_model', false, true],
+ zerooneai_model: ['#model_01ai_select', 'zerooneai_model', false, true],
+ xai_model: ['#model_xai_select', 'xai_model', false, true],
+ pollinations_model: ['#model_pollinations_select', 'pollinations_model', false, true],
+ custom_model: ['#custom_model_id', 'custom_model', false, true],
+ custom_url: ['#custom_api_url_text', 'custom_url', false, true],
+ custom_include_body: ['#custom_include_body', 'custom_include_body', false, true],
+ custom_exclude_body: ['#custom_exclude_body', 'custom_exclude_body', false, true],
+ custom_include_headers: ['#custom_include_headers', 'custom_include_headers', false, true],
+ custom_prompt_post_processing: ['#custom_prompt_post_processing', 'custom_prompt_post_processing', false, true],
+ google_model: ['#model_google_select', 'google_model', false, true],
+ openai_max_context: ['#openai_max_context', 'openai_max_context', false, false],
+ openai_max_tokens: ['#openai_max_tokens', 'openai_max_tokens', false, false],
+ wrap_in_quotes: ['#wrap_in_quotes', 'wrap_in_quotes', true, false],
+ names_behavior: ['#names_behavior', 'names_behavior', false, false],
+ send_if_empty: ['#send_if_empty_textarea', 'send_if_empty', false, false],
+ impersonation_prompt: ['#impersonation_prompt_textarea', 'impersonation_prompt', false, false],
+ new_chat_prompt: ['#newchat_prompt_textarea', 'new_chat_prompt', false, false],
+ new_group_chat_prompt: ['#newgroupchat_prompt_textarea', 'new_group_chat_prompt', false, false],
+ new_example_chat_prompt: ['#newexamplechat_prompt_textarea', 'new_example_chat_prompt', false, false],
+ continue_nudge_prompt: ['#continue_nudge_prompt_textarea', 'continue_nudge_prompt', false, false],
+ bias_preset_selected: ['#openai_logit_bias_preset', 'bias_preset_selected', false, false],
+ reverse_proxy: ['#openai_reverse_proxy', 'reverse_proxy', false, true],
+ wi_format: ['#wi_format_textarea', 'wi_format', false, false],
+ scenario_format: ['#scenario_format_textarea', 'scenario_format', false, false],
+ personality_format: ['#personality_format_textarea', 'personality_format', false, false],
+ group_nudge_prompt: ['#group_nudge_prompt_textarea', 'group_nudge_prompt', false, false],
+ stream_openai: ['#stream_toggle', 'stream_openai', true, false],
+ prompts: ['', 'prompts', false, false],
+ prompt_order: ['', 'prompt_order', false, false],
+ api_url_scale: ['#api_url_scale', 'api_url_scale', false, true],
+ show_external_models: ['#openai_show_external_models', 'show_external_models', true, true],
+ proxy_password: ['#openai_proxy_password', 'proxy_password', false, true],
+ assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false, false],
+ assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false, false],
+ claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true, false],
+ use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true, false],
+ use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true, true],
+ squash_system_messages: ['#squash_system_messages', 'squash_system_messages', true, false],
+ image_inlining: ['#openai_image_inlining', 'image_inlining', true, false],
+ inline_image_quality: ['#openai_inline_image_quality', 'inline_image_quality', false, false],
+ continue_prefill: ['#continue_prefill', 'continue_prefill', true, false],
+ continue_postfix: ['#continue_postfix', 'continue_postfix', false, false],
+ function_calling: ['#openai_function_calling', 'function_calling', true, false],
+ show_thoughts: ['#openai_show_thoughts', 'show_thoughts', true, false],
+ reasoning_effort: ['#openai_reasoning_effort', 'reasoning_effort', false, false],
+ enable_web_search: ['#openai_enable_web_search', 'enable_web_search', true, false],
+ seed: ['#seed_openai', 'seed', false, false],
+ n: ['#n_openai', 'n', false, false],
+ bypass_status_check: ['#openai_bypass_status_check', 'bypass_status_check', true, true],
+ request_images: ['#openai_request_images', 'request_images', true, false],
};
const default_settings = {
@@ -399,6 +399,7 @@ const default_settings = {
request_images: false,
seed: -1,
n: 1,
+ bind_preset_to_connection: true,
};
const oai_settings = {
@@ -481,6 +482,7 @@ const oai_settings = {
request_images: false,
seed: -1,
n: 1,
+ bind_preset_to_connection: true,
};
export let proxies = [
@@ -3395,6 +3397,7 @@ function loadOpenAISettings(data, settings) {
oai_settings.continue_postfix = settings.continue_postfix ?? default_settings.continue_postfix;
oai_settings.function_calling = settings.function_calling ?? default_settings.function_calling;
oai_settings.openrouter_providers = settings.openrouter_providers ?? default_settings.openrouter_providers;
+ oai_settings.bind_preset_to_connection = settings.bind_preset_to_connection ?? default_settings.bind_preset_to_connection;
// Migrate from old settings
if (settings.names_in_completion === true) {
@@ -3511,6 +3514,7 @@ function loadOpenAISettings(data, settings) {
$('#openai_show_thoughts').prop('checked', oai_settings.show_thoughts);
$('#openai_enable_web_search').prop('checked', oai_settings.enable_web_search);
$('#openai_request_images').prop('checked', oai_settings.request_images);
+ $('#bind_preset_to_connection').prop('checked', oai_settings.bind_preset_to_connection);
$('#openai_reasoning_effort').val(oai_settings.reasoning_effort);
$(`#openai_reasoning_effort option[value="${oai_settings.reasoning_effort}"]`).prop('selected', true);
@@ -4039,20 +4043,33 @@ async function onExportPresetClick() {
const preset = structuredClone(openai_settings[openai_setting_names[oai_settings.preset_settings_openai]]);
const fieldValues = sensitiveFields.filter(field => preset[field]).map(field => `${field}: ${preset[field]}
`);
- const shouldConfirm = fieldValues.length > 0;
- const textHeader = t`Your preset contains proxy and/or custom endpoint settings.`;
- const textMessage = '' + t`Do you want to remove these fields before exporting?` + `
${DOMPurify.sanitize(fieldValues.join('
'))}`;
- const cancelButton = { text: 'Cancel', result: POPUP_RESULT.CANCELLED, appendAtEnd: true };
- const popupOptions = { customButtons: [cancelButton] };
- const popupResult = await Popup.show.confirm(textHeader, textMessage, popupOptions);
+ if (fieldValues.length > 0) {
+ const textHeader = t`Your preset contains proxy and/or custom endpoint settings.`;
+ const textMessage = '' + t`Do you want to remove these fields before exporting?` + `
${DOMPurify.sanitize(fieldValues.join('
'))}`;
+ const cancelButton = { text: 'Cancel', result: POPUP_RESULT.CANCELLED, appendAtEnd: true };
+ const popupOptions = { customButtons: [cancelButton] };
+ const popupResult = await Popup.show.confirm(textHeader, textMessage, popupOptions);
- if (popupResult === POPUP_RESULT.CANCELLED) {
- console.log('Export cancelled by user');
- return;
+ if (popupResult === POPUP_RESULT.CANCELLED) {
+ console.log('Export cancelled by user');
+ return;
+ }
+
+ if (popupResult === POPUP_RESULT.AFFIRMATIVE) {
+ sensitiveFields.forEach(field => delete preset[field]);
+ }
}
- if (!shouldConfirm || popupResult === POPUP_RESULT.AFFIRMATIVE) {
- sensitiveFields.forEach(field => delete preset[field]);
+ const exportConnectionTemplate = $(await renderTemplateAsync('exportPreset'));
+ await new Popup(exportConnectionTemplate, POPUP_TYPE.TEXT).show();
+
+ const removeConnectionData = exportConnectionTemplate.find('input[name="export_connection_data"]:checked').val() === 'false';
+ if (removeConnectionData) {
+ for (const [, [, settingName, , isConnection]] of Object.entries(settingsToUpdate)) {
+ if (isConnection) {
+ delete preset[settingName];
+ }
+ }
}
await eventSource.emit(event_types.OAI_PRESET_EXPORT_READY, preset);
@@ -4200,9 +4217,15 @@ function onSettingsPresetChange() {
savePreset: saveOpenAIPreset,
presetNameBefore: presetNameBefore,
}).finally(r => {
- $('.model_custom_select').empty();
+ if (oai_settings.bind_preset_to_connection) {
+ $('.model_custom_select').empty();
+ }
+
+ for (const [key, [selector, setting, isCheckbox, isConnection]] of Object.entries(settingsToUpdate)) {
+ if (isConnection && !oai_settings.bind_preset_to_connection) {
+ continue;
+ }
- for (const [key, [selector, setting, isCheckbox]] of Object.entries(settingsToUpdate)) {
if (preset[key] !== undefined) {
if (isCheckbox) {
updateCheckbox(selector, preset[key]);
@@ -4213,9 +4236,13 @@ function onSettingsPresetChange() {
}
}
- $('#chat_completion_source').trigger('change');
+ // These cannot be changed via preset if unbound to connection
+ if (oai_settings.bind_preset_to_connection) {
+ $('#chat_completion_source').trigger('change');
+ $('#openrouter_providers_chat').trigger('change');
+ }
+
$('#openai_logit_bias_preset').trigger('change');
- $('#openrouter_providers_chat').trigger('change');
saveSettingsDebounced();
eventSource.emit(event_types.OAI_PRESET_CHANGED_AFTER);
@@ -5848,6 +5875,11 @@ export function initOpenAI() {
saveSettingsDebounced();
});
+ $('#bind_preset_to_connection').on('input', function () {
+ oai_settings.bind_preset_to_connection = !!$(this).prop('checked');
+ saveSettingsDebounced();
+ });
+
$('#api_button_openai').on('click', onConnectButtonClick);
$('#openai_reverse_proxy').on('input', onReverseProxyInput);
$('#model_openai_select').on('change', onModelChange);
diff --git a/public/scripts/templates/exportPreset.html b/public/scripts/templates/exportPreset.html
new file mode 100644
index 000000000..4d00b572f
--- /dev/null
+++ b/public/scripts/templates/exportPreset.html
@@ -0,0 +1,27 @@
+
+
+ Do you want to export connection data with the preset?
+
+
+
+ This includes the selected source, models, and other preferences set in the API Connections panel.
+
+
+
+ Your stored API keys are never exported.
+
+
+
+
+
+