Move text completion settings to respective module (#4239)

c51ce1d018a946018aeff1673414ba4b04ac20a2

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

Signed
2 files changed, +186 -199Showing whitespace changes
public/script.js+5 -196
@@ -30,10 +30,9 @@ import {
30 generateTextGenWithStreaming,30 generateTextGenWithStreaming,
31 getTextGenGenerationData,31 getTextGenGenerationData,
32 textgen_types,32 textgen_types,
33 getTextGenServer,
34 validateTextGenUrl,
35 parseTextgenLogprobs,33 parseTextgenLogprobs,
36 parseTabbyLogprobs,34 parseTabbyLogprobs,
35 initTextGenSettings,
37} from './scripts/textgen-settings.js';36} from './scripts/textgen-settings.js';
3837
39import {38import {
@@ -206,12 +205,7 @@ import {
206 tag_import_setting,205 tag_import_setting,
207 applyCharacterTagsToMessageDivs,206 applyCharacterTagsToMessageDivs,
208} from './scripts/tags.js';207} from './scripts/tags.js';
209import {208import { initSecrets, readSecretState } from './scripts/secrets.js';
210 SECRET_KEYS,
211 initSecrets,
212 readSecretState,
213 writeSecret,
214} from './scripts/secrets.js';
215import { EventEmitter } from './lib/eventemitter.js';209import { EventEmitter } from './lib/eventemitter.js';
216import { markdownExclusionExt } from './scripts/showdown-exclusion.js';210import { markdownExclusionExt } from './scripts/showdown-exclusion.js';
217import { markdownUnderscoreExt } from './scripts/showdown-underscore.js';211import { markdownUnderscoreExt } from './scripts/showdown-underscore.js';
@@ -227,14 +221,13 @@ import {
227 formatInstructModePrompt,221 formatInstructModePrompt,
228 formatInstructModeExamples,222 formatInstructModeExamples,
229 getInstructStoppingSequences,223 getInstructStoppingSequences,
230 autoSelectInstructPreset,
231 formatInstructModeSystemPrompt,224 formatInstructModeSystemPrompt,
232 selectInstructPreset,225 selectInstructPreset,
233 instruct_presets,226 instruct_presets,
234 selectContextPreset,227 selectContextPreset,
235} from './scripts/instruct-mode.js';228} from './scripts/instruct-mode.js';
236import { initLocales, t } from './scripts/i18n.js';229import { initLocales, t } from './scripts/i18n.js';
237import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js';230import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache } from './scripts/tokenizers.js';
238import {231import {
239 user_avatar,232 user_avatar,
240 getUserAvatars,233 getUserAvatars,
@@ -249,20 +242,7 @@ import {
249import { getBackgrounds, initBackgrounds, loadBackgroundSettings, background_settings } from './scripts/backgrounds.js';242import { getBackgrounds, initBackgrounds, loadBackgroundSettings, background_settings } from './scripts/backgrounds.js';
250import { hideLoader, showLoader } from './scripts/loader.js';243import { hideLoader, showLoader } from './scripts/loader.js';
251import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay.js';244import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay.js';
252import {245import { initTextGenModels } from './scripts/textgen-models.js';
253 loadFeatherlessModels,
254 loadMancerModels,
255 loadOllamaModels,
256 loadTogetherAIModels,
257 loadInfermaticAIModels,
258 loadOpenRouterModels,
259 loadVllmModels,
260 loadAphroditeModels,
261 loadDreamGenModels,
262 initTextGenModels,
263 loadTabbyModels,
264 loadGenericModels,
265} from './scripts/textgen-models.js';
266import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, preserveNeutralChat, restoreNeutralChat, formatCreatorNotes, initChatUtilities, addDOMPurifyHooks } from './scripts/chats.js';246import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, preserveNeutralChat, restoreNeutralChat, formatCreatorNotes, initChatUtilities, addDOMPurifyHooks } from './scripts/chats.js';
267import { getPresetManager, initPresetManager } from './scripts/preset-manager.js';247import { getPresetManager, initPresetManager } from './scripts/preset-manager.js';
268import { evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js';248import { evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js';
@@ -290,7 +270,6 @@ import { applyBrowserFixes } from './scripts/browser-fixes.js';
290import { initServerHistory } from './scripts/server-history.js';270import { initServerHistory } from './scripts/server-history.js';
291import { initSettingsSearch } from './scripts/setting-search.js';271import { initSettingsSearch } from './scripts/setting-search.js';
292import { initBulkEdit } from './scripts/bulk-edit.js';272import { initBulkEdit } from './scripts/bulk-edit.js';
293import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js';
294import { getContext } from './scripts/st-context.js';273import { getContext } from './scripts/st-context.js';
295import { extractReasoningFromData, initReasoning, parseReasoningInSwipes, PromptReasoning, ReasoningHandler, removeReasoningFromString, updateReasoningUI } from './scripts/reasoning.js';274import { extractReasoningFromData, initReasoning, parseReasoningInSwipes, PromptReasoning, ReasoningHandler, removeReasoningFromString, updateReasoningUI } from './scripts/reasoning.js';
296import { accountStorage } from './scripts/util/AccountStorage.js';275import { accountStorage } from './scripts/util/AccountStorage.js';
@@ -880,6 +859,7 @@ async function firstLoadInit() {
880 initDefaultSlashCommands();859 initDefaultSlashCommands();
881 initTextGenModels();860 initTextGenModels();
882 initOpenAI();861 initOpenAI();
862 initTextGenSettings();
883 initKoboldSettings();863 initKoboldSettings();
884 initNovelAISettings();864 initNovelAISettings();
885 initSystemPrompts();865 initSystemPrompts();
@@ -993,145 +973,6 @@ async function getStatusHorde() {
993 return resultCheckStatus();973 return resultCheckStatus();
994}974}
995975
996async function getStatusTextgen() {
997 const url = '/api/backends/text-completions/status';
998
999 const endpoint = getTextGenServer();
1000
1001 if (!endpoint) {
1002 console.warn('No endpoint for status check');
1003 setOnlineStatus('no_connection');
1004 return resultCheckStatus();
1005 }
1006
1007 if ([textgen_types.GENERIC, textgen_types.OOBA].includes(textgen_settings.type) && textgen_settings.bypass_status_check) {
1008 setOnlineStatus(t`Status check bypassed`);
1009 return resultCheckStatus();
1010 }
1011
1012 try {
1013 const response = await fetch(url, {
1014 method: 'POST',
1015 headers: getRequestHeaders(),
1016 body: JSON.stringify({
1017 api_server: endpoint,
1018 api_type: textgen_settings.type,
1019 }),
1020 signal: abortStatusCheck.signal,
1021 });
1022
1023 const data = await response.json();
1024
1025 if (textgen_settings.type === textgen_types.MANCER) {
1026 loadMancerModels(data?.data);
1027 setOnlineStatus(textgen_settings.mancer_model);
1028 } else if (textgen_settings.type === textgen_types.TOGETHERAI) {
1029 loadTogetherAIModels(data?.data);
1030 setOnlineStatus(textgen_settings.togetherai_model);
1031 } else if (textgen_settings.type === textgen_types.OLLAMA) {
1032 loadOllamaModels(data?.data);
1033 setOnlineStatus(textgen_settings.ollama_model || t`Connected`);
1034 } else if (textgen_settings.type === textgen_types.INFERMATICAI) {
1035 loadInfermaticAIModels(data?.data);
1036 setOnlineStatus(textgen_settings.infermaticai_model);
1037 } else if (textgen_settings.type === textgen_types.DREAMGEN) {
1038 loadDreamGenModels(data?.data);
1039 setOnlineStatus(textgen_settings.dreamgen_model);
1040 } else if (textgen_settings.type === textgen_types.OPENROUTER) {
1041 loadOpenRouterModels(data?.data);
1042 setOnlineStatus(textgen_settings.openrouter_model);
1043 } else if (textgen_settings.type === textgen_types.VLLM) {
1044 loadVllmModels(data?.data);
1045 setOnlineStatus(textgen_settings.vllm_model);
1046 } else if (textgen_settings.type === textgen_types.APHRODITE) {
1047 loadAphroditeModels(data?.data);
1048 setOnlineStatus(textgen_settings.aphrodite_model);
1049 } else if (textgen_settings.type === textgen_types.FEATHERLESS) {
1050 loadFeatherlessModels(data?.data);
1051 setOnlineStatus(textgen_settings.featherless_model);
1052 } else if (textgen_settings.type === textgen_types.TABBY) {
1053 loadTabbyModels(data?.data);
1054 setOnlineStatus(textgen_settings.tabby_model || data?.result);
1055 } else if (textgen_settings.type === textgen_types.GENERIC) {
1056 loadGenericModels(data?.data);
1057 setOnlineStatus(textgen_settings.generic_model || data?.result || t`Connected`);
1058 } else {
1059 setOnlineStatus(data?.result);
1060 }
1061
1062 if (!online_status) {
1063 setOnlineStatus('no_connection');
1064 }
1065
1066 power_user.chat_template_hash = '';
1067
1068 // Determine instruct mode preset
1069 const autoSelected = autoSelectInstructPreset(online_status);
1070
1071 const supportsTokenization = response.headers.get('x-supports-tokenization') === 'true';
1072 supportsTokenization ? sessionStorage.setItem(TOKENIZER_SUPPORTED_KEY, 'true') : sessionStorage.removeItem(TOKENIZER_SUPPORTED_KEY);
1073
1074 const wantsInstructDerivation = !autoSelected && (power_user.instruct.enabled && power_user.instruct_derived);
1075 const wantsContextDerivation = !autoSelected && power_user.context_derived;
1076 const wantsContextSize = power_user.context_size_derived;
1077 const supportsChatTemplate = [textgen_types.KOBOLDCPP, textgen_types.LLAMACPP].includes(textgen_settings.type);
1078
1079 if (supportsChatTemplate && (wantsInstructDerivation || wantsContextDerivation || wantsContextSize)) {
1080 const response = await fetch('/api/backends/text-completions/props', {
1081 method: 'POST',
1082 headers: getRequestHeaders(),
1083 body: JSON.stringify({
1084 api_server: endpoint,
1085 api_type: textgen_settings.type,
1086 }),
1087 });
1088
1089 if (response.ok) {
1090 const data = await response.json();
1091 if (data) {
1092 const { chat_template, chat_template_hash } = data;
1093 power_user.chat_template_hash = chat_template_hash;
1094
1095 if (wantsContextSize && 'default_generation_settings' in data) {
1096 const backend_max_context = data['default_generation_settings']['n_ctx'];
1097 const old_value = max_context;
1098 if (max_context !== backend_max_context) {
1099 setGenerationParamsFromPreset({ max_length: backend_max_context });
1100 }
1101 if (old_value !== max_context) {
1102 console.log(`Auto-switched max context from ${old_value} to ${max_context}`);
1103 toastr.info(`${old_value} ⇒ ${max_context}`, 'Context Size Changed');
1104 }
1105 }
1106 console.log(`We have chat template ${chat_template.split('\n')[0]}...`);
1107 const { context, instruct } = await deriveTemplatesFromChatTemplate(chat_template, chat_template_hash);
1108 if (wantsContextDerivation && context) {
1109 selectContextPreset(context, { isAuto: true });
1110 }
1111 if (wantsInstructDerivation && power_user.instruct.enabled && instruct) {
1112 selectInstructPreset(instruct, { isAuto: true });
1113 }
1114 }
1115 }
1116 }
1117
1118 // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.
1119 if (online_status === 'no_connection' && data.response) {
1120 toastr.error(data.response, t`API Error`, { timeOut: 5000, preventDuplicates: true });
1121 }
1122 } catch (err) {
1123 if (err instanceof AbortReason) {
1124 console.info('Status check aborted.', err.reason);
1125 } else {
1126 console.error('Error getting status', err);
1127
1128 }
1129 setOnlineStatus('no_connection');
1130 }
1131
1132 return resultCheckStatus();
1133}
1134
1135export function startStatusLoading() {976export function startStatusLoading() {
1136 $('.api_loading').show();977 $('.api_loading').show();
1137 $('.api_button').addClass('disabled');978 $('.api_button').addClass('disabled');
@@ -10636,38 +10477,6 @@ jQuery(async function () {
10636 }10477 }
10637 });10478 });
1063810479
10639 $('#api_button_textgenerationwebui').on('click', async function (e) {
10640 const keys = [
10641 { id: 'api_key_mancer', secret: SECRET_KEYS.MANCER },
10642 { id: 'api_key_vllm', secret: SECRET_KEYS.VLLM },
10643 { id: 'api_key_aphrodite', secret: SECRET_KEYS.APHRODITE },
10644 { id: 'api_key_tabby', secret: SECRET_KEYS.TABBY },
10645 { id: 'api_key_togetherai', secret: SECRET_KEYS.TOGETHERAI },
10646 { id: 'api_key_ooba', secret: SECRET_KEYS.OOBA },
10647 { id: 'api_key_infermaticai', secret: SECRET_KEYS.INFERMATICAI },
10648 { id: 'api_key_dreamgen', secret: SECRET_KEYS.DREAMGEN },
10649 { id: 'api_key_openrouter-tg', secret: SECRET_KEYS.OPENROUTER },
10650 { id: 'api_key_koboldcpp', secret: SECRET_KEYS.KOBOLDCPP },
10651 { id: 'api_key_llamacpp', secret: SECRET_KEYS.LLAMACPP },
10652 { id: 'api_key_featherless', secret: SECRET_KEYS.FEATHERLESS },
10653 { id: 'api_key_huggingface', secret: SECRET_KEYS.HUGGINGFACE },
10654 { id: 'api_key_generic', secret: SECRET_KEYS.GENERIC },
10655 ];
10656
10657 for (const key of keys) {
10658 const keyValue = String($(`#${key.id}`).val()).trim();
10659 if (keyValue.length) {
10660 await writeSecret(key.secret, keyValue);
10661 }
10662 }
10663
10664 validateTextGenUrl();
10665 startStatusLoading();
10666 main_api = 'textgenerationwebui';
10667 saveSettingsDebounced();
10668 getStatusTextgen();
10669 });
10670
1067110480
10672 const button = $('#options_button');10481 const button = $('#options_button');
10673 const menu = $('#options');10482 const menu = $('#options');
public/scripts/textgen-settings.js+181 -3
@@ -1,22 +1,30 @@
1import {1import {
2 abortStatusCheck,
2 eventSource,3 eventSource,
3 event_types,4 event_types,
4 getRequestHeaders,5 getRequestHeaders,
5 getStoppingStrings,6 getStoppingStrings,
6 main_api,7 main_api,
7 max_context,8 max_context,
9 online_status,
10 resultCheckStatus,
8 saveSettingsDebounced,11 saveSettingsDebounced,
9 setGenerationParamsFromPreset,12 setGenerationParamsFromPreset,
10 setOnlineStatus,13 setOnlineStatus,
14 startStatusLoading,
11 substituteParams,15 substituteParams,
12} from '../script.js';16} from '../script.js';
17import { deriveTemplatesFromChatTemplate } from './chat-templates.js';
13import { t } from './i18n.js';18import { t } from './i18n.js';
19import { autoSelectInstructPreset, selectContextPreset, selectInstructPreset } from './instruct-mode.js';
14import { BIAS_CACHE, createNewLogitBiasEntry, displayLogitBias, getLogitBiasListResult } from './logit-bias.js';20import { BIAS_CACHE, createNewLogitBiasEntry, displayLogitBias, getLogitBiasListResult } from './logit-bias.js';
1521
16import { power_user, registerDebugFunction } from './power-user.js';22import { power_user, registerDebugFunction } from './power-user.js';
23import { SECRET_KEYS, writeSecret } from './secrets.js';
17import { getEventSourceStream } from './sse-stream.js';24import { getEventSourceStream } from './sse-stream.js';
18import { getCurrentDreamGenModelTokenizer, getCurrentOpenRouterModelTokenizer } from './textgen-models.js';25import { getCurrentDreamGenModelTokenizer, getCurrentOpenRouterModelTokenizer, loadAphroditeModels, loadDreamGenModels, loadFeatherlessModels, loadGenericModels, loadInfermaticAIModels, loadMancerModels, loadOllamaModels, loadOpenRouterModels, loadTabbyModels, loadTogetherAIModels, loadVllmModels } from './textgen-models.js';
19import { ENCODE_TOKENIZERS, TEXTGEN_TOKENIZERS, getTextTokens, tokenizers } from './tokenizers.js';26import { ENCODE_TOKENIZERS, TEXTGEN_TOKENIZERS, TOKENIZER_SUPPORTED_KEY, getTextTokens, tokenizers } from './tokenizers.js';
27import { AbortReason } from './util/AbortReason.js';
20import { getSortableDelay, onlyUnique, arraysEqual } from './utils.js';28import { getSortableDelay, onlyUnique, arraysEqual } from './utils.js';
2129
22export const textgen_types = {30export const textgen_types = {
@@ -621,7 +629,146 @@ function sortAphroditeItemsByOrder(orderArray) {
621 });629 });
622}630}
623631
624jQuery(function () {632async function getStatusTextgen() {
633 const url = '/api/backends/text-completions/status';
634
635 const endpoint = getTextGenServer();
636
637 if (!endpoint) {
638 console.warn('No endpoint for status check');
639 setOnlineStatus('no_connection');
640 return resultCheckStatus();
641 }
642
643 if ([textgen_types.GENERIC, textgen_types.OOBA].includes(settings.type) && settings.bypass_status_check) {
644 setOnlineStatus(t`Status check bypassed`);
645 return resultCheckStatus();
646 }
647
648 try {
649 const response = await fetch(url, {
650 method: 'POST',
651 headers: getRequestHeaders(),
652 body: JSON.stringify({
653 api_server: endpoint,
654 api_type: settings.type,
655 }),
656 signal: abortStatusCheck.signal,
657 });
658
659 const data = await response.json();
660
661 if (settings.type === textgen_types.MANCER) {
662 loadMancerModels(data?.data);
663 setOnlineStatus(settings.mancer_model);
664 } else if (settings.type === textgen_types.TOGETHERAI) {
665 loadTogetherAIModels(data?.data);
666 setOnlineStatus(settings.togetherai_model);
667 } else if (settings.type === textgen_types.OLLAMA) {
668 loadOllamaModels(data?.data);
669 setOnlineStatus(settings.ollama_model || t`Connected`);
670 } else if (settings.type === textgen_types.INFERMATICAI) {
671 loadInfermaticAIModels(data?.data);
672 setOnlineStatus(settings.infermaticai_model);
673 } else if (settings.type === textgen_types.DREAMGEN) {
674 loadDreamGenModels(data?.data);
675 setOnlineStatus(settings.dreamgen_model);
676 } else if (settings.type === textgen_types.OPENROUTER) {
677 loadOpenRouterModels(data?.data);
678 setOnlineStatus(settings.openrouter_model);
679 } else if (settings.type === textgen_types.VLLM) {
680 loadVllmModels(data?.data);
681 setOnlineStatus(settings.vllm_model);
682 } else if (settings.type === textgen_types.APHRODITE) {
683 loadAphroditeModels(data?.data);
684 setOnlineStatus(settings.aphrodite_model);
685 } else if (settings.type === textgen_types.FEATHERLESS) {
686 loadFeatherlessModels(data?.data);
687 setOnlineStatus(settings.featherless_model);
688 } else if (settings.type === textgen_types.TABBY) {
689 loadTabbyModels(data?.data);
690 setOnlineStatus(settings.tabby_model || data?.result);
691 } else if (settings.type === textgen_types.GENERIC) {
692 loadGenericModels(data?.data);
693 setOnlineStatus(settings.generic_model || data?.result || t`Connected`);
694 } else {
695 setOnlineStatus(data?.result);
696 }
697
698 if (!online_status) {
699 setOnlineStatus('no_connection');
700 }
701
702 power_user.chat_template_hash = '';
703
704 // Determine instruct mode preset
705 const autoSelected = autoSelectInstructPreset(online_status);
706
707 const supportsTokenization = response.headers.get('x-supports-tokenization') === 'true';
708 supportsTokenization ? sessionStorage.setItem(TOKENIZER_SUPPORTED_KEY, 'true') : sessionStorage.removeItem(TOKENIZER_SUPPORTED_KEY);
709
710 const wantsInstructDerivation = !autoSelected && (power_user.instruct.enabled && power_user.instruct_derived);
711 const wantsContextDerivation = !autoSelected && power_user.context_derived;
712 const wantsContextSize = power_user.context_size_derived;
713 const supportsChatTemplate = [textgen_types.KOBOLDCPP, textgen_types.LLAMACPP].includes(settings.type);
714
715 if (supportsChatTemplate && (wantsInstructDerivation || wantsContextDerivation || wantsContextSize)) {
716 const response = await fetch('/api/backends/text-completions/props', {
717 method: 'POST',
718 headers: getRequestHeaders(),
719 body: JSON.stringify({
720 api_server: endpoint,
721 api_type: settings.type,
722 }),
723 });
724
725 if (response.ok) {
726 const data = await response.json();
727 if (data) {
728 const { chat_template, chat_template_hash } = data;
729 power_user.chat_template_hash = chat_template_hash;
730
731 if (wantsContextSize && 'default_generation_settings' in data) {
732 const backend_max_context = data['default_generation_settings']['n_ctx'];
733 const old_value = max_context;
734 if (max_context !== backend_max_context) {
735 setGenerationParamsFromPreset({ max_length: backend_max_context });
736 }
737 if (old_value !== max_context) {
738 console.log(`Auto-switched max context from ${old_value} to ${max_context}`);
739 toastr.info(`${old_value} ⇒ ${max_context}`, 'Context Size Changed');
740 }
741 }
742 console.log(`We have chat template ${chat_template.split('\n')[0]}...`);
743 const { context, instruct } = await deriveTemplatesFromChatTemplate(chat_template, chat_template_hash);
744 if (wantsContextDerivation && context) {
745 selectContextPreset(context, { isAuto: true });
746 }
747 if (wantsInstructDerivation && power_user.instruct.enabled && instruct) {
748 selectInstructPreset(instruct, { isAuto: true });
749 }
750 }
751 }
752 }
753
754 // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.
755 if (online_status === 'no_connection' && data.response) {
756 toastr.error(data.response, t`API Error`, { timeOut: 5000, preventDuplicates: true });
757 }
758 } catch (err) {
759 if (err instanceof AbortReason) {
760 console.info('Status check aborted.', err.reason);
761 } else {
762 console.error('Error getting status', err);
763
764 }
765 setOnlineStatus('no_connection');
766 }
767
768 return resultCheckStatus();
769}
770
771export function initTextGenSettings() {
625 $('#send_banned_tokens_textgenerationwebui').on('change', function () {772 $('#send_banned_tokens_textgenerationwebui').on('change', function () {
626 const checked = !!$(this).prop('checked');773 const checked = !!$(this).prop('checked');
627 toggleBannedStringsKillSwitch(checked,774 toggleBannedStringsKillSwitch(checked,
@@ -877,7 +1024,38 @@ jQuery(function () {
8771024
878 saveSettingsDebounced();1025 saveSettingsDebounced();
879 });1026 });
1027
1028 $('#api_button_textgenerationwebui').on('click', async function (e) {
1029 const keys = [
1030 { id: 'api_key_mancer', secret: SECRET_KEYS.MANCER },
1031 { id: 'api_key_vllm', secret: SECRET_KEYS.VLLM },
1032 { id: 'api_key_aphrodite', secret: SECRET_KEYS.APHRODITE },
1033 { id: 'api_key_tabby', secret: SECRET_KEYS.TABBY },
1034 { id: 'api_key_togetherai', secret: SECRET_KEYS.TOGETHERAI },
1035 { id: 'api_key_ooba', secret: SECRET_KEYS.OOBA },
1036 { id: 'api_key_infermaticai', secret: SECRET_KEYS.INFERMATICAI },
1037 { id: 'api_key_dreamgen', secret: SECRET_KEYS.DREAMGEN },
1038 { id: 'api_key_openrouter-tg', secret: SECRET_KEYS.OPENROUTER },
1039 { id: 'api_key_koboldcpp', secret: SECRET_KEYS.KOBOLDCPP },
1040 { id: 'api_key_llamacpp', secret: SECRET_KEYS.LLAMACPP },
1041 { id: 'api_key_featherless', secret: SECRET_KEYS.FEATHERLESS },
1042 { id: 'api_key_huggingface', secret: SECRET_KEYS.HUGGINGFACE },
1043 { id: 'api_key_generic', secret: SECRET_KEYS.GENERIC },
1044 ];
1045
1046 for (const key of keys) {
1047 const keyValue = String($(`#${key.id}`).val()).trim();
1048 if (keyValue.length) {
1049 await writeSecret(key.secret, keyValue);
1050 }
1051 }
1052
1053 validateTextGenUrl();
1054 startStatusLoading();
1055 saveSettingsDebounced();
1056 getStatusTextgen();
880 });1057 });
1058}
8811059
882function showTypeSpecificControls(type) {1060function showTypeSpecificControls(type) {
883 $('[data-tg-type]').each(function () {1061 $('[data-tg-type]').each(function () {