Chat Completion: Streamline settings and API key handling (#4687) * refactor: streamline CC settings and API key handling * Add types for API settings * Allow keyless custom source connection

55969bc96efc75beb95ac2e9c269d61fd8a0e74c

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

Signed
2 files changed, +44 -364Ignore whitespace
public/global.d.ts+4 -0
@@ -2,12 +2,16 @@ import libs from './lib';
22import getContext from './scripts/st-context';
33import { power_user } from './scripts/power-user';
44import { QuickReplyApi } from './scripts/extensions/quick-reply/api/QuickReplyApi';
5+import { oai_settings } from './scripts/openai';
6+import { textgenerationwebui_settings } from './scripts/textgen-settings';
57
68declare global {
79 // Custom types
810 type InstructSettings = typeof power_user.instruct;
911 type ContextSettings = typeof power_user.context;
1012 type ReasoningSettings = typeof power_user.reasoning;
13+ type ChatCompletionSettings = typeof oai_settings;
14+ type TextCompletionSettings = typeof textgenerationwebui_settings;
1115
1216 // Global namespace modules
1317 interface Window {
public/scripts/openai.js+40 -364
@@ -387,7 +387,7 @@ const default_settings = {
387387 electronhub_group_models: false,
388388 nanogpt_model: 'gpt-4o-mini',
389389 deepseek_model: 'deepseek-chat',
390390 aimlapi_model: 'gptchatgpt-4o-mini-2024-07-18latest',
391391 xai_model: 'grok-3-beta',
392392 pollinations_model: 'openai',
393393 cometapi_model: 'gpt-4o',
@@ -442,102 +442,7 @@ const default_settings = {
442442 extensions: {},
443443};
444444
445445const oai_settings = {structuredClone(default_settings);
446- preset_settings_openai: 'Default',
447- temp_openai: 1.0,
448- freq_pen_openai: 0,
449- pres_pen_openai: 0,
450- top_p_openai: 1.0,
451- top_k_openai: 0,
452- min_p_openai: 0,
453- top_a_openai: 0,
454- repetition_penalty_openai: 1,
455- stream_openai: false,
456- openai_max_context: max_4k,
457- openai_max_tokens: 300,
458- wrap_in_quotes: false,
459- ...chatCompletionDefaultPrompts,
460- ...promptManagerDefaultPromptOrders,
461- send_if_empty: '',
462- impersonation_prompt: default_impersonation_prompt,
463- new_chat_prompt: default_new_chat_prompt,
464- new_group_chat_prompt: default_new_group_chat_prompt,
465- new_example_chat_prompt: default_new_example_chat_prompt,
466- continue_nudge_prompt: default_continue_nudge_prompt,
467- bias_preset_selected: default_bias,
468- bias_presets: default_bias_presets,
469- wi_format: default_wi_format,
470- group_nudge_prompt: default_group_nudge_prompt,
471- scenario_format: default_scenario_format,
472- personality_format: default_personality_format,
473- openai_model: 'gpt-4-turbo',
474- claude_model: 'claude-sonnet-4-5',
475- google_model: 'gemini-2.5-pro',
476- vertexai_model: 'gemini-2.5-pro',
477- ai21_model: 'jamba-large',
478- mistralai_model: 'mistral-large-latest',
479- cohere_model: 'command-r-plus',
480- perplexity_model: 'sonar-pro',
481- groq_model: 'llama-3.1-70b-versatile',
482- electronhub_model: 'gpt-4o-mini',
483- electronhub_sort_models: 'alphabetically',
484- electronhub_group_models: false,
485- nanogpt_model: 'gpt-4o-mini',
486- deepseek_model: 'deepseek-chat',
487- aimlapi_model: 'gpt-4-turbo',
488- xai_model: 'grok-3-beta',
489- pollinations_model: 'openai',
490- cometapi_model: 'gpt-4o',
491- moonshot_model: 'kimi-latest',
492- fireworks_model: 'accounts/fireworks/models/kimi-k2-instruct',
493- zai_model: 'glm-4.6',
494- azure_base_url: '',
495- azure_deployment_name: '',
496- azure_api_version: '2024-02-15-preview',
497- azure_openai_model: '',
498- custom_model: '',
499- custom_url: '',
500- custom_include_body: '',
501- custom_exclude_body: '',
502- custom_include_headers: '',
503- openrouter_model: openrouter_website_model,
504- openrouter_use_fallback: false,
505- openrouter_group_models: false,
506- openrouter_sort_models: 'alphabetically',
507- openrouter_providers: [],
508- openrouter_allow_fallbacks: true,
509- openrouter_middleout: openrouter_middleout_types.ON,
510- reverse_proxy: '',
511- chat_completion_source: chat_completion_sources.OPENAI,
512- max_context_unlocked: false,
513- show_external_models: false,
514- proxy_password: '',
515- assistant_prefill: '',
516- assistant_impersonation: '',
517- claude_use_sysprompt: false,
518- use_makersuite_sysprompt: true,
519- vertexai_auth_mode: 'express',
520- vertexai_region: 'us-central1',
521- vertexai_express_project_id: '',
522- squash_system_messages: false,
523- image_inlining: false,
524- inline_image_quality: 'low',
525- video_inlining: false,
526- bypass_status_check: false,
527- continue_prefill: false,
528- function_calling: false,
529- names_behavior: character_names_behavior.DEFAULT,
530- continue_postfix: continue_postfix_types.SPACE,
531- custom_prompt_post_processing: custom_prompt_post_processing_types.NONE,
532- show_thoughts: true,
533- reasoning_effort: reasoning_effort_types.auto,
534- enable_web_search: false,
535- request_images: false,
536- seed: -1,
537- n: 1,
538- bind_preset_to_connection: true,
539- extensions: {},
540-};
541446
542447export let proxies = [
543448 {
@@ -5373,282 +5278,53 @@ function onReverseProxyInput() {
53735278async function onConnectButtonClick(e) {
53745279 e.stopPropagation();
53755280
5376- if (oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER) {
5281+ /** @type {Object.<string, {key: string, selector: string, proxy?: boolean, keyless?: boolean}>} */
5377- const api_key_openrouter = String($('#api_key_openrouter').val()).trim();
5282+ const apiSourceConfig = {
5378-
5283+ [chat_completion_sources.OPENROUTER]: { key: SECRET_KEYS.OPENROUTER, selector: '#api_key_openrouter', proxy: false },
5379- if (api_key_openrouter.length) {
5284+ [chat_completion_sources.MAKERSUITE]: { key: SECRET_KEYS.MAKERSUITE, selector: '#api_key_makersuite', proxy: true },
5380- await writeSecret(SECRET_KEYS.OPENROUTER, api_key_openrouter);
5285+ [chat_completion_sources.CLAUDE]: { key: SECRET_KEYS.CLAUDE, selector: '#api_key_claude', proxy: true },
5381- }
5286+ [chat_completion_sources.OPENAI]: { key: SECRET_KEYS.OPENAI, selector: '#api_key_openai', proxy: true },
5382-
5287+ [chat_completion_sources.AI21]: { key: SECRET_KEYS.AI21, selector: '#api_key_ai21', proxy: false },
5383- if (!secret_state[SECRET_KEYS.OPENROUTER]) {
5288+ [chat_completion_sources.MISTRALAI]: { key: SECRET_KEYS.MISTRALAI, selector: '#api_key_mistralai', proxy: true },
5384- console.log('No secret key saved for OpenRouter');
5289+ [chat_completion_sources.CUSTOM]: { key: SECRET_KEYS.CUSTOM, selector: '#api_key_custom', proxy: false, keyless: true },
5385- return;
5290+ [chat_completion_sources.COHERE]: { key: SECRET_KEYS.COHERE, selector: '#api_key_cohere', proxy: false },
5386- }
5291+ [chat_completion_sources.PERPLEXITY]: { key: SECRET_KEYS.PERPLEXITY, selector: '#api_key_perplexity', proxy: false },
5387- }
5292+ [chat_completion_sources.GROQ]: { key: SECRET_KEYS.GROQ, selector: '#api_key_groq', proxy: false },
5388-
5293+ [chat_completion_sources.ELECTRONHUB]: { key: SECRET_KEYS.ELECTRONHUB, selector: '#api_key_electronhub', proxy: false },
5389- if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) {
5294+ [chat_completion_sources.NANOGPT]: { key: SECRET_KEYS.NANOGPT, selector: '#api_key_nanogpt', proxy: false },
5390- const api_key_makersuite = String($('#api_key_makersuite').val()).trim();
5295+ [chat_completion_sources.DEEPSEEK]: { key: SECRET_KEYS.DEEPSEEK, selector: '#api_key_deepseek', proxy: true },
5391-
5296+ [chat_completion_sources.XAI]: { key: SECRET_KEYS.XAI, selector: '#api_key_xai', proxy: true },
5392- if (api_key_makersuite.length) {
5297+ [chat_completion_sources.AIMLAPI]: { key: SECRET_KEYS.AIMLAPI, selector: '#api_key_aimlapi', proxy: false },
5393- await writeSecret(SECRET_KEYS.MAKERSUITE, api_key_makersuite);
5298+ [chat_completion_sources.MOONSHOT]: { key: SECRET_KEYS.MOONSHOT, selector: '#api_key_moonshot', proxy: false },
5394- }
5299+ [chat_completion_sources.FIREWORKS]: { key: SECRET_KEYS.FIREWORKS, selector: '#api_key_fireworks', proxy: false },
5395-
5300+ [chat_completion_sources.COMETAPI]: { key: SECRET_KEYS.COMETAPI, selector: '#api_key_cometapi', proxy: false },
5396- if (!secret_state[SECRET_KEYS.MAKERSUITE] && !oai_settings.reverse_proxy) {
5301+ [chat_completion_sources.AZURE_OPENAI]: { key: SECRET_KEYS.AZURE_OPENAI, selector: '#api_key_azure_openai', proxy: false },
5397- console.log('No secret key saved for Google AI Studio');
5302+ [chat_completion_sources.ZAI]: { key: SECRET_KEYS.ZAI, selector: '#api_key_zai', proxy: false },
5398- return;
5303+ };
5399- }
5400- }
5401-
5402- if (oai_settings.chat_completion_source == chat_completion_sources.VERTEXAI) {
5403- if (oai_settings.vertexai_auth_mode === 'express') {
5404- // Express mode - use API key
5405- const api_key_vertexai = String($('#api_key_vertexai').val()).trim();
5406-
5407- if (api_key_vertexai.length) {
5408- await writeSecret(SECRET_KEYS.VERTEXAI, api_key_vertexai);
5409- }
5410-
5411- if (!secret_state[SECRET_KEYS.VERTEXAI] && !oai_settings.reverse_proxy) {
5412- console.log('No secret key saved for Vertex AI Express mode');
5413- return;
5414- }
5415- } else {
5416- // Full version - use service account
5417- // Project ID will be extracted from the Service Account JSON
5418-
5419- // Check if service account JSON is saved in backend
5420- if (!secret_state[SECRET_KEYS.VERTEXAI_SERVICE_ACCOUNT]) {
5421- toastr.error('Service Account JSON is required for Vertex AI full version. Please validate and save your Service Account JSON.');
5422- return;
5423- }
5424- }
5425- }
5426-
5427- if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
5428- const api_key_claude = String($('#api_key_claude').val()).trim();
5429-
5430- if (api_key_claude.length) {
5431- await writeSecret(SECRET_KEYS.CLAUDE, api_key_claude);
5432- }
5433-
5434- if (!secret_state[SECRET_KEYS.CLAUDE] && !oai_settings.reverse_proxy) {
5435- console.log('No secret key saved for Claude');
5436- return;
5437- }
5438- }
5439-
5440- if (oai_settings.chat_completion_source == chat_completion_sources.OPENAI) {
5441- const api_key_openai = String($('#api_key_openai').val()).trim();
5442-
5443- if (api_key_openai.length) {
5444- await writeSecret(SECRET_KEYS.OPENAI, api_key_openai);
5445- }
5446-
5447- if (!secret_state[SECRET_KEYS.OPENAI] && !oai_settings.reverse_proxy) {
5448- console.log('No secret key saved for OpenAI');
5449- return;
5450- }
5451- }
5452-
5453- if (oai_settings.chat_completion_source == chat_completion_sources.AI21) {
5454- const api_key_ai21 = String($('#api_key_ai21').val()).trim();
5455-
5456- if (api_key_ai21.length) {
5457- await writeSecret(SECRET_KEYS.AI21, api_key_ai21);
5458- }
5459-
5460- if (!secret_state[SECRET_KEYS.AI21]) {
5461- console.log('No secret key saved for AI21');
5462- return;
5463- }
5464- }
5465-
5466- if (oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI) {
5467- const api_key_mistralai = String($('#api_key_mistralai').val()).trim();
5468-
5469- if (api_key_mistralai.length) {
5470- await writeSecret(SECRET_KEYS.MISTRALAI, api_key_mistralai);
5471- }
5472-
5473- if (!secret_state[SECRET_KEYS.MISTRALAI] && !oai_settings.reverse_proxy) {
5474- console.log('No secret key saved for MistralAI');
5475- return;
5476- }
5477- }
5478-
5479- if (oai_settings.chat_completion_source == chat_completion_sources.CUSTOM) {
5480- const api_key_custom = String($('#api_key_custom').val()).trim();
5481-
5482- if (api_key_custom.length) {
5483- await writeSecret(SECRET_KEYS.CUSTOM, api_key_custom);
5484- }
5485- }
5486-
5487- if (oai_settings.chat_completion_source == chat_completion_sources.COHERE) {
5488- const api_key_cohere = String($('#api_key_cohere').val()).trim();
5489-
5490- if (api_key_cohere.length) {
5491- await writeSecret(SECRET_KEYS.COHERE, api_key_cohere);
5492- }
5493-
5494- if (!secret_state[SECRET_KEYS.COHERE]) {
5495- console.log('No secret key saved for Cohere');
5496- return;
5497- }
5498- }
5499-
5500- if (oai_settings.chat_completion_source == chat_completion_sources.PERPLEXITY) {
5501- const api_key_perplexity = String($('#api_key_perplexity').val()).trim();
5502-
5503- if (api_key_perplexity.length) {
5504- await writeSecret(SECRET_KEYS.PERPLEXITY, api_key_perplexity);
5505- }
5506-
5507- if (!secret_state[SECRET_KEYS.PERPLEXITY]) {
5508- console.log('No secret key saved for Perplexity');
5509- return;
5510- }
5511- }
5512-
5513- if (oai_settings.chat_completion_source == chat_completion_sources.GROQ) {
5514- const api_key_groq = String($('#api_key_groq').val()).trim();
5515-
5516- if (api_key_groq.length) {
5517- await writeSecret(SECRET_KEYS.GROQ, api_key_groq);
5518- }
5519-
5520- if (!secret_state[SECRET_KEYS.GROQ]) {
5521- console.log('No secret key saved for Groq');
5522- return;
5523- }
5524- }
5525-
5526- if (oai_settings.chat_completion_source == chat_completion_sources.ELECTRONHUB) {
5527- const api_key_electronhub = String($('#api_key_electronhub').val()).trim();
5528-
5529- if (api_key_electronhub.length) {
5530- await writeSecret(SECRET_KEYS.ELECTRONHUB, api_key_electronhub);
5531- }
5532-
5533- if (!secret_state[SECRET_KEYS.ELECTRONHUB]) {
5534- console.log('No secret key saved for Electron Hub');
5535- return;
5536- }
5537- }
5538-
5539- if (oai_settings.chat_completion_source == chat_completion_sources.NANOGPT) {
5540- const api_key_nanogpt = String($('#api_key_nanogpt').val()).trim();
5541-
5542- if (api_key_nanogpt.length) {
5543- await writeSecret(SECRET_KEYS.NANOGPT, api_key_nanogpt);
5544- }
5545-
5546- if (!secret_state[SECRET_KEYS.NANOGPT]) {
5547- console.log('No secret key saved for NanoGPT');
5548- return;
5549- }
5550- }
5551-
5552- if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) {
5553- const api_key_deepseek = String($('#api_key_deepseek').val()).trim();
5554-
5555- if (api_key_deepseek.length) {
5556- await writeSecret(SECRET_KEYS.DEEPSEEK, api_key_deepseek);
5557- }
5558-
5559- if (!secret_state[SECRET_KEYS.DEEPSEEK] && !oai_settings.reverse_proxy) {
5560- console.log('No secret key saved for DeepSeek');
5561- return;
5562- }
5563- }
5564-
5565- if (oai_settings.chat_completion_source === chat_completion_sources.XAI) {
5566- const api_key_xai = String($('#api_key_xai').val()).trim();
5567-
5568- if (api_key_xai.length) {
5569- await writeSecret(SECRET_KEYS.XAI, api_key_xai);
5570- }
5571-
5572- if (!secret_state[SECRET_KEYS.XAI] && !oai_settings.reverse_proxy) {
5573- console.log('No secret key saved for XAI');
5574- return;
5575- }
5576- }
5577-
5578- if (oai_settings.chat_completion_source === chat_completion_sources.AIMLAPI) {
5579- const api_key_aimlapi = String($('#api_key_aimlapi').val()).trim();
5580-
5581- if (api_key_aimlapi.length) {
5582- await writeSecret(SECRET_KEYS.AIMLAPI, api_key_aimlapi);
5583- }
5584-
5585- if (!secret_state[SECRET_KEYS.AIMLAPI]) {
5586- console.log('No secret key saved for AI/ML API');
5587- return;
5588- }
5589- }
5590-
5591- if (oai_settings.chat_completion_source == chat_completion_sources.MOONSHOT) {
5592- const api_key_moonshot = String($('#api_key_moonshot').val()).trim();
5593-
5594- if (api_key_moonshot.length) {
5595- await writeSecret(SECRET_KEYS.MOONSHOT, api_key_moonshot);
5596- }
5597-
5598- if (!secret_state[SECRET_KEYS.MOONSHOT]) {
5599- console.log('No secret key saved for Moonshot');
5600- return;
5601- }
5602- }
5603-
5604- if (oai_settings.chat_completion_source == chat_completion_sources.FIREWORKS) {
5605- const api_key_fireworks = String($('#api_key_fireworks').val()).trim();
5606-
5607- if (api_key_fireworks.length) {
5608- await writeSecret(SECRET_KEYS.FIREWORKS, api_key_fireworks);
5609- }
5610-
5611- if (!secret_state[SECRET_KEYS.FIREWORKS]) {
5612- console.log('No secret key saved for Fireworks');
5613- return;
5614- }
5615- }
5616-
5617- if (oai_settings.chat_completion_source == chat_completion_sources.COMETAPI) {
5618- const api_key_cometapi = String($('#api_key_cometapi').val()).trim();
5619-
5620- if (api_key_cometapi.length) {
5621- await writeSecret(SECRET_KEYS.COMETAPI, api_key_cometapi);
5622- }
56235304
5624- if (!api_key_cometapi && !secret_state[SECRET_KEYS.COMETAPI]) {
5305+ // Vertex AI Express version - use API key
5625- console.log('No secret key saved for CometAPI');
5306+ if (oai_settings.vertexai_auth_mode === 'express') {
5626- return;
5307+ apiSourceConfig[chat_completion_sources.VERTEXAI] = { key: SECRET_KEYS.VERTEXAI, selector: '#api_key_vertexai', proxy: true };
5627- }
56285308 }
56295309
5630- if (oai_settings.chat_completion_source == chat_completion_sources.AZURE_OPENAI) {
5310+ // Vertex AI Full version - use service account
5631- const api_key_azure_openai = String($('#api_key_azure_openai').val()).trim();
5311+ if (oai_settings.chat_completion_source === chat_completion_sources.VERTEXAI && oai_settings.vertexai_auth_mode === 'full') {
5632-
5312+ if (!secret_state[SECRET_KEYS.VERTEXAI_SERVICE_ACCOUNT]) {
5633- if (api_key_azure_openai.length) {
5313+ toastr.error(t`Service Account JSON is required for Vertex AI full version. Please validate and save your Service Account JSON.`);
5634- await writeSecret(SECRET_KEYS.AZURE_OPENAI, api_key_azure_openai);
5635- }
5636-
5637- if (!api_key_azure_openai && !secret_state[SECRET_KEYS.AZURE_OPENAI]) {
5638- console.log('No secret key saved for Azure OpenAI');
56395314 return;
56405315 }
56415316 }
56425317
5643- if (oai_settings.chat_completion_source == chat_completion_sources.ZAI) {
5318+ // Other generic configs
5644- const api_key_zai = String($('#api_key_zai').val()).trim();
5319+ const config = apiSourceConfig[oai_settings.chat_completion_source];
5645-
5320+ if (config) {
5646- if (api_key_zai.length) {
5321+ const apiKey = String($(config.selector).val()).trim();
5647- await writeSecret(SECRET_KEYS.ZAI, api_key_zai);
5322+ if (apiKey.length) {
5323+ await writeSecret(config.key, apiKey);
56485324 }
56495325
5650- if (!api_key_zai && !secret_state[SECRET_KEYS.ZAI]) {
5326+ if (!secret_state[config.key] && (!config.proxy || !oai_settings.reverse_proxy) && !config.keyless) {
56515327 console.log('`No secret key saved for ZAI'${oai_settings.chat_completion_source}`);
56525328 return;
56535329 }
56545330 }