Merge pull request #1751 from kingbased/proxypreset
Reverse proxy presets
This commit is contained in:
commit
da7b435b7c
|
@ -746,48 +746,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr data-source="openai,claude">
|
|
||||||
<div class="range-block" data-source="openai,claude">
|
|
||||||
<div class="range-block-title justifyLeft" data-i18n="OpenAI Reverse Proxy">
|
|
||||||
OpenAI / Claude Reverse Proxy
|
|
||||||
</div>
|
|
||||||
<div class="toggle-description justifyLeft">
|
|
||||||
<span data-i18n="Alternative server URL (leave empty to use the default value).">
|
|
||||||
Alternative server URL (leave empty to use the default value).<br>
|
|
||||||
</span>
|
|
||||||
<div id="ReverseProxyWarningMessage" class="reverse_proxy_warning">
|
|
||||||
<b data-i18n="Remove your real OAI API Key from the API panel BEFORE typing anything into this box">
|
|
||||||
Remove your real OAI API Key from the API panel BEFORE typing anything
|
|
||||||
into this box.
|
|
||||||
</b>
|
|
||||||
<hr>
|
|
||||||
<b data-i18n="We cannot provide support for problems encountered while using an unofficial OpenAI proxy">
|
|
||||||
We cannot provide support for problems encountered while using an
|
|
||||||
unofficial OpenAI proxy.
|
|
||||||
</b>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="wide100p">
|
|
||||||
<input id="openai_reverse_proxy" type="text" class="text_pole" placeholder="https://api.openai.com/v1" maxlength="500" />
|
|
||||||
<small class="reverse_proxy_warning">
|
|
||||||
Doesn't work? Try adding <code>/v1</code> at the end!
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="range-block" data-source="openai,claude">
|
|
||||||
<div class="range-block-title justifyLeft" data-i18n="Proxy Password">
|
|
||||||
Proxy Password
|
|
||||||
</div>
|
|
||||||
<div class="toggle-description justifyLeft">
|
|
||||||
<span data-i18n="Will be used as a password for the proxy instead of API key.">
|
|
||||||
Will be used as a password for the proxy instead of API key.<br>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex-container width100p">
|
|
||||||
<input id="openai_proxy_password" type="password" class="text_pole flex1" placeholder="" maxlength="500" form="openai_form" />
|
|
||||||
<div id="openai_proxy_password_show" title="Peek a password" class="menu_button fa-solid fa-eye-slash fa-fw"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div data-newbie-hidden class="range-block" data-source="openai,openrouter,mistralai,custom">
|
<div data-newbie-hidden class="range-block" data-source="openai,openrouter,mistralai,custom">
|
||||||
<div class="range-block-title justifyLeft" data-i18n="Seed">
|
<div class="range-block-title justifyLeft" data-i18n="Seed">
|
||||||
Seed
|
Seed
|
||||||
|
@ -2073,6 +2031,76 @@
|
||||||
<option value="mistralai">MistralAI</option>
|
<option value="mistralai">MistralAI</option>
|
||||||
<option value="custom">Custom (OpenAI-compatible)</option>
|
<option value="custom">Custom (OpenAI-compatible)</option>
|
||||||
</select>
|
</select>
|
||||||
|
<div data-newbie-hidden class="inline-drawer wide100p" data-source="openai,claude,mistralai">
|
||||||
|
<div class="inline-drawer-toggle inline-drawer-header">
|
||||||
|
<b data-i18n="Reverse Proxy">Reverse Proxy</b>
|
||||||
|
<div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
|
||||||
|
</div>
|
||||||
|
<div class="inline-drawer-content">
|
||||||
|
<div class="range-block-title justifyLeft" data-i18n="Proxy Presets">
|
||||||
|
Proxy Presets
|
||||||
|
</div>
|
||||||
|
<div class="toggle-description justifyLeft">
|
||||||
|
<span data-i18n="Saved addresses and passwords.">
|
||||||
|
Saved addresses and passwords.<br>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="openai_logit_bias_preset_form">
|
||||||
|
<select id="openai_proxy_preset">
|
||||||
|
</select>
|
||||||
|
<i id="save_proxy" class="menu_button fa-solid fa-save" title="Save Proxy" data-i18n="[title]Save Proxy"></i>
|
||||||
|
<i id="delete_proxy" class="menu_button fa-solid fa-trash" title="Delete Proxy" data-i18n="[title]Delete Proxy"></i>
|
||||||
|
</div>
|
||||||
|
<div class="range-block-title justifyLeft" data-i18n="Proxy Name">
|
||||||
|
Proxy Name
|
||||||
|
</div>
|
||||||
|
<div class="toggle-description justifyLeft">
|
||||||
|
<span data-i18n="This will show up as your saved preset.">
|
||||||
|
This will show up as your saved preset.<br>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="wide100p">
|
||||||
|
<input id="openai_reverse_proxy_name" type="text" class="text_pole" placeholder="..." maxlength="100" />
|
||||||
|
</div>
|
||||||
|
<div class="range-block-title justifyLeft" data-i18n="Proxy Server URL">
|
||||||
|
Proxy Server URL
|
||||||
|
</div>
|
||||||
|
<div class="toggle-description justifyLeft wide100p">
|
||||||
|
<span data-i18n="Alternative server URL (leave empty to use the default value).">
|
||||||
|
Alternative server URL (leave empty to use the default value).<br>
|
||||||
|
</span>
|
||||||
|
<div id="ReverseProxyWarningMessage" class="reverse_proxy_warning">
|
||||||
|
<b data-i18n="Remove your real OAI API Key from the API panel BEFORE typing anything into this box">
|
||||||
|
Remove your real OAI API Key from the API panel BEFORE typing anything
|
||||||
|
into this box.
|
||||||
|
</b>
|
||||||
|
<hr>
|
||||||
|
<b data-i18n="We cannot provide support for problems encountered while using an unofficial OpenAI proxy">
|
||||||
|
We cannot provide support for problems encountered while using an
|
||||||
|
unofficial OpenAI proxy.
|
||||||
|
</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="wide100p">
|
||||||
|
<input id="openai_reverse_proxy" type="text" class="text_pole" placeholder="https://api.openai.com/v1" maxlength="500" />
|
||||||
|
<small class="reverse_proxy_warning">
|
||||||
|
Doesn't work? Try adding <code>/v1</code> at the end!
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="range-block-title justifyLeft" data-i18n="Proxy Password">
|
||||||
|
Proxy Password
|
||||||
|
</div>
|
||||||
|
<div class="toggle-description justifyLeft">
|
||||||
|
<span data-i18n="Will be used as a password for the proxy instead of API key.">
|
||||||
|
Will be used as a password for the proxy instead of API key.<br>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex-container width100p">
|
||||||
|
<input id="openai_proxy_password" type="password" class="text_pole flex1" placeholder="" maxlength="500" form="openai_form" />
|
||||||
|
<div id="openai_proxy_password_show" title="Peek a password" class="menu_button fa-solid fa-eye-slash fa-fw"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<form id="openai_form" data-source="openai" action="javascript:void(null);" method="post" enctype="multipart/form-data">
|
<form id="openai_form" data-source="openai" action="javascript:void(null);" method="post" enctype="multipart/form-data">
|
||||||
<h4><span data-i18n="OpenAI API key">OpenAI API key</span></h4>
|
<h4><span data-i18n="OpenAI API key">OpenAI API key</span></h4>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -93,6 +93,9 @@ import {
|
||||||
chat_completion_sources,
|
chat_completion_sources,
|
||||||
getChatCompletionModel,
|
getChatCompletionModel,
|
||||||
isOpenRouterWithInstruct,
|
isOpenRouterWithInstruct,
|
||||||
|
proxies,
|
||||||
|
loadProxyPresets,
|
||||||
|
selected_proxy,
|
||||||
} from './scripts/openai.js';
|
} from './scripts/openai.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -5664,6 +5667,9 @@ async function getSettings() {
|
||||||
// Load background
|
// Load background
|
||||||
loadBackgroundSettings(settings);
|
loadBackgroundSettings(settings);
|
||||||
|
|
||||||
|
// Load proxy presets
|
||||||
|
loadProxyPresets(settings);
|
||||||
|
|
||||||
// Allow subscribers to mutate settings
|
// Allow subscribers to mutate settings
|
||||||
eventSource.emit(event_types.SETTINGS_LOADED_AFTER, settings);
|
eventSource.emit(event_types.SETTINGS_LOADED_AFTER, settings);
|
||||||
|
|
||||||
|
@ -5742,7 +5748,6 @@ async function saveSettings(type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('Entering settings with name1 = '+name1);
|
//console.log('Entering settings with name1 = '+name1);
|
||||||
|
|
||||||
return jQuery.ajax({
|
return jQuery.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: '/api/settings/save',
|
url: '/api/settings/save',
|
||||||
|
@ -5770,6 +5775,8 @@ async function saveSettings(type) {
|
||||||
kai_settings: kai_settings,
|
kai_settings: kai_settings,
|
||||||
oai_settings: oai_settings,
|
oai_settings: oai_settings,
|
||||||
background: background_settings,
|
background: background_settings,
|
||||||
|
proxies: proxies,
|
||||||
|
selected_proxy: selected_proxy,
|
||||||
}, null, 4),
|
}, null, 4),
|
||||||
beforeSend: function () { },
|
beforeSend: function () { },
|
||||||
cache: false,
|
cache: false,
|
||||||
|
|
|
@ -317,6 +317,15 @@ const oai_settings = {
|
||||||
seed: -1,
|
seed: -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export let proxies = [
|
||||||
|
{
|
||||||
|
name: 'None',
|
||||||
|
url: '',
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
export let selected_proxy = proxies[0];
|
||||||
|
|
||||||
let openai_setting_names;
|
let openai_setting_names;
|
||||||
let openai_settings;
|
let openai_settings;
|
||||||
|
|
||||||
|
@ -1596,8 +1605,8 @@ async function sendOpenAIRequest(type, messages, signal) {
|
||||||
delete generate_data.stop;
|
delete generate_data.stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proxy is only supported for Claude and OpenAI
|
// Proxy is only supported for Claude, OpenAI and Mistral
|
||||||
if (oai_settings.reverse_proxy && [chat_completion_sources.CLAUDE, chat_completion_sources.OPENAI].includes(oai_settings.chat_completion_source)) {
|
if (oai_settings.reverse_proxy && [chat_completion_sources.CLAUDE, chat_completion_sources.OPENAI, chat_completion_sources.MISTRALAI].includes(oai_settings.chat_completion_source)) {
|
||||||
validateReverseProxy();
|
validateReverseProxy();
|
||||||
generate_data['reverse_proxy'] = oai_settings.reverse_proxy;
|
generate_data['reverse_proxy'] = oai_settings.reverse_proxy;
|
||||||
generate_data['proxy_password'] = oai_settings.proxy_password;
|
generate_data['proxy_password'] = oai_settings.proxy_password;
|
||||||
|
@ -3803,6 +3812,110 @@ export function isImageInliningSupported() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy stuff
|
||||||
|
*/
|
||||||
|
export function loadProxyPresets(settings) {
|
||||||
|
let proxyPresets = settings.proxies;
|
||||||
|
selected_proxy = settings.selected_proxy || selected_proxy;
|
||||||
|
if (!Array.isArray(proxyPresets) || proxyPresets.length === 0) {
|
||||||
|
proxyPresets = proxies;
|
||||||
|
} else {
|
||||||
|
proxies = proxyPresets;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#openai_proxy_preset').empty();
|
||||||
|
|
||||||
|
for (const preset of proxyPresets) {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.innerText = preset.name;
|
||||||
|
option.value = preset.name;
|
||||||
|
option.selected = preset.name === 'None';
|
||||||
|
$('#openai_proxy_preset').append(option);
|
||||||
|
}
|
||||||
|
$('#openai_proxy_preset').val(selected_proxy.name);
|
||||||
|
setProxyPreset(selected_proxy.name, selected_proxy.url, selected_proxy.password);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setProxyPreset(name, url, password) {
|
||||||
|
const preset = proxies.find(p => p.name === name);
|
||||||
|
if (preset) {
|
||||||
|
preset.url = url;
|
||||||
|
preset.password = password;
|
||||||
|
selected_proxy = preset;
|
||||||
|
} else {
|
||||||
|
let new_proxy = { name, url, password };
|
||||||
|
proxies.push(new_proxy);
|
||||||
|
selected_proxy = new_proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#openai_reverse_proxy_name').val(name);
|
||||||
|
oai_settings.reverse_proxy = url;
|
||||||
|
$('#openai_reverse_proxy').val(oai_settings.reverse_proxy);
|
||||||
|
oai_settings.proxy_password = password;
|
||||||
|
$('#openai_proxy_password').val(oai_settings.proxy_password);
|
||||||
|
reconnectOpenAi();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onProxyPresetChange() {
|
||||||
|
const value = String($('#openai_proxy_preset').find(':selected').val());
|
||||||
|
const selectedPreset = proxies.find(preset => preset.name === value);
|
||||||
|
|
||||||
|
if (selectedPreset) {
|
||||||
|
setProxyPreset(selectedPreset.name, selectedPreset.url, selectedPreset.password);
|
||||||
|
} else {
|
||||||
|
console.error(`Proxy preset "${value}" not found in proxies array.`);
|
||||||
|
}
|
||||||
|
saveSettingsDebounced();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#save_proxy').on('click', async function () {
|
||||||
|
const presetName = $('#openai_reverse_proxy_name').val();
|
||||||
|
const reverseProxy = $('#openai_reverse_proxy').val();
|
||||||
|
const proxyPassword = $('#openai_proxy_password').val();
|
||||||
|
|
||||||
|
setProxyPreset(presetName, reverseProxy, proxyPassword);
|
||||||
|
saveSettingsDebounced();
|
||||||
|
toastr.success('Proxy Saved');
|
||||||
|
if($('#openai_proxy_preset').val() !== presetName) {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.text = presetName;
|
||||||
|
option.value = presetName;
|
||||||
|
|
||||||
|
$('#openai_proxy_preset').append(option);
|
||||||
|
}
|
||||||
|
$('#openai_proxy_preset').val(presetName);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#delete_proxy').on('click', async function () {
|
||||||
|
const presetName = $('#openai_reverse_proxy_name').val();
|
||||||
|
const index = proxies.findIndex(preset => preset.name === presetName);
|
||||||
|
|
||||||
|
if (index !== -1) {
|
||||||
|
proxies.splice(index, 1);
|
||||||
|
$('#openai_proxy_preset option[value="' + presetName + '"]').remove();
|
||||||
|
|
||||||
|
if (proxies.length > 0) {
|
||||||
|
const newIndex = Math.max(0, index - 1);
|
||||||
|
selected_proxy = proxies[newIndex];
|
||||||
|
} else {
|
||||||
|
selected_proxy = { name: 'None', url: '', password: '' };
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#openai_reverse_proxy_name').val(selected_proxy.name);
|
||||||
|
oai_settings.reverse_proxy = selected_proxy.url;
|
||||||
|
$('#openai_reverse_proxy').val(selected_proxy.url);
|
||||||
|
oai_settings.proxy_password = selected_proxy.password;
|
||||||
|
$('#openai_proxy_password').val(selected_proxy.password);
|
||||||
|
|
||||||
|
saveSettingsDebounced();
|
||||||
|
$('#openai_proxy_preset').val(selected_proxy.name);
|
||||||
|
toastr.success('Proxy Deleted');
|
||||||
|
} else {
|
||||||
|
toastr.error(`Could not find proxy with name "${presetName}"`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$(document).ready(async function () {
|
$(document).ready(async function () {
|
||||||
$('#test_api_button').on('click', testApiConnection);
|
$('#test_api_button').on('click', testApiConnection);
|
||||||
|
|
||||||
|
@ -4183,4 +4296,5 @@ $(document).ready(async function () {
|
||||||
$('#import_oai_preset').on('click', onImportPresetClick);
|
$('#import_oai_preset').on('click', onImportPresetClick);
|
||||||
$('#openai_proxy_password_show').on('click', onProxyPasswordShowClick);
|
$('#openai_proxy_password_show').on('click', onProxyPasswordShowClick);
|
||||||
$('#customize_additional_parameters').on('click', onCustomizeParametersClick);
|
$('#customize_additional_parameters').on('click', onCustomizeParametersClick);
|
||||||
|
$('#openai_proxy_preset').on('change', onProxyPresetChange);
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,7 +12,7 @@ const { getTokenizerModel, getSentencepiceTokenizer, getTiktokenTokenizer, sente
|
||||||
|
|
||||||
const API_OPENAI = 'https://api.openai.com/v1';
|
const API_OPENAI = 'https://api.openai.com/v1';
|
||||||
const API_CLAUDE = 'https://api.anthropic.com/v1';
|
const API_CLAUDE = 'https://api.anthropic.com/v1';
|
||||||
|
const API_MISTRAL = 'https://api.mistral.ai/v1';
|
||||||
/**
|
/**
|
||||||
* Sends a request to Claude API.
|
* Sends a request to Claude API.
|
||||||
* @param {express.Request} request Express request
|
* @param {express.Request} request Express request
|
||||||
|
@ -436,7 +436,8 @@ async function sendAI21Request(request, response) {
|
||||||
* @param {express.Response} response Express response
|
* @param {express.Response} response Express response
|
||||||
*/
|
*/
|
||||||
async function sendMistralAIRequest(request, response) {
|
async function sendMistralAIRequest(request, response) {
|
||||||
const apiKey = readSecret(SECRET_KEYS.MISTRALAI);
|
const apiUrl = new URL(request.body.reverse_proxy || API_MISTRAL).toString();
|
||||||
|
const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(SECRET_KEYS.MISTRALAI);
|
||||||
|
|
||||||
if (!apiKey) {
|
if (!apiKey) {
|
||||||
console.log('MistralAI API key is missing.');
|
console.log('MistralAI API key is missing.');
|
||||||
|
@ -500,7 +501,7 @@ async function sendMistralAIRequest(request, response) {
|
||||||
|
|
||||||
console.log('MisralAI request:', requestBody);
|
console.log('MisralAI request:', requestBody);
|
||||||
|
|
||||||
const generateResponse = await fetch('https://api.mistral.ai/v1/chat/completions', config);
|
const generateResponse = await fetch(apiUrl + '/chat/completions', config);
|
||||||
if (request.body.stream) {
|
if (request.body.stream) {
|
||||||
forwardFetchResponse(generateResponse, response);
|
forwardFetchResponse(generateResponse, response);
|
||||||
} else {
|
} else {
|
||||||
|
@ -543,8 +544,8 @@ router.post('/status', jsonParser, async function (request, response_getstatus_o
|
||||||
// OpenRouter needs to pass the referer: https://openrouter.ai/docs
|
// OpenRouter needs to pass the referer: https://openrouter.ai/docs
|
||||||
headers = { 'HTTP-Referer': request.headers.referer };
|
headers = { 'HTTP-Referer': request.headers.referer };
|
||||||
} else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MISTRALAI) {
|
} else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MISTRALAI) {
|
||||||
api_url = 'https://api.mistral.ai/v1';
|
api_url = new URL(request.body.reverse_proxy || API_MISTRAL).toString();
|
||||||
api_key_openai = readSecret(SECRET_KEYS.MISTRALAI);
|
api_key_openai = request.body.reverse_proxy ? request.body.proxy_password : readSecret(SECRET_KEYS.MISTRALAI);
|
||||||
headers = {};
|
headers = {};
|
||||||
} else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.CUSTOM) {
|
} else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.CUSTOM) {
|
||||||
api_url = request.body.custom_url;
|
api_url = request.body.custom_url;
|
||||||
|
|
Loading…
Reference in New Issue