diff --git a/.env.example b/.env.example index b102cf93c13e..4ec9439d91c5 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,7 @@ ACCESS_CODE=lobe66 # add your custom model name, multi model separate by comma. for example gpt-3.5-1106,gpt-4-1106 -# NEXT_PUBLIC_CUSTOM_MODELS=model1,model2,model3 +# CUSTOM_MODELS=model1,model2,model3 # ---- only choose one from OpenAI Service and Azure OpenAI Service ---- # diff --git a/Dockerfile b/Dockerfile index 03f16e8fe1af..c04632819311 100644 --- a/Dockerfile +++ b/Dockerfile @@ -58,7 +58,7 @@ ENV PORT=3210 # General Variables ENV ACCESS_CODE "lobe66" -ENV NEXT_PUBLIC_CUSTOM_MODELS "" +ENV CUSTOM_MODELS "" # OpenAI ENV OPENAI_API_KEY "" diff --git a/docs/Deployment/Environment-Variable.md b/docs/Deployment/Environment-Variable.md index ce195e617d9c..79b26fae84c8 100644 --- a/docs/Deployment/Environment-Variable.md +++ b/docs/Deployment/Environment-Variable.md @@ -6,7 +6,7 @@ LobeChat provides additional configuration options during deployment, which can - [General Variables](#general-variables) - [`ACCESS_CODE`](#access_code) - - [`NEXT_PUBLIC_CUSTOM_MODELS`](#next_public_custom_models) + - [`CUSTOM_MODELS`](#custom_models) - [OpenAI](#openai) - [`OPENAI_API_KEY`](#openai_api_key) - [`OPENAI_PROXY_URL`](#openai_proxy_url) @@ -31,7 +31,7 @@ LobeChat provides additional configuration options during deployment, which can - Default: `-` - Example: `awCTe)re_r74` or `rtrt_ewee3@09!` or `code1,code2,code3` -### `NEXT_PUBLIC_CUSTOM_MODELS` +### `CUSTOM_MODELS` - Type: Optional - Description: Used to control the model list. Use `+` to add a model, `-` to hide a model, and `model_name=display_name` to customize the display name of a model, separated by commas. diff --git a/docs/Deployment/Environment-Variable.zh-CN.md b/docs/Deployment/Environment-Variable.zh-CN.md index 9def8293e5f5..24011fcfc6d6 100644 --- a/docs/Deployment/Environment-Variable.zh-CN.md +++ b/docs/Deployment/Environment-Variable.zh-CN.md @@ -6,7 +6,7 @@ LobeChat 在部署时提供了一些额外的配置项,使用环境变量进 - [通用变量](#通用变量) - [`ACCESS_CODE`](#access_code) - - [`NEXT_PUBLIC_CUSTOM_MODELS`](#next_public_custom_models) + - [`CUSTOM_MODELS`](#custom_models) - [OpenAI](#openai) - [`OPENAI_API_KEY`](#openai_api_key) - [`OPENAI_PROXY_URL`](#openai_proxy_url) @@ -31,7 +31,7 @@ LobeChat 在部署时提供了一些额外的配置项,使用环境变量进 - 默认值:- - 示例:`awCTe)re_r74` or `rtrt_ewee3@09!` -### `NEXT_PUBLIC_CUSTOM_MODELS` +### `CUSTOM_MODELS` - 类型:可选 - 描述:用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。 diff --git a/src/app/api/config/route.ts b/src/app/api/config/route.ts new file mode 100644 index 000000000000..61db882bc305 --- /dev/null +++ b/src/app/api/config/route.ts @@ -0,0 +1,15 @@ +import { getServerConfig } from '@/config/server'; +import { GlobalServerConfig } from '@/types/settings'; + +export const runtime = 'edge'; + +/** + * get Server config to client + */ +export const GET = async () => { + const { CUSTOM_MODELS } = getServerConfig(); + + const config: GlobalServerConfig = { customModelName: CUSTOM_MODELS }; + + return new Response(JSON.stringify(config)); +}; diff --git a/src/layout/GlobalLayout/StoreHydration.tsx b/src/layout/GlobalLayout/StoreHydration.tsx index 89d87b97f2d9..f58bdc6488d2 100644 --- a/src/layout/GlobalLayout/StoreHydration.tsx +++ b/src/layout/GlobalLayout/StoreHydration.tsx @@ -8,12 +8,16 @@ import { useEffectAfterSessionHydrated, useSessionStore } from '@/store/session' const StoreHydration = memo(() => { const router = useRouter(); + const useFetchGlobalConfig = useGlobalStore((s) => s.useFetchGlobalConfig); + useEffect(() => { // refs: https://github.com/pmndrs/zustand/blob/main/docs/integrations/persisting-store-data.md#hashydrated useSessionStore.persist.rehydrate(); useGlobalStore.persist.rehydrate(); }, []); + useFetchGlobalConfig(); + const { mobile } = useResponsive(); useEffectAfterSessionHydrated( @@ -29,6 +33,7 @@ const StoreHydration = memo(() => { }, [router], ); + useEffect(() => { router.prefetch('/chat'); router.prefetch('/market'); diff --git a/src/services/__tests__/chat.test.ts b/src/services/__tests__/chat.test.ts index 6fe0874c1c2c..4d137bef2e1e 100644 --- a/src/services/__tests__/chat.test.ts +++ b/src/services/__tests__/chat.test.ts @@ -1,6 +1,6 @@ import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk'; import { act } from '@testing-library/react'; -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { VISION_MODEL_WHITE_LIST } from '@/const/llm'; import { DEFAULT_AGENT_CONFIG } from '@/const/settings'; diff --git a/src/services/_url.ts b/src/services/_url.ts index 8aad8796e47d..a827f89b2059 100644 --- a/src/services/_url.ts +++ b/src/services/_url.ts @@ -1,4 +1,5 @@ export const URLS = { + config: '/api/config', market: '/api/market', plugins: '/api/plugins', }; diff --git a/src/services/global.ts b/src/services/global.ts index 182f49a7899f..a661728c522f 100644 --- a/src/services/global.ts +++ b/src/services/global.ts @@ -1,3 +1,6 @@ +import { URLS } from '@/services/_url'; +import { GlobalServerConfig } from '@/types/settings'; + const VERSION_URL = 'https://registry.npmmirror.com/@lobehub/chat'; class GlobalService { @@ -10,6 +13,12 @@ class GlobalService { return data['dist-tags']?.latest; }; + + getGlobalConfig = async (): Promise => { + const res = await fetch(URLS.config); + + return res.json(); + }; } export const globalService = new GlobalService(); diff --git a/src/store/global/initialState.ts b/src/store/global/initialState.ts index 136510f30ad8..0ab951914b07 100644 --- a/src/store/global/initialState.ts +++ b/src/store/global/initialState.ts @@ -1,5 +1,5 @@ import { DEFAULT_SETTINGS } from '@/const/settings'; -import type { GlobalSettings } from '@/types/settings'; +import type { GlobalServerConfig, GlobalSettings } from '@/types/settings'; export enum SidebarTabKey { Chat = 'chat', @@ -38,6 +38,7 @@ export interface GlobalState { * @localStorage */ preference: GlobalPreference; + serverConfig: GlobalServerConfig; /** * @localStorage * 用户设置 @@ -58,6 +59,7 @@ export const initialState: GlobalState = { showSessionPanel: true, showSystemRole: false, }, + serverConfig: {}, settings: DEFAULT_SETTINGS, settingsTab: SettingsTabs.Common, sidebarKey: SidebarTabKey.Chat, diff --git a/src/store/global/selectors/settings.test.ts b/src/store/global/selectors/settings.test.ts index fb9aba4d40a1..079ce97a1a85 100644 --- a/src/store/global/selectors/settings.test.ts +++ b/src/store/global/selectors/settings.test.ts @@ -61,12 +61,13 @@ describe('settingsSelectors', () => { describe('CUSTOM_MODELS', () => { it('custom deletion, addition, and renaming of models', () => { const s = { + serverConfig: { + customModelName: + '-all,+llama,+claude-2,-gpt-3.5-turbo,gpt-4-1106-preview=gpt-4-turbo,gpt-4-1106-preview=gpt-4-32k', + }, settings: { languageModel: { - openAI: { - customModelName: - '-all,+llama,+claude-2,-gpt-3.5-turbo,gpt-4-1106-preview=gpt-4-turbo,gpt-4-1106-preview=gpt-4-32k', - }, + openAI: {}, }, }, } as unknown as GlobalStore; @@ -78,6 +79,7 @@ describe('settingsSelectors', () => { it('duplicate naming model', () => { const s = { + serverConfig: {}, settings: { languageModel: { openAI: { @@ -94,6 +96,7 @@ describe('settingsSelectors', () => { it('only add the model', () => { const s = { + serverConfig: {}, settings: { languageModel: { openAI: { diff --git a/src/store/global/selectors/settings.ts b/src/store/global/selectors/settings.ts index 3979a25085d6..4cd939664300 100644 --- a/src/store/global/selectors/settings.ts +++ b/src/store/global/selectors/settings.ts @@ -34,6 +34,7 @@ const modelListSelectors = (s: GlobalStore) => { const modelNames = [ ...DEFAULT_OPENAI_MODEL_LIST, + ...(s.serverConfig.customModelName || '').split(/[,,]/).filter(Boolean), ...(s.settings.languageModel.openAI.customModelName || '').split(/[,,]/).filter(Boolean), ]; diff --git a/src/store/global/slices/common.ts b/src/store/global/slices/common.ts index 309d64bfe4fa..7bde724f3f04 100644 --- a/src/store/global/slices/common.ts +++ b/src/store/global/slices/common.ts @@ -5,6 +5,7 @@ import type { StateCreator } from 'zustand/vanilla'; import { CURRENT_VERSION } from '@/const/version'; import { globalService } from '@/services/global'; +import type { GlobalServerConfig } from '@/types/settings'; import { merge } from '@/utils/merge'; import { setNamespace } from '@/utils/storeDebug'; @@ -28,6 +29,7 @@ export interface CommonAction { updateGuideState: (guide: Partial) => void; updatePreference: (preference: Partial, action?: string) => void; useCheckLatestVersion: () => SWRResponse; + useFetchGlobalConfig: () => SWRResponse; } export const createCommonSlice: StateCreator< @@ -78,4 +80,11 @@ export const createCommonSlice: StateCreator< set({ hasNewVersion: true, latestVersion: data }, false, n('checkLatestVersion')); }, }), + useFetchGlobalConfig: () => + useSWR('fetchGlobalConfig', globalService.getGlobalConfig, { + onSuccess: (data) => { + set({ serverConfig: data }); + }, + revalidateOnFocus: false, + }), }); diff --git a/src/types/settings.ts b/src/types/settings.ts index 8b3b469743fc..771c746b3888 100644 --- a/src/types/settings.ts +++ b/src/types/settings.ts @@ -91,3 +91,7 @@ export interface GlobalSettings extends GlobalBaseSettings { } export type ConfigKeys = keyof GlobalSettings; + +export interface GlobalServerConfig { + customModelName?: string; +}