mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 08:12:00 +02:00

* docs: make_your_own.md formatting fix for mkdocs * feat: add express-mongo-sanitize feat: add login/registration rate limiting * chore: remove unnecessary console log * wip: remove token handling from localStorage to encrypted DB solution * refactor: minor change to UserService * fix mongo query and add keys route to server * fix backend controllers and simplify schema/crud * refactor: rename token to key to separate from access/refresh tokens, setTokenDialog -> setKeyDialog * refactor(schemas): TEndpointOption token -> key * refactor(api): use new encrypted key retrieval system * fix(SetKeyDialog): fix key prop error * fix(abortMiddleware): pass random UUID if messageId is not generated yet for proper error display on frontend * fix(getUserKey): wrong prop passed in arg, adds error handling * fix: prevent message without conversationId from saving to DB, prevents branching on the frontend to a new top-level branch * refactor: change wording of multiple display messages * refactor(checkExpiry -> checkUserKeyExpiry): move to UserService file * fix: type imports from common * refactor(SubmitButton): convert to TS * refactor(key.ts): change localStorage map key name * refactor: add new custom tailwind classes to better match openAI colors * chore: remove unnecessary warning and catch ScreenShot error * refactor: move userKey frontend logic to hooks and remove use of localStorage and instead query the DB * refactor: invalidate correct query key, memoize userKey hook, conditionally render SetKeyDialog to avoid unnecessary calls, refactor SubmitButton props and useEffect for showing 'provide key first' * fix(SetKeyDialog): use enum-like object for expiry values feat(Dropdown): add optionsClassName to dynamically change dropdown options container classes * fix: handle edge case where user had provided a key but the server changes to env variable for keys * refactor(OpenAI/titleConvo): move titling to client to retain authorized credentials in message lifecycle for titling * fix(azure): handle user_provided keys correctly for azure * feat: send user Id to OpenAI to differentiate users in completion requests * refactor(OpenAI/titleConvo): adding tokens helps minimize LLM from using the language in title response * feat: add delete endpoint for keys * chore: remove throttling of title * feat: add 'Data controls' to Settings, add 'Revoke' keys feature in Key Dialog and Data controls * refactor: reorganize PluginsClient files in langchain format * feat: use langchain for titling convos * chore: cleanup titling convo, with fallback to original method, escape braces, use only snippet for language detection * refactor: move helper functions to appropriate langchain folders for reusability * fix: userProvidesKey handling for gptPlugins * fix: frontend handling of plugins key * chore: cleanup logging and ts-ignore SSE * fix: forwardRef misuse in DangerButton * fix(GoogleConfig/FileUpload): localize errors and simplify validation with zod * fix: cleanup google logging and fix user provided key handling * chore: remove titling from google * chore: removing logging from browser endpoint * wip: fix menu flicker * feat: useLocalStorage hook * feat: add Tooltip for UI * refactor(EndpointMenu): utilize Tooltip and useLocalStorage, remove old 'New Chat' slide-over * fix(e2e): use testId for endpoint menu trigger * chore: final touches to EndpointMenu before future refactor to declutter component * refactor(localization): change select endpoint to open menu and add translations * chore: add final prop to error message response * ci: minor edits to facilitate testing * ci: new e2e test which tests for new key setting/revoking features
188 lines
5.1 KiB
JavaScript
188 lines
5.1 KiB
JavaScript
const axios = require('axios');
|
|
const express = require('express');
|
|
const router = express.Router();
|
|
const { availableTools } = require('../../app/clients/tools');
|
|
const { addOpenAPISpecs } = require('../../app/clients/tools/util/addOpenAPISpecs');
|
|
// const { getAzureCredentials, genAzureChatCompletion } = require('../../utils/');
|
|
|
|
const openAIApiKey = process.env.OPENAI_API_KEY;
|
|
const azureOpenAIApiKey = process.env.AZURE_API_KEY;
|
|
const useAzurePlugins = !!process.env.PLUGINS_USE_AZURE;
|
|
const userProvidedOpenAI = useAzurePlugins
|
|
? azureOpenAIApiKey === 'user_provided'
|
|
: openAIApiKey === 'user_provided';
|
|
|
|
const fetchOpenAIModels = async (opts = { azure: false, plugins: false }, _models = []) => {
|
|
let models = _models.slice() ?? [];
|
|
let apiKey = openAIApiKey;
|
|
let basePath = 'https://api.openai.com/v1';
|
|
if (opts.azure) {
|
|
return models;
|
|
// const azure = getAzureCredentials();
|
|
// basePath = (genAzureChatCompletion(azure))
|
|
// .split('/deployments')[0]
|
|
// .concat(`/models?api-version=${azure.azureOpenAIApiVersion}`);
|
|
// apiKey = azureOpenAIApiKey;
|
|
}
|
|
|
|
const reverseProxyUrl = process.env.OPENAI_REVERSE_PROXY;
|
|
if (reverseProxyUrl) {
|
|
basePath = reverseProxyUrl.match(/.*v1/)[0];
|
|
}
|
|
|
|
if (basePath.includes('v1') || opts.azure) {
|
|
try {
|
|
const res = await axios.get(`${basePath}${opts.azure ? '' : '/models'}`, {
|
|
headers: {
|
|
Authorization: `Bearer ${apiKey}`,
|
|
},
|
|
});
|
|
|
|
models = res.data.data.map((item) => item.id);
|
|
// console.log(`Fetched ${models.length} models from ${opts.azure ? 'Azure ' : ''}OpenAI API`);
|
|
} catch (err) {
|
|
console.log(`Failed to fetch models from ${opts.azure ? 'Azure ' : ''}OpenAI API`);
|
|
}
|
|
}
|
|
|
|
if (!reverseProxyUrl) {
|
|
const regex = /(text-davinci-003|gpt-)/;
|
|
models = models.filter((model) => regex.test(model));
|
|
}
|
|
return models;
|
|
};
|
|
|
|
const getOpenAIModels = async (opts = { azure: false, plugins: false }) => {
|
|
let models = [
|
|
'gpt-4',
|
|
'gpt-4-0613',
|
|
'gpt-3.5-turbo',
|
|
'gpt-3.5-turbo-16k',
|
|
'gpt-3.5-turbo-0613',
|
|
'gpt-3.5-turbo-0301',
|
|
];
|
|
|
|
if (!opts.plugins) {
|
|
models.push('text-davinci-003');
|
|
}
|
|
|
|
let key;
|
|
if (opts.azure) {
|
|
key = 'AZURE_OPENAI_MODELS';
|
|
} else if (opts.plugins) {
|
|
key = 'PLUGIN_MODELS';
|
|
} else {
|
|
key = 'OPENAI_MODELS';
|
|
}
|
|
|
|
if (process.env[key]) {
|
|
models = String(process.env[key]).split(',');
|
|
return models;
|
|
}
|
|
|
|
if (userProvidedOpenAI) {
|
|
return models;
|
|
}
|
|
|
|
models = await fetchOpenAIModels(opts, models);
|
|
return models;
|
|
};
|
|
|
|
const getChatGPTBrowserModels = () => {
|
|
let models = ['text-davinci-002-render-sha', 'gpt-4'];
|
|
if (process.env.CHATGPT_MODELS) {
|
|
models = String(process.env.CHATGPT_MODELS).split(',');
|
|
}
|
|
|
|
return models;
|
|
};
|
|
const getAnthropicModels = () => {
|
|
let models = [
|
|
'claude-1',
|
|
'claude-1-100k',
|
|
'claude-instant-1',
|
|
'claude-instant-1-100k',
|
|
'claude-2',
|
|
];
|
|
if (process.env.ANTHROPIC_MODELS) {
|
|
models = String(process.env.ANTHROPIC_MODELS).split(',');
|
|
}
|
|
|
|
return models;
|
|
};
|
|
|
|
let i = 0;
|
|
router.get('/', async function (req, res) {
|
|
let key, palmUser;
|
|
try {
|
|
key = require('../../data/auth.json');
|
|
} catch (e) {
|
|
if (i === 0) {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (process.env.PALM_KEY === 'user_provided') {
|
|
palmUser = true;
|
|
if (i <= 1) {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
const tools = await addOpenAPISpecs(availableTools);
|
|
function transformToolsToMap(tools) {
|
|
return tools.reduce((map, obj) => {
|
|
map[obj.pluginKey] = obj.name;
|
|
return map;
|
|
}, {});
|
|
}
|
|
const plugins = transformToolsToMap(tools);
|
|
|
|
const google =
|
|
key || palmUser
|
|
? { userProvide: palmUser, availableModels: ['chat-bison', 'text-bison', 'codechat-bison'] }
|
|
: false;
|
|
const openAI = openAIApiKey
|
|
? { availableModels: await getOpenAIModels(), userProvide: openAIApiKey === 'user_provided' }
|
|
: false;
|
|
const azureOpenAI = azureOpenAIApiKey
|
|
? {
|
|
availableModels: await getOpenAIModels({ azure: true }),
|
|
userProvide: azureOpenAIApiKey === 'user_provided',
|
|
}
|
|
: false;
|
|
const gptPlugins =
|
|
openAIApiKey || azureOpenAIApiKey
|
|
? {
|
|
availableModels: await getOpenAIModels({ azure: useAzurePlugins, plugins: true }),
|
|
plugins,
|
|
availableAgents: ['classic', 'functions'],
|
|
userProvide: userProvidedOpenAI,
|
|
azure: useAzurePlugins,
|
|
}
|
|
: false;
|
|
const bingAI = process.env.BINGAI_TOKEN
|
|
? {
|
|
availableModels: ['BingAI', 'Sydney'],
|
|
userProvide: process.env.BINGAI_TOKEN == 'user_provided',
|
|
}
|
|
: false;
|
|
const chatGPTBrowser = process.env.CHATGPT_TOKEN
|
|
? {
|
|
userProvide: process.env.CHATGPT_TOKEN == 'user_provided',
|
|
availableModels: getChatGPTBrowserModels(),
|
|
}
|
|
: false;
|
|
const anthropic = process.env.ANTHROPIC_API_KEY
|
|
? {
|
|
userProvide: process.env.ANTHROPIC_API_KEY == 'user_provided',
|
|
availableModels: getAnthropicModels(),
|
|
}
|
|
: false;
|
|
|
|
res.send(
|
|
JSON.stringify({ azureOpenAI, openAI, google, bingAI, chatGPTBrowser, gptPlugins, anthropic }),
|
|
);
|
|
});
|
|
|
|
module.exports = { router, getOpenAIModels, getChatGPTBrowserModels };
|