diff --git a/web/src/interfaces/database/knowledge.ts b/web/src/interfaces/database/knowledge.ts index b6939d1c4..bc82ed24c 100644 --- a/web/src/interfaces/database/knowledge.ts +++ b/web/src/interfaces/database/knowledge.ts @@ -104,6 +104,7 @@ export interface ITenantInfo { tenant_id: string; chat_id: string; speech2text_id: string; + rerank_id?: string; tts_id: string; } diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index 0dcc84163..6759ebcc0 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -680,6 +680,8 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s tocEnhanceTip: ` During the parsing of the document, table of contents information was generated (see the 'Enable Table of Contents Extraction' option in the General method). This allows the large model to return table of contents items relevant to the user's query, thereby using these items to retrieve related chunks and apply weighting to these chunks during the sorting process. This approach is derived from mimicking the behavioral logic of how humans search for knowledge in books.`, }, setting: { + search: 'Search', + availableModels: 'Available models', profile: 'Profile', avatar: 'Avatar', avatarTip: 'This will be displayed on your profile.', @@ -693,7 +695,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s passwordDescription: 'Please enter your current password to change your password.', model: 'Model providers', - modelDescription: 'Configure model parameters and API KEY here.', + systemModelDescription: 'Please complete these settings before beginning', team: 'Team', system: 'System', logout: 'Log out', @@ -726,7 +728,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s cancel: 'Cancel', addedModels: 'Added models', modelsToBeAdded: 'Models to be added', - addTheModel: 'Add Model', + addTheModel: 'Add', apiKey: 'API-Key', apiKeyMessage: 'Please enter the API key (for locally deployed model,ignore this).', @@ -742,21 +744,20 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s tongyiBaseUrlPlaceholder: '(International users only, please see tip)', modify: 'Modify', systemModelSettings: 'Set default models', - chatModel: 'Chat model', - chatModelTip: - 'The default chat model for each newly created knowledge base.', - embeddingModel: 'Embedding model', + chatModel: 'LLM', + chatModelTip: 'The default LLM for each newly created knowledge base.', + embeddingModel: 'Embedding', embeddingModelTip: 'The default embedding model for each newly created knowledge base. If you cannot find an embedding model from the dropdown, check if you are using RAGFlow slim edition (which does not include embedding models) or check https://ragflow.io/docs/dev/supported_models to see if your model provider supports this model.', - img2txtModel: 'Img2txt model', + img2txtModel: 'VLM', img2txtModelTip: - 'The default img2txt model for each newly created knowledge base. It describes a picture or video. If you cannot find a model from the dropdown, check https://ragflow.io/docs/dev/supported_models to see if your model provider supports this model.', - sequence2txtModel: 'Speech2txt model', + 'The default VLM for each newly created knowledge base. It describes a picture or video. If you cannot find a model from the dropdown, check https://ragflow.io/docs/dev/supported_models to see if your model provider supports this model.', + sequence2txtModel: 'ASR', sequence2txtModelTip: 'The default ASR model for each newly created knowledgebase. Use this model to translate voices to corresponding text.', - rerankModel: 'Rerank model', + rerankModel: 'Rerank', rerankModelTip: `The default rerank model for reranking chunks. If you cannot find a model from the dropdown, check https://ragflow.io/docs/dev/supported_models to see if your model provider supports this model.`, - ttsModel: 'TTS Model', + ttsModel: 'TTS', ttsModelTip: 'The default text-to-speech model. If you cannot find a model from the dropdown, check https://ragflow.io/docs/dev/supported_models to see if your model provider supports this model.', workspace: 'Workspace', diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index c1057433c..fd5994835 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -671,6 +671,8 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 tocEnhanceTip: `解析文档时生成了目录信息(见General方法的‘启用目录抽取’),让大模型返回和用户问题相关的目录项,从而利用目录项拿到相关chunk,对这些chunk在排序中进行加权。这种方法来源于模仿人类查询书本中知识的行为逻辑`, }, setting: { + search: '搜索', + availableModels: '可选模型', profile: '概要', avatar: '头像', avatarTip: '这会在你的个人主页展示', @@ -684,7 +686,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 password: '密码', passwordDescription: '请输入您当前的密码以更改您的密码。', model: '模型提供商', - modelDescription: '在此设置模型参数和 API KEY。', + systemModelDescription: '请在开始之前完成这些设置', team: '团队', system: '系统', logout: '登出', @@ -715,7 +717,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 cancel: '取消', addedModels: '添加了的模型', modelsToBeAdded: '待添加的模型', - addTheModel: '添加模型', + addTheModel: '添加', apiKey: 'API-Key', apiKeyMessage: '请输入api key(如果是本地部署的模型,请忽略它)', apiKeyTip: 'API key可以通过注册相应的LLM供应商来获取。', @@ -729,21 +731,21 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 tongyiBaseUrlPlaceholder: '(仅国际用户需要)', modify: '修改', systemModelSettings: '设置默认模型', - chatModel: '聊天模型', + chatModel: 'LLM', chatModelTip: '所有新创建的知识库都会使用默认的聊天模型。', - ttsModel: 'TTS模型', + ttsModel: 'TTS', ttsModelTip: '默认的tts模型会被用于在对话过程中请求语音生成时使用。如未显示可选模型,请根据 https://ragflow.io/docs/dev/supported_models 确认你的模型供应商是否提供该模型。', - embeddingModel: '嵌入模型', + embeddingModel: 'Embedding', embeddingModelTip: '所有新创建的知识库使用的默认嵌入模型。如未显示可选模型,请检查你是否在使用 RAGFlow slim 版(不含嵌入模型);或根据 https://ragflow.io/docs/dev/supported_models 确认你的模型供应商是否提供该模型。', - img2txtModel: 'Img2txt模型', + img2txtModel: 'VLM', img2txtModelTip: '所有新创建的知识库都将使用默认的 img2txt 模型。 它可以描述图片或视频。如未显示可选模型,请根据 https://ragflow.io/docs/dev/supported_models 确认你的模型供应商是否提供该模型。', - sequence2txtModel: 'Speech2txt模型', + sequence2txtModel: 'ASR', sequence2txtModelTip: '所有新创建的知识库都将使用默认的 ASR 模型。 使用此模型将语音翻译为相应的文本。如未显示可选模型,请根据 https://ragflow.io/docs/dev/supported_models 确认你的模型供应商是否提供该模型。', - rerankModel: 'Rerank模型', + rerankModel: 'Rerank', rerankModelTip: `默认的 reranking 模型。如未显示可选模型,请根据 https://ragflow.io/docs/dev/supported_models 确认你的模型供应商是否提供该模型。`, workspace: '工作空间', upgrade: '升级', diff --git a/web/src/pages/agents/template-card.tsx b/web/src/pages/agents/template-card.tsx index e443a920d..7d7f9c744 100644 --- a/web/src/pages/agents/template-card.tsx +++ b/web/src/pages/agents/template-card.tsx @@ -31,9 +31,16 @@ export function TemplateCard({ data, showModal }: IProps) { avatar={data.avatar ? data.avatar : 'https://github.com/shadcn.png'} name={data?.title[language] || 'CN'} > -
{data?.title[language]}
+
+ {data?.title[language]} +
-

{data?.description[language]}

+

+ {data?.description[language]} +

+ + + + +
+ + + {/* Content */} + {visible && ( +
+
+ {sortTags(item.tags).map((tag, index) => ( + + {tag} + + ))} +
+
+
+ {item.llm.map((model) => ( +
+
+ + {getRealModelName(model.name)} + + + {model.type} + +
+ +
+ {isLocalLlmFactory(item.name) && ( + + )} + +
+
+ ))} +
+
+
+ )} + + ); +}; diff --git a/web/src/pages/user-setting/setting-model/components/system-setting.tsx b/web/src/pages/user-setting/setting-model/components/system-setting.tsx new file mode 100644 index 000000000..07941c0ff --- /dev/null +++ b/web/src/pages/user-setting/setting-model/components/system-setting.tsx @@ -0,0 +1,194 @@ +import { + SelectWithSearch, + SelectWithSearchFlagOptionType, +} from '@/components/originui/select-with-search'; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/components/ui/tooltip'; +import { LlmModelType } from '@/constants/knowledge'; +import { useTranslate } from '@/hooks/common-hooks'; +import { + ISystemModelSettingSavingParams, + useComposeLlmOptionsByModelTypes, +} from '@/hooks/llm-hooks'; +import { CircleQuestionMark } from 'lucide-react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useFetchSystemModelSettingOnMount } from '../hooks'; + +interface IProps { + loading: boolean; + onOk: ( + payload: Omit, + ) => void; +} + +const SystemSetting = ({ onOk, loading }: IProps) => { + const { systemSetting: initialValues, allOptions } = + useFetchSystemModelSettingOnMount(); + const { t } = useTranslate('setting'); + + const [formData, setFormData] = useState({ + llm_id: '', + embd_id: '', + img2txt_id: '', + asr_id: '', + rerank_id: '', + tts_id: '', + }); + + const handleFieldChange = useCallback( + (field: string, value: string) => { + const updatedData = { ...formData, [field]: value || '' }; + setFormData(updatedData); + console.log('updatedData', updatedData); + onOk(updatedData); + }, + [formData, onOk], + ); + + useEffect(() => { + setFormData({ + llm_id: initialValues.llm_id ?? '', + embd_id: initialValues.embd_id ?? '', + img2txt_id: initialValues.img2txt_id ?? '', + asr_id: initialValues.asr_id ?? '', + rerank_id: initialValues.rerank_id ?? '', + tts_id: initialValues.tts_id ?? '', + }); + }, [initialValues]); + + const modelOptions = useComposeLlmOptionsByModelTypes([ + LlmModelType.Chat, + LlmModelType.Image2text, + ]); + + const llmList = useMemo(() => { + return [ + { + id: 'llm_id', + label: t('chatModel'), + isRequired: true, + value: formData.llm_id, + options: modelOptions as SelectWithSearchFlagOptionType[], + tooltip: t('chatModelTip'), + }, + { + id: 'embd_id', + label: t('embeddingModel'), + value: formData.embd_id, + options: allOptions[ + LlmModelType.Embedding + ] as SelectWithSearchFlagOptionType[], + tooltip: t('embeddingModelTip'), + }, + { + id: 'img2txt_id', + label: t('img2txtModel'), + value: formData.img2txt_id, + options: allOptions[ + LlmModelType.Image2text + ] as SelectWithSearchFlagOptionType[], + tooltip: t('img2txtModelTip'), + }, + { + id: 'asr_id', + label: t('sequence2txtModel'), + value: formData.asr_id, + options: allOptions[ + LlmModelType.Speech2text + ] as SelectWithSearchFlagOptionType[], + tooltip: t('sequence2txtModelTip'), + }, + { + id: 'rerank_id', + label: t('rerankModel'), + value: formData.rerank_id, + options: allOptions[ + LlmModelType.Rerank + ] as SelectWithSearchFlagOptionType[], + tooltip: t('rerankModelTip'), + }, + { + id: 'tts_id', + label: t('ttsModel'), + value: formData.tts_id, + options: allOptions[ + LlmModelType.TTS + ] as SelectWithSearchFlagOptionType[], + tooltip: t('ttsModelTip'), + }, + ]; + }, [formData, modelOptions, t, allOptions]); + + const Items = ({ + label, + value, + options, + tooltip, + id, + isRequired, + }: { + id: string; + label: string; + value: string; + options: SelectWithSearchFlagOptionType[]; + tooltip?: string; + isRequired?: boolean; + }) => { + return ( +
+ + handleFieldChange(id, value)} + placeholder={t('common:selectPlaceholder')} + /> +
+ ); + }; + + return ( +
+
+
{t('systemModelSettings')}
+
+ {t('systemModelDescription')} +
+
+
+ {llmList.map((item) => ( + + ))} +
+ {/*
+ +
*/} +
+ ); +}; + +export default SystemSetting; diff --git a/web/src/pages/user-setting/setting-model/components/un-add-model.tsx b/web/src/pages/user-setting/setting-model/components/un-add-model.tsx new file mode 100644 index 000000000..88df289ab --- /dev/null +++ b/web/src/pages/user-setting/setting-model/components/un-add-model.tsx @@ -0,0 +1,157 @@ +// src/components/AvailableModels.tsx +import { LlmIcon } from '@/components/svg-icon'; +import { Button } from '@/components/ui/button'; +import { useTranslate } from '@/hooks/common-hooks'; +import { useSelectLlmList } from '@/hooks/llm-hooks'; +import { Plus, Search } from 'lucide-react'; +import { FC, useMemo, useState } from 'react'; + +type TagType = + | 'LLM' + | 'TEXT EMBEDDING' + | 'TEXT RE-RANK' + | 'TTS' + | 'SPEECH2TEXT' + | 'IMAGE2TEXT' + | 'MODERATION'; + +const sortTags = (tags: string) => { + const orderMap: Record = { + LLM: 1, + 'TEXT EMBEDDING': 2, + 'TEXT RE-RANK': 3, + TTS: 4, + SPEECH2TEXT: 5, + IMAGE2TEXT: 6, + MODERATION: 7, + }; + + return tags + .split(',') + .map((tag) => tag.trim()) + .sort( + (a, b) => + (orderMap[a as TagType] || 999) - (orderMap[b as TagType] || 999), + ); +}; + +export const AvailableModels: FC<{ + handleAddModel: (factory: string) => void; +}> = ({ handleAddModel }) => { + const { t } = useTranslate('setting'); + const { factoryList } = useSelectLlmList(); + + const [searchTerm, setSearchTerm] = useState(''); + const [selectedTag, setSelectedTag] = useState(null); + + // 过滤模型列表 + const filteredModels = useMemo(() => { + return factoryList.filter((model) => { + const matchesSearch = model.name + .toLowerCase() + .includes(searchTerm.toLowerCase()); + const matchesTag = + selectedTag === null || + model.tags.split(',').some((tag) => tag.trim() === selectedTag); + return matchesSearch && matchesTag; + }); + }, [factoryList, searchTerm, selectedTag]); + + // 获取所有唯一的标签 + const allTags = useMemo(() => { + const tagsSet = new Set(); + factoryList.forEach((model) => { + model.tags.split(',').forEach((tag) => tagsSet.add(tag.trim())); + }); + return Array.from(tagsSet).sort(); + }, [factoryList]); + + const handleTagClick = (tag: string) => { + setSelectedTag(selectedTag === tag ? null : tag); + }; + + return ( +
+
+ {t('availableModels')} +
+ {/* Search Bar */} +
+
+ setSearchTerm(e.target.value)} + className="w-full px-4 py-3 pl-10 bg-bg-input border border-border-default rounded-lg focus:outline-none focus:ring-1 focus:ring-border-button transition-colors" + /> + +
+
+ + {/* Tags Filter */} +
+ + {allTags.map((tag) => ( + + ))} +
+ + {/* Models List */} +
+ {filteredModels.map((model) => ( +
+
+ +
+

{model.name}

+
+ +
+ +
+ {sortTags(model.tags).map((tag, index) => ( + + {tag} + + ))} +
+
+ ))} +
+
+ ); +}; diff --git a/web/src/pages/user-setting/setting-model/components/used-model.tsx b/web/src/pages/user-setting/setting-model/components/used-model.tsx new file mode 100644 index 000000000..d6a704e29 --- /dev/null +++ b/web/src/pages/user-setting/setting-model/components/used-model.tsx @@ -0,0 +1,27 @@ +import { LlmItem, useSelectLlmList } from '@/hooks/llm-hooks'; +import { ModelProviderCard } from './modal-card'; + +export const UsedModel = ({ + handleAddModel, + handleEditModel, +}: { + handleAddModel: (factory: string) => void; + handleEditModel: (model: any, factory: LlmItem) => void; +}) => { + const { factoryList, myLlmList: llmList, loading } = useSelectLlmList(); + return ( +
+
Added models
+ {llmList.map((llm) => { + return ( + + ); + })} +
+ ); +}; diff --git a/web/src/pages/user-setting/setting-model/index.less b/web/src/pages/user-setting/setting-model/index.less index ae2069937..08fa25a20 100644 --- a/web/src/pages/user-setting/setting-model/index.less +++ b/web/src/pages/user-setting/setting-model/index.less @@ -3,8 +3,6 @@ .factoryOperationWrapper { text-align: right; } - .modelItem { - } .llmList { padding-top: 10px; } diff --git a/web/src/pages/user-setting/setting-model/index.tsx b/web/src/pages/user-setting/setting-model/index.tsx index 73f7ff0bf..dc0008c35 100644 --- a/web/src/pages/user-setting/setting-model/index.tsx +++ b/web/src/pages/user-setting/setting-model/index.tsx @@ -1,47 +1,16 @@ -import { ReactComponent as MoreModelIcon } from '@/assets/svg/more-model.svg'; -import { LlmIcon } from '@/components/svg-icon'; -import { useTheme } from '@/components/theme-provider'; import { LLMFactory } from '@/constants/llm'; -import { useSetModalState, useTranslate } from '@/hooks/common-hooks'; -import { - LlmItem, - useFetchMyLlmListDetailed, - useSelectLlmList, -} from '@/hooks/llm-hooks'; -import { getRealModelName } from '@/utils/llm-util'; -import { - CloseCircleOutlined, - EditOutlined, - SettingOutlined, -} from '@ant-design/icons'; -import { - Button, - Card, - Col, - Collapse, - CollapseProps, - Divider, - Flex, - List, - Row, - Space, - Spin, - Tag, - Tooltip, - Typography, -} from 'antd'; -import { CircleHelp } from 'lucide-react'; +import { LlmItem, useFetchMyLlmListDetailed } from '@/hooks/llm-hooks'; import { useCallback, useMemo } from 'react'; -import SettingTitle from '../components/setting-title'; import { isLocalLlmFactory } from '../utils'; import ApiKeyModal from './api-key-modal'; import AzureOpenAIModal from './azure-openai-modal'; import BedrockModal from './bedrock-modal'; +import SystemSetting from './components/system-setting'; +import { AvailableModels } from './components/un-add-model'; +import { UsedModel } from './components/used-model'; import FishAudioModal from './fish-audio-modal'; import GoogleModal from './google-modal'; import { - useHandleDeleteFactory, - useHandleDeleteLlm, useSubmitApiKey, useSubmitAzure, useSubmitBedrock, @@ -56,164 +25,15 @@ import { useSubmityiyan, } from './hooks'; import HunyuanModal from './hunyuan-modal'; -import styles from './index.less'; import TencentCloudModal from './next-tencent-modal'; import OllamaModal from './ollama-modal'; import SparkModal from './spark-modal'; -import SystemModelSettingModal from './system-model-setting-modal'; import VolcEngineModal from './volcengine-modal'; import YiyanModal from './yiyan-modal'; - -const { Text } = Typography; -interface IModelCardProps { - item: LlmItem; - clickApiKey: (llmFactory: string) => void; - handleEditModel: (model: any, factory: LlmItem) => void; -} - -type TagType = - | 'LLM' - | 'TEXT EMBEDDING' - | 'TEXT RE-RANK' - | 'TTS' - | 'SPEECH2TEXT' - | 'IMAGE2TEXT' - | 'MODERATION'; - -const sortTags = (tags: string) => { - const orderMap: Record = { - LLM: 1, - 'TEXT EMBEDDING': 2, - 'TEXT RE-RANK': 3, - TTS: 4, - SPEECH2TEXT: 5, - IMAGE2TEXT: 6, - MODERATION: 7, - }; - - return tags - .split(',') - .map((tag) => tag.trim()) - .sort( - (a, b) => - (orderMap[a as TagType] || 999) - (orderMap[b as TagType] || 999), - ); -}; - -const ModelCard = ({ item, clickApiKey, handleEditModel }: IModelCardProps) => { - const { visible, switchVisible } = useSetModalState(); - const { t } = useTranslate('setting'); - const { theme } = useTheme(); - const { handleDeleteLlm } = useHandleDeleteLlm(item.name); - const { handleDeleteFactory } = useHandleDeleteFactory(item.name); - - const handleApiKeyClick = () => { - clickApiKey(item.name); - }; - - const handleShowMoreClick = () => { - switchVisible(); - }; - - return ( - - - - - - - - {item.name} - - {sortTags(item.tags).map((tag, index) => ( - - {tag} - - ))} - - - - - - - - - - - - - {visible && ( - ( - - - {getRealModelName(model.name)} - {model.type} - {isLocalLlmFactory(item.name) && ( - - - - )} - - - - - - )} - /> - )} - - - ); -}; - -const UserSettingModel = () => { - const { factoryList, myLlmList: llmList, loading } = useSelectLlmList(); +const ModelProviders = () => { + const { saveSystemModelSettingLoading, onSystemSettingSavingOk } = + useSubmitSystemModelSetting(); const { data: detailedLlmList } = useFetchMyLlmListDetailed(); - const { theme } = useTheme(); const { saveApiKeyLoading, initialApiKey, @@ -224,14 +44,6 @@ const UserSettingModel = () => { hideApiKeyModal, showApiKeyModal, } = useSubmitApiKey(); - const { - saveSystemModelSettingLoading, - onSystemSettingSavingOk, - systemSettingVisible, - hideSystemSettingModal, - showSystemSettingModal, - } = useSubmitSystemModelSetting(); - const { t } = useTranslate('setting'); const { llmAddingVisible, hideLlmAddingModal, @@ -342,6 +154,7 @@ const UserSettingModel = () => { const handleAddModel = useCallback( (llmFactory: string) => { + console.log('handleAddModel', llmFactory); if (isLocalLlmFactory(llmFactory)) { showLlmAddingModal(llmFactory); } else if (llmFactory in ModalMap) { @@ -378,116 +191,21 @@ const UserSettingModel = () => { }, [showApiKeyModal, showLlmAddingModal, ModalMap, detailedLlmList], ); - - const items: CollapseProps['items'] = [ - { - key: '1', - label: t('addedModels'), - children: ( - ( - - )} - /> - ), - }, - { - key: '2', - label: ( -
- {t('modelsToBeAdded')} - - - -
- ), - children: ( - ( - - - - - - - {item.name} - - - {sortTags(item.tags).map((tag, index) => ( - - {tag} - - ))} - - - - - - - - )} - /> - ), - }, - ]; - return ( -
- -
- - - -
-
+
+
+ + +
+
+ +
{ onOk={onApiKeySavingOk} llmFactory={llmFactory} > - {systemSettingVisible && ( - - )} { loading={AzureAddingLoading} llmFactory={LLMFactory.AzureOpenAI} > -
+ ); }; - -export default UserSettingModel; +export default ModelProviders; diff --git a/web/src/pages/user-setting/setting-model/index1.tsx b/web/src/pages/user-setting/setting-model/index1.tsx new file mode 100644 index 000000000..73f7ff0bf --- /dev/null +++ b/web/src/pages/user-setting/setting-model/index1.tsx @@ -0,0 +1,584 @@ +import { ReactComponent as MoreModelIcon } from '@/assets/svg/more-model.svg'; +import { LlmIcon } from '@/components/svg-icon'; +import { useTheme } from '@/components/theme-provider'; +import { LLMFactory } from '@/constants/llm'; +import { useSetModalState, useTranslate } from '@/hooks/common-hooks'; +import { + LlmItem, + useFetchMyLlmListDetailed, + useSelectLlmList, +} from '@/hooks/llm-hooks'; +import { getRealModelName } from '@/utils/llm-util'; +import { + CloseCircleOutlined, + EditOutlined, + SettingOutlined, +} from '@ant-design/icons'; +import { + Button, + Card, + Col, + Collapse, + CollapseProps, + Divider, + Flex, + List, + Row, + Space, + Spin, + Tag, + Tooltip, + Typography, +} from 'antd'; +import { CircleHelp } from 'lucide-react'; +import { useCallback, useMemo } from 'react'; +import SettingTitle from '../components/setting-title'; +import { isLocalLlmFactory } from '../utils'; +import ApiKeyModal from './api-key-modal'; +import AzureOpenAIModal from './azure-openai-modal'; +import BedrockModal from './bedrock-modal'; +import FishAudioModal from './fish-audio-modal'; +import GoogleModal from './google-modal'; +import { + useHandleDeleteFactory, + useHandleDeleteLlm, + useSubmitApiKey, + useSubmitAzure, + useSubmitBedrock, + useSubmitFishAudio, + useSubmitGoogle, + useSubmitHunyuan, + useSubmitOllama, + useSubmitSpark, + useSubmitSystemModelSetting, + useSubmitTencentCloud, + useSubmitVolcEngine, + useSubmityiyan, +} from './hooks'; +import HunyuanModal from './hunyuan-modal'; +import styles from './index.less'; +import TencentCloudModal from './next-tencent-modal'; +import OllamaModal from './ollama-modal'; +import SparkModal from './spark-modal'; +import SystemModelSettingModal from './system-model-setting-modal'; +import VolcEngineModal from './volcengine-modal'; +import YiyanModal from './yiyan-modal'; + +const { Text } = Typography; +interface IModelCardProps { + item: LlmItem; + clickApiKey: (llmFactory: string) => void; + handleEditModel: (model: any, factory: LlmItem) => void; +} + +type TagType = + | 'LLM' + | 'TEXT EMBEDDING' + | 'TEXT RE-RANK' + | 'TTS' + | 'SPEECH2TEXT' + | 'IMAGE2TEXT' + | 'MODERATION'; + +const sortTags = (tags: string) => { + const orderMap: Record = { + LLM: 1, + 'TEXT EMBEDDING': 2, + 'TEXT RE-RANK': 3, + TTS: 4, + SPEECH2TEXT: 5, + IMAGE2TEXT: 6, + MODERATION: 7, + }; + + return tags + .split(',') + .map((tag) => tag.trim()) + .sort( + (a, b) => + (orderMap[a as TagType] || 999) - (orderMap[b as TagType] || 999), + ); +}; + +const ModelCard = ({ item, clickApiKey, handleEditModel }: IModelCardProps) => { + const { visible, switchVisible } = useSetModalState(); + const { t } = useTranslate('setting'); + const { theme } = useTheme(); + const { handleDeleteLlm } = useHandleDeleteLlm(item.name); + const { handleDeleteFactory } = useHandleDeleteFactory(item.name); + + const handleApiKeyClick = () => { + clickApiKey(item.name); + }; + + const handleShowMoreClick = () => { + switchVisible(); + }; + + return ( + + + + + + + + {item.name} + + {sortTags(item.tags).map((tag, index) => ( + + {tag} + + ))} + + + + + + + + + + + + + {visible && ( + ( + + + {getRealModelName(model.name)} + {model.type} + {isLocalLlmFactory(item.name) && ( + + + + )} + + + + + + )} + /> + )} + + + ); +}; + +const UserSettingModel = () => { + const { factoryList, myLlmList: llmList, loading } = useSelectLlmList(); + const { data: detailedLlmList } = useFetchMyLlmListDetailed(); + const { theme } = useTheme(); + const { + saveApiKeyLoading, + initialApiKey, + llmFactory, + editMode, + onApiKeySavingOk, + apiKeyVisible, + hideApiKeyModal, + showApiKeyModal, + } = useSubmitApiKey(); + const { + saveSystemModelSettingLoading, + onSystemSettingSavingOk, + systemSettingVisible, + hideSystemSettingModal, + showSystemSettingModal, + } = useSubmitSystemModelSetting(); + const { t } = useTranslate('setting'); + const { + llmAddingVisible, + hideLlmAddingModal, + showLlmAddingModal, + onLlmAddingOk, + llmAddingLoading, + editMode: llmEditMode, + initialValues: llmInitialValues, + selectedLlmFactory, + } = useSubmitOllama(); + + const { + volcAddingVisible, + hideVolcAddingModal, + showVolcAddingModal, + onVolcAddingOk, + volcAddingLoading, + } = useSubmitVolcEngine(); + + const { + HunyuanAddingVisible, + hideHunyuanAddingModal, + showHunyuanAddingModal, + onHunyuanAddingOk, + HunyuanAddingLoading, + } = useSubmitHunyuan(); + + const { + GoogleAddingVisible, + hideGoogleAddingModal, + showGoogleAddingModal, + onGoogleAddingOk, + GoogleAddingLoading, + } = useSubmitGoogle(); + + const { + TencentCloudAddingVisible, + hideTencentCloudAddingModal, + showTencentCloudAddingModal, + onTencentCloudAddingOk, + TencentCloudAddingLoading, + } = useSubmitTencentCloud(); + + const { + SparkAddingVisible, + hideSparkAddingModal, + showSparkAddingModal, + onSparkAddingOk, + SparkAddingLoading, + } = useSubmitSpark(); + + const { + yiyanAddingVisible, + hideyiyanAddingModal, + showyiyanAddingModal, + onyiyanAddingOk, + yiyanAddingLoading, + } = useSubmityiyan(); + + const { + FishAudioAddingVisible, + hideFishAudioAddingModal, + showFishAudioAddingModal, + onFishAudioAddingOk, + FishAudioAddingLoading, + } = useSubmitFishAudio(); + + const { + bedrockAddingLoading, + onBedrockAddingOk, + bedrockAddingVisible, + hideBedrockAddingModal, + showBedrockAddingModal, + } = useSubmitBedrock(); + + const { + AzureAddingVisible, + hideAzureAddingModal, + showAzureAddingModal, + onAzureAddingOk, + AzureAddingLoading, + } = useSubmitAzure(); + + const ModalMap = useMemo( + () => ({ + [LLMFactory.Bedrock]: showBedrockAddingModal, + [LLMFactory.VolcEngine]: showVolcAddingModal, + [LLMFactory.TencentHunYuan]: showHunyuanAddingModal, + [LLMFactory.XunFeiSpark]: showSparkAddingModal, + [LLMFactory.BaiduYiYan]: showyiyanAddingModal, + [LLMFactory.FishAudio]: showFishAudioAddingModal, + [LLMFactory.TencentCloud]: showTencentCloudAddingModal, + [LLMFactory.GoogleCloud]: showGoogleAddingModal, + [LLMFactory.AzureOpenAI]: showAzureAddingModal, + }), + [ + showBedrockAddingModal, + showVolcAddingModal, + showHunyuanAddingModal, + showTencentCloudAddingModal, + showSparkAddingModal, + showyiyanAddingModal, + showFishAudioAddingModal, + showGoogleAddingModal, + showAzureAddingModal, + ], + ); + + const handleAddModel = useCallback( + (llmFactory: string) => { + if (isLocalLlmFactory(llmFactory)) { + showLlmAddingModal(llmFactory); + } else if (llmFactory in ModalMap) { + ModalMap[llmFactory as keyof typeof ModalMap](); + } else { + showApiKeyModal({ llm_factory: llmFactory }); + } + }, + [showApiKeyModal, showLlmAddingModal, ModalMap], + ); + + const handleEditModel = useCallback( + (model: any, factory: LlmItem) => { + if (factory) { + const detailedFactory = detailedLlmList[factory.name]; + const detailedModel = detailedFactory?.llm?.find( + (m: any) => m.name === model.name, + ); + + const editData = { + llm_factory: factory.name, + llm_name: model.name, + model_type: model.type, + }; + + if (isLocalLlmFactory(factory.name)) { + showLlmAddingModal(factory.name, true, editData, detailedModel); + } else if (factory.name in ModalMap) { + ModalMap[factory.name as keyof typeof ModalMap](); + } else { + showApiKeyModal(editData, true); + } + } + }, + [showApiKeyModal, showLlmAddingModal, ModalMap, detailedLlmList], + ); + + const items: CollapseProps['items'] = [ + { + key: '1', + label: t('addedModels'), + children: ( + ( + + )} + /> + ), + }, + { + key: '2', + label: ( +
+ {t('modelsToBeAdded')} + + + +
+ ), + children: ( + ( + + + + + + + {item.name} + + + {sortTags(item.tags).map((tag, index) => ( + + {tag} + + ))} + + + + + + + + )} + /> + ), + }, + ]; + + return ( +
+ +
+ + + +
+
+ + {systemSettingVisible && ( + + )} + + + + + + + + + + +
+ ); +}; + +export default UserSettingModel;