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. + +
+
+ + +