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>
Signed| @@ -435,6 +435,7 @@ export class ConnectionManagerRequestService { | ||
| 435 | 435 | max_tokens: maxTokens, |
| 436 | 436 | model: profile.model, |
| 437 | 437 | chat_completion_source: selectedApiMap.source, |
| 438 | + secret_id: profile['secret-id'], | |
| 438 | 439 | custom_url: profile['api-url'], |
| 439 | 440 | vertexai_region: profile['api-url'], |
| 440 | 441 | zai_endpoint: profile['api-url'], |
| @@ -459,6 +460,7 @@ export class ConnectionManagerRequestService { | ||
| 459 | 460 | model: profile.model, |
| 460 | 461 | api_type: selectedApiMap.type, |
| 461 | 462 | api_server: profile['api-url'], |
| 463 | + secret_id: profile['secret-id'], | |
| 462 | 464 | ...overridePayload, |
| 463 | 465 | }, { |
| 464 | 466 | instructName: includeInstruct ? profile.instruct : undefined, |
| @@ -5,10 +5,11 @@ import { getConfigValue } from './util.js'; | ||
| 5 | 5 | /** |
| 6 | 6 | * Gets the headers for the Mancer API. |
| 7 | 7 | * @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) | |
| 8 | 9 | * @returns {object} Headers for the request |
| 9 | 10 | */ |
| 10 | 11 | function getMancerHeaders(directories, secretId = null) { |
| 11 | 12 | const apiKey = readSecret(directories, SECRET_KEYS.MANCER, secretId); |
| 12 | 13 | |
| 13 | 14 | return apiKey ? ({ |
| 14 | 15 | 'X-API-KEY': apiKey, |
| @@ -19,10 +20,11 @@ function getMancerHeaders(directories) { | ||
| 19 | 20 | /** |
| 20 | 21 | * Gets the headers for the TogetherAI API. |
| 21 | 22 | * @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) | |
| 22 | 24 | * @returns {object} Headers for the request |
| 23 | 25 | */ |
| 24 | 26 | function getTogetherAIHeaders(directories, secretId = null) { |
| 25 | 27 | const apiKey = readSecret(directories, SECRET_KEYS.TOGETHERAI, secretId); |
| 26 | 28 | |
| 27 | 29 | return apiKey ? ({ |
| 28 | 30 | 'Authorization': `Bearer ${apiKey}`, |
| @@ -32,10 +34,11 @@ function getTogetherAIHeaders(directories) { | ||
| 32 | 34 | /** |
| 33 | 35 | * Gets the headers for the InfermaticAI API. |
| 34 | 36 | * @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) | |
| 35 | 38 | * @returns {object} Headers for the request |
| 36 | 39 | */ |
| 37 | 40 | function getInfermaticAIHeaders(directories, secretId = null) { |
| 38 | 41 | const apiKey = readSecret(directories, SECRET_KEYS.INFERMATICAI, secretId); |
| 39 | 42 | |
| 40 | 43 | return apiKey ? ({ |
| 41 | 44 | 'Authorization': `Bearer ${apiKey}`, |
| @@ -45,10 +48,11 @@ function getInfermaticAIHeaders(directories) { | ||
| 45 | 48 | /** |
| 46 | 49 | * Gets the headers for the DreamGen API. |
| 47 | 50 | * @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) | |
| 48 | 52 | * @returns {object} Headers for the request |
| 49 | 53 | */ |
| 50 | 54 | function getDreamGenHeaders(directories, secretId = null) { |
| 51 | 55 | const apiKey = readSecret(directories, SECRET_KEYS.DREAMGEN, secretId); |
| 52 | 56 | |
| 53 | 57 | return apiKey ? ({ |
| 54 | 58 | 'Authorization': `Bearer ${apiKey}`, |
| @@ -58,10 +62,11 @@ function getDreamGenHeaders(directories) { | ||
| 58 | 62 | /** |
| 59 | 63 | * Gets the headers for the OpenRouter API. |
| 60 | 64 | * @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) | |
| 61 | 66 | * @returns {object} Headers for the request |
| 62 | 67 | */ |
| 63 | 68 | function getOpenRouterHeaders(directories, secretId = null) { |
| 64 | 69 | const apiKey = readSecret(directories, SECRET_KEYS.OPENROUTER, secretId); |
| 65 | 70 | const baseHeaders = { ...OPENROUTER_HEADERS }; |
| 66 | 71 | |
| 67 | 72 | return apiKey ? Object.assign(baseHeaders, { 'Authorization': `Bearer ${apiKey}` }) : baseHeaders; |
| @@ -70,10 +75,11 @@ function getOpenRouterHeaders(directories) { | ||
| 70 | 75 | /** |
| 71 | 76 | * Gets the headers for the vLLM API. |
| 72 | 77 | * @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) | |
| 73 | 79 | * @returns {object} Headers for the request |
| 74 | 80 | */ |
| 75 | 81 | function getVllmHeaders(directories, secretId = null) { |
| 76 | 82 | const apiKey = readSecret(directories, SECRET_KEYS.VLLM, secretId); |
| 77 | 83 | |
| 78 | 84 | return apiKey ? ({ |
| 79 | 85 | 'Authorization': `Bearer ${apiKey}`, |
| @@ -83,10 +89,11 @@ function getVllmHeaders(directories) { | ||
| 83 | 89 | /** |
| 84 | 90 | * Gets the headers for the Aphrodite API. |
| 85 | 91 | * @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) | |
| 86 | 93 | * @returns {object} Headers for the request |
| 87 | 94 | */ |
| 88 | 95 | function getAphroditeHeaders(directories, secretId = null) { |
| 89 | 96 | const apiKey = readSecret(directories, SECRET_KEYS.APHRODITE, secretId); |
| 90 | 97 | |
| 91 | 98 | return apiKey ? ({ |
| 92 | 99 | 'X-API-KEY': apiKey, |
| @@ -97,10 +104,11 @@ function getAphroditeHeaders(directories) { | ||
| 97 | 104 | /** |
| 98 | 105 | * Gets the headers for the Tabby API. |
| 99 | 106 | * @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) | |
| 100 | 108 | * @returns {object} Headers for the request |
| 101 | 109 | */ |
| 102 | 110 | function getTabbyHeaders(directories, secretId = null) { |
| 103 | 111 | const apiKey = readSecret(directories, SECRET_KEYS.TABBY, secretId); |
| 104 | 112 | |
| 105 | 113 | return apiKey ? ({ |
| 106 | 114 | 'x-api-key': apiKey, |
| @@ -111,10 +119,11 @@ function getTabbyHeaders(directories) { | ||
| 111 | 119 | /** |
| 112 | 120 | * Gets the headers for the LlamaCPP API. |
| 113 | 121 | * @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) | |
| 114 | 123 | * @returns {object} Headers for the request |
| 115 | 124 | */ |
| 116 | 125 | function getLlamaCppHeaders(directories, secretId = null) { |
| 117 | 126 | const apiKey = readSecret(directories, SECRET_KEYS.LLAMACPP, secretId); |
| 118 | 127 | |
| 119 | 128 | return apiKey ? ({ |
| 120 | 129 | 'Authorization': `Bearer ${apiKey}`, |
| @@ -123,11 +132,12 @@ function getLlamaCppHeaders(directories) { | ||
| 123 | 132 | |
| 124 | 133 | /** |
| 125 | 134 | * Gets the headers for the Ooba API. |
| 126 | 135 | * @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) | |
| 127 | 137 | * @returns {object} Headers for the request |
| 128 | 138 | */ |
| 129 | 139 | function getOobaHeaders(directories, secretId = null) { |
| 130 | 140 | const apiKey = readSecret(directories, SECRET_KEYS.OOBA, secretId); |
| 131 | 141 | |
| 132 | 142 | return apiKey ? ({ |
| 133 | 143 | 'Authorization': `Bearer ${apiKey}`, |
| @@ -137,10 +147,11 @@ function getOobaHeaders(directories) { | ||
| 137 | 147 | /** |
| 138 | 148 | * Gets the headers for the KoboldCpp API. |
| 139 | 149 | * @param {import('./users.js').UserDirectoryList} directories |
| 150 | + * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use) | |
| 140 | 151 | * @returns {object} Headers for the request |
| 141 | 152 | */ |
| 142 | 153 | function getKoboldCppHeaders(directories, secretId = null) { |
| 143 | 154 | const apiKey = readSecret(directories, SECRET_KEYS.KOBOLDCPP, secretId); |
| 144 | 155 | |
| 145 | 156 | return apiKey ? ({ |
| 146 | 157 | 'Authorization': `Bearer ${apiKey}`, |
| @@ -150,10 +161,11 @@ function getKoboldCppHeaders(directories) { | ||
| 150 | 161 | /** |
| 151 | 162 | * Gets the headers for the Featherless API. |
| 152 | 163 | * @param {import('./users.js').UserDirectoryList} directories |
| 164 | + * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use) | |
| 153 | 165 | * @returns {object} Headers for the request |
| 154 | 166 | */ |
| 155 | 167 | function getFeatherlessHeaders(directories, secretId = null) { |
| 156 | 168 | const apiKey = readSecret(directories, SECRET_KEYS.FEATHERLESS, secretId); |
| 157 | 169 | const baseHeaders = { ...FEATHERLESS_HEADERS }; |
| 158 | 170 | |
| 159 | 171 | return apiKey ? Object.assign(baseHeaders, { 'Authorization': `Bearer ${apiKey}` }) : baseHeaders; |
| @@ -162,10 +174,11 @@ function getFeatherlessHeaders(directories) { | ||
| 162 | 174 | /** |
| 163 | 175 | * Gets the headers for the HuggingFace API. |
| 164 | 176 | * @param {import('./users.js').UserDirectoryList} directories |
| 177 | + * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use) | |
| 165 | 178 | * @returns {object} Headers for the request |
| 166 | 179 | */ |
| 167 | 180 | function getHuggingFaceHeaders(directories, secretId = null) { |
| 168 | 181 | const apiKey = readSecret(directories, SECRET_KEYS.HUGGINGFACE, secretId); |
| 169 | 182 | |
| 170 | 183 | return apiKey ? ({ |
| 171 | 184 | 'Authorization': `Bearer ${apiKey}`, |
| @@ -175,10 +188,11 @@ function getHuggingFaceHeaders(directories) { | ||
| 175 | 188 | /** |
| 176 | 189 | * Gets the headers for the Generic text completion API. |
| 177 | 190 | * @param {import('./users.js').UserDirectoryList} directories |
| 191 | + * @param {string|null} secretId Secret ID for the request (optional, used to determine which secret to use) | |
| 178 | 192 | * @returns {object} Headers for the request |
| 179 | 193 | */ |
| 180 | 194 | function getGenericHeaders(directories, secretId = null) { |
| 181 | 195 | const apiKey = readSecret(directories, SECRET_KEYS.GENERIC, secretId); |
| 182 | 196 | |
| 183 | 197 | return apiKey ? ({ |
| 184 | 198 | 'Authorization': `Bearer ${apiKey}`, |
| @@ -202,7 +216,7 @@ export function getOverrideHeaders(urlHost) { | ||
| 202 | 216 | * @param {string|null} server API server for new request |
| 203 | 217 | */ |
| 204 | 218 | export function setAdditionalHeaders(request, args, server) { |
| 205 | 219 | setAdditionalHeadersByType(args.headers, request.body.api_type, server, request.user.directories, request.body.secret_id); |
| 206 | 220 | } |
| 207 | 221 | |
| 208 | 222 | /** |
| @@ -211,8 +225,9 @@ export function setAdditionalHeaders(request, args, server) { | ||
| 211 | 225 | * @param {string} type API type |
| 212 | 226 | * @param {string|null} server API server for new request |
| 213 | 227 | * @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) | |
| 214 | 229 | */ |
| 215 | 230 | export function setAdditionalHeadersByType(requestHeaders, type, server, directories, secretId = null) { |
| 216 | 231 | const headerGetters = { |
| 217 | 232 | [TEXTGEN_TYPES.MANCER]: getMancerHeaders, |
| 218 | 233 | [TEXTGEN_TYPES.VLLM]: getVllmHeaders, |
| @@ -231,7 +246,7 @@ export function setAdditionalHeadersByType(requestHeaders, type, server, directo | ||
| 231 | 246 | }; |
| 232 | 247 | |
| 233 | 248 | const getHeaders = headerGetters[type]; |
| 234 | 249 | const headers = getHeaders ? getHeaders(directories, secretId) : {}; |
| 235 | 250 | |
| 236 | 251 | if (typeof server === 'string' && server.length > 0) { |
| 237 | 252 | try { |
| @@ -208,7 +208,7 @@ function setJsonObjectFormat(bodyParams, messages, jsonSchema) { | ||
| 208 | 208 | */ |
| 209 | 209 | async function sendClaudeRequest(request, response) { |
| 210 | 210 | const apiUrl = new URL(request.body.reverse_proxy || API_CLAUDE).toString(); |
| 211 | 211 | const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.CLAUDE, request.body.secret_id); |
| 212 | 212 | const divider = '-'.repeat(process.stdout.columns); |
| 213 | 213 | |
| 214 | 214 | if (!apiKey) { |
| @@ -425,7 +425,7 @@ async function sendMakerSuiteRequest(request, response) { | ||
| 425 | 425 | } |
| 426 | 426 | } else { |
| 427 | 427 | apiUrl = new URL(request.body.reverse_proxy || API_MAKERSUITE); |
| 428 | 428 | apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE, request.body.secret_id); |
| 429 | 429 | |
| 430 | 430 | if (!request.body.reverse_proxy && !apiKey) { |
| 431 | 431 | console.warn(`${apiName} API key is missing.`); |
| @@ -641,7 +641,7 @@ async function sendMakerSuiteRequest(request, response) { | ||
| 641 | 641 | } else if (authType === 'full') { |
| 642 | 642 | // For Full mode (service account authentication), use project-specific URL |
| 643 | 643 | // Get project ID from Service Account JSON |
| 644 | 644 | const serviceAccountJson = readSecret(request.user.directories, SECRET_KEYS.VERTEXAI_SERVICE_ACCOUNT, request.body.secret_id); |
| 645 | 645 | if (!serviceAccountJson) { |
| 646 | 646 | console.warn('Vertex AI Service Account JSON is missing.'); |
| 647 | 647 | return response.status(400).send({ error: true }); |
| @@ -742,7 +742,7 @@ async function sendMakerSuiteRequest(request, response) { | ||
| 742 | 742 | async function sendAI21Request(request, response) { |
| 743 | 743 | if (!request.body) return response.sendStatus(400); |
| 744 | 744 | |
| 745 | 745 | const apiKey = readSecret(request.user.directories, SECRET_KEYS.AI21, request.body.secret_id); |
| 746 | 746 | if (!apiKey) { |
| 747 | 747 | console.warn('AI21 API key is missing.'); |
| 748 | 748 | return response.status(400).send({ error: true }); |
| @@ -822,7 +822,7 @@ async function sendAI21Request(request, response) { | ||
| 822 | 822 | */ |
| 823 | 823 | async function sendMistralAIRequest(request, response) { |
| 824 | 824 | const apiUrl = new URL(request.body.reverse_proxy || API_MISTRAL).toString(); |
| 825 | 825 | const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MISTRALAI, request.body.secret_id); |
| 826 | 826 | |
| 827 | 827 | if (!apiKey) { |
| 828 | 828 | console.warn('MistralAI API key is missing.'); |
| @@ -911,7 +911,7 @@ async function sendMistralAIRequest(request, response) { | ||
| 911 | 911 | * @param {express.Response} response Express response |
| 912 | 912 | */ |
| 913 | 913 | async function sendCohereRequest(request, response) { |
| 914 | 914 | const apiKey = readSecret(request.user.directories, SECRET_KEYS.COHERE, request.body.secret_id); |
| 915 | 915 | const controller = new AbortController(); |
| 916 | 916 | request.socket.removeAllListeners('close'); |
| 917 | 917 | request.socket.on('close', function () { |
| @@ -1012,7 +1012,7 @@ async function sendCohereRequest(request, response) { | ||
| 1012 | 1012 | */ |
| 1013 | 1013 | async function sendDeepSeekRequest(request, response) { |
| 1014 | 1014 | const apiUrl = new URL(request.body.reverse_proxy || API_DEEPSEEK).toString(); |
| 1015 | 1015 | const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK, request.body.secret_id); |
| 1016 | 1016 | |
| 1017 | 1017 | if (!apiKey && !request.body.reverse_proxy) { |
| 1018 | 1018 | console.warn('DeepSeek API key is missing.'); |
| @@ -1122,7 +1122,7 @@ async function sendDeepSeekRequest(request, response) { | ||
| 1122 | 1122 | */ |
| 1123 | 1123 | async function sendXaiRequest(request, response) { |
| 1124 | 1124 | const apiUrl = new URL(request.body.reverse_proxy || API_XAI).toString(); |
| 1125 | 1125 | const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.XAI, request.body.secret_id); |
| 1126 | 1126 | |
| 1127 | 1127 | if (!apiKey && !request.body.reverse_proxy) { |
| 1128 | 1128 | console.warn('xAI API key is missing.'); |
| @@ -1228,7 +1228,7 @@ async function sendXaiRequest(request, response) { | ||
| 1228 | 1228 | */ |
| 1229 | 1229 | async function sendAimlapiRequest(request, response) { |
| 1230 | 1230 | const apiUrl = API_AIMLAPI; |
| 1231 | 1231 | const apiKey = readSecret(request.user.directories, SECRET_KEYS.AIMLAPI, request.body.secret_id); |
| 1232 | 1232 | |
| 1233 | 1233 | if (!apiKey) { |
| 1234 | 1234 | console.warn('AI/ML API key is missing.'); |
| @@ -1333,7 +1333,7 @@ async function sendAimlapiRequest(request, response) { | ||
| 1333 | 1333 | */ |
| 1334 | 1334 | async function sendElectronHubRequest(request, response) { |
| 1335 | 1335 | const apiUrl = API_ELECTRONHUB; |
| 1336 | 1336 | const apiKey = readSecret(request.user.directories, SECRET_KEYS.ELECTRONHUB, request.body.secret_id); |
| 1337 | 1337 | |
| 1338 | 1338 | if (!apiKey) { |
| 1339 | 1339 | console.warn('Electron Hub key is missing.'); |
| @@ -1445,7 +1445,7 @@ async function sendElectronHubRequest(request, response) { | ||
| 1445 | 1445 | */ |
| 1446 | 1446 | async function sendChutesRequest(request, response) { |
| 1447 | 1447 | const apiUrl = API_CHUTES; |
| 1448 | 1448 | const apiKey = readSecret(request.user.directories, SECRET_KEYS.CHUTES, request.body.secret_id); |
| 1449 | 1449 | |
| 1450 | 1450 | if (!apiKey) { |
| 1451 | 1451 | console.warn('Chutes key is missing.'); |
| @@ -1547,7 +1547,7 @@ async function sendChutesRequest(request, response) { | ||
| 1547 | 1547 | async function sendAzureOpenAIRequest(request, response) { |
| 1548 | 1548 | // 1. GATHER & VALIDATE SETTINGS |
| 1549 | 1549 | const { azure_base_url, azure_deployment_name, azure_api_version } = request.body; |
| 1550 | 1550 | const apiKey = readSecret(request.user.directories, SECRET_KEYS.AZURE_OPENAI, request.body.secret_id); |
| 1551 | 1551 | if (!azure_base_url || !azure_deployment_name || !azure_api_version || !apiKey) { |
| 1552 | 1552 | return response.status(400).send({ |
| 1553 | 1553 | error: { |
| @@ -1646,74 +1646,74 @@ router.post('/status', async function (request, statusResponse) { | ||
| 1646 | 1646 | |
| 1647 | 1647 | if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.OPENAI) { |
| 1648 | 1648 | apiUrl = new URL(request.body.reverse_proxy || API_OPENAI).toString(); |
| 1649 | 1649 | apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.OPENAI, request.body.secret_id); |
| 1650 | 1650 | headers = {}; |
| 1651 | 1651 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.OPENROUTER) { |
| 1652 | 1652 | apiUrl = 'https://openrouter.ai/api/v1'; |
| 1653 | 1653 | apiKey = readSecret(request.user.directories, SECRET_KEYS.OPENROUTER, request.body.secret_id); |
| 1654 | 1654 | // OpenRouter needs to pass the Referer and X-Title: https://openrouter.ai/docs#requests |
| 1655 | 1655 | headers = { ...OPENROUTER_HEADERS }; |
| 1656 | 1656 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MISTRALAI) { |
| 1657 | 1657 | apiUrl = new URL(request.body.reverse_proxy || API_MISTRAL).toString(); |
| 1658 | 1658 | apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MISTRALAI, request.body.secret_id); |
| 1659 | 1659 | headers = {}; |
| 1660 | 1660 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.CUSTOM) { |
| 1661 | 1661 | apiUrl = request.body.custom_url; |
| 1662 | 1662 | apiKey = readSecret(request.user.directories, SECRET_KEYS.CUSTOM, request.body.secret_id); |
| 1663 | 1663 | headers = {}; |
| 1664 | 1664 | mergeObjectWithYaml(headers, request.body.custom_include_headers); |
| 1665 | 1665 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.COHERE) { |
| 1666 | 1666 | apiUrl = API_COHERE_V1; |
| 1667 | 1667 | apiKey = readSecret(request.user.directories, SECRET_KEYS.COHERE, request.body.secret_id); |
| 1668 | 1668 | headers = {}; |
| 1669 | 1669 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.CHUTES) { |
| 1670 | 1670 | apiUrl = API_CHUTES; |
| 1671 | 1671 | apiKey = readSecret(request.user.directories, SECRET_KEYS.CHUTES, request.body.secret_id); |
| 1672 | 1672 | headers = {}; |
| 1673 | 1673 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.ELECTRONHUB) { |
| 1674 | 1674 | apiUrl = API_ELECTRONHUB; |
| 1675 | 1675 | apiKey = readSecret(request.user.directories, SECRET_KEYS.ELECTRONHUB, request.body.secret_id); |
| 1676 | 1676 | headers = {}; |
| 1677 | 1677 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.NANOGPT) { |
| 1678 | 1678 | apiUrl = API_NANOGPT; |
| 1679 | 1679 | apiKey = readSecret(request.user.directories, SECRET_KEYS.NANOGPT, request.body.secret_id); |
| 1680 | 1680 | headers = {}; |
| 1681 | 1681 | queryParams = { detailed: true }; |
| 1682 | 1682 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.DEEPSEEK) { |
| 1683 | 1683 | apiUrl = new URL(request.body.reverse_proxy || API_DEEPSEEK.replace('/beta', '')).toString(); |
| 1684 | 1684 | apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK, request.body.secret_id); |
| 1685 | 1685 | headers = {}; |
| 1686 | 1686 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.XAI) { |
| 1687 | 1687 | apiUrl = new URL(request.body.reverse_proxy || API_XAI).toString(); |
| 1688 | 1688 | apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.XAI, request.body.secret_id); |
| 1689 | 1689 | headers = {}; |
| 1690 | 1690 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.AIMLAPI) { |
| 1691 | 1691 | apiUrl = API_AIMLAPI; |
| 1692 | 1692 | apiKey = readSecret(request.user.directories, SECRET_KEYS.AIMLAPI, request.body.secret_id); |
| 1693 | 1693 | headers = { ...AIMLAPI_HEADERS }; |
| 1694 | 1694 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.POLLINATIONS) { |
| 1695 | 1695 | apiUrl = 'https://gen.pollinations.ai/text'; |
| 1696 | 1696 | apiKey = readSecret(request.user.directories, SECRET_KEYS.POLLINATIONS, request.body.secret_id); |
| 1697 | 1697 | headers = {}; |
| 1698 | 1698 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.GROQ) { |
| 1699 | 1699 | apiUrl = API_GROQ; |
| 1700 | 1700 | apiKey = readSecret(request.user.directories, SECRET_KEYS.GROQ, request.body.secret_id); |
| 1701 | 1701 | headers = {}; |
| 1702 | 1702 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.COMETAPI) { |
| 1703 | 1703 | apiUrl = API_COMETAPI; |
| 1704 | 1704 | apiKey = readSecret(request.user.directories, SECRET_KEYS.COMETAPI, request.body.secret_id); |
| 1705 | 1705 | headers = {}; |
| 1706 | 1706 | throw new Error('This provider is temporarily disabled.'); |
| 1707 | 1707 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MOONSHOT) { |
| 1708 | 1708 | apiUrl = new URL(request.body.reverse_proxy || API_MOONSHOT).toString(); |
| 1709 | 1709 | apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MOONSHOT, request.body.secret_id); |
| 1710 | 1710 | headers = {}; |
| 1711 | 1711 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.FIREWORKS) { |
| 1712 | 1712 | apiUrl = API_FIREWORKS; |
| 1713 | 1713 | apiKey = readSecret(request.user.directories, SECRET_KEYS.FIREWORKS, request.body.secret_id); |
| 1714 | 1714 | headers = {}; |
| 1715 | 1715 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MAKERSUITE) { |
| 1716 | 1716 | apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE, request.body.secret_id); |
| 1717 | 1717 | apiUrl = trimTrailingSlash(request.body.reverse_proxy || API_MAKERSUITE); |
| 1718 | 1718 | const apiVersion = getConfigValue('gemini.apiVersion', 'v1beta'); |
| 1719 | 1719 | const modelsUrl = !apiKey && request.body.reverse_proxy |
| @@ -1750,7 +1750,7 @@ router.post('/status', async function (request, statusResponse) { | ||
| 1750 | 1750 | } |
| 1751 | 1751 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.AZURE_OPENAI) { |
| 1752 | 1752 | const { azure_base_url, azure_deployment_name, azure_api_version } = request.body; |
| 1753 | 1753 | const apiKey = readSecret(request.user.directories, SECRET_KEYS.AZURE_OPENAI, request.body.secret_id); |
| 1754 | 1754 | |
| 1755 | 1755 | // 1) Validate configuration from the frontend |
| 1756 | 1756 | if (!apiKey || !azure_base_url || !azure_deployment_name || !azure_api_version) { |
| @@ -1830,7 +1830,7 @@ router.post('/status', async function (request, statusResponse) { | ||
| 1830 | 1830 | const defaultApiUrl = request.body.siliconflow_endpoint === SILICONFLOW_ENDPOINT.CN |
| 1831 | 1831 | ? API_SILICONFLOW_CN : API_SILICONFLOW; |
| 1832 | 1832 | apiUrl = defaultApiUrl; |
| 1833 | 1833 | apiKey = readSecret(request.user.directories, SECRET_KEYS.SILICONFLOW, request.body.secret_id); |
| 1834 | 1834 | headers = {}; |
| 1835 | 1835 | queryParams = { type: 'text', sub_type: 'chat' }; |
| 1836 | 1836 | } else { |
| @@ -2053,7 +2053,7 @@ router.post('/generate', async function (request, response) { | ||
| 2053 | 2053 | |
| 2054 | 2054 | if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.OPENAI) { |
| 2055 | 2055 | apiUrl = new URL(request.body.reverse_proxy || API_OPENAI).toString(); |
| 2056 | 2056 | apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.OPENAI, request.body.secret_id); |
| 2057 | 2057 | headers = {}; |
| 2058 | 2058 | bodyParams = { |
| 2059 | 2059 | logprobs: request.body.logprobs, |
| @@ -2073,7 +2073,7 @@ router.post('/generate', async function (request, response) { | ||
| 2073 | 2073 | embedOpenRouterMedia(request.body.messages, { audio: true, video: false }); |
| 2074 | 2074 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.OPENROUTER) { |
| 2075 | 2075 | apiUrl = 'https://openrouter.ai/api/v1'; |
| 2076 | 2076 | apiKey = readSecret(request.user.directories, SECRET_KEYS.OPENROUTER, request.body.secret_id); |
| 2077 | 2077 | // OpenRouter needs to pass the Referer and X-Title: https://openrouter.ai/docs#requests |
| 2078 | 2078 | headers = { ...OPENROUTER_HEADERS }; |
| 2079 | 2079 | const includeReasoning = Boolean(request.body.include_reasoning); |
| @@ -2161,7 +2161,7 @@ router.post('/generate', async function (request, response) { | ||
| 2161 | 2161 | } |
| 2162 | 2162 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.CUSTOM) { |
| 2163 | 2163 | apiUrl = request.body.custom_url; |
| 2164 | 2164 | apiKey = readSecret(request.user.directories, SECRET_KEYS.CUSTOM, request.body.secret_id); |
| 2165 | 2165 | headers = {}; |
| 2166 | 2166 | bodyParams = { |
| 2167 | 2167 | logprobs: request.body.logprobs, |
| @@ -2179,7 +2179,7 @@ router.post('/generate', async function (request, response) { | ||
| 2179 | 2179 | embedOpenRouterMedia(request.body.messages, { audio: true, video: false }); |
| 2180 | 2180 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.PERPLEXITY) { |
| 2181 | 2181 | apiUrl = API_PERPLEXITY; |
| 2182 | 2182 | apiKey = readSecret(request.user.directories, SECRET_KEYS.PERPLEXITY, request.body.secret_id); |
| 2183 | 2183 | headers = {}; |
| 2184 | 2184 | bodyParams = { |
| 2185 | 2185 | reasoning_effort: request.body.reasoning_effort, |
| @@ -2195,7 +2195,7 @@ router.post('/generate', async function (request, response) { | ||
| 2195 | 2195 | } |
| 2196 | 2196 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.GROQ) { |
| 2197 | 2197 | apiUrl = API_GROQ; |
| 2198 | 2198 | apiKey = readSecret(request.user.directories, SECRET_KEYS.GROQ, request.body.secret_id); |
| 2199 | 2199 | headers = {}; |
| 2200 | 2200 | bodyParams = {}; |
| 2201 | 2201 | if (request.body.json_schema) { |
| @@ -2211,7 +2211,7 @@ router.post('/generate', async function (request, response) { | ||
| 2211 | 2211 | } |
| 2212 | 2212 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.FIREWORKS) { |
| 2213 | 2213 | apiUrl = API_FIREWORKS; |
| 2214 | 2214 | apiKey = readSecret(request.user.directories, SECRET_KEYS.FIREWORKS, request.body.secret_id); |
| 2215 | 2215 | headers = {}; |
| 2216 | 2216 | bodyParams = {}; |
| 2217 | 2217 | if (request.body.json_schema) { |
| @@ -2227,7 +2227,7 @@ router.post('/generate', async function (request, response) { | ||
| 2227 | 2227 | } |
| 2228 | 2228 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.NANOGPT) { |
| 2229 | 2229 | apiUrl = API_NANOGPT; |
| 2230 | 2230 | apiKey = readSecret(request.user.directories, SECRET_KEYS.NANOGPT, request.body.secret_id); |
| 2231 | 2231 | headers = {}; |
| 2232 | 2232 | bodyParams = {}; |
| 2233 | 2233 | if (request.body.enable_web_search && !/:online$/.test(request.body.model)) { |
| @@ -2256,7 +2256,7 @@ router.post('/generate', async function (request, response) { | ||
| 2256 | 2256 | } |
| 2257 | 2257 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.POLLINATIONS) { |
| 2258 | 2258 | apiUrl = API_POLLINATIONS; |
| 2259 | 2259 | apiKey = readSecret(request.user.directories, SECRET_KEYS.POLLINATIONS, request.body.secret_id); |
| 2260 | 2260 | headers = {}; |
| 2261 | 2261 | bodyParams = { |
| 2262 | 2262 | reasoning_effort: request.body.reasoning_effort, |
| @@ -2272,7 +2272,7 @@ router.post('/generate', async function (request, response) { | ||
| 2272 | 2272 | } |
| 2273 | 2273 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MOONSHOT) { |
| 2274 | 2274 | apiUrl = new URL(request.body.reverse_proxy || API_MOONSHOT).toString(); |
| 2275 | 2275 | apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MOONSHOT, request.body.secret_id); |
| 2276 | 2276 | headers = {}; |
| 2277 | 2277 | bodyParams = { |
| 2278 | 2278 | thinking: { |
| @@ -2284,7 +2284,7 @@ router.post('/generate', async function (request, response) { | ||
| 2284 | 2284 | : addAssistantPrefix(request.body.messages, [], 'partial'); |
| 2285 | 2285 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.COMETAPI) { |
| 2286 | 2286 | apiUrl = API_COMETAPI; |
| 2287 | 2287 | apiKey = readSecret(request.user.directories, SECRET_KEYS.COMETAPI, request.body.secret_id); |
| 2288 | 2288 | headers = {}; |
| 2289 | 2289 | bodyParams = { |
| 2290 | 2290 | reasoning_effort: request.body.reasoning_effort, |
| @@ -2293,7 +2293,7 @@ router.post('/generate', async function (request, response) { | ||
| 2293 | 2293 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.ZAI) { |
| 2294 | 2294 | const defaultApiUrl = request.body.zai_endpoint === ZAI_ENDPOINT.CODING ? API_ZAI_CODING : API_ZAI_COMMON; |
| 2295 | 2295 | apiUrl = new URL(request.body.reverse_proxy || defaultApiUrl).toString(); |
| 2296 | 2296 | apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.ZAI, request.body.secret_id); |
| 2297 | 2297 | headers = { |
| 2298 | 2298 | 'Accept-Language': 'en-US,en', |
| 2299 | 2299 | }; |
| @@ -2309,7 +2309,7 @@ router.post('/generate', async function (request, response) { | ||
| 2309 | 2309 | const defaultApiUrl = request.body.siliconflow_endpoint === SILICONFLOW_ENDPOINT.CN |
| 2310 | 2310 | ? API_SILICONFLOW_CN : API_SILICONFLOW; |
| 2311 | 2311 | apiUrl = defaultApiUrl; |
| 2312 | 2312 | apiKey = readSecret(request.user.directories, SECRET_KEYS.SILICONFLOW, request.body.secret_id); |
| 2313 | 2313 | headers = {}; |
| 2314 | 2314 | bodyParams = {}; |
| 2315 | 2315 | if (request.body.json_schema) { |
| @@ -441,10 +441,11 @@ export function deleteSecret(directories, key) { | ||
| 441 | 441 | * Reads a secret from the secrets file |
| 442 | 442 | * @param {import('../users.js').UserDirectoryList} directories User directories |
| 443 | 443 | * @param {string} key Secret key |
| 444 | + * @param {string?} id Secret ID (optional) | |
| 444 | 445 | * @returns {string} Secret value |
| 445 | 446 | */ |
| 446 | 447 | export function readSecret(directories, key, id = null) { |
| 447 | 448 | return new SecretManager(directories).readSecret(key, nullid); |
| 448 | 449 | } |
| 449 | 450 | |
| 450 | 451 | /** |