diff --git a/web/src/components/layout-recognize-form-field.tsx b/web/src/components/layout-recognize-form-field.tsx index 2b2dc7eda..efaaba52f 100644 --- a/web/src/components/layout-recognize-form-field.tsx +++ b/web/src/components/layout-recognize-form-field.tsx @@ -17,7 +17,6 @@ import { export const enum ParseDocumentType { DeepDOC = 'DeepDOC', PlainText = 'Plain Text', - MinerU = 'MinerU', Docling = 'Docling', TCADPParser = 'TCADP Parser', } @@ -44,7 +43,6 @@ export function LayoutRecognizeFormField({ : [ ParseDocumentType.DeepDOC, ParseDocumentType.PlainText, - ParseDocumentType.MinerU, ParseDocumentType.Docling, ParseDocumentType.TCADPParser, ].map((x) => ({ @@ -52,7 +50,10 @@ export function LayoutRecognizeFormField({ value: x, })); - const image2TextList = allOptions[LlmModelType.Image2text].map((x) => { + const image2TextList = [ + ...allOptions[LlmModelType.Image2text], + ...allOptions[LlmModelType.Ocr], + ].map((x) => { return { ...x, options: x.options.map((y) => { diff --git a/web/src/constants/knowledge.ts b/web/src/constants/knowledge.ts index 130b7ed91..afd2e218b 100644 --- a/web/src/constants/knowledge.ts +++ b/web/src/constants/knowledge.ts @@ -62,6 +62,7 @@ export enum LlmModelType { Speech2text = 'speech2text', Rerank = 'rerank', TTS = 'tts', + Ocr = 'ocr', } export enum KnowledgeSearchParams { diff --git a/web/src/constants/llm.ts b/web/src/constants/llm.ts index a5f5e4b82..e95481082 100644 --- a/web/src/constants/llm.ts +++ b/web/src/constants/llm.ts @@ -60,6 +60,7 @@ export enum LLMFactory { DeerAPI = 'DeerAPI', JiekouAI = 'Jiekou.AI', Builtin = 'Builtin', + MinerU = 'MinerU', } // Please lowercase the file name @@ -125,6 +126,7 @@ export const IconMap = { [LLMFactory.DeerAPI]: 'deerapi', [LLMFactory.JiekouAI]: 'jiekouai', [LLMFactory.Builtin]: 'builtin', + [LLMFactory.MinerU]: 'openai', }; export const APIMapUrl = { diff --git a/web/src/hooks/use-llm-request.tsx b/web/src/hooks/use-llm-request.tsx index 3436b7506..cdd46c222 100644 --- a/web/src/hooks/use-llm-request.tsx +++ b/web/src/hooks/use-llm-request.tsx @@ -147,6 +147,7 @@ export const useSelectLlmOptionsByModelType = () => { ), [LlmModelType.Rerank]: groupOptionsByModelType(LlmModelType.Rerank), [LlmModelType.TTS]: groupOptionsByModelType(LlmModelType.TTS), + [LlmModelType.Ocr]: groupOptionsByModelType(LlmModelType.Ocr), }; }; @@ -245,7 +246,7 @@ export const useSelectLlmList = () => { name: key, logo: factoryList.find((x) => x.name === key)?.logo ?? '', ...value, - llm: value.llm.map((x) => ({ ...x, name: x.name })), + llm: value.llm?.map((x) => ({ ...x, name: x.name })), })); }, [myLlmList, factoryList]); diff --git a/web/src/interfaces/request/llm.ts b/web/src/interfaces/request/llm.ts index 05f8f470e..a5ca42fdc 100644 --- a/web/src/interfaces/request/llm.ts +++ b/web/src/interfaces/request/llm.ts @@ -3,7 +3,7 @@ export interface IAddLlmRequestBody { llm_name: string; model_type: string; api_base?: string; // chat|embedding|speech2text|image2text - api_key: string; + api_key: string | Record; max_tokens: number; } diff --git a/web/src/pages/user-setting/setting-model/hooks.tsx b/web/src/pages/user-setting/setting-model/hooks.tsx index 4d0708a42..edc31fa2a 100644 --- a/web/src/pages/user-setting/setting-model/hooks.tsx +++ b/web/src/pages/user-setting/setting-model/hooks.tsx @@ -1,3 +1,4 @@ +import { LLMFactory } from '@/constants/llm'; import { useSetModalState, useShowDeleteConfirm } from '@/hooks/common-hooks'; import { IApiKeySavingParams, @@ -459,3 +460,64 @@ export const useHandleDeleteFactory = (llmFactory: string) => { return { handleDeleteFactory, deleteFactory }; }; + +type MinerUFormValues = { + llm_name: string; + mineru_apiserver?: string; + mineru_output_dir?: string; + mineru_backend?: string; + mineru_server_url?: string; + mineru_delete_output?: boolean; +}; + +export const useSubmitMinerU = () => { + const { addLlm, loading } = useAddLlm(); + const { + visible: mineruVisible, + hideModal: hideMineruModal, + showModal: showMineruModal, + } = useSetModalState(); + const [initialValues, setInitialValues] = useState< + Partial | undefined + >(); + + const onMineruOk = useCallback( + async (payload: MinerUFormValues) => { + const cfg = { + MINERU_APISERVER: payload.mineru_apiserver || '', + MINERU_OUTPUT_DIR: payload.mineru_output_dir || '', + MINERU_BACKEND: payload.mineru_backend || 'pipeline', + MINERU_SERVER_URL: payload.mineru_server_url || '', + MINERU_DELETE_OUTPUT: payload.mineru_delete_output ?? true ? '1' : '0', + }; + const req: IAddLlmRequestBody = { + llm_factory: LLMFactory.MinerU, + llm_name: payload.llm_name, + model_type: 'ocr', + api_key: cfg, + api_base: '', + max_tokens: 0, + }; + const ret = await addLlm(req); + if (ret === 0) { + hideMineruModal(); + setInitialValues(undefined); + } + }, + [addLlm, hideMineruModal], + ); + + const handleShowMineruModal = (values?: Partial) => { + setInitialValues(values); + showMineruModal(); + }; + + return { + mineruVisible, + hideMineruModal, + showMineruModal: handleShowMineruModal, + onMineruOk, + mineruLoading: loading, + mineruInitialValues: initialValues, + }; +}; diff --git a/web/src/pages/user-setting/setting-model/index.tsx b/web/src/pages/user-setting/setting-model/index.tsx index 1e7086019..1ba713bbf 100644 --- a/web/src/pages/user-setting/setting-model/index.tsx +++ b/web/src/pages/user-setting/setting-model/index.tsx @@ -13,6 +13,7 @@ import { useSubmitFishAudio, useSubmitGoogle, useSubmitHunyuan, + useSubmitMinerU, useSubmitOllama, useSubmitSpark, useSubmitSystemModelSetting, @@ -26,6 +27,7 @@ import BedrockModal from './modal/bedrock-modal'; import FishAudioModal from './modal/fish-audio-modal'; import GoogleModal from './modal/google-modal'; import HunyuanModal from './modal/hunyuan-modal'; +import MinerUModal from './modal/mineru-modal'; import TencentCloudModal from './modal/next-tencent-modal'; import OllamaModal from './modal/ollama-modal'; import SparkModal from './modal/spark-modal'; @@ -128,6 +130,15 @@ const ModelProviders = () => { AzureAddingLoading, } = useSubmitAzure(); + const { + mineruVisible, + hideMineruModal, + showMineruModal, + onMineruOk, + mineruLoading, + mineruInitialValues, + } = useSubmitMinerU(); + const ModalMap = useMemo( () => ({ [LLMFactory.Bedrock]: showBedrockAddingModal, @@ -139,6 +150,7 @@ const ModelProviders = () => { [LLMFactory.TencentCloud]: showTencentCloudAddingModal, [LLMFactory.GoogleCloud]: showGoogleAddingModal, [LLMFactory.AzureOpenAI]: showAzureAddingModal, + [LLMFactory.MinerU]: showMineruModal, }), [ showBedrockAddingModal, @@ -289,6 +301,13 @@ const ModelProviders = () => { loading={AzureAddingLoading} llmFactory={LLMFactory.AzureOpenAI} > + ); }; diff --git a/web/src/pages/user-setting/setting-model/modal/mineru-modal/index.tsx b/web/src/pages/user-setting/setting-model/modal/mineru-modal/index.tsx new file mode 100644 index 000000000..aad5df724 --- /dev/null +++ b/web/src/pages/user-setting/setting-model/modal/mineru-modal/index.tsx @@ -0,0 +1,147 @@ +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { ButtonLoading } from '@/components/ui/button'; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { Form } from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { RAGFlowSelect, RAGFlowSelectOptionType } from '@/components/ui/select'; +import { Switch } from '@/components/ui/switch'; +import { useTranslate } from '@/hooks/common-hooks'; +import { IModalProps } from '@/interfaces/common'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useEffect } from 'react'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { LLMHeader } from '../../components/llm-header'; + +const FormSchema = z.object({ + llm_name: z.string().min(1, { + message: 'Model name is required', + }), + mineru_apiserver: z.string().optional(), + mineru_output_dir: z.string().optional(), + mineru_backend: z.enum([ + 'pipeline', + 'vlm-transformers', + 'vlm-vllm-engine', + 'vlm-http-client', + ]), + mineru_server_url: z.string().optional(), + mineru_delete_output: z.boolean(), +}); + +type MinerUFormValues = z.infer; + +const backendOptions: RAGFlowSelectOptionType[] = [ + { value: 'pipeline', label: 'pipeline' }, + { value: 'vlm-transformers', label: 'vlm-transformers' }, + { value: 'vlm-vllm-engine', label: 'vlm-vllm-engine' }, + { value: 'vlm-http-client', label: 'vlm-http-client' }, +]; + +const MinerUModal = ({ + visible, + hideModal, + onOk, + loading, + initialValues, +}: IModalProps & { + initialValues?: Partial; +}) => { + const { t } = useTranslate('setting'); + + const form = useForm({ + resolver: zodResolver(FormSchema), + defaultValues: { + mineru_backend: 'pipeline', + mineru_delete_output: true, + }, + }); + + const handleOk = async (values: MinerUFormValues) => { + const ret = await onOk?.(values as any); + if (ret) { + hideModal?.(); + } + }; + + useEffect(() => { + if (visible) { + form.reset(); + if (initialValues) { + form.reset({ + mineru_backend: 'pipeline', + mineru_delete_output: true, + ...initialValues, + }); + } + } + }, [visible, initialValues, form]); + + return ( + + + + + + + +
+ + + + + + + + + + + + {(field) => ( + + )} + + + + + + {(field) => ( + + )} + +
+ + + + {t('common.save', 'Save')} + + +
+
+ ); +}; + +export default MinerUModal;