From 423b70c24ebc219293538cd5033e3b5ddeb1aa0f Mon Sep 17 00:00:00 2001 From: arvinxx Date: Thu, 28 Nov 2024 23:59:43 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=9A=A7=20refactor:=20wip=20of=20refac?= =?UTF-8?q?tor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(main)/settings/hooks/useCategory.tsx | 6 +- .../llm/ProviderList/OpenAI/index.tsx | 17 - .../settings/llm/ProviderList/providers.tsx | 106 - .../ProviderModelList/ModelFetcher.tsx | 107 - .../components/ProviderModelList/Option.tsx | 66 - .../components/ProviderModelList/index.tsx | 147 - src/app/(main)/settings/llm/index.tsx | 26 - .../settings/provider/(detail)/[id]/index.tsx | 21 + .../settings/provider/(detail)/[id]/page.tsx | 73 + .../(detail)/azure/page.tsx} | 11 +- .../(detail)/bedrock/page.tsx} | 11 +- .../(detail)/cloudflare/page.tsx} | 11 +- .../(detail)/github/page.tsx} | 11 +- .../(detail)/huggingface/page.tsx} | 11 +- .../(detail)/ollama}/Checker.tsx | 0 .../(detail)/ollama/page.tsx} | 11 +- .../provider/(detail)/openai/page.tsx | 21 + .../(detail)/wenxin/page.tsx} | 11 +- .../features => provider/(list)}/Footer.tsx | 1 + .../provider/(list)/ProviderGrid/Card.tsx | 110 + .../provider/(list)/ProviderGrid/index.tsx | 69 + .../(main)/settings/provider/(list)/index.tsx | 20 + .../settings/provider/ProviderMenu/All.tsx | 29 + .../settings/provider/ProviderMenu/Item.tsx | 61 + .../provider/ProviderMenu/SearchResult.tsx | 43 + .../settings/provider/ProviderMenu/index.tsx | 74 + .../{llm => provider}/components/Checker.tsx | 0 .../ModelList}/CustomModelOption.tsx | 0 .../ModelList/EnabledModelList/index.tsx | 42 + .../ModelList}/ModelConfigModal/Form.tsx | 0 .../ModelConfigModal/MaxTokenSlider.tsx | 0 .../ModelList}/ModelConfigModal/index.tsx | 0 .../components/ModelList/ModelItem.tsx | 159 + .../components/ModelList/ModelTitle.tsx | 118 + .../provider/components/ModelList/index.tsx | 111 + .../components/ProviderConfig/index.tsx | 57 +- .../settings/{llm => provider}/const.ts | 0 src/app/(main)/settings/provider/layout.tsx | 42 + .../settings/{llm => provider}/page.tsx | 12 +- .../(main)/settings/{llm => provider}/type.ts | 0 src/app/@modal/(.)settings/modal/index.tsx | 45 - src/app/@modal/(.)settings/modal/layout.tsx | 47 - src/app/@modal/(.)settings/modal/loading.tsx | 5 - src/app/@modal/(.)settings/modal/page.tsx | 19 - src/config/aiModels/openai.ts | 365 ++ src/config/featureFlags/schema.ts | 3 + src/config/modelProviders/openai.ts | 9 +- src/database/client/migrations.json | 11 + src/database/migrations/0013_add_ai_infra.sql | 39 + .../migrations/meta/0013_snapshot.json | 3925 +++++++++++++++++ src/database/migrations/meta/_journal.json | 7 + src/database/schemas/aiInfra.ts | 62 + src/database/schemas/index.ts | 1 + .../server/models/__tests__/aiModel.test.ts | 130 + .../models/__tests__/aiProvider.test.ts | 149 + src/database/server/models/aiModel.ts | 54 + src/database/server/models/aiProvider.ts | 67 + src/features/Setting/SettingContainer.tsx | 9 +- .../ShareModal/ShareImage/ChatList/index.tsx | 2 +- src/features/User/UserPanel/useMenu.tsx | 20 +- src/hooks/useInterceptingRoutes.ts | 20 +- src/locales/default/modelProvider.ts | 12 + src/locales/default/setting.ts | 3 +- src/server/routers/lambda/aiProvider.ts | 64 + src/store/global/initialState.ts | 1 + .../modelList/selectors/modelProvider.ts | 14 +- src/types/aiModel.ts | 111 + src/types/aiProvider.ts | 82 + src/types/llm.ts | 21 +- 69 files changed, 6220 insertions(+), 692 deletions(-) delete mode 100644 src/app/(main)/settings/llm/ProviderList/OpenAI/index.tsx delete mode 100644 src/app/(main)/settings/llm/ProviderList/providers.tsx delete mode 100644 src/app/(main)/settings/llm/components/ProviderModelList/ModelFetcher.tsx delete mode 100644 src/app/(main)/settings/llm/components/ProviderModelList/Option.tsx delete mode 100644 src/app/(main)/settings/llm/components/ProviderModelList/index.tsx delete mode 100644 src/app/(main)/settings/llm/index.tsx create mode 100644 src/app/(main)/settings/provider/(detail)/[id]/index.tsx create mode 100644 src/app/(main)/settings/provider/(detail)/[id]/page.tsx rename src/app/(main)/settings/{llm/ProviderList/Azure/index.tsx => provider/(detail)/azure/page.tsx} (93%) rename src/app/(main)/settings/{llm/ProviderList/Bedrock/index.tsx => provider/(detail)/bedrock/page.tsx} (91%) rename src/app/(main)/settings/{llm/ProviderList/Cloudflare/index.tsx => provider/(detail)/cloudflare/page.tsx} (84%) rename src/app/(main)/settings/{llm/ProviderList/Github/index.tsx => provider/(detail)/github/page.tsx} (87%) rename src/app/(main)/settings/{llm/ProviderList/HuggingFace/index.tsx => provider/(detail)/huggingface/page.tsx} (87%) rename src/app/(main)/settings/{llm/ProviderList/Ollama => provider/(detail)/ollama}/Checker.tsx (100%) rename src/app/(main)/settings/{llm/ProviderList/Ollama/index.tsx => provider/(detail)/ollama/page.tsx} (74%) create mode 100644 src/app/(main)/settings/provider/(detail)/openai/page.tsx rename src/app/(main)/settings/{llm/ProviderList/Wenxin/index.tsx => provider/(detail)/wenxin/page.tsx} (85%) rename src/app/(main)/settings/{llm/features => provider/(list)}/Footer.tsx (97%) create mode 100644 src/app/(main)/settings/provider/(list)/ProviderGrid/Card.tsx create mode 100644 src/app/(main)/settings/provider/(list)/ProviderGrid/index.tsx create mode 100644 src/app/(main)/settings/provider/(list)/index.tsx create mode 100644 src/app/(main)/settings/provider/ProviderMenu/All.tsx create mode 100644 src/app/(main)/settings/provider/ProviderMenu/Item.tsx create mode 100644 src/app/(main)/settings/provider/ProviderMenu/SearchResult.tsx create mode 100644 src/app/(main)/settings/provider/ProviderMenu/index.tsx rename src/app/(main)/settings/{llm => provider}/components/Checker.tsx (100%) rename src/app/(main)/settings/{llm/components/ProviderModelList => provider/components/ModelList}/CustomModelOption.tsx (100%) create mode 100644 src/app/(main)/settings/provider/components/ModelList/EnabledModelList/index.tsx rename src/app/(main)/settings/{llm/components/ProviderModelList => provider/components/ModelList}/ModelConfigModal/Form.tsx (100%) rename src/app/(main)/settings/{llm/components/ProviderModelList => provider/components/ModelList}/ModelConfigModal/MaxTokenSlider.tsx (100%) rename src/app/(main)/settings/{llm/components/ProviderModelList => provider/components/ModelList}/ModelConfigModal/index.tsx (100%) create mode 100644 src/app/(main)/settings/provider/components/ModelList/ModelItem.tsx create mode 100644 src/app/(main)/settings/provider/components/ModelList/ModelTitle.tsx create mode 100644 src/app/(main)/settings/provider/components/ModelList/index.tsx rename src/app/(main)/settings/{llm => provider}/components/ProviderConfig/index.tsx (88%) rename src/app/(main)/settings/{llm => provider}/const.ts (100%) create mode 100644 src/app/(main)/settings/provider/layout.tsx rename src/app/(main)/settings/{llm => provider}/page.tsx (54%) rename src/app/(main)/settings/{llm => provider}/type.ts (100%) delete mode 100644 src/app/@modal/(.)settings/modal/index.tsx delete mode 100644 src/app/@modal/(.)settings/modal/layout.tsx delete mode 100644 src/app/@modal/(.)settings/modal/loading.tsx delete mode 100644 src/app/@modal/(.)settings/modal/page.tsx create mode 100644 src/config/aiModels/openai.ts create mode 100644 src/database/migrations/0013_add_ai_infra.sql create mode 100644 src/database/migrations/meta/0013_snapshot.json create mode 100644 src/database/schemas/aiInfra.ts create mode 100644 src/database/server/models/__tests__/aiModel.test.ts create mode 100644 src/database/server/models/__tests__/aiProvider.test.ts create mode 100644 src/database/server/models/aiModel.ts create mode 100644 src/database/server/models/aiProvider.ts create mode 100644 src/server/routers/lambda/aiProvider.ts create mode 100644 src/types/aiModel.ts create mode 100644 src/types/aiProvider.ts diff --git a/src/app/(main)/settings/hooks/useCategory.tsx b/src/app/(main)/settings/hooks/useCategory.tsx index 4afb134cf489..4e07646dca65 100644 --- a/src/app/(main)/settings/hooks/useCategory.tsx +++ b/src/app/(main)/settings/hooks/useCategory.tsx @@ -52,10 +52,10 @@ export const useCategory = () => { }, showLLM && { icon: , - key: SettingsTabs.LLM, + key: SettingsTabs.Provider, label: ( - e.preventDefault()}> - {t('tab.llm')} + e.preventDefault()}> + {t('tab.provider')} ), }, diff --git a/src/app/(main)/settings/llm/ProviderList/OpenAI/index.tsx b/src/app/(main)/settings/llm/ProviderList/OpenAI/index.tsx deleted file mode 100644 index 461c4ba91bb0..000000000000 --- a/src/app/(main)/settings/llm/ProviderList/OpenAI/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -'use client'; - -import { OpenAIProviderCard } from '@/config/modelProviders'; -import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig'; - -import { ProviderItem } from '../../type'; - -export const useOpenAIProvider = (): ProviderItem => { - const { showOpenAIProxyUrl, showOpenAIApiKey } = useServerConfigStore(featureFlagsSelectors); - return { - ...OpenAIProviderCard, - proxyUrl: showOpenAIProxyUrl && { - placeholder: 'https://api.openai.com/v1', - }, - showApiKey: showOpenAIApiKey, - }; -}; diff --git a/src/app/(main)/settings/llm/ProviderList/providers.tsx b/src/app/(main)/settings/llm/ProviderList/providers.tsx deleted file mode 100644 index f108683bfa07..000000000000 --- a/src/app/(main)/settings/llm/ProviderList/providers.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { useMemo } from 'react'; - -import { - Ai21ProviderCard, - Ai360ProviderCard, - AnthropicProviderCard, - BaichuanProviderCard, - DeepSeekProviderCard, - FireworksAIProviderCard, - GiteeAIProviderCard, - GoogleProviderCard, - GroqProviderCard, - HigressProviderCard, - HunyuanProviderCard, - InternLMProviderCard, - MinimaxProviderCard, - MistralProviderCard, - MoonshotProviderCard, - NovitaProviderCard, - OpenRouterProviderCard, - PerplexityProviderCard, - QwenProviderCard, - SenseNovaProviderCard, - SiliconCloudProviderCard, - SparkProviderCard, - StepfunProviderCard, - TaichuProviderCard, - TogetherAIProviderCard, - UpstageProviderCard, - XAIProviderCard, - ZeroOneProviderCard, - ZhiPuProviderCard, -} from '@/config/modelProviders'; - -import { ProviderItem } from '../type'; -import { useAzureProvider } from './Azure'; -import { useBedrockProvider } from './Bedrock'; -import { useCloudflareProvider } from './Cloudflare'; -import { useGithubProvider } from './Github'; -import { useHuggingFaceProvider } from './HuggingFace'; -import { useOllamaProvider } from './Ollama'; -import { useOpenAIProvider } from './OpenAI'; -import { useWenxinProvider } from './Wenxin'; - -export const useProviderList = (): ProviderItem[] => { - const AzureProvider = useAzureProvider(); - const OllamaProvider = useOllamaProvider(); - const OpenAIProvider = useOpenAIProvider(); - const BedrockProvider = useBedrockProvider(); - const CloudflareProvider = useCloudflareProvider(); - const GithubProvider = useGithubProvider(); - const HuggingFaceProvider = useHuggingFaceProvider(); - const WenxinProvider = useWenxinProvider(); - - return useMemo( - () => [ - OpenAIProvider, - AzureProvider, - OllamaProvider, - AnthropicProviderCard, - BedrockProvider, - GoogleProviderCard, - DeepSeekProviderCard, - HuggingFaceProvider, - OpenRouterProviderCard, - CloudflareProvider, - GithubProvider, - NovitaProviderCard, - TogetherAIProviderCard, - FireworksAIProviderCard, - GroqProviderCard, - PerplexityProviderCard, - MistralProviderCard, - Ai21ProviderCard, - UpstageProviderCard, - XAIProviderCard, - QwenProviderCard, - WenxinProvider, - HunyuanProviderCard, - SparkProviderCard, - ZhiPuProviderCard, - ZeroOneProviderCard, - SenseNovaProviderCard, - StepfunProviderCard, - MoonshotProviderCard, - BaichuanProviderCard, - MinimaxProviderCard, - Ai360ProviderCard, - TaichuProviderCard, - InternLMProviderCard, - SiliconCloudProviderCard, - HigressProviderCard, - GiteeAIProviderCard, - ], - [ - AzureProvider, - OllamaProvider, - OpenAIProvider, - BedrockProvider, - CloudflareProvider, - GithubProvider, - WenxinProvider, - HuggingFaceProvider, - ], - ); -}; diff --git a/src/app/(main)/settings/llm/components/ProviderModelList/ModelFetcher.tsx b/src/app/(main)/settings/llm/components/ProviderModelList/ModelFetcher.tsx deleted file mode 100644 index fc3623cb9d05..000000000000 --- a/src/app/(main)/settings/llm/components/ProviderModelList/ModelFetcher.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { ActionIcon, Icon, Tooltip } from '@lobehub/ui'; -import { Typography } from 'antd'; -import { createStyles } from 'antd-style'; -import dayjs from 'dayjs'; -import isEqual from 'fast-deep-equal'; -import { CircleX, LucideLoaderCircle, LucideRefreshCcwDot } from 'lucide-react'; -import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Flexbox } from 'react-layout-kit'; - -import { useUserStore } from '@/store/user'; -import { - modelConfigSelectors, - modelProviderSelectors, - settingsSelectors, -} from '@/store/user/selectors'; -import { GlobalLLMProviderKey } from '@/types/user/settings'; - -const useStyles = createStyles(({ css, token }) => ({ - hover: css` - cursor: pointer; - - padding-block: 4px; - padding-inline: 8px; - - border-radius: ${token.borderRadius}px; - - transition: all 0.2s ease-in-out; - - &:hover { - color: ${token.colorText}; - background-color: ${token.colorFillSecondary}; - } - `, -})); - -interface ModelFetcherProps { - provider: GlobalLLMProviderKey; -} - -const ModelFetcher = memo(({ provider }) => { - const { styles } = useStyles(); - const { t } = useTranslation('setting'); - const [useFetchProviderModelList, clearObtainedModels] = useUserStore((s) => [ - s.useFetchProviderModelList, - s.clearObtainedModels, - s.setModelProviderConfig, - ]); - const enabledAutoFetch = useUserStore(modelConfigSelectors.isAutoFetchModelsEnabled(provider)); - const latestFetchTime = useUserStore( - (s) => settingsSelectors.providerConfig(provider)(s)?.latestFetchTime, - ); - const totalModels = useUserStore( - (s) => modelProviderSelectors.getModelCardsById(provider)(s).length, - ); - - const remoteModels = useUserStore( - modelProviderSelectors.remoteProviderModelCards(provider), - isEqual, - ); - - const { mutate, isValidating } = useFetchProviderModelList(provider, enabledAutoFetch); - - return ( - - -
- {t('llm.modelList.total', { count: totalModels })} - {remoteModels && remoteModels.length > 0 && ( - clearObtainedModels(provider)} - size={'small'} - title={t('llm.fetcher.clear')} - /> - )} -
- - mutate()} - > - -
{isValidating ? t('llm.fetcher.fetching') : t('llm.fetcher.fetch')}
-
-
-
-
- ); -}); -export default ModelFetcher; diff --git a/src/app/(main)/settings/llm/components/ProviderModelList/Option.tsx b/src/app/(main)/settings/llm/components/ProviderModelList/Option.tsx deleted file mode 100644 index c37901ee48d1..000000000000 --- a/src/app/(main)/settings/llm/components/ProviderModelList/Option.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { ModelIcon } from '@lobehub/icons'; -import { ActionIcon, Tooltip } from '@lobehub/ui'; -import { Typography } from 'antd'; -import { useTheme } from 'antd-style'; -import isEqual from 'fast-deep-equal'; -import { Recycle } from 'lucide-react'; -import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Flexbox } from 'react-layout-kit'; - -import { ModelInfoTags } from '@/components/ModelSelect'; -import { useUserStore } from '@/store/user'; -import { modelProviderSelectors } from '@/store/user/selectors'; -import { GlobalLLMProviderKey } from '@/types/user/settings'; - -import CustomModelOption from './CustomModelOption'; - -interface OptionRenderProps { - displayName: string; - id: string; - isAzure?: boolean; - provider: GlobalLLMProviderKey; - removed?: boolean; -} -const OptionRender = memo(({ displayName, id, provider, isAzure, removed }) => { - const model = useUserStore((s) => modelProviderSelectors.getModelCardById(id)(s), isEqual); - const { t } = useTranslation('components'); - const theme = useTheme(); - // if there is isCustom, it means it is a user defined custom model - if (model?.isCustom || isAzure) return ; - - return ( - - - - - - {displayName} - - - - {id} - - - - {removed && ( - - - - )} - - ); -}); - -export default OptionRender; diff --git a/src/app/(main)/settings/llm/components/ProviderModelList/index.tsx b/src/app/(main)/settings/llm/components/ProviderModelList/index.tsx deleted file mode 100644 index a5e6a8338f2d..000000000000 --- a/src/app/(main)/settings/llm/components/ProviderModelList/index.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import { ActionIcon } from '@lobehub/ui'; -import { Select } from 'antd'; -import { css, cx } from 'antd-style'; -import isEqual from 'fast-deep-equal'; -import { RotateCwIcon } from 'lucide-react'; -import { ReactNode, memo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Flexbox } from 'react-layout-kit'; - -import { useUserStore } from '@/store/user'; -import { modelProviderSelectors } from '@/store/user/selectors'; -import { GlobalLLMProviderKey } from '@/types/user/settings'; - -import ModelConfigModal from './ModelConfigModal'; -import ModelFetcher from './ModelFetcher'; -import OptionRender from './Option'; - -const styles = { - divStyle: css` - position: relative; - - .ant-select-selector { - padding-inline-end: 50px !important; - } - `, - popup: css` - &.ant-select-dropdown { - .ant-select-item-option-selected { - font-weight: normal; - } - } - `, - reset: css` - position: absolute; - z-index: 20; - inset-block-start: 50%; - inset-inline-end: 28px; - transform: translateY(-50%); - `, -}; - -interface CustomModelSelectProps { - notFoundContent?: ReactNode; - placeholder?: string; - provider: GlobalLLMProviderKey; - showAzureDeployName?: boolean; - showModelFetcher?: boolean; -} - -const ProviderModelListSelect = memo( - ({ showModelFetcher = false, provider, showAzureDeployName, notFoundContent, placeholder }) => { - const { t } = useTranslation('common'); - const { t: transSetting } = useTranslation('setting'); - const [setModelProviderConfig, updateEnabledModels] = useUserStore((s) => [ - s.setModelProviderConfig, - s.updateEnabledModels, - ]); - - const chatModelCards = useUserStore( - modelProviderSelectors.getModelCardsById(provider), - isEqual, - ); - - const defaultEnableModel = useUserStore( - modelProviderSelectors.getDefaultEnabledModelsById(provider), - isEqual, - ); - const enabledModels = useUserStore( - modelProviderSelectors.getEnableModelsById(provider), - isEqual, - ); - - const showReset = !!enabledModels && !isEqual(defaultEnableModel, enabledModels); - - return ( - <> - -
-
- {showReset && ( - { - setModelProviderConfig(provider, { enabledModels: null }); - }} - size={'small'} - title={t('reset')} - /> - )} -
- - allowClear - mode="tags" - notFoundContent={notFoundContent} - onChange={(value, options) => { - updateEnabledModels(provider, value, options as any[]); - }} - optionFilterProp="label" - optionRender={({ label, value }) => { - // model is in the chatModels - if (chatModelCards.some((c) => c.id === value)) - return ( - - ); - - if (enabledModels?.some((m) => value === m)) { - return ( - - ); - } - - // model is defined by user in client - return ( - - {transSetting('llm.customModelCards.addNew', { id: value })} - - ); - }} - options={chatModelCards.map((model) => ({ - label: model.displayName || model.id, - value: model.id, - }))} - placeholder={placeholder} - popupClassName={cx(styles.popup)} - value={enabledModels ?? defaultEnableModel} - /> -
- {showModelFetcher && } -
- - - ); - }, -); - -export default ProviderModelListSelect; diff --git a/src/app/(main)/settings/llm/index.tsx b/src/app/(main)/settings/llm/index.tsx deleted file mode 100644 index 6d0c823f7d53..000000000000 --- a/src/app/(main)/settings/llm/index.tsx +++ /dev/null @@ -1,26 +0,0 @@ -'use client'; - -import { Flexbox } from 'react-layout-kit'; - -import { isCustomBranding } from '@/const/version'; - -import { useProviderList } from './ProviderList/providers'; -import ProviderConfig from './components/ProviderConfig'; -import Footer from './features/Footer'; - -const Page = () => { - const list = useProviderList(); - - return ( - - {list.map(({ id, ...res }) => ( - - ))} - {!isCustomBranding &&