mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
🧑💻 refactor: Display Client-facing Errors (#2476)
* fix(Google): allow presets to configure expected maxOutputTokens * refactor: standardize client-facing errors * refactor(checkUserKeyExpiry): pass endpoint instead of custom message * feat(UserService): JSDocs and getUserKeyValues * refactor: add NO_BASE_URL error type, make use of getUserKeyValues, throw user-specific errors * ci: update tests with recent changes
This commit is contained in:
parent
6db91978ca
commit
c937b8cd07
24 changed files with 343 additions and 149 deletions
|
|
@ -1,5 +1,6 @@
|
|||
require('dotenv').config();
|
||||
const { KeyvFile } = require('keyv-file');
|
||||
const { EModelEndpoint } = require('librechat-data-provider');
|
||||
const { getUserKey, checkUserKeyExpiry } = require('~/server/services/UserService');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
|
|
@ -23,10 +24,7 @@ const askBing = async ({
|
|||
|
||||
let key = null;
|
||||
if (expiresAt && isUserProvided) {
|
||||
checkUserKeyExpiry(
|
||||
expiresAt,
|
||||
'Your BingAI Cookies have expired. Please provide your cookies again.',
|
||||
);
|
||||
checkUserKeyExpiry(expiresAt, EModelEndpoint.bingAI);
|
||||
key = await getUserKey({ userId, name: 'bingAI' });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
require('dotenv').config();
|
||||
const { KeyvFile } = require('keyv-file');
|
||||
const { Constants } = require('librechat-data-provider');
|
||||
const { Constants, EModelEndpoint } = require('librechat-data-provider');
|
||||
const { getUserKey, checkUserKeyExpiry } = require('../server/services/UserService');
|
||||
|
||||
const browserClient = async ({
|
||||
|
|
@ -18,10 +18,7 @@ const browserClient = async ({
|
|||
|
||||
let key = null;
|
||||
if (expiresAt && isUserProvided) {
|
||||
checkUserKeyExpiry(
|
||||
expiresAt,
|
||||
'Your ChatGPT Access Token has expired. Please provide your token again.',
|
||||
);
|
||||
checkUserKeyExpiry(expiresAt, EModelEndpoint.chatGPTBrowser);
|
||||
key = await getUserKey({ userId, name: 'chatGPTBrowser' });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ class GoogleClient extends BaseClient {
|
|||
this.isVisionModel = true;
|
||||
}
|
||||
|
||||
if (this.isVisionModel && !attachments) {
|
||||
if (this.isVisionModel && !attachments && this.modelOptions.model.includes('gemini-pro')) {
|
||||
this.modelOptions.model = 'gemini-pro';
|
||||
this.isVisionModel = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,9 @@ const handleAbortError = async (res, req, error, data) => {
|
|||
);
|
||||
}
|
||||
|
||||
const errorText = 'An error occurred while processing your request. Please contact the Admin.';
|
||||
const errorText = error?.message?.includes('"type"')
|
||||
? error.message
|
||||
: 'An error occurred while processing your request. Please contact the Admin.';
|
||||
|
||||
const respondWithError = async (partialText) => {
|
||||
let options = {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
const axios = require('axios');
|
||||
const { ErrorTypes } = require('librechat-data-provider');
|
||||
const denyRequest = require('./denyRequest');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
|
|
@ -24,7 +25,7 @@ async function moderateText(req, res, next) {
|
|||
const flagged = results.some((result) => result.flagged);
|
||||
|
||||
if (flagged) {
|
||||
const type = 'moderation';
|
||||
const type = ErrorTypes.MODERATION;
|
||||
const errorMessage = { type };
|
||||
return await denyRequest(req, res, errorMessage);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const { AnthropicClient } = require('~/app');
|
||||
const { EModelEndpoint } = require('librechat-data-provider');
|
||||
const { getUserKey, checkUserKeyExpiry } = require('~/server/services/UserService');
|
||||
const { AnthropicClient } = require('~/app');
|
||||
|
||||
const initializeClient = async ({ req, res, endpointOption }) => {
|
||||
const { ANTHROPIC_API_KEY, ANTHROPIC_REVERSE_PROXY, PROXY } = process.env;
|
||||
|
|
@ -7,14 +8,15 @@ const initializeClient = async ({ req, res, endpointOption }) => {
|
|||
const isUserProvided = ANTHROPIC_API_KEY === 'user_provided';
|
||||
|
||||
const anthropicApiKey = isUserProvided
|
||||
? await getAnthropicUserKey(req.user.id)
|
||||
? await getUserKey({ userId: req.user.id, name: EModelEndpoint.anthropic })
|
||||
: ANTHROPIC_API_KEY;
|
||||
|
||||
if (!anthropicApiKey) {
|
||||
throw new Error('Anthropic API key not provided. Please provide it again.');
|
||||
}
|
||||
|
||||
if (expiresAt && isUserProvided) {
|
||||
checkUserKeyExpiry(
|
||||
expiresAt,
|
||||
'Your ANTHROPIC_API_KEY has expired. Please provide your API key again.',
|
||||
);
|
||||
checkUserKeyExpiry(expiresAt, EModelEndpoint.anthropic);
|
||||
}
|
||||
|
||||
const client = new AnthropicClient(anthropicApiKey, {
|
||||
|
|
@ -31,8 +33,4 @@ const initializeClient = async ({ req, res, endpointOption }) => {
|
|||
};
|
||||
};
|
||||
|
||||
const getAnthropicUserKey = async (userId) => {
|
||||
return await getUserKey({ userId, name: 'anthropic' });
|
||||
};
|
||||
|
||||
module.exports = initializeClient;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
const OpenAI = require('openai');
|
||||
const { HttpsProxyAgent } = require('https-proxy-agent');
|
||||
const {
|
||||
ErrorTypes,
|
||||
EModelEndpoint,
|
||||
resolveHeaders,
|
||||
mapModelToAzureConfig,
|
||||
} = require('librechat-data-provider');
|
||||
const {
|
||||
getUserKey,
|
||||
getUserKeyValues,
|
||||
getUserKeyExpiry,
|
||||
checkUserKeyExpiry,
|
||||
} = require('~/server/services/UserService');
|
||||
|
|
@ -26,18 +27,8 @@ const initializeClient = async ({ req, res, endpointOption, initAppClient = fals
|
|||
userId: req.user.id,
|
||||
name: EModelEndpoint.assistants,
|
||||
});
|
||||
checkUserKeyExpiry(
|
||||
expiresAt,
|
||||
'Your Assistants API key has expired. Please provide your API key again.',
|
||||
);
|
||||
userValues = await getUserKey({ userId: req.user.id, name: EModelEndpoint.assistants });
|
||||
try {
|
||||
userValues = JSON.parse(userValues);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
'Invalid JSON provided for Assistants API user values. Please provide them again.',
|
||||
);
|
||||
}
|
||||
checkUserKeyExpiry(expiresAt, EModelEndpoint.assistants);
|
||||
userValues = await getUserKeyValues({ userId: req.user.id, name: EModelEndpoint.assistants });
|
||||
}
|
||||
|
||||
let apiKey = userProvidesKey ? userValues.apiKey : ASSISTANTS_API_KEY;
|
||||
|
|
@ -101,6 +92,14 @@ const initializeClient = async ({ req, res, endpointOption, initAppClient = fals
|
|||
}
|
||||
}
|
||||
|
||||
if (userProvidesKey & !apiKey) {
|
||||
throw new Error(
|
||||
JSON.stringify({
|
||||
type: ErrorTypes.NO_USER_KEY,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error('Assistants API key not provided. Please provide it again.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
// const OpenAI = require('openai');
|
||||
const { HttpsProxyAgent } = require('https-proxy-agent');
|
||||
const { getUserKey, getUserKeyExpiry } = require('~/server/services/UserService');
|
||||
const { ErrorTypes } = require('librechat-data-provider');
|
||||
const { getUserKey, getUserKeyExpiry, getUserKeyValues } = require('~/server/services/UserService');
|
||||
const initializeClient = require('./initializeClient');
|
||||
// const { OpenAIClient } = require('~/app');
|
||||
|
||||
jest.mock('~/server/services/UserService', () => ({
|
||||
getUserKey: jest.fn(),
|
||||
getUserKeyExpiry: jest.fn(),
|
||||
getUserKeyValues: jest.fn(),
|
||||
checkUserKeyExpiry: jest.requireActual('~/server/services/UserService').checkUserKeyExpiry,
|
||||
}));
|
||||
|
||||
|
|
@ -52,9 +54,7 @@ describe('initializeClient', () => {
|
|||
process.env.ASSISTANTS_API_KEY = 'user_provided';
|
||||
process.env.ASSISTANTS_BASE_URL = 'user_provided';
|
||||
|
||||
getUserKey.mockResolvedValue(
|
||||
JSON.stringify({ apiKey: 'user-api-key', baseURL: 'https://user.api.url' }),
|
||||
);
|
||||
getUserKeyValues.mockResolvedValue({ apiKey: 'user-api-key', baseURL: 'https://user.api.url' });
|
||||
getUserKeyExpiry.mockResolvedValue(isoString);
|
||||
|
||||
const req = { user: { id: 'user123' }, app };
|
||||
|
|
@ -70,11 +70,24 @@ describe('initializeClient', () => {
|
|||
process.env.ASSISTANTS_API_KEY = 'user_provided';
|
||||
getUserKey.mockResolvedValue('invalid-json');
|
||||
getUserKeyExpiry.mockResolvedValue(isoString);
|
||||
getUserKeyValues.mockImplementation(() => {
|
||||
let userValues = getUserKey();
|
||||
try {
|
||||
userValues = JSON.parse(userValues);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
JSON.stringify({
|
||||
type: ErrorTypes.INVALID_USER_KEY,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return userValues;
|
||||
});
|
||||
|
||||
const req = { user: { id: 'user123' } };
|
||||
const res = {};
|
||||
|
||||
await expect(initializeClient({ req, res })).rejects.toThrow(/Invalid JSON/);
|
||||
await expect(initializeClient({ req, res })).rejects.toThrow(/invalid_user_key/);
|
||||
});
|
||||
|
||||
test('throws error if API key is not provided', async () => {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
const {
|
||||
CacheKeys,
|
||||
ErrorTypes,
|
||||
envVarRegex,
|
||||
EModelEndpoint,
|
||||
FetchTokenConfig,
|
||||
extractEnvVariable,
|
||||
} = require('librechat-data-provider');
|
||||
const { getUserKey, checkUserKeyExpiry } = require('~/server/services/UserService');
|
||||
const { getUserKeyValues, checkUserKeyExpiry } = require('~/server/services/UserService');
|
||||
const getCustomConfig = require('~/server/services/Config/getCustomConfig');
|
||||
const { fetchModels } = require('~/server/services/ModelService');
|
||||
const getLogStores = require('~/cache/getLogStores');
|
||||
|
|
@ -48,21 +49,29 @@ const initializeClient = async ({ req, res, endpointOption }) => {
|
|||
|
||||
let userValues = null;
|
||||
if (expiresAt && (userProvidesKey || userProvidesURL)) {
|
||||
checkUserKeyExpiry(
|
||||
expiresAt,
|
||||
`Your API values for ${endpoint} have expired. Please configure them again.`,
|
||||
);
|
||||
userValues = await getUserKey({ userId: req.user.id, name: endpoint });
|
||||
try {
|
||||
userValues = JSON.parse(userValues);
|
||||
} catch (e) {
|
||||
throw new Error(`Invalid JSON provided for ${endpoint} user values.`);
|
||||
}
|
||||
checkUserKeyExpiry(expiresAt, endpoint);
|
||||
userValues = await getUserKeyValues({ userId: req.user.id, name: endpoint });
|
||||
}
|
||||
|
||||
let apiKey = userProvidesKey ? userValues?.apiKey : CUSTOM_API_KEY;
|
||||
let baseURL = userProvidesURL ? userValues?.baseURL : CUSTOM_BASE_URL;
|
||||
|
||||
if (userProvidesKey & !apiKey) {
|
||||
throw new Error(
|
||||
JSON.stringify({
|
||||
type: ErrorTypes.NO_USER_KEY,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (userProvidesURL && !baseURL) {
|
||||
throw new Error(
|
||||
JSON.stringify({
|
||||
type: ErrorTypes.NO_BASE_URL,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error(`${endpoint} API key not provided.`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
const { GoogleClient } = require('~/app');
|
||||
const { EModelEndpoint, AuthKeys } = require('librechat-data-provider');
|
||||
const { getUserKey, checkUserKeyExpiry } = require('~/server/services/UserService');
|
||||
const { GoogleClient } = require('~/app');
|
||||
|
||||
const initializeClient = async ({ req, res, endpointOption }) => {
|
||||
const { GOOGLE_KEY, GOOGLE_REVERSE_PROXY, PROXY } = process.env;
|
||||
|
|
@ -9,10 +9,7 @@ const initializeClient = async ({ req, res, endpointOption }) => {
|
|||
|
||||
let userKey = null;
|
||||
if (expiresAt && isUserProvided) {
|
||||
checkUserKeyExpiry(
|
||||
expiresAt,
|
||||
'Your Google Credentials have expired. Please provide your Service Account JSON Key or Generative Language API Key again.',
|
||||
);
|
||||
checkUserKeyExpiry(expiresAt, EModelEndpoint.google);
|
||||
userKey = await getUserKey({ userId: req.user.id, name: EModelEndpoint.google });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,10 @@
|
|||
// file deepcode ignore HardcodedNonCryptoSecret: No hardcoded secrets
|
||||
|
||||
const { getUserKey } = require('~/server/services/UserService');
|
||||
const initializeClient = require('./initializeClient');
|
||||
const { GoogleClient } = require('~/app');
|
||||
const { checkUserKeyExpiry, getUserKey } = require('../../UserService');
|
||||
|
||||
jest.mock('../../UserService', () => ({
|
||||
checkUserKeyExpiry: jest.fn().mockImplementation((expiresAt, errorMessage) => {
|
||||
if (new Date(expiresAt) < new Date()) {
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
}),
|
||||
jest.mock('~/server/services/UserService', () => ({
|
||||
checkUserKeyExpiry: jest.requireActual('~/server/services/UserService').checkUserKeyExpiry,
|
||||
getUserKey: jest.fn().mockImplementation(() => ({})),
|
||||
}));
|
||||
|
||||
|
|
@ -74,13 +69,8 @@ describe('google/initializeClient', () => {
|
|||
};
|
||||
const res = {};
|
||||
const endpointOption = { modelOptions: { model: 'default-model' } };
|
||||
|
||||
checkUserKeyExpiry.mockImplementation((expiresAt, errorMessage) => {
|
||||
throw new Error(errorMessage);
|
||||
});
|
||||
|
||||
await expect(initializeClient({ req, res, endpointOption })).rejects.toThrow(
|
||||
/Your Google Credentials have expired/,
|
||||
/expired_user_key/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ const {
|
|||
mapModelToAzureConfig,
|
||||
resolveHeaders,
|
||||
} = require('librechat-data-provider');
|
||||
const { getUserKey, checkUserKeyExpiry } = require('~/server/services/UserService');
|
||||
const { getUserKeyValues, checkUserKeyExpiry } = require('~/server/services/UserService');
|
||||
const { isEnabled, isUserProvided } = require('~/server/utils');
|
||||
const { getAzureCredentials } = require('~/utils');
|
||||
const { PluginsClient } = require('~/app');
|
||||
|
|
@ -49,18 +49,8 @@ const initializeClient = async ({ req, res, endpointOption }) => {
|
|||
|
||||
let userValues = null;
|
||||
if (expiresAt && (userProvidesKey || userProvidesURL)) {
|
||||
checkUserKeyExpiry(
|
||||
expiresAt,
|
||||
'Your OpenAI API values have expired. Please provide them again.',
|
||||
);
|
||||
userValues = await getUserKey({ userId: req.user.id, name: endpoint });
|
||||
try {
|
||||
userValues = JSON.parse(userValues);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Invalid JSON provided for ${endpoint} user values. Please provide them again.`,
|
||||
);
|
||||
}
|
||||
checkUserKeyExpiry(expiresAt, endpoint);
|
||||
userValues = await getUserKeyValues({ userId: req.user.id, name: endpoint });
|
||||
}
|
||||
|
||||
let apiKey = userProvidesKey ? userValues?.apiKey : credentials[endpoint];
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
// gptPlugins/initializeClient.spec.js
|
||||
const { EModelEndpoint, validateAzureGroups } = require('librechat-data-provider');
|
||||
const { getUserKey } = require('~/server/services/UserService');
|
||||
const { EModelEndpoint, ErrorTypes, validateAzureGroups } = require('librechat-data-provider');
|
||||
const { getUserKey, getUserKeyValues } = require('~/server/services/UserService');
|
||||
const initializeClient = require('./initializeClient');
|
||||
const { PluginsClient } = require('~/app');
|
||||
|
||||
// Mock getUserKey since it's the only function we want to mock
|
||||
jest.mock('~/server/services/UserService', () => ({
|
||||
getUserKey: jest.fn(),
|
||||
getUserKeyValues: jest.fn(),
|
||||
checkUserKeyExpiry: jest.requireActual('~/server/services/UserService').checkUserKeyExpiry,
|
||||
}));
|
||||
|
||||
|
|
@ -205,7 +206,7 @@ describe('gptPlugins/initializeClient', () => {
|
|||
const res = {};
|
||||
const endpointOption = { modelOptions: { model: 'default-model' } };
|
||||
|
||||
getUserKey.mockResolvedValue(JSON.stringify({ apiKey: 'test-user-provided-openai-api-key' }));
|
||||
getUserKeyValues.mockResolvedValue({ apiKey: 'test-user-provided-openai-api-key' });
|
||||
|
||||
const { openAIApiKey } = await initializeClient({ req, res, endpointOption });
|
||||
|
||||
|
|
@ -225,14 +226,12 @@ describe('gptPlugins/initializeClient', () => {
|
|||
const res = {};
|
||||
const endpointOption = { modelOptions: { model: 'test-model' } };
|
||||
|
||||
getUserKey.mockResolvedValue(
|
||||
JSON.stringify({
|
||||
apiKey: JSON.stringify({
|
||||
azureOpenAIApiKey: 'test-user-provided-azure-api-key',
|
||||
azureOpenAIApiDeploymentName: 'test-deployment',
|
||||
}),
|
||||
getUserKeyValues.mockResolvedValue({
|
||||
apiKey: JSON.stringify({
|
||||
azureOpenAIApiKey: 'test-user-provided-azure-api-key',
|
||||
azureOpenAIApiDeploymentName: 'test-deployment',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
const { azure } = await initializeClient({ req, res, endpointOption });
|
||||
|
||||
|
|
@ -251,7 +250,9 @@ describe('gptPlugins/initializeClient', () => {
|
|||
const res = {};
|
||||
const endpointOption = { modelOptions: { model: 'default-model' } };
|
||||
|
||||
await expect(initializeClient({ req, res, endpointOption })).rejects.toThrow(/Your OpenAI API/);
|
||||
await expect(initializeClient({ req, res, endpointOption })).rejects.toThrow(
|
||||
/expired_user_key/,
|
||||
);
|
||||
});
|
||||
|
||||
test('should throw an error if the user-provided Azure key is invalid JSON', async () => {
|
||||
|
|
@ -268,9 +269,22 @@ describe('gptPlugins/initializeClient', () => {
|
|||
|
||||
// Simulate an invalid JSON string returned from getUserKey
|
||||
getUserKey.mockResolvedValue('invalid-json');
|
||||
getUserKeyValues.mockImplementation(() => {
|
||||
let userValues = getUserKey();
|
||||
try {
|
||||
userValues = JSON.parse(userValues);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
JSON.stringify({
|
||||
type: ErrorTypes.INVALID_USER_KEY,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return userValues;
|
||||
});
|
||||
|
||||
await expect(initializeClient({ req, res, endpointOption })).rejects.toThrow(
|
||||
/Invalid JSON provided/,
|
||||
/invalid_user_key/,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -305,9 +319,22 @@ describe('gptPlugins/initializeClient', () => {
|
|||
|
||||
// Mock getUserKey to return a non-JSON string
|
||||
getUserKey.mockResolvedValue('not-a-json');
|
||||
getUserKeyValues.mockImplementation(() => {
|
||||
let userValues = getUserKey();
|
||||
try {
|
||||
userValues = JSON.parse(userValues);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
JSON.stringify({
|
||||
type: ErrorTypes.INVALID_USER_KEY,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return userValues;
|
||||
});
|
||||
|
||||
await expect(initializeClient({ req, res, endpointOption })).rejects.toThrow(
|
||||
/Invalid JSON provided for openAI user values/,
|
||||
/invalid_user_key/,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -369,9 +396,10 @@ describe('gptPlugins/initializeClient', () => {
|
|||
const res = {};
|
||||
const endpointOption = {};
|
||||
|
||||
getUserKey.mockResolvedValue(
|
||||
JSON.stringify({ apiKey: 'test', baseURL: 'https://user-provided-url.com' }),
|
||||
);
|
||||
getUserKeyValues.mockResolvedValue({
|
||||
apiKey: 'test',
|
||||
baseURL: 'https://user-provided-url.com',
|
||||
});
|
||||
|
||||
const result = await initializeClient({ req, res, endpointOption });
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
const {
|
||||
ErrorTypes,
|
||||
EModelEndpoint,
|
||||
resolveHeaders,
|
||||
mapModelToAzureConfig,
|
||||
} = require('librechat-data-provider');
|
||||
const { getUserKey, checkUserKeyExpiry } = require('~/server/services/UserService');
|
||||
const { getUserKeyValues, checkUserKeyExpiry } = require('~/server/services/UserService');
|
||||
const { isEnabled, isUserProvided } = require('~/server/utils');
|
||||
const { getAzureCredentials } = require('~/utils');
|
||||
const { OpenAIClient } = require('~/app');
|
||||
|
|
@ -36,18 +37,8 @@ const initializeClient = async ({ req, res, endpointOption }) => {
|
|||
|
||||
let userValues = null;
|
||||
if (expiresAt && (userProvidesKey || userProvidesURL)) {
|
||||
checkUserKeyExpiry(
|
||||
expiresAt,
|
||||
'Your OpenAI API values have expired. Please provide them again.',
|
||||
);
|
||||
userValues = await getUserKey({ userId: req.user.id, name: endpoint });
|
||||
try {
|
||||
userValues = JSON.parse(userValues);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Invalid JSON provided for ${endpoint} user values. Please provide them again.`,
|
||||
);
|
||||
}
|
||||
checkUserKeyExpiry(expiresAt, endpoint);
|
||||
userValues = await getUserKeyValues({ userId: req.user.id, name: endpoint });
|
||||
}
|
||||
|
||||
let apiKey = userProvidesKey ? userValues?.apiKey : credentials[endpoint];
|
||||
|
|
@ -99,8 +90,16 @@ const initializeClient = async ({ req, res, endpointOption }) => {
|
|||
apiKey = clientOptions.azure.azureOpenAIApiKey;
|
||||
}
|
||||
|
||||
if (userProvidesKey & !apiKey) {
|
||||
throw new Error(
|
||||
JSON.stringify({
|
||||
type: ErrorTypes.NO_USER_KEY,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error(`${endpoint} API key not provided. Please provide it again.`);
|
||||
throw new Error(`${endpoint} API Key not provided.`);
|
||||
}
|
||||
|
||||
const client = new OpenAIClient(apiKey, clientOptions);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
const { EModelEndpoint, validateAzureGroups } = require('librechat-data-provider');
|
||||
const { getUserKey } = require('~/server/services/UserService');
|
||||
const { EModelEndpoint, ErrorTypes, validateAzureGroups } = require('librechat-data-provider');
|
||||
const { getUserKey, getUserKeyValues } = require('~/server/services/UserService');
|
||||
const initializeClient = require('./initializeClient');
|
||||
const { OpenAIClient } = require('~/app');
|
||||
|
||||
// Mock getUserKey since it's the only function we want to mock
|
||||
jest.mock('~/server/services/UserService', () => ({
|
||||
getUserKey: jest.fn(),
|
||||
getUserKeyValues: jest.fn(),
|
||||
checkUserKeyExpiry: jest.requireActual('~/server/services/UserService').checkUserKeyExpiry,
|
||||
}));
|
||||
|
||||
|
|
@ -200,7 +201,9 @@ describe('initializeClient', () => {
|
|||
const res = {};
|
||||
const endpointOption = {};
|
||||
|
||||
await expect(initializeClient({ req, res, endpointOption })).rejects.toThrow(/Your OpenAI API/);
|
||||
await expect(initializeClient({ req, res, endpointOption })).rejects.toThrow(
|
||||
/expired_user_key/,
|
||||
);
|
||||
});
|
||||
|
||||
test('should throw an error if no API keys are provided in the environment', async () => {
|
||||
|
|
@ -217,7 +220,7 @@ describe('initializeClient', () => {
|
|||
const endpointOption = {};
|
||||
|
||||
await expect(initializeClient({ req, res, endpointOption })).rejects.toThrow(
|
||||
`${EModelEndpoint.openAI} API key not provided.`,
|
||||
`${EModelEndpoint.openAI} API Key not provided.`,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -241,7 +244,7 @@ describe('initializeClient', () => {
|
|||
process.env.OPENAI_API_KEY = 'user_provided';
|
||||
|
||||
// Mock getUserKey to return the expected key
|
||||
getUserKey.mockResolvedValue(JSON.stringify({ apiKey: 'test-user-provided-openai-api-key' }));
|
||||
getUserKeyValues.mockResolvedValue({ apiKey: 'test-user-provided-openai-api-key' });
|
||||
|
||||
// Call the initializeClient function
|
||||
const result = await initializeClient({ req, res, endpointOption });
|
||||
|
|
@ -266,7 +269,9 @@ describe('initializeClient', () => {
|
|||
// Mock getUserKey to return an invalid key
|
||||
getUserKey.mockResolvedValue(invalidKey);
|
||||
|
||||
await expect(initializeClient({ req, res, endpointOption })).rejects.toThrow(/Your OpenAI API/);
|
||||
await expect(initializeClient({ req, res, endpointOption })).rejects.toThrow(
|
||||
/expired_user_key/,
|
||||
);
|
||||
});
|
||||
|
||||
test('should throw an error when user-provided values are not valid JSON', async () => {
|
||||
|
|
@ -281,9 +286,22 @@ describe('initializeClient', () => {
|
|||
|
||||
// Mock getUserKey to return a non-JSON string
|
||||
getUserKey.mockResolvedValue('not-a-json');
|
||||
getUserKeyValues.mockImplementation(() => {
|
||||
let userValues = getUserKey();
|
||||
try {
|
||||
userValues = JSON.parse(userValues);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
JSON.stringify({
|
||||
type: ErrorTypes.INVALID_USER_KEY,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return userValues;
|
||||
});
|
||||
|
||||
await expect(initializeClient({ req, res, endpointOption })).rejects.toThrow(
|
||||
/Invalid JSON provided for openAI user values/,
|
||||
/invalid_user_key/,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -347,9 +365,10 @@ describe('initializeClient', () => {
|
|||
const res = {};
|
||||
const endpointOption = {};
|
||||
|
||||
getUserKey.mockResolvedValue(
|
||||
JSON.stringify({ apiKey: 'test', baseURL: 'https://user-provided-url.com' }),
|
||||
);
|
||||
getUserKeyValues.mockResolvedValue({
|
||||
apiKey: 'test',
|
||||
baseURL: 'https://user-provided-url.com',
|
||||
});
|
||||
|
||||
const result = await initializeClient({ req, res, endpointOption });
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,19 @@
|
|||
const { User, Key } = require('~/models');
|
||||
const { ErrorTypes } = require('librechat-data-provider');
|
||||
const { encrypt, decrypt } = require('~/server/utils');
|
||||
const { User, Key } = require('~/models');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
/**
|
||||
* Updates the plugins for a user based on the action specified (install/uninstall).
|
||||
* @async
|
||||
* @param {Object} user - The user whose plugins are to be updated.
|
||||
* @param {string} pluginKey - The key of the plugin to install or uninstall.
|
||||
* @param {'install' | 'uninstall'} action - The action to perform, 'install' or 'uninstall'.
|
||||
* @returns {Promise<Object>} The result of the update operation.
|
||||
* @throws Logs the error internally if the update operation fails.
|
||||
* @description This function updates the plugin array of a user document based on the specified action.
|
||||
* It adds a plugin key to the plugins array for an 'install' action, and removes it for an 'uninstall' action.
|
||||
*/
|
||||
const updateUserPluginsService = async (user, pluginKey, action) => {
|
||||
try {
|
||||
if (action === 'install') {
|
||||
|
|
@ -21,14 +33,64 @@ const updateUserPluginsService = async (user, pluginKey, action) => {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves and decrypts the key value for a given user identified by userId and identifier name.
|
||||
* @param {Object} params - The parameters object.
|
||||
* @param {string} params.userId - The unique identifier for the user.
|
||||
* @param {string} params.name - The name associated with the key.
|
||||
* @returns {Promise<string>} The decrypted key value.
|
||||
* @throws {Error} Throws an error if the key is not found or if there is a problem during key retrieval.
|
||||
* @description This function searches for a user's key in the database using their userId and name.
|
||||
* If found, it decrypts the value of the key and returns it. If no key is found, it throws
|
||||
* an error indicating that there is no user key available.
|
||||
*/
|
||||
const getUserKey = async ({ userId, name }) => {
|
||||
const keyValue = await Key.findOne({ userId, name }).lean();
|
||||
if (!keyValue) {
|
||||
throw new Error('User-provided key not found');
|
||||
throw new Error(
|
||||
JSON.stringify({
|
||||
type: ErrorTypes.NO_USER_KEY,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return decrypt(keyValue.value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves, decrypts, and parses the key values for a given user identified by userId and name.
|
||||
* @param {Object} params - The parameters object.
|
||||
* @param {string} params.userId - The unique identifier for the user.
|
||||
* @param {string} params.name - The name associated with the key.
|
||||
* @returns {Promise<Record<string,string>>} The decrypted and parsed key values.
|
||||
* @throws {Error} Throws an error if the key is invalid or if there is a problem during key value parsing.
|
||||
* @description This function retrieves a user's encrypted key using their userId and name, decrypts it,
|
||||
* and then attempts to parse the decrypted string into a JSON object. If the parsing fails,
|
||||
* it throws an error indicating that the user key is invalid.
|
||||
*/
|
||||
const getUserKeyValues = async ({ userId, name }) => {
|
||||
let userValues = await getUserKey({ userId, name });
|
||||
try {
|
||||
userValues = JSON.parse(userValues);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
JSON.stringify({
|
||||
type: ErrorTypes.INVALID_USER_KEY,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return userValues;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the expiry information of a user's key identified by userId and name.
|
||||
* @async
|
||||
* @param {Object} params - The parameters object.
|
||||
* @param {string} params.userId - The unique identifier for the user.
|
||||
* @param {string} params.name - The name associated with the key.
|
||||
* @returns {Promise<{expiresAt: Date | null}>} The expiry date of the key or null if the key doesn't exist.
|
||||
* @description This function fetches a user's key from the database using their userId and name and
|
||||
* returns its expiry date. If the key is not found, it returns null for the expiry date.
|
||||
*/
|
||||
const getUserKeyExpiry = async ({ userId, name }) => {
|
||||
const keyValue = await Key.findOne({ userId, name }).lean();
|
||||
if (!keyValue) {
|
||||
|
|
@ -37,6 +99,18 @@ const getUserKeyExpiry = async ({ userId, name }) => {
|
|||
return { expiresAt: keyValue.expiresAt };
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates or inserts a new key for a given user identified by userId and name, with a specified value and expiry date.
|
||||
* @async
|
||||
* @param {Object} params - The parameters object.
|
||||
* @param {string} params.userId - The unique identifier for the user.
|
||||
* @param {string} params.name - The name associated with the key.
|
||||
* @param {string} params.value - The value to be encrypted and stored as the key's value.
|
||||
* @param {Date} params.expiresAt - The expiry date for the key.
|
||||
* @returns {Promise<Object>} The updated or newly inserted key document.
|
||||
* @description This function either updates an existing user key or inserts a new one into the database,
|
||||
* after encrypting the provided value. It sets the provided expiry date for the key.
|
||||
*/
|
||||
const updateUserKey = async ({ userId, name, value, expiresAt }) => {
|
||||
const encryptedValue = encrypt(value);
|
||||
return await Key.findOneAndUpdate(
|
||||
|
|
@ -51,6 +125,18 @@ const updateUserKey = async ({ userId, name, value, expiresAt }) => {
|
|||
).lean();
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes a key or all keys for a given user identified by userId, optionally based on a specified name.
|
||||
* @async
|
||||
* @param {Object} params - The parameters object.
|
||||
* @param {string} params.userId - The unique identifier for the user.
|
||||
* @param {string} [params.name] - The name associated with the key to delete. If not provided and all is true, deletes all keys.
|
||||
* @param {boolean} [params.all=false] - Whether to delete all keys for the user.
|
||||
* @returns {Promise<Object>} The result of the deletion operation.
|
||||
* @description This function deletes a specific key or all keys for a user from the database.
|
||||
* If a name is provided and all is false, it deletes only the key with that name.
|
||||
* If all is true, it ignores the name and deletes all keys for the user.
|
||||
*/
|
||||
const deleteUserKey = async ({ userId, name, all = false }) => {
|
||||
if (all) {
|
||||
return await Key.deleteMany({ userId });
|
||||
|
|
@ -59,11 +145,23 @@ const deleteUserKey = async ({ userId, name, all = false }) => {
|
|||
await Key.findOneAndDelete({ userId, name }).lean();
|
||||
};
|
||||
|
||||
const checkUserKeyExpiry = (expiresAt, message) => {
|
||||
/**
|
||||
* Checks if a user key has expired based on the provided expiration date and endpoint.
|
||||
* If the key has expired, it throws an Error with details including the type of error, the expiration date, and the endpoint.
|
||||
*
|
||||
* @param {string} expiresAt - The expiration date of the user key in a format that can be parsed by the Date constructor.
|
||||
* @param {string} endpoint - The endpoint associated with the user key to be checked.
|
||||
* @throws {Error} Throws an error if the user key has expired. The error message is a stringified JSON object
|
||||
* containing the type of error (`ErrorTypes.EXPIRED_USER_KEY`), the expiration date in the local string format, and the endpoint.
|
||||
*/
|
||||
const checkUserKeyExpiry = (expiresAt, endpoint) => {
|
||||
const expiresAtDate = new Date(expiresAt);
|
||||
if (expiresAtDate < new Date()) {
|
||||
const expiryStr = `User-provided key expired at ${expiresAtDate.toLocaleString()}`;
|
||||
const errorMessage = message ? `${message}\n${expiryStr}` : expiryStr;
|
||||
const errorMessage = JSON.stringify({
|
||||
type: ErrorTypes.EXPIRED_USER_KEY,
|
||||
expiredAt: expiresAtDate.toLocaleString(),
|
||||
endpoint,
|
||||
});
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
};
|
||||
|
|
@ -71,6 +169,7 @@ const checkUserKeyExpiry = (expiresAt, message) => {
|
|||
module.exports = {
|
||||
updateUserPluginsService,
|
||||
getUserKey,
|
||||
getUserKeyValues,
|
||||
getUserKeyExpiry,
|
||||
updateUserKey,
|
||||
deleteUserKey,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ export type GenericSetter<T> = (value: T | ((currentValue: T) => T)) => void;
|
|||
|
||||
export type LastSelectedModels = Record<EModelEndpoint, string>;
|
||||
|
||||
export type LocalizeFunction = (phraseKey: string, ...values: string[]) => string;
|
||||
|
||||
export const mainTextareaId = 'prompt-textarea';
|
||||
|
||||
export enum IconContext {
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@ export default function Settings({ conversation, setOption, models, readonly }:
|
|||
const isGemini = model?.toLowerCase()?.includes('gemini');
|
||||
|
||||
const maxOutputTokensMax = isGemini
|
||||
? google.maxOutputTokens.maxGeminiPro
|
||||
? google.maxOutputTokens.maxGemini
|
||||
: google.maxOutputTokens.max;
|
||||
const maxOutputTokensDefault = isGemini
|
||||
? google.maxOutputTokens.defaultGeminiPro
|
||||
? google.maxOutputTokens.defaultGemini
|
||||
: google.maxOutputTokens.default;
|
||||
|
||||
useEffect(
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
// file deepcode ignore HardcodedNonCryptoSecret: No hardcoded secrets
|
||||
import { ViolationTypes } from 'librechat-data-provider';
|
||||
import { ViolationTypes, ErrorTypes } from 'librechat-data-provider';
|
||||
import type { TOpenAIMessage } from 'librechat-data-provider';
|
||||
import type { LocalizeFunction } from '~/common';
|
||||
import { formatJSON, extractJson, isJson } from '~/utils/json';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import CodeBlock from './CodeBlock';
|
||||
|
||||
const localizedErrorPrefix = 'com_error';
|
||||
|
||||
type TConcurrent = {
|
||||
limit: number;
|
||||
};
|
||||
|
|
@ -14,7 +18,7 @@ type TMessageLimit = {
|
|||
};
|
||||
|
||||
type TTokenBalance = {
|
||||
type: ViolationTypes;
|
||||
type: ViolationTypes | ErrorTypes;
|
||||
balance: number;
|
||||
tokenCost: number;
|
||||
promptTokens: number;
|
||||
|
|
@ -24,14 +28,26 @@ type TTokenBalance = {
|
|||
generations?: TOpenAIMessage[];
|
||||
};
|
||||
|
||||
type TExpiredKey = {
|
||||
expiredAt: string;
|
||||
endpoint: string;
|
||||
};
|
||||
|
||||
const errorMessages = {
|
||||
ban: 'Your account has been temporarily banned due to violations of our service.',
|
||||
[ErrorTypes.MODERATION]: 'com_error_moderation',
|
||||
[ErrorTypes.NO_USER_KEY]: 'com_error_no_user_key',
|
||||
[ErrorTypes.INVALID_USER_KEY]: 'com_error_invalid_user_key',
|
||||
[ErrorTypes.NO_BASE_URL]: 'com_error_no_base_url',
|
||||
[ErrorTypes.EXPIRED_USER_KEY]: (json: TExpiredKey, localize: LocalizeFunction) => {
|
||||
const { expiredAt, endpoint } = json;
|
||||
return localize('com_error_expired_user_key', endpoint, expiredAt);
|
||||
},
|
||||
[ViolationTypes.BAN]:
|
||||
'Your account has been temporarily banned due to violations of our service.',
|
||||
invalid_api_key:
|
||||
'Invalid API key. Please check your API key and try again. You can do this by clicking on the model logo in the left corner of the textbox and selecting "Set Token" for the current selected endpoint. Thank you for your understanding.',
|
||||
insufficient_quota:
|
||||
'We apologize for any inconvenience caused. The default API key has reached its limit. To continue using this service, please set up your own API key. You can do this by clicking on the model logo in the left corner of the textbox and selecting "Set Token" for the current selected endpoint. Thank you for your understanding.',
|
||||
moderation:
|
||||
'It appears that the content submitted has been flagged by our moderation system for not aligning with our community guidelines. We\'re unable to proceed with this specific topic. If you have any other questions or topics you\'d like to explore, please edit your message, or create a new conversation.',
|
||||
concurrent: (json: TConcurrent) => {
|
||||
const { limit } = json;
|
||||
const plural = limit > 1 ? 's' : '';
|
||||
|
|
@ -69,6 +85,7 @@ const errorMessages = {
|
|||
};
|
||||
|
||||
const Error = ({ text }: { text: string }) => {
|
||||
const localize = useLocalize();
|
||||
const jsonString = extractJson(text);
|
||||
const errorMessage = text.length > 512 && !jsonString ? text.slice(0, 512) + '...' : text;
|
||||
const defaultResponse = `Something went wrong. Here's the specific error message we encountered: ${errorMessage}`;
|
||||
|
|
@ -82,7 +99,9 @@ const Error = ({ text }: { text: string }) => {
|
|||
const keyExists = errorKey && errorMessages[errorKey];
|
||||
|
||||
if (keyExists && typeof errorMessages[errorKey] === 'function') {
|
||||
return errorMessages[errorKey](json);
|
||||
return errorMessages[errorKey](json, localize);
|
||||
} else if (keyExists && keyExists.startsWith(localizedErrorPrefix)) {
|
||||
return localize(errorMessages[errorKey]);
|
||||
} else if (keyExists) {
|
||||
return errorMessages[errorKey];
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,13 @@
|
|||
// file deepcode ignore HardcodedNonCryptoSecret: No hardcoded secrets present in this file
|
||||
|
||||
export default {
|
||||
com_error_moderation:
|
||||
'It appears that the content submitted has been flagged by our moderation system for not aligning with our community guidelines. We\'re unable to proceed with this specific topic. If you have any other questions or topics you\'d like to explore, please edit your message, or create a new conversation.',
|
||||
com_error_no_user_key: 'No key found. Please provide a key and try again.',
|
||||
com_error_no_base_url: 'No base URL found. Please provide one and try again.',
|
||||
com_error_invalid_user_key: 'Invalid key provided. Please provide a key and try again.',
|
||||
com_error_expired_user_key:
|
||||
'Provided key for {0} expired at {1}. Please provide a key and try again.',
|
||||
com_files_no_results: 'No results.',
|
||||
com_files_filter: 'Filter files...',
|
||||
com_files_number_selected: '{0} of {1} file(s) selected',
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { defaultEndpoints } from 'librechat-data-provider';
|
||||
import type { EModelEndpoint, TEndpointsConfig, TConfig } from 'librechat-data-provider';
|
||||
import type { LocalizeFunction } from '~/common';
|
||||
|
||||
export const getAssistantName = ({
|
||||
name,
|
||||
localize,
|
||||
}: {
|
||||
name?: string;
|
||||
localize: (phraseKey: string, ...values: string[]) => string;
|
||||
localize: LocalizeFunction;
|
||||
}) => {
|
||||
if (name && name.length > 0) {
|
||||
return name;
|
||||
|
|
|
|||
|
|
@ -515,6 +515,32 @@ export enum ViolationTypes {
|
|||
BAN = 'ban',
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum for error message types that are not "violations" as above, used to identify client-facing errors.
|
||||
*/
|
||||
export enum ErrorTypes {
|
||||
/**
|
||||
* No User-provided Key.
|
||||
*/
|
||||
NO_USER_KEY = 'no_user_key',
|
||||
/**
|
||||
* Expired User-provided Key.
|
||||
*/
|
||||
EXPIRED_USER_KEY = 'expired_user_key',
|
||||
/**
|
||||
* Invalid User-provided Key.
|
||||
*/
|
||||
INVALID_USER_KEY = 'invalid_user_key',
|
||||
/**
|
||||
* No Base URL Provided.
|
||||
*/
|
||||
NO_BASE_URL = 'no_base_url',
|
||||
/**
|
||||
* Moderation error
|
||||
*/
|
||||
MODERATION = 'moderation',
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum for authentication keys.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -437,13 +437,13 @@ export const generateGoogleSchema = (customGoogle: GoogleSettings) => {
|
|||
topK: true,
|
||||
})
|
||||
.transform((obj) => {
|
||||
const isGeminiPro = obj?.model?.toLowerCase()?.includes('gemini-pro');
|
||||
const isGemini = obj?.model?.toLowerCase()?.includes('gemini');
|
||||
|
||||
const maxOutputTokensMax = isGeminiPro
|
||||
? defaults.maxOutputTokens.maxGeminiPro
|
||||
const maxOutputTokensMax = isGemini
|
||||
? defaults.maxOutputTokens.maxGemini
|
||||
: defaults.maxOutputTokens.max;
|
||||
const maxOutputTokensDefault = isGeminiPro
|
||||
? defaults.maxOutputTokens.defaultGeminiPro
|
||||
const maxOutputTokensDefault = isGemini
|
||||
? defaults.maxOutputTokens.defaultGemini
|
||||
: defaults.maxOutputTokens.default;
|
||||
|
||||
let maxOutputTokens = obj.maxOutputTokens ?? maxOutputTokensDefault;
|
||||
|
|
|
|||
|
|
@ -111,8 +111,8 @@ export const googleSettings = {
|
|||
max: 2048,
|
||||
step: 1,
|
||||
default: 1024,
|
||||
maxGeminiPro: 8192,
|
||||
defaultGeminiPro: 8192,
|
||||
maxGemini: 8192,
|
||||
defaultGemini: 8192,
|
||||
},
|
||||
temperature: {
|
||||
min: 0,
|
||||
|
|
@ -374,13 +374,13 @@ export const googleSchema = tConversationSchema
|
|||
topK: true,
|
||||
})
|
||||
.transform((obj) => {
|
||||
const isGeminiPro = obj?.model?.toLowerCase()?.includes('gemini-pro');
|
||||
const isGemini = obj?.model?.toLowerCase()?.includes('gemini');
|
||||
|
||||
const maxOutputTokensMax = isGeminiPro
|
||||
? google.maxOutputTokens.maxGeminiPro
|
||||
const maxOutputTokensMax = isGemini
|
||||
? google.maxOutputTokens.maxGemini
|
||||
: google.maxOutputTokens.max;
|
||||
const maxOutputTokensDefault = isGeminiPro
|
||||
? google.maxOutputTokens.defaultGeminiPro
|
||||
const maxOutputTokensDefault = isGemini
|
||||
? google.maxOutputTokens.defaultGemini
|
||||
: google.maxOutputTokens.default;
|
||||
|
||||
let maxOutputTokens = obj.maxOutputTokens ?? maxOutputTokensDefault;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue