diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 5951ed694ea..41ca0502b46 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -4,7 +4,7 @@ Thank you to all the contributors who have helped make this project possible! We ## Contributing Guidelines -If the feature you would like to contribute has not already received prior approval from the project maintainers (i.e., the feature is currently on the [roadmap](https://github.com/users/danny-avila/projects/2)), please submit a request in the [Feature Requests & Suggestions category](https://github.com/danny-avila/LibreChat/discussions/new?category=feature-requests-suggestions) of the discussions board before beginning work on it. The requests should include specific implementation details, including areas of the application that will be affected by the change (including designs if applicable), and any other relevant information that might be required for a speedy review. However, proposals are not required for small changes, bug fixes, or documentation improvements. Small changes and bug fixes should be tied to an [issue](https://github.com/danny-avila/LibreChat/issues) and included in the corresponding pull request for tracking purposes. +If the feature you would like to contribute has not already received prior approval from the project maintainers (i.e., the feature is currently on the [roadmap](https://github.com/users/enricai/projects/2)), please submit a request in the [Feature Requests & Suggestions category](https://github.com/enricai/LibreChat/discussions/new?category=feature-requests-suggestions) of the discussions board before beginning work on it. The requests should include specific implementation details, including areas of the application that will be affected by the change (including designs if applicable), and any other relevant information that might be required for a speedy review. However, proposals are not required for small changes, bug fixes, or documentation improvements. Small changes and bug fixes should be tied to an [issue](https://github.com/enricai/LibreChat/issues) and included in the corresponding pull request for tracking purposes. Please note that a pull request involving a feature that has not been reviewed and approved by the project maintainers may be rejected. We appreciate your understanding and cooperation. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 37ef799acbd..e0456c0a195 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,6 +1,6 @@ # These are supported funding model platforms -github: [danny-avila] +github: [enricai] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username diff --git a/.github/ISSUE_TEMPLATE/BUG-REPORT.yml b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml index 5c88b9f70dc..a4a5aff2e21 100644 --- a/.github/ISSUE_TEMPLATE/BUG-REPORT.yml +++ b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml @@ -50,7 +50,7 @@ body: id: terms attributes: label: Code of Conduct - description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/danny-avila/LibreChat/blob/main/.github/CODE_OF_CONDUCT.md) + description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/enricai/LibreChat/blob/main/.github/CODE_OF_CONDUCT.md) options: - label: I agree to follow this project's Code of Conduct required: true diff --git a/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml b/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml index d85957fd22e..8339b60b567 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml +++ b/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml @@ -43,7 +43,7 @@ body: id: terms attributes: label: Code of Conduct - description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/danny-avila/LibreChat/blob/main/.github/CODE_OF_CONDUCT.md) + description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/enricai/LibreChat/blob/main/.github/CODE_OF_CONDUCT.md) options: - label: I agree to follow this project's Code of Conduct required: true diff --git a/.github/ISSUE_TEMPLATE/QUESTION.yml b/.github/ISSUE_TEMPLATE/QUESTION.yml index 0669fd67244..23a19db7509 100644 --- a/.github/ISSUE_TEMPLATE/QUESTION.yml +++ b/.github/ISSUE_TEMPLATE/QUESTION.yml @@ -44,7 +44,7 @@ body: id: terms attributes: label: Code of Conduct - description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/danny-avila/LibreChat/blob/main/.github/CODE_OF_CONDUCT.md) + description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/enricai/LibreChat/blob/main/.github/CODE_OF_CONDUCT.md) options: - label: I agree to follow this project's Code of Conduct required: true diff --git a/.github/SECURITY.md b/.github/SECURITY.md index b01e04e0160..c8afca899c2 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -43,7 +43,7 @@ This security policy applies to the following GitHub repository: ## Contact -If you have any questions or concerns regarding the security of our project, please join our [Discord community](https://discord.librechat.ai) and report them in the appropriate channel. You can also reach out to us by [opening an issue](https://github.com/danny-avila/LibreChat/issues/new) on GitHub. Please note that the response time may vary depending on the nature and severity of the inquiry. +If you have any questions or concerns regarding the security of our project, please join our [Discord community](https://discord.librechat.ai) and report them in the appropriate channel. You can also reach out to us by [opening an issue](https://github.com/enricai/LibreChat/issues/new) on GitHub. Please note that the response time may vary depending on the nature and severity of the inquiry. ## Acknowledgments diff --git a/.github/playwright.yml b/.github/playwright.yml index 28eca14d581..984eb42c255 100644 --- a/.github/playwright.yml +++ b/.github/playwright.yml @@ -13,7 +13,7 @@ # jobs: # tests_e2e: # name: Run Playwright tests -# if: github.event.pull_request.head.repo.full_name == 'danny-avila/LibreChat' +# if: github.event.pull_request.head.repo.full_name == 'enricai/LibreChat' # timeout-minutes: 60 # runs-on: ubuntu-latest # env: diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index cb637787f12..7d4e3bd8d3c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,7 +1,7 @@ # Pull Request Template ⚠️ Before Submitting a PR, Please Review: -- Please ensure that you have thoroughly read and understood the [Contributing Docs](https://github.com/danny-avila/LibreChat/blob/main/.github/CONTRIBUTING.md) before submitting your Pull Request. +- Please ensure that you have thoroughly read and understood the [Contributing Docs](https://github.com/enricai/LibreChat/blob/main/.github/CONTRIBUTING.md) before submitting your Pull Request. ⚠️ Documentation Updates Notice: - Kindly note that documentation updates are managed in this repository: [librechat.ai](https://github.com/LibreChat-AI/librechat.ai) diff --git a/.github/workflows/a11y.yml b/.github/workflows/a11y.yml index a7cfd08169b..075aa7c17ac 100644 --- a/.github/workflows/a11y.yml +++ b/.github/workflows/a11y.yml @@ -15,7 +15,7 @@ jobs: axe-linter: runs-on: ubuntu-latest if: > - (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'danny-avila/LibreChat') || + (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'enricai/LibreChat') || (github.event_name == 'workflow_dispatch' && github.event.inputs.run_workflow == 'true') steps: diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index fc1c02db69f..dd35f38a05b 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -11,7 +11,7 @@ jobs: deploy: runs-on: ubuntu-latest if: | - github.repository == 'danny-avila/LibreChat' && + github.repository == 'enricai/LibreChat' && (github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success') steps: - name: Checkout repository diff --git a/README.md b/README.md index 317a7a0669d..549bb5d1f1b 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ - + @@ -136,7 +136,7 @@ Click on the thumbnail to open the video☝️ ## 📝 Changelog Keep up with the latest updates by visiting the releases page and notes: -- [Releases](https://github.com/danny-avila/LibreChat/releases) +- [Releases](https://github.com/enricai/LibreChat/releases) - [Changelog](https://www.librechat.ai/changelog) **⚠️ Please consult the [changelog](https://www.librechat.ai/changelog) for breaking changes before updating.** @@ -146,13 +146,13 @@ Keep up with the latest updates by visiting the releases page and notes: ## ⭐ Star History

- - Star History Chart + + Star History Chart

- danny-avila%2FLibreChat | Trendshift + enricai%2FLibreChat | Trendshift ROSS Index - Fastest Growing Open-Source Startups in Q1 2024 | Runa Capital @@ -171,6 +171,6 @@ For new features, components, or extensions, please open an issue and discuss be ## 💖 This project exists in its current state thanks to all the people who contribute - - + + diff --git a/api/app/clients/ChatGPTClient.js b/api/app/clients/ChatGPTClient.js index 6a7ba7b9896..451d88569e3 100644 --- a/api/app/clients/ChatGPTClient.js +++ b/api/app/clients/ChatGPTClient.js @@ -272,7 +272,7 @@ class ChatGPTClient extends BaseClient { if (this.useOpenRouter) { opts.headers['HTTP-Referer'] = 'https://librechat.ai'; - opts.headers['X-Title'] = 'LibreChat'; + opts.headers['X-Title'] = 'EnricChat'; } if (this.options.proxy) { diff --git a/api/app/clients/OpenAIClient.js b/api/app/clients/OpenAIClient.js index 15fd20aefe7..3c0c90b8b98 100644 --- a/api/app/clients/OpenAIClient.js +++ b/api/app/clients/OpenAIClient.js @@ -646,7 +646,7 @@ class OpenAIClient extends BaseClient { configOptions.baseOptions = { headers: { 'HTTP-Referer': 'https://librechat.ai', - 'X-Title': 'LibreChat', + 'X-Title': 'EnricChat', }, }; } @@ -1090,7 +1090,7 @@ ${convo} if (this.useOpenRouter) { opts.defaultHeaders = { 'HTTP-Referer': 'https://librechat.ai', - 'X-Title': 'LibreChat', + 'X-Title': 'EnricChat', }; } diff --git a/api/app/clients/tools/manifest.json b/api/app/clients/tools/manifest.json index d2748cdea11..9fd8e2b50a6 100644 --- a/api/app/clients/tools/manifest.json +++ b/api/app/clients/tools/manifest.json @@ -21,12 +21,12 @@ { "authField": "GOOGLE_CSE_ID", "label": "Google CSE ID", - "description": "This is your Google Custom Search Engine ID. For instructions on how to obtain this, see Our Docs." + "description": "This is your Google Custom Search Engine ID. For instructions on how to obtain this, see Our Docs." }, { "authField": "GOOGLE_SEARCH_API_KEY", "label": "Google API Key", - "description": "This is your Google Custom Search API Key. For instructions on how to obtain this, see Our Docs." + "description": "This is your Google Custom Search API Key. For instructions on how to obtain this, see Our Docs." } ] }, diff --git a/api/config/parsers.js b/api/config/parsers.js index a3bab7d3cb0..51e9c052d0f 100644 --- a/api/config/parsers.js +++ b/api/config/parsers.js @@ -4,6 +4,7 @@ const traverse = require('traverse'); const SPLAT_SYMBOL = Symbol.for('splat'); const MESSAGE_SYMBOL = Symbol.for('message'); +const CONSOLE_JSON_LONG_STRING_LENGTH=parseInt(process.env.CONSOLE_JSON_LONG_STRING_LENGTH) || 255; const sensitiveKeys = [ /^(sk-)[^\s]+/, // OpenAI API key pattern @@ -211,7 +212,7 @@ const jsonTruncateFormat = winston.format((info) => { const newObj = {}; Object.entries(obj).forEach(([key, value]) => { if (typeof value === 'string') { - newObj[key] = truncateLongStrings(value, 255); + newObj[key] = truncateLongStrings(value, CONSOLE_JSON_LONG_STRING_LENGTH); } else { newObj[key] = truncateObject(value); } diff --git a/api/package.json b/api/package.json index e364b68eb9e..b40de9b6df9 100644 --- a/api/package.json +++ b/api/package.json @@ -18,7 +18,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/danny-avila/LibreChat.git" + "url": "git+https://github.com/enricai/LibreChat.git" }, "keywords": [], "author": "", @@ -30,7 +30,7 @@ "~/*": "./*" }, "bugs": { - "url": "https://github.com/danny-avila/LibreChat/issues" + "url": "https://github.com/enricai/LibreChat/issues" }, "homepage": "https://librechat.ai", "dependencies": { @@ -78,7 +78,7 @@ "meilisearch": "^0.38.0", "mime": "^3.0.0", "module-alias": "^2.2.3", - "mongoose": "^8.8.3", + "mongoose": "^8.9.5", "multer": "^1.4.5-lts.1", "nanoid": "^3.3.7", "nodejs-gpt": "^1.37.4", diff --git a/api/server/controllers/agents/client.js b/api/server/controllers/agents/client.js index e3c6dd5679c..02194cb544c 100644 --- a/api/server/controllers/agents/client.js +++ b/api/server/controllers/agents/client.js @@ -397,7 +397,7 @@ class AgentClient extends BaseClient { // if (this.useOpenRouter) { // opts.defaultHeaders = { // 'HTTP-Referer': 'https://librechat.ai', - // 'X-Title': 'LibreChat', + // 'X-Title': 'EnricChat', // }; // } diff --git a/api/server/controllers/assistants/v2.js b/api/server/controllers/assistants/v2.js index 54f9a6fbc6b..74de1fa40b0 100644 --- a/api/server/controllers/assistants/v2.js +++ b/api/server/controllers/assistants/v2.js @@ -139,10 +139,11 @@ const updateAssistant = async ({ req, openai, assistant_id, updateData }) => { } if (hasFileSearch && !updateData.tool_resources?.file_search) { + const vectorStore = await openai.beta.assistants.vector_store.create(assistant_id); updateData.tool_resources = { ...(updateData.tool_resources ?? {}), file_search: { - vector_store_ids: [], + vector_store_ids: [vectorStore.id], }, }; } @@ -175,10 +176,26 @@ const updateAssistant = async ({ req, openai, assistant_id, updateData }) => { const addResourceFileId = async ({ req, openai, assistant_id, tool_resource, file_id }) => { const assistant = await openai.beta.assistants.retrieve(assistant_id); const { tool_resources = {} } = assistant; + const isFileSearch = tool_resource === ToolCallTypes.FILE_SEARCH; + if (isFileSearch) { + const vectorStore = await openai.beta.assistants.vector_store.create(assistant_id); + await openai.beta.assistants.vector_store.files.create(vectorStore.id, { file_id }); + file_id = vectorStore.id; + } + if (tool_resources[tool_resource]) { - tool_resources[tool_resource].file_ids.push(file_id); + if (isFileSearch) { + // only one vectordb per assistant is needed + tool_resources[tool_resource] = { vector_store_ids: [file_id] }; + } else { + tool_resources[tool_resource].file_ids.push(file_id); + } } else { - tool_resources[tool_resource] = { file_ids: [file_id] }; + if (isFileSearch) { + tool_resources[tool_resource] = { vector_store_ids: [file_id] }; + } else { + tool_resources[tool_resource] = { file_ids: [file_id] }; + } } delete assistant.id; @@ -204,7 +221,14 @@ const addResourceFileId = async ({ req, openai, assistant_id, tool_resource, fil const deleteResourceFileId = async ({ req, openai, assistant_id, tool_resource, file_id }) => { const assistant = await openai.beta.assistants.retrieve(assistant_id); const { tool_resources = {} } = assistant; + const isFileSearch = tool_resource === ToolCallTypes.FILE_SEARCH; + if (isFileSearch) { + const vectorStore = await openai.beta.assistants.vector_store.create(assistant_id); + await openai.beta.assistants.vector_store.files.del(vectorStore.id, { file_id }); + } + // only one vectordb per assistant is needed, no need to delete it once it's + // created since it's OK to have a vector db with no docs if (tool_resource && tool_resources[tool_resource]) { const resource = tool_resources[tool_resource]; const index = resource.file_ids.indexOf(file_id); diff --git a/api/server/routes/config.js b/api/server/routes/config.js index f6669169acd..305ef379914 100644 --- a/api/server/routes/config.js +++ b/api/server/routes/config.js @@ -39,7 +39,7 @@ router.get('/', async function (req, res) { try { /** @type {TStartupConfig} */ const payload = { - appTitle: process.env.APP_TITLE || 'LibreChat', + appTitle: process.env.APP_TITLE || 'EnricChat', socialLogins: req.app.locals.socialLogins ?? defaultSocialLogins, discordLoginEnabled: !!process.env.DISCORD_CLIENT_ID && !!process.env.DISCORD_CLIENT_SECRET, facebookLoginEnabled: diff --git a/api/server/routes/files/files.js b/api/server/routes/files/files.js index c320f7705b2..b9c0fa03f90 100644 --- a/api/server/routes/files/files.js +++ b/api/server/routes/files/files.js @@ -18,6 +18,7 @@ const { getStrategyFunctions } = require('~/server/services/Files/strategies'); const { getOpenAIClient } = require('~/server/controllers/assistants/helpers'); const { loadAuthValues } = require('~/app/clients/tools/util'); const { getAgent } = require('~/models/Agent'); +const { getAssistant } = require('~/models/Assistant'); const { getFiles } = require('~/models/File'); const { logger } = require('~/config'); @@ -78,7 +79,7 @@ router.delete('/', async (req, res) => { }); } - /* Handle entity unlinking even if no valid files to delete */ + /* Handle agent unlinking even if no valid files to delete */ if (req.body.agent_id && req.body.tool_resource && dbFiles.length === 0) { const agent = await getAgent({ id: req.body.agent_id, @@ -88,7 +89,21 @@ router.delete('/', async (req, res) => { const agentFiles = files.filter((f) => toolResourceFiles.includes(f.file_id)); await processDeleteRequest({ req, files: agentFiles }); - res.status(200).json({ message: 'File associations removed successfully' }); + res.status(200).json({ message: 'File associations removed successfully from agent' }); + return; + } + + /* Handle assistant unlinking even if no valid files to delete */ + if (req.body.assistant_id && req.body.tool_resource && dbFiles.length === 0) { + const assistant = await getAssistant({ + id: req.body.assistant_id, + }); + + const toolResourceFiles = assistant.tool_resources?.[req.body.tool_resource]?.file_ids ?? []; + const assistantFiles = files.filter((f) => toolResourceFiles.includes(f.file_id)); + + await processDeleteRequest({ req, files: assistantFiles }); + res.status(200).json({ message: 'File associations removed successfully from assistant' }); return; } diff --git a/api/server/services/AuthService.js b/api/server/services/AuthService.js index 49c3c4a17d9..b48249e3bc5 100644 --- a/api/server/services/AuthService.js +++ b/api/server/services/AuthService.js @@ -83,7 +83,7 @@ const sendVerificationEmail = async (user) => { email: user.email, subject: 'Verify your email', payload: { - appName: process.env.APP_TITLE || 'LibreChat', + appName: process.env.APP_TITLE || 'EnricChat', name: user.name, verificationLink: verificationLink, year: new Date().getFullYear(), @@ -255,7 +255,7 @@ const requestPasswordReset = async (req) => { email: user.email, subject: 'Password Reset Request', payload: { - appName: process.env.APP_TITLE || 'LibreChat', + appName: process.env.APP_TITLE || 'EnricChat', name: user.name, link: link, year: new Date().getFullYear(), @@ -308,7 +308,7 @@ const resetPassword = async (userId, token, password) => { email: user.email, subject: 'Password Reset Successfully', payload: { - appName: process.env.APP_TITLE || 'LibreChat', + appName: process.env.APP_TITLE || 'EnricChat', name: user.name, year: new Date().getFullYear(), }, @@ -391,7 +391,7 @@ const resendVerificationEmail = async (req) => { email: user.email, subject: 'Verify your email', payload: { - appName: process.env.APP_TITLE || 'LibreChat', + appName: process.env.APP_TITLE || 'EnricChat', name: user.name, verificationLink: verificationLink, year: new Date().getFullYear(), diff --git a/api/server/services/Endpoints/azureAssistants/initialize.js b/api/server/services/Endpoints/azureAssistants/initialize.js index fc8024af072..b35dc537954 100644 --- a/api/server/services/Endpoints/azureAssistants/initialize.js +++ b/api/server/services/Endpoints/azureAssistants/initialize.js @@ -53,6 +53,135 @@ class Files { } } +class VectorStoreFiles { + constructor(client) { + this._client = client; + } + + /** + * Create a vector store file + * [File](https://platform.openai.com/docs/api-reference/vector-stores-files) to an + * [Vector store](https://platform.openai.com/docs/api-reference/vector-stores). + */ + create(vectorstoreId, body, options) { + return this._client.post(`/vector_stores/${vectorstoreId}/files`, { + body, + ...options, + headers: { 'OpenAI-Beta': 'assistants=v1', ...options?.headers }, + }); + } + + /** + * Lists all vector store files + */ + async list(vectorstoreId, options) { + // TODO paginate instead of setting a hard limit + return this._client.get(`/vector_stores/${vectorstoreId}`, { + query: { limit: 100 }, + headers: { 'OpenAI-Beta': 'assistants=v1', ...options?.headers }, + }); + } + + /** + * Retrieves a vector store file + */ + retrieve(vectorstoreId, fileId, options) { + return this._client.get(`/vector_stores/${vectorstoreId}/files/${fileId}`, { + ...options, + headers: { 'OpenAI-Beta': 'assistants=v1', ...options?.headers }, + }); + } + + /** + * Delete a vector store file + */ + del(vectorstoreId, fileId, options) { + return this._client.delete(`/vector_stores/${vectorstoreId}/files/${fileId}`, { + ...options, + headers: { 'OpenAI-Beta': 'assistants=v1', ...options?.headers }, + }); + } +} + +class VectorStore { + constructor(client) { + this._client = client; + this._assistants = {}; + this.files = new VectorStoreFiles(client); + } + + async _list(options) { + // TODO paginate instead of setting a hard limit + return this._client.get("/vector_stores", { + query: { limit: 100 }, + headers: { 'OpenAI-Beta': 'assistants=v1', ...options?.headers }, + }); + } + + async _retrieveForAssistant(assistantId, options) { + if (!this._assistants[assistantId]) { + this._assistants[assistantId] = {}; + const vectorStores = await this._list(options); + const aVectorStore = (vectorStores?.data || []).find((vs) => { + return vs.name == this.name(assistantId); + }); + if (aVectorStore) { + this._assistants[assistantId] = {[aVectorStore.name]: aVectorStore}; + } + } + + return this._assistants[assistantId][this.name(assistantId)] + } + + name(assistantId) { + return `${assistantId}_vector_store`; + } + + /** + * Create a vector store + * [Vector store](https://platform.openai.com/docs/api-reference/vector-stores). + */ + async create(assistantId, options) { + const existingVectorStore = await this._retrieveForAssistant(assistantId, options); + if (existingVectorStore) { + return existingVectorStore; + } + + const newVectorStore = await this._client.post(`/vector_stores`, { + body: { name: this.name(assistantId) }, + headers: { 'OpenAI-Beta': 'assistants=v1', ...options?.headers }, + }); + this._assistants[assistantId] = {[newVectorStore.name]: newVectorStore}; + + return newVectorStore; + } + + /** + * Retrieves a vector store + */ + async retrieve(assistantId, options) { + const existingVectorStore = await this._retrieveForAssistant(assistantId, options); + if (existingVectorStore) { + return existingVectorStore; + } + + return this._client.get(`/vector_stores/${this.name(assistantId)}`, { + ...options, + headers: { 'OpenAI-Beta': 'assistants=v1', ...options?.headers }, + }); + } + + /** + * Deletes a vector store + */ + del(assistantId, options) { + return this._client.delete(`/vector_stores/${this.name(assistantId)}`, { + ...options, + headers: { 'OpenAI-Beta': 'assistants=v1', ...options?.headers }, + }); + } +} + const initializeClient = async ({ req, res, version, endpointOption, initAppClient = false }) => { const { PROXY, OPENAI_ORGANIZATION, AZURE_ASSISTANTS_API_KEY, AZURE_ASSISTANTS_BASE_URL } = process.env; @@ -175,6 +304,7 @@ const initializeClient = async ({ req, res, version, endpointOption, initAppClie }); openai.beta.assistants.files = new Files(openai); + openai.beta.assistants.vector_store = new VectorStore(openai); openai.req = req; openai.res = res; diff --git a/api/server/services/Endpoints/openAI/llm.js b/api/server/services/Endpoints/openAI/llm.js index 2587b242c99..3a3f943d886 100644 --- a/api/server/services/Endpoints/openAI/llm.js +++ b/api/server/services/Endpoints/openAI/llm.js @@ -60,14 +60,16 @@ function getLLMConfig(apiKey, options = {}) { // Handle OpenRouter or custom reverse proxy if (useOpenRouter || reverseProxyUrl === 'https://openrouter.ai/api/v1') { - configOptions.baseURL = 'https://openrouter.ai/api/v1'; - configOptions.defaultHeaders = Object.assign( - { - 'HTTP-Referer': 'https://librechat.ai', - 'X-Title': 'LibreChat', - }, - headers, - ); + configOptions.basePath = 'https://openrouter.ai/api/v1'; + configOptions.baseOptions = { + headers: Object.assign( + { + 'HTTP-Referer': 'https://librechat.ai', + 'X-Title': 'EnricChat', + }, + headers, + ), + }; } else if (reverseProxyUrl) { configOptions.baseURL = reverseProxyUrl; if (headers) { diff --git a/api/server/services/Files/process.js b/api/server/services/Files/process.js index a5d9c8c1e05..acdfe5beea0 100644 --- a/api/server/services/Files/process.js +++ b/api/server/services/Files/process.js @@ -137,21 +137,29 @@ const processDeleteRequest = async ({ req, files }) => { /** @type {Record} */ const client = { [FileSources.openai]: undefined, [FileSources.azure]: undefined }; const initializeClients = async () => { - const openAIClient = await getOpenAIClient({ - req, - overrideEndpoint: EModelEndpoint.assistants, - }); - client[FileSources.openai] = openAIClient.openai; + try { + const openAIClient = await getOpenAIClient({ + req, + overrideEndpoint: EModelEndpoint.assistants, + }); + client[FileSources.openai] = openAIClient.openai; + } catch (error) { + logger.warn(`Could not init OpenAI client: ${error}`); + } if (!req.app.locals[EModelEndpoint.azureOpenAI]?.assistants) { return; } - const azureClient = await getOpenAIClient({ - req, - overrideEndpoint: EModelEndpoint.azureAssistants, - }); - client[FileSources.azure] = azureClient.openai; + try { + const azureClient = await getOpenAIClient({ + req, + overrideEndpoint: EModelEndpoint.azureAssistants, + }); + client[FileSources.azure] = azureClient.openai; + } catch (error) { + logger.warn(`Could not init Azure OpenAI client: ${error}`); + } }; if (req.body.assistant_id !== undefined) { diff --git a/api/server/services/start/assistants.js b/api/server/services/start/assistants.js index b46edc676b9..a855cf8c9c8 100644 --- a/api/server/services/start/assistants.js +++ b/api/server/services/start/assistants.js @@ -11,7 +11,7 @@ const { logger } = require('~/config'); */ function azureAssistantsDefaults() { return { - capabilities: [Capabilities.tools, Capabilities.actions, Capabilities.code_interpreter], + capabilities: [Capabilities.tools, Capabilities.actions, Capabilities.code_interpreter, Capabilities.retrieval], version: defaultAssistantsVersion.azureAssistants, }; } diff --git a/charts/librechat/values.yaml b/charts/librechat/values.yaml index c21f84586a6..6908e049e63 100644 --- a/charts/librechat/values.yaml +++ b/charts/librechat/values.yaml @@ -5,7 +5,7 @@ replicaCount: 1 image: - repository: ghcr.io/danny-avila/librechat + repository: ghcr.io/enricai/librechat pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. tag: "latest" @@ -95,7 +95,7 @@ config: env: # Full list of possible values - # https://github.com/danny-avila/LibreChat/blob/main/.env.example + # https://github.com/enricai/LibreChat/blob/main/.env.example ALLOW_EMAIL_LOGIN: "true" ALLOW_REGISTRATION: "true" ALLOW_SOCIAL_LOGIN: "false" diff --git a/client/index.html b/client/index.html index c72d3951cbf..3c74a7db256 100644 --- a/client/index.html +++ b/client/index.html @@ -6,7 +6,7 @@ - LibreChat + EnricChat diff --git a/client/nginx.conf b/client/nginx.conf index c91c47a23f0..28a742d30eb 100644 --- a/client/nginx.conf +++ b/client/nginx.conf @@ -1,27 +1,20 @@ -# Secure default configuration generated by Mozilla SSL Configuration Generator -# generated 2024-01-21, Mozilla Guideline v5.7, nginx 1.24.0, OpenSSL 3.1.4, intermediate configuration -# https://ssl-config.mozilla.org/#server=nginx&version=1.24.0&config=intermediate&openssl=3.1.4&guideline=5.7 - server { listen 80 default_server; listen [::]:80 default_server; - # To Configure SSL, comment all lines within the Non-SSL section and uncomment all lines under the SSL section. - ######################################## Non-SSL ######################################## server_name localhost; - - # https://docs.nginx.com/nginx/admin-guide/web-server/compression/ - # gzip on; - # gzip_vary on; - # gzip_proxied any; - # gzip_comp_level 6; - # gzip_buffers 16 8k; - # gzip_http_version 1.1; - # gzip_types text/css application/javascript application/json application/octet-stream; - # Increase the client_max_body_size to allow larger file uploads - # The default limits for image uploads as of 11/22/23 is 20MB/file, and 25MB/request - client_max_body_size 25M; + client_max_body_size 1024M; + proxy_read_timeout 300s; + + # https://docs.nginx.com/nginx/admin-guide/web-server/compression/ + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_types text/css application/javascript application/json application/octet-stream; location /api/ { proxy_pass http://api:3080$request_uri; @@ -30,65 +23,4 @@ server { location / { proxy_pass http://api:3080/; } - - ######################################## SSL ######################################## -# # Redirect all http traffic to https -# location / { -# return 301 https://$host$request_uri; -# } } - -#server { -# listen 443 ssl http2; -# listen [::]:443 ssl http2; - -# https://docs.nginx.com/nginx/admin-guide/web-server/compression/ -# gzip on; -# gzip_vary on; -# gzip_proxied any; -# gzip_comp_level 6; -# gzip_buffers 16 8k; -# gzip_http_version 1.1; -# gzip_types text/css application/javascript application/json application/octet-stream; - -# ssl_certificate /etc/nginx/ssl/nginx.crt; -# ssl_certificate_key /etc/nginx/ssl/nginx.key; -# ssl_session_timeout 1d; -# ssl_session_cache shared:MozSSL:10m; # about 40000 sessions -# ssl_session_tickets off; - -# # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /etc/nginx/ssl/dhparam -# ssl_dhparam /etc/nginx/ssl/dhparam; - -# # intermediate configuration -# ssl_protocols TLSv1.2 TLSv1.3; -# ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305; -# ssl_prefer_server_ciphers off; - -# # HSTS (ngx_http_headers_module is required) (63072000 seconds) -# add_header Strict-Transport-Security "max-age=63072000" always; - -# # OCSP stapling -# ssl_stapling on; -# ssl_stapling_verify on; - -# # verify chain of trust of OCSP response using Root CA and Intermediate certs -# ssl_trusted_certificate /etc/nginx/ssl/ca.crt; - -# # replace with the IP address of your resolver -# resolver 127.0.0.1; - -# server_name localhost; - -# # Increase the client_max_body_size to allow larger file uploads -# # The default limits for image uploads as of 11/22/23 is 20MB/file, and 25MB/request -# client_max_body_size 25M; - -# location /api { -# proxy_pass http://api:3080/api; -# } - -# location / { -# proxy_pass http://api:3080; -# } -#} diff --git a/client/package.json b/client/package.json index 029ca3cdee1..e67368bf0eb 100644 --- a/client/package.json +++ b/client/package.json @@ -18,13 +18,13 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/danny-avila/LibreChat.git" + "url": "git+https://github.com/enricai/LibreChat.git" }, "keywords": [], "author": "", "license": "ISC", "bugs": { - "url": "https://github.com/danny-avila/LibreChat/issues" + "url": "https://github.com/enricai/LibreChat/issues" }, "homepage": "https://librechat.ai", "dependencies": { diff --git a/client/public/assets/apple-touch-icon-180x180.png b/client/public/assets/apple-touch-icon-180x180.png index 91dde5d139d..932a63e5215 100644 Binary files a/client/public/assets/apple-touch-icon-180x180.png and b/client/public/assets/apple-touch-icon-180x180.png differ diff --git a/client/public/assets/favicon-16x16.png b/client/public/assets/favicon-16x16.png index 03975d8ec0b..787b1355d8a 100644 Binary files a/client/public/assets/favicon-16x16.png and b/client/public/assets/favicon-16x16.png differ diff --git a/client/public/assets/favicon-32x32.png b/client/public/assets/favicon-32x32.png index df89fb33b01..85c444ff33d 100644 Binary files a/client/public/assets/favicon-32x32.png and b/client/public/assets/favicon-32x32.png differ diff --git a/client/public/assets/logo.png b/client/public/assets/logo.png new file mode 100644 index 00000000000..f20e3609b7d Binary files /dev/null and b/client/public/assets/logo.png differ diff --git a/client/public/fonts/ClashDisplay-Light.ttf b/client/public/fonts/ClashDisplay-Light.ttf new file mode 100644 index 00000000000..b4f37110117 Binary files /dev/null and b/client/public/fonts/ClashDisplay-Light.ttf differ diff --git a/client/public/fonts/ClashDisplay-Medium.ttf b/client/public/fonts/ClashDisplay-Medium.ttf new file mode 100644 index 00000000000..9e704d1a4de Binary files /dev/null and b/client/public/fonts/ClashDisplay-Medium.ttf differ diff --git a/client/public/fonts/ClashDisplay-Regular.ttf b/client/public/fonts/ClashDisplay-Regular.ttf new file mode 100644 index 00000000000..42376646ba7 Binary files /dev/null and b/client/public/fonts/ClashDisplay-Regular.ttf differ diff --git a/client/public/fonts/ClashDisplay-Semibold.ttf b/client/public/fonts/ClashDisplay-Semibold.ttf new file mode 100644 index 00000000000..6715249c537 Binary files /dev/null and b/client/public/fonts/ClashDisplay-Semibold.ttf differ diff --git a/client/public/fonts/ClashDisplay-Variable.ttf b/client/public/fonts/ClashDisplay-Variable.ttf new file mode 100644 index 00000000000..24f060c48e2 Binary files /dev/null and b/client/public/fonts/ClashDisplay-Variable.ttf differ diff --git a/client/public/fonts/DMSans-Bold.ttf b/client/public/fonts/DMSans-Bold.ttf new file mode 100644 index 00000000000..e70172a067f Binary files /dev/null and b/client/public/fonts/DMSans-Bold.ttf differ diff --git a/client/public/fonts/DMSans-BoldItalic.ttf b/client/public/fonts/DMSans-BoldItalic.ttf new file mode 100644 index 00000000000..8b972ca08ca Binary files /dev/null and b/client/public/fonts/DMSans-BoldItalic.ttf differ diff --git a/client/public/fonts/DMSans-Italic.ttf b/client/public/fonts/DMSans-Italic.ttf new file mode 100644 index 00000000000..b965526a842 Binary files /dev/null and b/client/public/fonts/DMSans-Italic.ttf differ diff --git a/client/public/fonts/DMSans-Medium.ttf b/client/public/fonts/DMSans-Medium.ttf new file mode 100644 index 00000000000..71b2277252b Binary files /dev/null and b/client/public/fonts/DMSans-Medium.ttf differ diff --git a/client/public/fonts/DMSans-MediumItalic.ttf b/client/public/fonts/DMSans-MediumItalic.ttf new file mode 100644 index 00000000000..d3293d6641e Binary files /dev/null and b/client/public/fonts/DMSans-MediumItalic.ttf differ diff --git a/client/public/fonts/DMSans-Regular.ttf b/client/public/fonts/DMSans-Regular.ttf new file mode 100644 index 00000000000..cad73f073f3 Binary files /dev/null and b/client/public/fonts/DMSans-Regular.ttf differ diff --git a/client/src/components/Auth/AuthLayout.tsx b/client/src/components/Auth/AuthLayout.tsx index 6df73d2cf94..7b8707d0e61 100644 --- a/client/src/components/Auth/AuthLayout.tsx +++ b/client/src/components/Auth/AuthLayout.tsx @@ -45,7 +45,7 @@ function AuthLayout({ return ( {localize('com_auth_error_invalid_reset_token')}{' '} - + {localize('com_auth_click_here')} {' '} {localize('com_auth_to_try_again')} @@ -61,11 +61,11 @@ function AuthLayout({

-
+
{localize('com_ui_logo',
@@ -78,7 +78,7 @@ function AuthLayout({
{!hasStartupConfigError && !isFetching && (

{header} diff --git a/client/src/components/Auth/Footer.tsx b/client/src/components/Auth/Footer.tsx index 8d797176838..fd09e9f5b28 100644 --- a/client/src/components/Auth/Footer.tsx +++ b/client/src/components/Auth/Footer.tsx @@ -11,7 +11,7 @@ function Footer({ startupConfig }: { startupConfig: TStartupConfig | null | unde const privacyPolicyRender = privacyPolicy?.externalUrl && ( {' '} {localize('com_auth_no_account')}{' '} - + {localize('com_auth_sign_up')}

diff --git a/client/src/components/Auth/LoginForm.tsx b/client/src/components/Auth/LoginForm.tsx index 0c532abe408..67877895251 100644 --- a/client/src/components/Auth/LoginForm.tsx +++ b/client/src/components/Auth/LoginForm.tsx @@ -108,7 +108,7 @@ const LoginForm: React.FC = ({ onSubmit, startupConfig, error, className=" absolute start-3 top-1.5 z-10 origin-[0] -translate-y-4 scale-75 transform bg-surface-primary px-2 text-sm text-text-secondary-alt duration-200 peer-placeholder-shown:top-1/2 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:scale-100 - peer-focus:top-1.5 peer-focus:-translate-y-4 peer-focus:scale-75 peer-focus:px-2 peer-focus:text-green-600 dark:peer-focus:text-green-500 + peer-focus:top-1.5 peer-focus:-translate-y-4 peer-focus:scale-75 peer-focus:px-2 peer-focus:text-secondary dark:peer-focus:text-primary rtl:peer-focus:left-auto rtl:peer-focus:translate-x-1/4 " > @@ -143,7 +143,7 @@ const LoginForm: React.FC = ({ onSubmit, startupConfig, error, className=" absolute start-3 top-1.5 z-10 origin-[0] -translate-y-4 scale-75 transform bg-surface-primary px-2 text-sm text-text-secondary-alt duration-200 peer-placeholder-shown:top-1/2 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:scale-100 - peer-focus:top-1.5 peer-focus:-translate-y-4 peer-focus:scale-75 peer-focus:px-2 peer-focus:text-green-600 dark:peer-focus:text-green-500 + peer-focus:top-1.5 peer-focus:-translate-y-4 peer-focus:scale-75 peer-focus:px-2 peer-focus:text-secondary dark:peer-focus:text-primary rtl:peer-focus:left-auto rtl:peer-focus:translate-x-1/4 " > @@ -153,10 +153,7 @@ const LoginForm: React.FC = ({ onSubmit, startupConfig, error, {renderError('password')}

{startupConfig.passwordResetEnabled && ( - + {localize('com_auth_password_forgot')} )} diff --git a/client/src/components/Auth/Registration.tsx b/client/src/components/Auth/Registration.tsx index 4ae4e03b793..8a85f3558be 100644 --- a/client/src/components/Auth/Registration.tsx +++ b/client/src/components/Auth/Registration.tsx @@ -81,7 +81,7 @@ const Registration: React.FC = () => { className=" absolute start-3 top-1.5 z-10 origin-[0] -translate-y-4 scale-75 transform bg-surface-primary px-2 text-sm text-text-secondary-alt duration-200 peer-placeholder-shown:top-1/2 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:scale-100 - peer-focus:top-1.5 peer-focus:-translate-y-4 peer-focus:scale-75 peer-focus:px-2 peer-focus:text-green-500 + peer-focus:top-1.5 peer-focus:-translate-y-4 peer-focus:scale-75 peer-focus:px-2 peer-focus:text-primary rtl:peer-focus:left-auto rtl:peer-focus:translate-x-1/4 " > @@ -197,11 +197,7 @@ const Registration: React.FC = () => {

{localize('com_auth_already_have_account')}{' '} - + {localize('com_auth_login')}

diff --git a/client/src/components/Auth/RequestPasswordReset.tsx b/client/src/components/Auth/RequestPasswordReset.tsx index 1010c90a98d..d0a4b80c841 100644 --- a/client/src/components/Auth/RequestPasswordReset.tsx +++ b/client/src/components/Auth/RequestPasswordReset.tsx @@ -21,14 +21,13 @@ const BodyTextWrapper: FC<{ children: ReactNode }> = ({ children }) => { const ResetPasswordBodyText = () => { const localize = useLocalize(); return ( -
-

{localize('com_auth_reset_password_if_email_exists')}

- - {localize('com_auth_back_to_login')} - +
+ {localize('com_auth_reset_password_if_email_exists')} + + + {localize('com_auth_back_to_login')} + +
); }; @@ -53,7 +52,7 @@ function RequestPasswordReset() { setBodyText( {localize('com_auth_click')}{' '} - + {localize('com_auth_here')} {' '} {localize('com_auth_to_reset_your_password')} @@ -116,11 +115,10 @@ function RequestPasswordReset() {
); diff --git a/client/src/components/Auth/ResetPassword.tsx b/client/src/components/Auth/ResetPassword.tsx index 3039e81c296..66916281a9c 100644 --- a/client/src/components/Auth/ResetPassword.tsx +++ b/client/src/components/Auth/ResetPassword.tsx @@ -100,7 +100,7 @@ function ResetPassword() { className=" absolute start-3 top-1.5 z-10 origin-[0] -translate-y-4 scale-75 transform bg-surface-primary px-2 text-sm text-text-secondary-alt duration-200 peer-placeholder-shown:top-1/2 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:scale-100 - peer-focus:top-1.5 peer-focus:-translate-y-4 peer-focus:scale-75 peer-focus:px-2 peer-focus:text-green-500 + peer-focus:top-1.5 peer-focus:-translate-y-4 peer-focus:scale-75 peer-focus:px-2 peer-focus:text-primary rtl:peer-focus:left-auto rtl:peer-focus:translate-x-1/4 " > @@ -135,7 +135,7 @@ function ResetPassword() { className=" absolute start-3 top-1.5 z-10 origin-[0] -translate-y-4 scale-75 transform bg-surface-primary px-2 text-sm text-text-secondary-alt duration-200 peer-placeholder-shown:top-1/2 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:scale-100 - peer-focus:top-1.5 peer-focus:-translate-y-4 peer-focus:scale-75 peer-focus:px-2 peer-focus:text-green-500 + peer-focus:top-1.5 peer-focus:-translate-y-4 peer-focus:scale-75 peer-focus:px-2 peer-focus:text-primary rtl:peer-focus:left-auto rtl:peer-focus:translate-x-1/4 " > diff --git a/client/src/components/Auth/VerifyEmail.tsx b/client/src/components/Auth/VerifyEmail.tsx index acb5abe5ad9..99e87652d13 100644 --- a/client/src/components/Auth/VerifyEmail.tsx +++ b/client/src/components/Auth/VerifyEmail.tsx @@ -116,7 +116,7 @@ function RequestPasswordReset() { {localize('com_auth_email_verification_in_progress')}
- +
); diff --git a/client/src/components/Chat/Footer.tsx b/client/src/components/Chat/Footer.tsx index b534aee0188..6c39ead7b0c 100644 --- a/client/src/components/Chat/Footer.tsx +++ b/client/src/components/Chat/Footer.tsx @@ -37,7 +37,7 @@ export default function Footer({ className }: { className?: string }) { const mainContentParts = ( typeof config?.customFooter === 'string' ? config.customFooter - : '[LibreChat ' + + : '[EnricChat ' + Constants.VERSION + '](https://librechat.ai) - ' + localize('com_ui_latest_footer') diff --git a/client/src/components/Chat/Input/Files/FileUpload.tsx b/client/src/components/Chat/Input/Files/FileUpload.tsx index 506f50c01de..de3b97b6bd4 100644 --- a/client/src/components/Chat/Input/Files/FileUpload.tsx +++ b/client/src/components/Chat/Input/Files/FileUpload.tsx @@ -45,7 +45,7 @@ const FileUpload: React.FC = ({ if (validator) { setStatus('success'); - setStatusColor('text-green-500 dark:text-green-500'); + setStatusColor('text-primary dark:text-primary'); } onFileSelected(jsonData); @@ -66,7 +66,7 @@ const FileUpload: React.FC = ({