diff --git a/src/components/AppSvgIcon.tsx b/src/components/AppSvgIcon.tsx index c64a847..4f555c5 100644 --- a/src/components/AppSvgIcon.tsx +++ b/src/components/AppSvgIcon.tsx @@ -40,8 +40,7 @@ export default function AppSvgIcon(props: AppSvgIconProps) { try { setLoading(true); const iconPath = `${svgPath}${pointPath}/${name}.svg?react`; - logger.debug(`[AppSvgIcon] 加载图标: ${iconPath}`); - + // logger.debug(`[AppSvgIcon] 加载图标: ${iconPath}`); const iconModule = await import(/* @vite-ignore */ iconPath); setIcon(() => iconModule.default); } catch (error) { diff --git a/src/interfaces/database/knowledge.ts b/src/interfaces/database/knowledge.ts index 7db96c0..eef789d 100644 --- a/src/interfaces/database/knowledge.ts +++ b/src/interfaces/database/knowledge.ts @@ -310,6 +310,8 @@ export interface ITenantInfo { speech2text_id: string; /** 文本转语音服务ID */ tts_id: string; + // rerank模型服务ID + rerank_id: string; } /** diff --git a/src/pages/setting/components/ModelDialogs.tsx b/src/pages/setting/components/ModelDialogs.tsx index aea3ed7..34972e8 100644 --- a/src/pages/setting/components/ModelDialogs.tsx +++ b/src/pages/setting/components/ModelDialogs.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { Dialog, DialogTitle, @@ -16,9 +16,49 @@ import { CircularProgress, IconButton, InputAdornment, + ListSubheader, } from '@mui/material'; import { Visibility, VisibilityOff } from '@mui/icons-material'; import { useForm, Controller } from 'react-hook-form'; +import { LlmSvgIcon } from '@/components/AppSvgIcon'; +import { IconMap, type LLMFactory } from '@/constants/llm'; +import type { ITenantInfo } from '@/interfaces/database/knowledge'; +import type { LlmModelType } from '@/constants/knowledge'; +import type { IMyLlmModel, IThirdOAIModel } from '@/interfaces/database/llm'; + +// 基础对话框状态 +interface BaseDialogState { + open: boolean; + loading: boolean; + editMode: boolean; + closeDialog: () => void; +} + +// ModelDialogs 整合组件的 Props 接口 +interface ModelDialogsProps { + apiKeyDialog: BaseDialogState & { + initialData: any; + factoryName: string; + submitApiKey: (data: ApiKeyFormData) => Promise; + }; + azureDialog: BaseDialogState & { + initialData: any; + submitAzureOpenAI: (data: AzureOpenAIFormData) => Promise; + }; + bedrockDialog: BaseDialogState & { + initialData: any; + submitBedrock: (data: BedrockFormData) => Promise; + }; + ollamaDialog: BaseDialogState & { + initialData: any; + submitOllama: (data: OllamaFormData) => Promise; + }; + systemDialog: BaseDialogState & { + allModelOptions: any; + initialData: Partial; + submitSystemModelSetting: (data: Partial) => Promise; + }; +} // 接口定义 export interface ApiKeyFormData { @@ -53,15 +93,54 @@ const BEDROCK_REGIONS = [ 'ap-south-1', 'ca-central-1', 'sa-east-1' ]; -// 通用 API Key 对话框 -interface ApiKeyDialogProps { +// 基础对话框 Props +interface BaseDialogProps { open: boolean; onClose: () => void; - onSubmit: (data: ApiKeyFormData) => Promise; loading: boolean; + editMode?: boolean; +} + +// 通用 API Key 对话框 +interface ApiKeyDialogProps extends BaseDialogProps { + onSubmit: (data: ApiKeyFormData) => Promise; factoryName: string; initialData?: Partial; - editMode?: boolean; +} + +// Azure OpenAI 对话框 +interface AzureOpenAIDialogProps extends BaseDialogProps { + onSubmit: (data: AzureOpenAIFormData) => Promise; + initialData?: Partial; +} + +// AWS Bedrock 对话框 +interface BedrockDialogProps extends BaseDialogProps { + onSubmit: (data: BedrockFormData) => Promise; + initialData?: Partial; +} + +// Ollama 对话框 +interface OllamaDialogProps extends BaseDialogProps { + onSubmit: (data: OllamaFormData) => Promise; + initialData?: Partial; +} + +interface AllModelOptionItem { + label: string; + options: { + value: string; + label: string; + disabled: boolean; + model: IThirdOAIModel + }[]; +} + +// 系统默认模型设置对话框 +interface SystemModelDialogProps extends BaseDialogProps { + onSubmit: (data: Partial) => Promise; + initialData?: Partial; + allModelOptions: Record; } /** @@ -192,15 +271,6 @@ export const ApiKeyDialog: React.FC = ({ ); }; -// Azure OpenAI 对话框 -interface AzureOpenAIDialogProps { - open: boolean; - onClose: () => void; - onSubmit: (data: AzureOpenAIFormData) => Promise; - loading: boolean; - initialData?: Partial; - editMode?: boolean; -} /** * Azure OpenAI 对话框 @@ -348,15 +418,6 @@ export const AzureOpenAIDialog: React.FC = ({ ); }; -// AWS Bedrock 对话框 -interface BedrockDialogProps { - open: boolean; - onClose: () => void; - onSubmit: (data: BedrockFormData) => Promise; - loading: boolean; - initialData?: Partial; - editMode?: boolean; -} /** * AWS Bedrock 对话框 @@ -492,14 +553,6 @@ export const BedrockDialog: React.FC = ({ ); }; -interface OllamaDialogProps { - open: boolean; - onClose: () => void; - onSubmit: (data: OllamaFormData) => Promise; - loading: boolean; - initialData?: Partial; - editMode?: boolean; -} /** * Ollama 对话框 @@ -598,4 +651,320 @@ export const OllamaDialog: React.FC = ({ ); +}; + + + +/** + * 系统默认模型设置对话框 + */ +export const SystemModelDialog: React.FC = ({ + open, + onClose, + onSubmit, + loading, + initialData, + allModelOptions +}) => { + const { control, handleSubmit, reset, formState: { errors } } = useForm({ + defaultValues: {} + }); + + // 获取工厂图标名称 + const getFactoryIconName = (factoryName: LLMFactory) => { + return IconMap[factoryName] || 'default'; + }; + + // all model options 包含了全部的 options + + const llmOptions = useMemo(() => allModelOptions?.llmOptions || [], [allModelOptions]); + const embdOptions = useMemo(() => allModelOptions?.embeddingOptions || [], [allModelOptions]); + const img2txtOptions = useMemo(() => allModelOptions?.image2textOptions || [], [allModelOptions]); + const asrOptions = useMemo(() => allModelOptions?.speech2textOptions || [], [allModelOptions]); + const ttsOptions = useMemo(() => allModelOptions?.ttsOptions || [], [allModelOptions]); + const rerankOptions = useMemo(() => allModelOptions?.rerankOptions || [], [allModelOptions]); + + useEffect(() => { + if (open && initialData) { + reset(initialData); + } else if (open) { + reset({ + llm_id: '', + embd_id: '', + img2txt_id: '', + asr_id: '', + tts_id: '', + rerank_id: '', + }); + } + }, [open, initialData, reset]); + + const handleFormSubmit = async (data: ITenantInfo) => { + try { + await onSubmit(data); + onClose(); + } catch (error) { + console.error('提交失败:', error); + } + }; + + return ( + + + 设置默认模型 + + + + ( + + 聊天模型 + + {errors.llm_id && ( + + {errors.llm_id.message} + + )} + + )} + /> + + ( + + 嵌入模型 + + {errors.embd_id && ( + + {errors.embd_id.message} + + )} + + )} + /> + + ( + + Img2txt模型 + + + )} + /> + + ( + + Speech2txt模型 + + + )} + /> + + ( + + Rerank模型 + + + )} + /> + + ( + + TTS模型 + + + )} + /> + + + + + + + + ); +}; + + +/** + * 模型对话框整合组件 + */ +export const ModelDialogs: React.FC = ({ + apiKeyDialog, + azureDialog, + bedrockDialog, + ollamaDialog, + systemDialog, +}) => { + return ( + <> + {/* API Key 对话框 */} + + + {/* Azure OpenAI 对话框 */} + + + {/* AWS Bedrock 对话框 */} + + + {/* Ollama 对话框 */} + + + {/* 系统默认模型设置对话框 */} + + + ); }; \ No newline at end of file diff --git a/src/pages/setting/hooks/useModelDialogs.ts b/src/pages/setting/hooks/useModelDialogs.ts index 236cc9b..1324da1 100644 --- a/src/pages/setting/hooks/useModelDialogs.ts +++ b/src/pages/setting/hooks/useModelDialogs.ts @@ -1,13 +1,16 @@ -import { useState, useCallback } from 'react'; +import { useState, useCallback, useMemo } from 'react'; import { useMessage } from '@/hooks/useSnackbar'; import userService from '@/services/user_service'; import logger from '@/utils/logger'; -import type { +import type { ApiKeyFormData, AzureOpenAIFormData, BedrockFormData, OllamaFormData, } from '../components/ModelDialogs'; +import type { ITenantInfo } from '@/interfaces/database/knowledge'; +import { useLlmList } from '@/hooks/llm-hooks'; +import type { LlmModelType } from '@/constants/knowledge'; // 对话框状态管理 hook export const useDialogState = () => { @@ -42,7 +45,7 @@ export const useDialogState = () => { // API Key 对话框管理 export const useApiKeyDialog = () => { const dialogState = useDialogState(); - const showMessage = useMessage(); + const showMessage = useMessage(); const [factoryName, setFactoryName] = useState(''); const openApiKeyDialog = useCallback((factory: string, data?: Partial, isEdit = false) => { @@ -81,7 +84,7 @@ export const useApiKeyDialog = () => { // Azure OpenAI 对话框管理 export const useAzureOpenAIDialog = () => { const dialogState = useDialogState(); - const showMessage = useMessage(); + const showMessage = useMessage(); const submitAzureOpenAI = useCallback(async (data: AzureOpenAIFormData) => { dialogState.setLoading(true); @@ -114,7 +117,7 @@ export const useAzureOpenAIDialog = () => { // AWS Bedrock 对话框管理 export const useBedrockDialog = () => { const dialogState = useDialogState(); - const showMessage = useMessage(); + const showMessage = useMessage(); const submitBedrock = useCallback(async (data: BedrockFormData) => { dialogState.setLoading(true); @@ -148,7 +151,7 @@ export const useBedrockDialog = () => { // Ollama 对话框管理 export const useOllamaDialog = () => { const dialogState = useDialogState(); - const showMessage = useMessage(); + const showMessage = useMessage(); const submitOllama = useCallback(async (data: OllamaFormData) => { dialogState.setLoading(true); @@ -178,7 +181,7 @@ export const useOllamaDialog = () => { // 删除操作管理 export const useDeleteOperations = () => { - const showMessage = useMessage(); + const showMessage = useMessage(); const [loading, setLoading] = useState(false); const deleteLlm = useCallback(async (factoryName: string, modelName: string) => { @@ -224,9 +227,56 @@ export const useDeleteOperations = () => { // 系统默认模型设置 export const useSystemModelSetting = () => { const dialogState = useDialogState(); - const showMessage = useMessage(); + const showMessage = useMessage(); - const submitSystemModelSetting = useCallback(async (data: { defaultModel: string }) => { + const { data: llmList } = useLlmList(); + + const getOptionsByModelType = useCallback((modelType: LlmModelType) => { + return Object.entries(llmList) + .filter(([, value]) => + modelType + ? value.some((x) => x.model_type.includes(modelType)) + : true, + ) + .map(([key, value]) => { + return { + label: key, + options: value + .filter( + (x) => + (modelType ? x.model_type.includes(modelType) : true) && + x.available, + ) + .map((x) => ({ + label: x.llm_name, + value: `${x.llm_name}@${x.fid}`, + disabled: !x.available, + model: x, + })), + }; + }) + .filter((x) => x.options.length > 0); + }, [llmList]); + + const allModelOptions = useMemo(() => { + const llmOptions = getOptionsByModelType('chat'); + const image2textOptions = getOptionsByModelType('image2text'); + const embeddingOptions = getOptionsByModelType('embedding'); + const speech2textOptions = getOptionsByModelType('speech2text'); + const rerankOptions = getOptionsByModelType('rerank'); + const ttsOptions = getOptionsByModelType('tts'); + + return { + llmOptions, + image2textOptions, + embeddingOptions, + speech2textOptions, + rerankOptions, + ttsOptions, + } + }, [llmList, getOptionsByModelType]); + + const submitSystemModelSetting = useCallback(async (data: Partial) => { dialogState.setLoading(true); try { // 这里需要根据实际的 API 接口调整 @@ -245,6 +295,7 @@ export const useSystemModelSetting = () => { return { ...dialogState, submitSystemModelSetting, + allModelOptions, }; }; diff --git a/src/pages/setting/models.tsx b/src/pages/setting/models.tsx index 5c68daa..5faab22 100644 --- a/src/pages/setting/models.tsx +++ b/src/pages/setting/models.tsx @@ -29,6 +29,7 @@ import AppSvgIcon, { LlmSvgIcon } from '@/components/AppSvgIcon'; import { LLM_FACTORY_LIST, IconMap, type LLMFactory } from '@/constants/llm'; import type { IFactory, IMyLlmModel, ILlmItem } from '@/interfaces/database/llm'; import LLMFactoryCard, { MODEL_TYPE_COLORS } from './components/LLMFactoryCard'; +import { ModelDialogs } from './components/ModelDialogs'; // 主页面组件 @@ -90,13 +91,25 @@ function ModelsPage() { return ( - - 模型设置 - - - 管理您的 LLM 模型工厂和个人模型配置 - - + + + + 模型设置 + + + 管理您的 LLM 模型工厂和个人模型配置 + + + {/* 设置默认模型 */} + + {/* My LLM 部分 */} {!myLlm || Object.keys(myLlm).length === 0 ? ( @@ -197,13 +210,12 @@ function ModelsPage() { LLM 模型工厂 - { llmFactory.map((factory) => ( - + {/* 模型配置对话框 */} - {/* */} + ); };