feat(secrets): update readSecret function to accept optional secret ID (#5356) * feat(secrets): update readSecret function to accept optional secret ID * add secret_id to ConnectionManagerRequestService payload * fix: pass secret_id for Text Completion types --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

ff1ca1412a0a39157727a476eb67af2515d27563

lunar sheep <102860538+dylenyedc@users.noreply.github.com>

Signed
4 files changed, +95 -77Ignore whitespace
public/scripts/extensions/shared.js+2 -0
@@ -435,6 +435,7 @@ export class ConnectionManagerRequestService {
435435 max_tokens: maxTokens,
436436 model: profile.model,
437437 chat_completion_source: selectedApiMap.source,
438+ secret_id: profile['secret-id'],
438439 custom_url: profile['api-url'],
439440 vertexai_region: profile['api-url'],
440441 zai_endpoint: profile['api-url'],
@@ -459,6 +460,7 @@ export class ConnectionManagerRequestService {
459460 model: profile.model,
460461 api_type: selectedApiMap.type,
461462 api_server: profile['api-url'],
463+ secret_id: profile['secret-id'],
462464 ...overridePayload,
463465 }, {
464466 instructName: includeInstruct ? profile.instruct : undefined,
src/additional-headers.js+47 -32
@@ -5,10 +5,11 @@ import { getConfigValue } from './util.js';
55/**
66 * Gets the headers for the Mancer API.
77 * @param {import('./users.js').UserDirectoryList} directories User directories
8+ * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use)
89 * @returns {object} Headers for the request
910 */
1011function getMancerHeaders(directories, secretId = null) {
1112 const apiKey = readSecret(directories, SECRET_KEYS.MANCER, secretId);
1213
1314 return apiKey ? ({
1415 'X-API-KEY': apiKey,
@@ -19,10 +20,11 @@ function getMancerHeaders(directories) {
1920/**
2021 * Gets the headers for the TogetherAI API.
2122 * @param {import('./users.js').UserDirectoryList} directories User directories
23+ * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use)
2224 * @returns {object} Headers for the request
2325 */
2426function getTogetherAIHeaders(directories, secretId = null) {
2527 const apiKey = readSecret(directories, SECRET_KEYS.TOGETHERAI, secretId);
2628
2729 return apiKey ? ({
2830 'Authorization': `Bearer ${apiKey}`,
@@ -32,10 +34,11 @@ function getTogetherAIHeaders(directories) {
3234/**
3335 * Gets the headers for the InfermaticAI API.
3436 * @param {import('./users.js').UserDirectoryList} directories User directories
37+ * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use)
3538 * @returns {object} Headers for the request
3639 */
3740function getInfermaticAIHeaders(directories, secretId = null) {
3841 const apiKey = readSecret(directories, SECRET_KEYS.INFERMATICAI, secretId);
3942
4043 return apiKey ? ({
4144 'Authorization': `Bearer ${apiKey}`,
@@ -45,10 +48,11 @@ function getInfermaticAIHeaders(directories) {
4548/**
4649 * Gets the headers for the DreamGen API.
4750 * @param {import('./users.js').UserDirectoryList} directories User directories
51+ * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use)
4852 * @returns {object} Headers for the request
4953 */
5054function getDreamGenHeaders(directories, secretId = null) {
5155 const apiKey = readSecret(directories, SECRET_KEYS.DREAMGEN, secretId);
5256
5357 return apiKey ? ({
5458 'Authorization': `Bearer ${apiKey}`,
@@ -58,10 +62,11 @@ function getDreamGenHeaders(directories) {
5862/**
5963 * Gets the headers for the OpenRouter API.
6064 * @param {import('./users.js').UserDirectoryList} directories User directories
65+ * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use)
6166 * @returns {object} Headers for the request
6267 */
6368function getOpenRouterHeaders(directories, secretId = null) {
6469 const apiKey = readSecret(directories, SECRET_KEYS.OPENROUTER, secretId);
6570 const baseHeaders = { ...OPENROUTER_HEADERS };
6671
6772 return apiKey ? Object.assign(baseHeaders, { 'Authorization': `Bearer ${apiKey}` }) : baseHeaders;
@@ -70,10 +75,11 @@ function getOpenRouterHeaders(directories) {
7075/**
7176 * Gets the headers for the vLLM API.
7277 * @param {import('./users.js').UserDirectoryList} directories User directories
78+ * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use)
7379 * @returns {object} Headers for the request
7480 */
7581function getVllmHeaders(directories, secretId = null) {
7682 const apiKey = readSecret(directories, SECRET_KEYS.VLLM, secretId);
7783
7884 return apiKey ? ({
7985 'Authorization': `Bearer ${apiKey}`,
@@ -83,10 +89,11 @@ function getVllmHeaders(directories) {
8389/**
8490 * Gets the headers for the Aphrodite API.
8591 * @param {import('./users.js').UserDirectoryList} directories User directories
92+ * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use)
8693 * @returns {object} Headers for the request
8794 */
8895function getAphroditeHeaders(directories, secretId = null) {
8996 const apiKey = readSecret(directories, SECRET_KEYS.APHRODITE, secretId);
9097
9198 return apiKey ? ({
9299 'X-API-KEY': apiKey,
@@ -97,10 +104,11 @@ function getAphroditeHeaders(directories) {
97104/**
98105 * Gets the headers for the Tabby API.
99106 * @param {import('./users.js').UserDirectoryList} directories User directories
107+ * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use)
100108 * @returns {object} Headers for the request
101109 */
102110function getTabbyHeaders(directories, secretId = null) {
103111 const apiKey = readSecret(directories, SECRET_KEYS.TABBY, secretId);
104112
105113 return apiKey ? ({
106114 'x-api-key': apiKey,
@@ -111,10 +119,11 @@ function getTabbyHeaders(directories) {
111119/**
112120 * Gets the headers for the LlamaCPP API.
113121 * @param {import('./users.js').UserDirectoryList} directories User directories
122+ * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use)
114123 * @returns {object} Headers for the request
115124 */
116125function getLlamaCppHeaders(directories, secretId = null) {
117126 const apiKey = readSecret(directories, SECRET_KEYS.LLAMACPP, secretId);
118127
119128 return apiKey ? ({
120129 'Authorization': `Bearer ${apiKey}`,
@@ -123,11 +132,12 @@ function getLlamaCppHeaders(directories) {
123132
124133/**
125134 * Gets the headers for the Ooba API.
126135 * @param {import('./users.js').UserDirectoryList} directories User directories
136+ * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use)
127137 * @returns {object} Headers for the request
128138 */
129139function getOobaHeaders(directories, secretId = null) {
130140 const apiKey = readSecret(directories, SECRET_KEYS.OOBA, secretId);
131141
132142 return apiKey ? ({
133143 'Authorization': `Bearer ${apiKey}`,
@@ -137,10 +147,11 @@ function getOobaHeaders(directories) {
137147/**
138148 * Gets the headers for the KoboldCpp API.
139149 * @param {import('./users.js').UserDirectoryList} directories
150+ * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use)
140151 * @returns {object} Headers for the request
141152 */
142153function getKoboldCppHeaders(directories, secretId = null) {
143154 const apiKey = readSecret(directories, SECRET_KEYS.KOBOLDCPP, secretId);
144155
145156 return apiKey ? ({
146157 'Authorization': `Bearer ${apiKey}`,
@@ -150,10 +161,11 @@ function getKoboldCppHeaders(directories) {
150161/**
151162 * Gets the headers for the Featherless API.
152163 * @param {import('./users.js').UserDirectoryList} directories
164+ * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use)
153165 * @returns {object} Headers for the request
154166 */
155167function getFeatherlessHeaders(directories, secretId = null) {
156168 const apiKey = readSecret(directories, SECRET_KEYS.FEATHERLESS, secretId);
157169 const baseHeaders = { ...FEATHERLESS_HEADERS };
158170
159171 return apiKey ? Object.assign(baseHeaders, { 'Authorization': `Bearer ${apiKey}` }) : baseHeaders;
@@ -162,10 +174,11 @@ function getFeatherlessHeaders(directories) {
162174/**
163175 * Gets the headers for the HuggingFace API.
164176 * @param {import('./users.js').UserDirectoryList} directories
177+ * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use)
165178 * @returns {object} Headers for the request
166179 */
167180function getHuggingFaceHeaders(directories, secretId = null) {
168181 const apiKey = readSecret(directories, SECRET_KEYS.HUGGINGFACE, secretId);
169182
170183 return apiKey ? ({
171184 'Authorization': `Bearer ${apiKey}`,
@@ -175,10 +188,11 @@ function getHuggingFaceHeaders(directories) {
175188/**
176189 * Gets the headers for the Generic text completion API.
177190 * @param {import('./users.js').UserDirectoryList} directories
191+ * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use)
178192 * @returns {object} Headers for the request
179193 */
180194function getGenericHeaders(directories, secretId = null) {
181195 const apiKey = readSecret(directories, SECRET_KEYS.GENERIC, secretId);
182196
183197 return apiKey ? ({
184198 'Authorization': `Bearer ${apiKey}`,
@@ -202,7 +216,7 @@ export function getOverrideHeaders(urlHost) {
202216 * @param {string|null} server API server for new request
203217 */
204218export function setAdditionalHeaders(request, args, server) {
205219 setAdditionalHeadersByType(args.headers, request.body.api_type, server, request.user.directories, request.body.secret_id);
206220}
207221
208222/**
@@ -211,8 +225,9 @@ export function setAdditionalHeaders(request, args, server) {
211225 * @param {string} type API type
212226 * @param {string|null} server API server for new request
213227 * @param {import('./users.js').UserDirectoryList} directories User directories
228+ * @param {string|null} secretId Secret ID for the request (optional, used for some API types to determine which secret to use)
214229 */
215230export function setAdditionalHeadersByType(requestHeaders, type, server, directories, secretId = null) {
216231 const headerGetters = {
217232 [TEXTGEN_TYPES.MANCER]: getMancerHeaders,
218233 [TEXTGEN_TYPES.VLLM]: getVllmHeaders,
@@ -231,7 +246,7 @@ export function setAdditionalHeadersByType(requestHeaders, type, server, directo
231246 };
232247
233248 const getHeaders = headerGetters[type];
234249 const headers = getHeaders ? getHeaders(directories, secretId) : {};
235250
236251 if (typeof server === 'string' && server.length > 0) {
237252 try {
src/endpoints/backends/chat-completions.js+43 -43
@@ -208,7 +208,7 @@ function setJsonObjectFormat(bodyParams, messages, jsonSchema) {
208208 */
209209async function sendClaudeRequest(request, response) {
210210 const apiUrl = new URL(request.body.reverse_proxy || API_CLAUDE).toString();
211211 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.CLAUDE, request.body.secret_id);
212212 const divider = '-'.repeat(process.stdout.columns);
213213
214214 if (!apiKey) {
@@ -425,7 +425,7 @@ async function sendMakerSuiteRequest(request, response) {
425425 }
426426 } else {
427427 apiUrl = new URL(request.body.reverse_proxy || API_MAKERSUITE);
428428 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE, request.body.secret_id);
429429
430430 if (!request.body.reverse_proxy && !apiKey) {
431431 console.warn(`${apiName} API key is missing.`);
@@ -641,7 +641,7 @@ async function sendMakerSuiteRequest(request, response) {
641641 } else if (authType === 'full') {
642642 // For Full mode (service account authentication), use project-specific URL
643643 // Get project ID from Service Account JSON
644644 const serviceAccountJson = readSecret(request.user.directories, SECRET_KEYS.VERTEXAI_SERVICE_ACCOUNT, request.body.secret_id);
645645 if (!serviceAccountJson) {
646646 console.warn('Vertex AI Service Account JSON is missing.');
647647 return response.status(400).send({ error: true });
@@ -742,7 +742,7 @@ async function sendMakerSuiteRequest(request, response) {
742742async function sendAI21Request(request, response) {
743743 if (!request.body) return response.sendStatus(400);
744744
745745 const apiKey = readSecret(request.user.directories, SECRET_KEYS.AI21, request.body.secret_id);
746746 if (!apiKey) {
747747 console.warn('AI21 API key is missing.');
748748 return response.status(400).send({ error: true });
@@ -822,7 +822,7 @@ async function sendAI21Request(request, response) {
822822 */
823823async function sendMistralAIRequest(request, response) {
824824 const apiUrl = new URL(request.body.reverse_proxy || API_MISTRAL).toString();
825825 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MISTRALAI, request.body.secret_id);
826826
827827 if (!apiKey) {
828828 console.warn('MistralAI API key is missing.');
@@ -911,7 +911,7 @@ async function sendMistralAIRequest(request, response) {
911911 * @param {express.Response} response Express response
912912 */
913913async function sendCohereRequest(request, response) {
914914 const apiKey = readSecret(request.user.directories, SECRET_KEYS.COHERE, request.body.secret_id);
915915 const controller = new AbortController();
916916 request.socket.removeAllListeners('close');
917917 request.socket.on('close', function () {
@@ -1012,7 +1012,7 @@ async function sendCohereRequest(request, response) {
10121012 */
10131013async function sendDeepSeekRequest(request, response) {
10141014 const apiUrl = new URL(request.body.reverse_proxy || API_DEEPSEEK).toString();
10151015 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK, request.body.secret_id);
10161016
10171017 if (!apiKey && !request.body.reverse_proxy) {
10181018 console.warn('DeepSeek API key is missing.');
@@ -1122,7 +1122,7 @@ async function sendDeepSeekRequest(request, response) {
11221122 */
11231123async function sendXaiRequest(request, response) {
11241124 const apiUrl = new URL(request.body.reverse_proxy || API_XAI).toString();
11251125 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.XAI, request.body.secret_id);
11261126
11271127 if (!apiKey && !request.body.reverse_proxy) {
11281128 console.warn('xAI API key is missing.');
@@ -1228,7 +1228,7 @@ async function sendXaiRequest(request, response) {
12281228 */
12291229async function sendAimlapiRequest(request, response) {
12301230 const apiUrl = API_AIMLAPI;
12311231 const apiKey = readSecret(request.user.directories, SECRET_KEYS.AIMLAPI, request.body.secret_id);
12321232
12331233 if (!apiKey) {
12341234 console.warn('AI/ML API key is missing.');
@@ -1333,7 +1333,7 @@ async function sendAimlapiRequest(request, response) {
13331333 */
13341334async function sendElectronHubRequest(request, response) {
13351335 const apiUrl = API_ELECTRONHUB;
13361336 const apiKey = readSecret(request.user.directories, SECRET_KEYS.ELECTRONHUB, request.body.secret_id);
13371337
13381338 if (!apiKey) {
13391339 console.warn('Electron Hub key is missing.');
@@ -1445,7 +1445,7 @@ async function sendElectronHubRequest(request, response) {
14451445 */
14461446async function sendChutesRequest(request, response) {
14471447 const apiUrl = API_CHUTES;
14481448 const apiKey = readSecret(request.user.directories, SECRET_KEYS.CHUTES, request.body.secret_id);
14491449
14501450 if (!apiKey) {
14511451 console.warn('Chutes key is missing.');
@@ -1547,7 +1547,7 @@ async function sendChutesRequest(request, response) {
15471547async function sendAzureOpenAIRequest(request, response) {
15481548 // 1. GATHER & VALIDATE SETTINGS
15491549 const { azure_base_url, azure_deployment_name, azure_api_version } = request.body;
15501550 const apiKey = readSecret(request.user.directories, SECRET_KEYS.AZURE_OPENAI, request.body.secret_id);
15511551 if (!azure_base_url || !azure_deployment_name || !azure_api_version || !apiKey) {
15521552 return response.status(400).send({
15531553 error: {
@@ -1646,74 +1646,74 @@ router.post('/status', async function (request, statusResponse) {
16461646
16471647 if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.OPENAI) {
16481648 apiUrl = new URL(request.body.reverse_proxy || API_OPENAI).toString();
16491649 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.OPENAI, request.body.secret_id);
16501650 headers = {};
16511651 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.OPENROUTER) {
16521652 apiUrl = 'https://openrouter.ai/api/v1';
16531653 apiKey = readSecret(request.user.directories, SECRET_KEYS.OPENROUTER, request.body.secret_id);
16541654 // OpenRouter needs to pass the Referer and X-Title: https://openrouter.ai/docs#requests
16551655 headers = { ...OPENROUTER_HEADERS };
16561656 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MISTRALAI) {
16571657 apiUrl = new URL(request.body.reverse_proxy || API_MISTRAL).toString();
16581658 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MISTRALAI, request.body.secret_id);
16591659 headers = {};
16601660 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.CUSTOM) {
16611661 apiUrl = request.body.custom_url;
16621662 apiKey = readSecret(request.user.directories, SECRET_KEYS.CUSTOM, request.body.secret_id);
16631663 headers = {};
16641664 mergeObjectWithYaml(headers, request.body.custom_include_headers);
16651665 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.COHERE) {
16661666 apiUrl = API_COHERE_V1;
16671667 apiKey = readSecret(request.user.directories, SECRET_KEYS.COHERE, request.body.secret_id);
16681668 headers = {};
16691669 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.CHUTES) {
16701670 apiUrl = API_CHUTES;
16711671 apiKey = readSecret(request.user.directories, SECRET_KEYS.CHUTES, request.body.secret_id);
16721672 headers = {};
16731673 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.ELECTRONHUB) {
16741674 apiUrl = API_ELECTRONHUB;
16751675 apiKey = readSecret(request.user.directories, SECRET_KEYS.ELECTRONHUB, request.body.secret_id);
16761676 headers = {};
16771677 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.NANOGPT) {
16781678 apiUrl = API_NANOGPT;
16791679 apiKey = readSecret(request.user.directories, SECRET_KEYS.NANOGPT, request.body.secret_id);
16801680 headers = {};
16811681 queryParams = { detailed: true };
16821682 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.DEEPSEEK) {
16831683 apiUrl = new URL(request.body.reverse_proxy || API_DEEPSEEK.replace('/beta', '')).toString();
16841684 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK, request.body.secret_id);
16851685 headers = {};
16861686 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.XAI) {
16871687 apiUrl = new URL(request.body.reverse_proxy || API_XAI).toString();
16881688 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.XAI, request.body.secret_id);
16891689 headers = {};
16901690 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.AIMLAPI) {
16911691 apiUrl = API_AIMLAPI;
16921692 apiKey = readSecret(request.user.directories, SECRET_KEYS.AIMLAPI, request.body.secret_id);
16931693 headers = { ...AIMLAPI_HEADERS };
16941694 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.POLLINATIONS) {
16951695 apiUrl = 'https://gen.pollinations.ai/text';
16961696 apiKey = readSecret(request.user.directories, SECRET_KEYS.POLLINATIONS, request.body.secret_id);
16971697 headers = {};
16981698 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.GROQ) {
16991699 apiUrl = API_GROQ;
17001700 apiKey = readSecret(request.user.directories, SECRET_KEYS.GROQ, request.body.secret_id);
17011701 headers = {};
17021702 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.COMETAPI) {
17031703 apiUrl = API_COMETAPI;
17041704 apiKey = readSecret(request.user.directories, SECRET_KEYS.COMETAPI, request.body.secret_id);
17051705 headers = {};
17061706 throw new Error('This provider is temporarily disabled.');
17071707 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MOONSHOT) {
17081708 apiUrl = new URL(request.body.reverse_proxy || API_MOONSHOT).toString();
17091709 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MOONSHOT, request.body.secret_id);
17101710 headers = {};
17111711 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.FIREWORKS) {
17121712 apiUrl = API_FIREWORKS;
17131713 apiKey = readSecret(request.user.directories, SECRET_KEYS.FIREWORKS, request.body.secret_id);
17141714 headers = {};
17151715 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MAKERSUITE) {
17161716 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE, request.body.secret_id);
17171717 apiUrl = trimTrailingSlash(request.body.reverse_proxy || API_MAKERSUITE);
17181718 const apiVersion = getConfigValue('gemini.apiVersion', 'v1beta');
17191719 const modelsUrl = !apiKey && request.body.reverse_proxy
@@ -1750,7 +1750,7 @@ router.post('/status', async function (request, statusResponse) {
17501750 }
17511751 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.AZURE_OPENAI) {
17521752 const { azure_base_url, azure_deployment_name, azure_api_version } = request.body;
17531753 const apiKey = readSecret(request.user.directories, SECRET_KEYS.AZURE_OPENAI, request.body.secret_id);
17541754
17551755 // 1) Validate configuration from the frontend
17561756 if (!apiKey || !azure_base_url || !azure_deployment_name || !azure_api_version) {
@@ -1830,7 +1830,7 @@ router.post('/status', async function (request, statusResponse) {
18301830 const defaultApiUrl = request.body.siliconflow_endpoint === SILICONFLOW_ENDPOINT.CN
18311831 ? API_SILICONFLOW_CN : API_SILICONFLOW;
18321832 apiUrl = defaultApiUrl;
18331833 apiKey = readSecret(request.user.directories, SECRET_KEYS.SILICONFLOW, request.body.secret_id);
18341834 headers = {};
18351835 queryParams = { type: 'text', sub_type: 'chat' };
18361836 } else {
@@ -2053,7 +2053,7 @@ router.post('/generate', async function (request, response) {
20532053
20542054 if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.OPENAI) {
20552055 apiUrl = new URL(request.body.reverse_proxy || API_OPENAI).toString();
20562056 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.OPENAI, request.body.secret_id);
20572057 headers = {};
20582058 bodyParams = {
20592059 logprobs: request.body.logprobs,
@@ -2073,7 +2073,7 @@ router.post('/generate', async function (request, response) {
20732073 embedOpenRouterMedia(request.body.messages, { audio: true, video: false });
20742074 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.OPENROUTER) {
20752075 apiUrl = 'https://openrouter.ai/api/v1';
20762076 apiKey = readSecret(request.user.directories, SECRET_KEYS.OPENROUTER, request.body.secret_id);
20772077 // OpenRouter needs to pass the Referer and X-Title: https://openrouter.ai/docs#requests
20782078 headers = { ...OPENROUTER_HEADERS };
20792079 const includeReasoning = Boolean(request.body.include_reasoning);
@@ -2161,7 +2161,7 @@ router.post('/generate', async function (request, response) {
21612161 }
21622162 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.CUSTOM) {
21632163 apiUrl = request.body.custom_url;
21642164 apiKey = readSecret(request.user.directories, SECRET_KEYS.CUSTOM, request.body.secret_id);
21652165 headers = {};
21662166 bodyParams = {
21672167 logprobs: request.body.logprobs,
@@ -2179,7 +2179,7 @@ router.post('/generate', async function (request, response) {
21792179 embedOpenRouterMedia(request.body.messages, { audio: true, video: false });
21802180 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.PERPLEXITY) {
21812181 apiUrl = API_PERPLEXITY;
21822182 apiKey = readSecret(request.user.directories, SECRET_KEYS.PERPLEXITY, request.body.secret_id);
21832183 headers = {};
21842184 bodyParams = {
21852185 reasoning_effort: request.body.reasoning_effort,
@@ -2195,7 +2195,7 @@ router.post('/generate', async function (request, response) {
21952195 }
21962196 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.GROQ) {
21972197 apiUrl = API_GROQ;
21982198 apiKey = readSecret(request.user.directories, SECRET_KEYS.GROQ, request.body.secret_id);
21992199 headers = {};
22002200 bodyParams = {};
22012201 if (request.body.json_schema) {
@@ -2211,7 +2211,7 @@ router.post('/generate', async function (request, response) {
22112211 }
22122212 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.FIREWORKS) {
22132213 apiUrl = API_FIREWORKS;
22142214 apiKey = readSecret(request.user.directories, SECRET_KEYS.FIREWORKS, request.body.secret_id);
22152215 headers = {};
22162216 bodyParams = {};
22172217 if (request.body.json_schema) {
@@ -2227,7 +2227,7 @@ router.post('/generate', async function (request, response) {
22272227 }
22282228 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.NANOGPT) {
22292229 apiUrl = API_NANOGPT;
22302230 apiKey = readSecret(request.user.directories, SECRET_KEYS.NANOGPT, request.body.secret_id);
22312231 headers = {};
22322232 bodyParams = {};
22332233 if (request.body.enable_web_search && !/:online$/.test(request.body.model)) {
@@ -2256,7 +2256,7 @@ router.post('/generate', async function (request, response) {
22562256 }
22572257 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.POLLINATIONS) {
22582258 apiUrl = API_POLLINATIONS;
22592259 apiKey = readSecret(request.user.directories, SECRET_KEYS.POLLINATIONS, request.body.secret_id);
22602260 headers = {};
22612261 bodyParams = {
22622262 reasoning_effort: request.body.reasoning_effort,
@@ -2272,7 +2272,7 @@ router.post('/generate', async function (request, response) {
22722272 }
22732273 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MOONSHOT) {
22742274 apiUrl = new URL(request.body.reverse_proxy || API_MOONSHOT).toString();
22752275 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MOONSHOT, request.body.secret_id);
22762276 headers = {};
22772277 bodyParams = {
22782278 thinking: {
@@ -2284,7 +2284,7 @@ router.post('/generate', async function (request, response) {
22842284 : addAssistantPrefix(request.body.messages, [], 'partial');
22852285 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.COMETAPI) {
22862286 apiUrl = API_COMETAPI;
22872287 apiKey = readSecret(request.user.directories, SECRET_KEYS.COMETAPI, request.body.secret_id);
22882288 headers = {};
22892289 bodyParams = {
22902290 reasoning_effort: request.body.reasoning_effort,
@@ -2293,7 +2293,7 @@ router.post('/generate', async function (request, response) {
22932293 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.ZAI) {
22942294 const defaultApiUrl = request.body.zai_endpoint === ZAI_ENDPOINT.CODING ? API_ZAI_CODING : API_ZAI_COMMON;
22952295 apiUrl = new URL(request.body.reverse_proxy || defaultApiUrl).toString();
22962296 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.ZAI, request.body.secret_id);
22972297 headers = {
22982298 'Accept-Language': 'en-US,en',
22992299 };
@@ -2309,7 +2309,7 @@ router.post('/generate', async function (request, response) {
23092309 const defaultApiUrl = request.body.siliconflow_endpoint === SILICONFLOW_ENDPOINT.CN
23102310 ? API_SILICONFLOW_CN : API_SILICONFLOW;
23112311 apiUrl = defaultApiUrl;
23122312 apiKey = readSecret(request.user.directories, SECRET_KEYS.SILICONFLOW, request.body.secret_id);
23132313 headers = {};
23142314 bodyParams = {};
23152315 if (request.body.json_schema) {
src/endpoints/secrets.js+3 -2
@@ -441,10 +441,11 @@ export function deleteSecret(directories, key) {
441441 * Reads a secret from the secrets file
442442 * @param {import('../users.js').UserDirectoryList} directories User directories
443443 * @param {string} key Secret key
444+ * @param {string?} id Secret ID (optional)
444445 * @returns {string} Secret value
445446 */
446447export function readSecret(directories, key, id = null) {
447448 return new SecretManager(directories).readSecret(key, nullid);
448449}
449450
450451/**