diff --git a/api/server/routes/config.js b/api/server/routes/config.js
index 01f2e4b7ea..a9f6772dee 100644
--- a/api/server/routes/config.js
+++ b/api/server/routes/config.js
@@ -44,6 +44,7 @@ router.get('/', async function (req, res) {
isEnabled(process.env.SHOW_BIRTHDAY_ICON) ||
process.env.SHOW_BIRTHDAY_ICON === '',
helpAndFaqURL: process.env.HELP_AND_FAQ_URL || 'https://librechat.ai',
+ interface: req.app.locals.interface,
};
if (typeof process.env.CUSTOM_FOOTER === 'string') {
diff --git a/api/server/services/AppService.js b/api/server/services/AppService.js
index 15e19428da..3b24cee646 100644
--- a/api/server/services/AppService.js
+++ b/api/server/services/AppService.js
@@ -135,6 +135,7 @@ const AppService = async (app) => {
availableTools,
fileStrategy,
fileConfig: config?.fileConfig,
+ interface: config?.interface,
paths,
...endpointLocals,
};
diff --git a/client/src/components/Auth/Login.tsx b/client/src/components/Auth/Login.tsx
index a9bb90d8fd..3d9322a23f 100644
--- a/client/src/components/Auth/Login.tsx
+++ b/client/src/components/Auth/Login.tsx
@@ -91,6 +91,29 @@ function Login() {
),
};
+ const privacyPolicy = startupConfig.interface?.privacyPolicy;
+ const termsOfService = startupConfig.interface?.termsOfService;
+
+ const privacyPolicyRender = privacyPolicy?.externalUrl && (
+
+ {localize('com_ui_privacy_policy')}
+
+ );
+
+ const termsOfServiceRender = termsOfService?.externalUrl && (
+
+ {localize('com_ui_terms_of_service')}
+
+ );
+
return (
@@ -139,6 +162,13 @@ function Login() {
>
)}
+
+ {privacyPolicyRender}
+ {privacyPolicyRender && termsOfServiceRender && (
+
+ )}
+ {termsOfServiceRender}
+
);
}
diff --git a/client/src/components/Chat/Footer.tsx b/client/src/components/Chat/Footer.tsx
index de78515159..7cd03d074e 100644
--- a/client/src/components/Chat/Footer.tsx
+++ b/client/src/components/Chat/Footer.tsx
@@ -4,20 +4,61 @@ import { useLocalize } from '~/hooks';
export default function Footer() {
const { data: config } = useGetStartupConfig();
const localize = useLocalize();
+
+ const privacyPolicy = config?.interface?.privacyPolicy;
+ const termsOfService = config?.interface?.termsOfService;
+
+ const privacyPolicyRender = privacyPolicy?.externalUrl && (
+
+ {localize('com_ui_privacy_policy')}
+
+ );
+
+ const termsOfServiceRender = termsOfService?.externalUrl && (
+
+ {localize('com_ui_terms_of_service')}
+
+ );
+
+ const mainContentRender = (
+
+ {typeof config?.customFooter === 'string' ? (
+ config.customFooter
+ ) : (
+ <>
+
+ {config?.appTitle || 'LibreChat'} v0.6.10
+
+ {' - '} {localize('com_ui_new_footer')}
+ >
+ )}
+
+ );
+
+ const footerElements = [mainContentRender, privacyPolicyRender, termsOfServiceRender].filter(
+ Boolean,
+ );
+
return (
-
-
- {typeof config?.customFooter === 'string' ? (
- config.customFooter
- ) : (
+
+ {footerElements.map((contentRender, index) => {
+ const isLastElement = index === footerElements.length - 1;
+
+ return (
<>
-
- {config?.appTitle || 'LibreChat'} v0.6.10
-
- {' - '} {localize('com_ui_new_footer')}
+ {contentRender}
+ {!isLastElement &&
}
>
- )}
-
+ );
+ })}
);
}
diff --git a/client/src/localization/languages/Eng.tsx b/client/src/localization/languages/Eng.tsx
index c21fc9e740..aaf6ef67ac 100644
--- a/client/src/localization/languages/Eng.tsx
+++ b/client/src/localization/languages/Eng.tsx
@@ -99,6 +99,8 @@ export default {
com_ui_preview: 'Preview',
com_ui_upload: 'Upload',
com_ui_connect: 'Connect',
+ com_ui_privacy_policy: 'Privacy policy',
+ com_ui_terms_of_service: 'Terms of service',
com_auth_error_login:
'Unable to login with the information provided. Please check your credentials and try again.',
com_auth_error_login_rl:
diff --git a/librechat.example.yaml b/librechat.example.yaml
index f0bf6088b0..038f571944 100644
--- a/librechat.example.yaml
+++ b/librechat.example.yaml
@@ -7,11 +7,23 @@ version: 1.0.4
# Cache settings: Set to true to enable caching
cache: true
+# Custom nterface configuration
+interface:
+ # Privacy policy settings
+ privacyPolicy:
+ externalUrl: 'https://librechat.ai/privacy-policy'
+ openNewTab: true
+
+ # Terms of service
+ termsOfService:
+ externalUrl: 'https://librechat.ai/tos'
+ openNewTab: true
+
# Example Registration Object Structure (optional)
registration:
- socialLogins: ["github", "google", "discord", "openid", "facebook"]
+ socialLogins: ['github', 'google', 'discord', 'openid', 'facebook']
# allowedDomains:
- # - "gmail.com"
+ # - "gmail.com"
# fileConfig:
# endpoints:
@@ -49,44 +61,40 @@ endpoints:
# # excludedIds: ["asst_excludedAssistantId"]
custom:
# Groq Example
- - name: "groq"
- apiKey: "${GROQ_API_KEY}"
- baseURL: "https://api.groq.com/openai/v1/"
+ - name: 'groq'
+ apiKey: '${GROQ_API_KEY}'
+ baseURL: 'https://api.groq.com/openai/v1/'
models:
- default: [
- "llama2-70b-4096",
- "mixtral-8x7b-32768",
- "gemma-7b-it"
- ]
+ default: ['llama2-70b-4096', 'mixtral-8x7b-32768', 'gemma-7b-it']
fetch: false
titleConvo: true
- titleModel: "mixtral-8x7b-32768"
- modelDisplayLabel: "groq"
+ titleModel: 'mixtral-8x7b-32768'
+ modelDisplayLabel: 'groq'
# Mistral AI Example
- - name: "Mistral" # Unique name for the endpoint
+ - name: 'Mistral' # Unique name for the endpoint
# For `apiKey` and `baseURL`, you can use environment variables that you define.
# recommended environment variables:
- apiKey: "${MISTRAL_API_KEY}"
- baseURL: "https://api.mistral.ai/v1"
+ apiKey: '${MISTRAL_API_KEY}'
+ baseURL: 'https://api.mistral.ai/v1'
# Models configuration
- models:
+ models:
# List of default models to use. At least one value is required.
- default: ["mistral-tiny", "mistral-small", "mistral-medium"]
+ default: ['mistral-tiny', 'mistral-small', 'mistral-medium']
# Fetch option: Set to true to fetch models from API.
- fetch: true # Defaults to false.
+ fetch: true # Defaults to false.
# Optional configurations
-
+
# Title Conversation setting
- titleConvo: true # Set to true to enable title conversation
+ titleConvo: true # Set to true to enable title conversation
# Title Method: Choose between "completion" or "functions".
# titleMethod: "completion" # Defaults to "completion" if omitted.
# Title Model: Specify the model to use for titles.
- titleModel: "mistral-tiny" # Defaults to "gpt-3.5-turbo" if omitted.
+ titleModel: 'mistral-tiny' # Defaults to "gpt-3.5-turbo" if omitted.
# Summarize setting: Set to true to enable summarization.
# summarize: false
@@ -98,31 +106,30 @@ endpoints:
# forcePrompt: false
# The label displayed for the AI model in messages.
- modelDisplayLabel: "Mistral" # Default is "AI" when not set.
+ modelDisplayLabel: 'Mistral' # Default is "AI" when not set.
# Add additional parameters to the request. Default params will be overwritten.
# addParams:
- # safe_prompt: true # This field is specific to Mistral AI: https://docs.mistral.ai/api/
-
+ # safe_prompt: true # This field is specific to Mistral AI: https://docs.mistral.ai/api/
+
# Drop Default params parameters from the request. See default params in guide linked below.
# NOTE: For Mistral, it is necessary to drop the following parameters or you will encounter a 422 Error:
- dropParams: ["stop", "user", "frequency_penalty", "presence_penalty"]
+ dropParams: ['stop', 'user', 'frequency_penalty', 'presence_penalty']
# OpenRouter Example
- - name: "OpenRouter"
+ - name: 'OpenRouter'
# For `apiKey` and `baseURL`, you can use environment variables that you define.
# recommended environment variables:
# Known issue: you should not use `OPENROUTER_API_KEY` as it will then override the `openAI` endpoint to use OpenRouter as well.
- apiKey: "${OPENROUTER_KEY}"
- baseURL: "https://openrouter.ai/api/v1"
+ apiKey: '${OPENROUTER_KEY}'
+ baseURL: 'https://openrouter.ai/api/v1'
models:
- default: ["gpt-3.5-turbo"]
+ default: ['gpt-3.5-turbo']
fetch: true
titleConvo: true
- titleModel: "gpt-3.5-turbo"
+ titleModel: 'gpt-3.5-turbo'
# Recommended: Drop the stop parameter from the request as Openrouter models use a variety of stop tokens.
- dropParams: ["stop"]
- modelDisplayLabel: "OpenRouter"
-
+ dropParams: ['stop']
+ modelDisplayLabel: 'OpenRouter'
# See the Custom Configuration Guide for more information:
# https://docs.librechat.ai/install/configuration/custom_config.html
diff --git a/package-lock.json b/package-lock.json
index a8e5b37ce0..75c9b241fe 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -27994,7 +27994,7 @@
},
"packages/data-provider": {
"name": "librechat-data-provider",
- "version": "0.4.6",
+ "version": "0.4.7",
"license": "ISC",
"dependencies": {
"@types/js-yaml": "^4.0.9",
diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts
index 3df0c94914..21e78ff130 100644
--- a/packages/data-provider/src/config.ts
+++ b/packages/data-provider/src/config.ts
@@ -147,6 +147,22 @@ export const rateLimitSchema = z.object({
export const configSchema = z.object({
version: z.string(),
cache: z.boolean(),
+ interface: z
+ .object({
+ privacyPolicy: z
+ .object({
+ externalUrl: z.string().optional(),
+ openNewTab: z.boolean().optional(),
+ })
+ .optional(),
+ termsOfService: z
+ .object({
+ externalUrl: z.string().optional(),
+ openNewTab: z.boolean().optional(),
+ })
+ .optional(),
+ })
+ .optional(),
fileStrategy: fileSourceSchema.optional(),
registration: z
.object({
diff --git a/packages/data-provider/src/types.ts b/packages/data-provider/src/types.ts
index 765471a550..467f1bc461 100644
--- a/packages/data-provider/src/types.ts
+++ b/packages/data-provider/src/types.ts
@@ -193,9 +193,21 @@ export type TResetPassword = {
confirm_password?: string;
};
+export type TInterfaceConfig = {
+ privacyPolicy?: {
+ externalUrl?: string;
+ openNewTab?: boolean;
+ };
+ termsOfService?: {
+ externalUrl?: string;
+ openNewTab?: boolean;
+ };
+};
+
export type TStartupConfig = {
appTitle: string;
socialLogins?: string[];
+ interface?: TInterfaceConfig;
discordLoginEnabled: boolean;
facebookLoginEnabled: boolean;
githubLoginEnabled: boolean;