Feat: Support MinerU

This commit is contained in:
balibabu 2025-12-11 11:27:09 +08:00
parent ab4b62031f
commit 2bb0b0faee
8 changed files with 238 additions and 5 deletions

View file

@ -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) => {

View file

@ -62,6 +62,7 @@ export enum LlmModelType {
Speech2text = 'speech2text',
Rerank = 'rerank',
TTS = 'tts',
Ocr = 'ocr',
}
export enum KnowledgeSearchParams {

View file

@ -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 = {

View file

@ -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]);

View file

@ -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<string, any>;
max_tokens: number;
}

View file

@ -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<MinerUFormValues> | 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<MinerUFormValues>) => {
setInitialValues(values);
showMineruModal();
};
return {
mineruVisible,
hideMineruModal,
showMineruModal: handleShowMineruModal,
onMineruOk,
mineruLoading: loading,
mineruInitialValues: initialValues,
};
};

View file

@ -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}
></AzureOpenAIModal>
<MinerUModal
visible={mineruVisible}
hideModal={hideMineruModal}
onOk={onMineruOk}
loading={mineruLoading}
initialValues={mineruInitialValues}
></MinerUModal>
</div>
);
};

View file

@ -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<typeof FormSchema>;
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<MinerUFormValues> & {
initialValues?: Partial<MinerUFormValues>;
}) => {
const { t } = useTranslate('setting');
const form = useForm<MinerUFormValues>({
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 (
<Dialog open={visible} onOpenChange={hideModal}>
<DialogContent>
<DialogHeader>
<DialogTitle>
<LLMHeader name="MinerU" />
</DialogTitle>
</DialogHeader>
<Form {...form}>
<form
onSubmit={form.handleSubmit(handleOk)}
className="space-y-6"
id="mineru-form"
>
<RAGFlowFormItem name="llm_name" label={t('modelName')} required>
<Input placeholder="mineru-from-env-1" />
</RAGFlowFormItem>
<RAGFlowFormItem name="mineru_apiserver" label="MINERU_APISERVER">
<Input placeholder="http://host.docker.internal:9987" />
</RAGFlowFormItem>
<RAGFlowFormItem name="mineru_output_dir" label="MINERU_OUTPUT_DIR">
<Input placeholder="/tmp/mineru" />
</RAGFlowFormItem>
<RAGFlowFormItem name="mineru_backend" label="MINERU_BACKEND">
{(field) => (
<RAGFlowSelect
value={field.value}
onChange={field.onChange}
options={backendOptions}
placeholder="Select backend"
/>
)}
</RAGFlowFormItem>
<RAGFlowFormItem name="mineru_server_url" label="MINERU_SERVER_URL">
<Input placeholder="http://your-vllm-server:30000" />
</RAGFlowFormItem>
<RAGFlowFormItem
name="mineru_delete_output"
label="MINERU_DELETE_OUTPUT"
className="flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm"
labelClassName="!mb-0"
>
{(field) => (
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
)}
</RAGFlowFormItem>
</form>
</Form>
<DialogFooter>
<ButtonLoading type="submit" form="mineru-form" loading={loading}>
{t('common.save', 'Save')}
</ButtonLoading>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
export default MinerUModal;