### What problem does this PR solve? Replace Avatar with RAGFlowAvatar component for knowledge base and agent, optimize Agent template page, and modify bugs in knowledge base #3221 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)tags/v0.20.0
| type EntityTypesFormFieldProps = { | type EntityTypesFormFieldProps = { | ||||
| name?: string; | name?: string; | ||||
| }; | }; | ||||
| const initialEntityTypes = [ | |||||
| 'organization', | |||||
| 'person', | |||||
| 'geo', | |||||
| 'event', | |||||
| 'category', | |||||
| ]; | |||||
| export function EntityTypesFormField({ | export function EntityTypesFormField({ | ||||
| name = 'parser_config.entity_types', | name = 'parser_config.entity_types', | ||||
| }: EntityTypesFormFieldProps) { | }: EntityTypesFormFieldProps) { | ||||
| <FormField | <FormField | ||||
| control={form.control} | control={form.control} | ||||
| name={name} | name={name} | ||||
| defaultValue={initialEntityTypes} | |||||
| render={({ field }) => { | render={({ field }) => { | ||||
| return ( | return ( | ||||
| <FormItem className=" items-center space-y-0 "> | <FormItem className=" items-center space-y-0 "> |
| import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks'; | import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks'; | ||||
| import { UserOutlined } from '@ant-design/icons'; | import { UserOutlined } from '@ant-design/icons'; | ||||
| import { Avatar as AntAvatar, Form, Select, Space } from 'antd'; | import { Avatar as AntAvatar, Form, Select, Space } from 'antd'; | ||||
| import { Book } from 'lucide-react'; | |||||
| import { useFormContext } from 'react-hook-form'; | import { useFormContext } from 'react-hook-form'; | ||||
| import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar'; | |||||
| import { RAGFlowAvatar } from './ragflow-avatar'; | |||||
| import { FormControl, FormField, FormItem, FormLabel } from './ui/form'; | import { FormControl, FormField, FormItem, FormLabel } from './ui/form'; | ||||
| import { MultiSelect } from './ui/multi-select'; | import { MultiSelect } from './ui/multi-select'; | ||||
| label: x.name, | label: x.name, | ||||
| value: x.id, | value: x.id, | ||||
| icon: () => ( | icon: () => ( | ||||
| <Avatar className="size-4 mr-2"> | |||||
| <AvatarImage src={x.avatar} /> | |||||
| <AvatarFallback> | |||||
| <Book /> | |||||
| </AvatarFallback> | |||||
| </Avatar> | |||||
| <RAGFlowAvatar className="size-4 mr-2" avatar={x.avatar} name={x.name} /> | |||||
| ), | ), | ||||
| })); | })); | ||||
| import { useTranslate } from '@/hooks/common-hooks'; | import { useTranslate } from '@/hooks/common-hooks'; | ||||
| import random from 'lodash/random'; | import random from 'lodash/random'; | ||||
| import { Plus } from 'lucide-react'; | import { Plus } from 'lucide-react'; | ||||
| import { useCallback } from 'react'; | |||||
| import { useCallback, useEffect } from 'react'; | |||||
| import { useFormContext, useWatch } from 'react-hook-form'; | import { useFormContext, useWatch } from 'react-hook-form'; | ||||
| import { SliderInputFormField } from '../slider-input-form-field'; | import { SliderInputFormField } from '../slider-input-form-field'; | ||||
| import { Button } from '../ui/button'; | import { Button } from '../ui/button'; | ||||
| const UseRaptorField = 'parser_config.raptor.use_raptor'; | const UseRaptorField = 'parser_config.raptor.use_raptor'; | ||||
| const RandomSeedField = 'parser_config.raptor.random_seed'; | const RandomSeedField = 'parser_config.raptor.random_seed'; | ||||
| const MaxTokenField = 'parser_config.raptor.max_token'; | |||||
| const ThresholdField = 'parser_config.raptor.threshold'; | |||||
| const MaxCluster = 'parser_config.raptor.max_cluster'; | |||||
| const Prompt = 'parser_config.raptor.prompt'; | |||||
| // The three types "table", "resume" and "one" do not display this configuration. | // The three types "table", "resume" and "one" do not display this configuration. | ||||
| const form = useFormContext(); | const form = useFormContext(); | ||||
| const { t } = useTranslate('knowledgeConfiguration'); | const { t } = useTranslate('knowledgeConfiguration'); | ||||
| const useRaptor = useWatch({ name: UseRaptorField }); | const useRaptor = useWatch({ name: UseRaptorField }); | ||||
| useEffect(() => { | |||||
| if (useRaptor) { | |||||
| form.setValue(MaxTokenField, 256); | |||||
| form.setValue(ThresholdField, 0.1); | |||||
| form.setValue(MaxCluster, 64); | |||||
| form.setValue(RandomSeedField, 0); | |||||
| form.setValue(Prompt, t('promptText')); | |||||
| } | |||||
| }, [form, useRaptor, t]); | |||||
| const handleGenerate = useCallback(() => { | const handleGenerate = useCallback(() => { | ||||
| form.setValue(RandomSeedField, random(10000)); | form.setValue(RandomSeedField, random(10000)); | ||||
| </FormLabel> | </FormLabel> | ||||
| <div className="w-3/4"> | <div className="w-3/4"> | ||||
| <FormControl> | <FormControl> | ||||
| <Textarea | |||||
| {...field} | |||||
| rows={8} | |||||
| defaultValue={t('promptText')} | |||||
| /> | |||||
| <Textarea {...field} rows={8} /> | |||||
| </FormControl> | </FormControl> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| name={'parser_config.raptor.max_token'} | name={'parser_config.raptor.max_token'} | ||||
| label={t('maxToken')} | label={t('maxToken')} | ||||
| tooltip={t('maxTokenTip')} | tooltip={t('maxTokenTip')} | ||||
| defaultValue={256} | |||||
| max={2048} | max={2048} | ||||
| min={0} | min={0} | ||||
| layout={FormLayout.Horizontal} | layout={FormLayout.Horizontal} | ||||
| name={'parser_config.raptor.threshold'} | name={'parser_config.raptor.threshold'} | ||||
| label={t('threshold')} | label={t('threshold')} | ||||
| tooltip={t('thresholdTip')} | tooltip={t('thresholdTip')} | ||||
| defaultValue={0.1} | |||||
| step={0.01} | step={0.01} | ||||
| max={1} | max={1} | ||||
| min={0} | min={0} | ||||
| name={'parser_config.raptor.max_cluster'} | name={'parser_config.raptor.max_cluster'} | ||||
| label={t('maxCluster')} | label={t('maxCluster')} | ||||
| tooltip={t('maxClusterTip')} | tooltip={t('maxClusterTip')} | ||||
| defaultValue={64} | |||||
| max={1024} | max={1024} | ||||
| min={1} | min={1} | ||||
| layout={FormLayout.Horizontal} | layout={FormLayout.Horizontal} |
| return modelTypes.reduce< | return modelTypes.reduce< | ||||
| (DefaultOptionType & { | (DefaultOptionType & { | ||||
| options: { label: JSX.Element; value: string; disabled: boolean; is_tools: boolean }[]; | |||||
| options: { | |||||
| label: JSX.Element; | |||||
| value: string; | |||||
| disabled: boolean; | |||||
| is_tools: boolean; | |||||
| }[]; | |||||
| })[] | })[] | ||||
| >((pre, cur) => { | >((pre, cur) => { | ||||
| const options = allOptions[cur]; | const options = allOptions[cur]; | ||||
| return { data, loading }; | return { data, loading }; | ||||
| }; | }; | ||||
| export const useSelectLlmList = () => { | export const useSelectLlmList = () => { | ||||
| const { data: myLlmList, loading: myLlmListLoading } = useFetchMyLlmList(); | const { data: myLlmList, loading: myLlmListLoading } = useFetchMyLlmList(); | ||||
| const { data: factoryList, loading: factoryListLoading } = | const { data: factoryList, loading: factoryListLoading } = | ||||
| if (data.code === 0) { | if (data.code === 0) { | ||||
| message.success(t('message.modified')); | message.success(t('message.modified')); | ||||
| queryClient.invalidateQueries({ queryKey: ['myLlmList'] }); | queryClient.invalidateQueries({ queryKey: ['myLlmList'] }); | ||||
| queryClient.invalidateQueries({ queryKey: ['myLlmListDetailed'] }); | |||||
| queryClient.invalidateQueries({ queryKey: ['myLlmListDetailed'] }); | |||||
| queryClient.invalidateQueries({ queryKey: ['factoryList'] }); | queryClient.invalidateQueries({ queryKey: ['factoryList'] }); | ||||
| } | } | ||||
| return data.code; | return data.code; | ||||
| const { data } = await userService.add_llm(params); | const { data } = await userService.add_llm(params); | ||||
| if (data.code === 0) { | if (data.code === 0) { | ||||
| queryClient.invalidateQueries({ queryKey: ['myLlmList'] }); | queryClient.invalidateQueries({ queryKey: ['myLlmList'] }); | ||||
| queryClient.invalidateQueries({ queryKey: ['myLlmListDetailed'] }); | |||||
| queryClient.invalidateQueries({ queryKey: ['myLlmListDetailed'] }); | |||||
| queryClient.invalidateQueries({ queryKey: ['factoryList'] }); | queryClient.invalidateQueries({ queryKey: ['factoryList'] }); | ||||
| message.success(t('message.modified')); | message.success(t('message.modified')); | ||||
| } | } | ||||
| const { data } = await userService.delete_llm(params); | const { data } = await userService.delete_llm(params); | ||||
| if (data.code === 0) { | if (data.code === 0) { | ||||
| queryClient.invalidateQueries({ queryKey: ['myLlmList'] }); | queryClient.invalidateQueries({ queryKey: ['myLlmList'] }); | ||||
| queryClient.invalidateQueries({ queryKey: ['myLlmListDetailed'] }); | |||||
| queryClient.invalidateQueries({ queryKey: ['myLlmListDetailed'] }); | |||||
| queryClient.invalidateQueries({ queryKey: ['factoryList'] }); | queryClient.invalidateQueries({ queryKey: ['factoryList'] }); | ||||
| message.success(t('message.deleted')); | message.success(t('message.deleted')); | ||||
| } | } | ||||
| const { data } = await userService.deleteFactory(params); | const { data } = await userService.deleteFactory(params); | ||||
| if (data.code === 0) { | if (data.code === 0) { | ||||
| queryClient.invalidateQueries({ queryKey: ['myLlmList'] }); | queryClient.invalidateQueries({ queryKey: ['myLlmList'] }); | ||||
| queryClient.invalidateQueries({ queryKey: ['myLlmListDetailed'] }); | |||||
| queryClient.invalidateQueries({ queryKey: ['myLlmListDetailed'] }); | |||||
| queryClient.invalidateQueries({ queryKey: ['factoryList'] }); | queryClient.invalidateQueries({ queryKey: ['factoryList'] }); | ||||
| message.success(t('message.deleted')); | message.success(t('message.deleted')); | ||||
| } | } |
| agentDescription: | agentDescription: | ||||
| 'Builds agent components equipped with reasoning, tool usage, and multi-agent collaboration. ', | 'Builds agent components equipped with reasoning, tool usage, and multi-agent collaboration. ', | ||||
| maxRecords: 'Max records', | maxRecords: 'Max records', | ||||
| createAgent: 'Create Agent', | |||||
| stringTransform: 'String transform', | stringTransform: 'String transform', | ||||
| userFillUp: 'Input', | userFillUp: 'Input', | ||||
| codeExec: 'Code', | codeExec: 'Code', |
| agent: 'Agent', | agent: 'Agent', | ||||
| agentDescription: '构建具备推理、工具调用和多智能体协同的智能体组件。', | agentDescription: '构建具备推理、工具调用和多智能体协同的智能体组件。', | ||||
| maxRecords: '最大记录数', | maxRecords: '最大记录数', | ||||
| createAgent: 'Create Agent', | |||||
| stringTransform: '文本处理', | stringTransform: '文本处理', | ||||
| userFillUp: '等待输入', | userFillUp: '等待输入', | ||||
| codeExec: '代码', | codeExec: '代码', |
| import { RAGFlowAvatar } from '@/components/ragflow-avatar'; | |||||
| import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks'; | import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks'; | ||||
| import { IRetrievalNode } from '@/interfaces/database/flow'; | import { IRetrievalNode } from '@/interfaces/database/flow'; | ||||
| import { UserOutlined } from '@ant-design/icons'; | |||||
| import { NodeProps, Position } from '@xyflow/react'; | import { NodeProps, Position } from '@xyflow/react'; | ||||
| import { Avatar, Flex } from 'antd'; | |||||
| import { Flex } from 'antd'; | |||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { get } from 'lodash'; | import { get } from 'lodash'; | ||||
| import { memo, useMemo } from 'react'; | import { memo, useMemo } from 'react'; | ||||
| return ( | return ( | ||||
| <div className={styles.nodeText} key={knowledge.id}> | <div className={styles.nodeText} key={knowledge.id}> | ||||
| <Flex align={'center'} gap={6}> | <Flex align={'center'} gap={6}> | ||||
| <Avatar | |||||
| size={26} | |||||
| icon={<UserOutlined />} | |||||
| src={knowledge.avatar} | |||||
| <RAGFlowAvatar | |||||
| className="size-6 rounded-lg" | |||||
| avatar={knowledge.avatar} | |||||
| name={knowledge.name || 'CN'} | |||||
| isPerson={true} | |||||
| /> | /> | ||||
| <Flex className={styles.knowledgeNodeName} flex={1}> | <Flex className={styles.knowledgeNodeName} flex={1}> | ||||
| {knowledge.name} | {knowledge.name} |
| import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | ||||
| import { useFetchAgentTemplates, useSetAgent } from '@/hooks/use-agent-request'; | import { useFetchAgentTemplates, useSetAgent } from '@/hooks/use-agent-request'; | ||||
| import { IFlowTemplate } from '@/interfaces/database/flow'; | import { IFlowTemplate } from '@/interfaces/database/flow'; | ||||
| import { useCallback, useState } from 'react'; | |||||
| import { useCallback, useEffect, useState } from 'react'; | |||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| import { CreateAgentDialog } from './create-agent-dialog'; | import { CreateAgentDialog } from './create-agent-dialog'; | ||||
| import { TemplateCard } from './template-card'; | import { TemplateCard } from './template-card'; | ||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const list = useFetchAgentTemplates(); | const list = useFetchAgentTemplates(); | ||||
| const { loading, setAgent } = useSetAgent(); | const { loading, setAgent } = useSetAgent(); | ||||
| const [templateList, setTemplateList] = useState<IFlowTemplate[]>([]); | |||||
| useEffect(() => { | |||||
| setTemplateList(list); | |||||
| }, [list]); | |||||
| const { | const { | ||||
| visible: creatingVisible, | visible: creatingVisible, | ||||
| hideModal: hideCreatingModal, | hideModal: hideCreatingModal, | ||||
| template?.dsl, | template?.dsl, | ||||
| ], | ], | ||||
| ); | ); | ||||
| const handleSiderBarChange = (keyword: string) => { | |||||
| const tempList = list.filter( | |||||
| (item, index) => | |||||
| item.title.toLocaleLowerCase().includes(keyword?.toLocaleLowerCase()) || | |||||
| index === 0, | |||||
| ); | |||||
| setTemplateList(tempList); | |||||
| }; | |||||
| return ( | return ( | ||||
| <section> | <section> | ||||
| <PageHeader> | <PageHeader> |
| import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; | |||||
| import { RAGFlowAvatar } from '@/components/ragflow-avatar'; | |||||
| import { Button } from '@/components/ui/button'; | import { Button } from '@/components/ui/button'; | ||||
| import { Card, CardContent } from '@/components/ui/card'; | import { Card, CardContent } from '@/components/ui/card'; | ||||
| import { IFlowTemplate } from '@/interfaces/database/flow'; | import { IFlowTemplate } from '@/interfaces/database/flow'; | ||||
| import { Plus } from 'lucide-react'; | |||||
| import { useCallback } from 'react'; | import { useCallback } from 'react'; | ||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| interface IProps { | interface IProps { | ||||
| data: IFlowTemplate; | data: IFlowTemplate; | ||||
| isCreate?: boolean; | |||||
| showModal(record: IFlowTemplate): void; | showModal(record: IFlowTemplate): void; | ||||
| } | } | ||||
| export function TemplateCard({ data, showModal }: IProps) { | |||||
| export function TemplateCard({ data, showModal, isCreate = false }: IProps) { | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const handleClick = useCallback(() => { | const handleClick = useCallback(() => { | ||||
| showModal(data); | showModal(data); | ||||
| }, [data, showModal]); | }, [data, showModal]); | ||||
| return ( | return ( | ||||
| <Card className="bg-colors-background-inverse-weak border-colors-outline-neutral-standard group relative"> | |||||
| <Card className="bg-colors-background-inverse-weak border-colors-outline-neutral-standard group relative min-h-40"> | |||||
| <CardContent className="p-4 "> | <CardContent className="p-4 "> | ||||
| <div className="flex justify-between mb-4"> | |||||
| {data.avatar ? ( | |||||
| <div | |||||
| className="w-[70px] h-[70px] rounded-xl bg-cover" | |||||
| style={{ backgroundImage: `url(${data.avatar})` }} | |||||
| /> | |||||
| ) : ( | |||||
| <Avatar className="w-[70px] h-[70px]"> | |||||
| <AvatarImage src="https://github.com/shadcn.png" /> | |||||
| <AvatarFallback>CN</AvatarFallback> | |||||
| </Avatar> | |||||
| )} | |||||
| </div> | |||||
| <h3 className="text-xl font-bold mb-2">{data.title}</h3> | |||||
| <p className="break-words">{data.description}</p> | |||||
| <Button | |||||
| variant="tertiary" | |||||
| className="absolute bottom-4 right-4 left-4 hidden justify-end group-hover:block text-center" | |||||
| onClick={handleClick} | |||||
| > | |||||
| {t('flow.useTemplate')} | |||||
| </Button> | |||||
| {isCreate && ( | |||||
| <div | |||||
| className="flex flex-col justify-center items-center gap-4 mb-4 absolute top-0 right-0 left-0 bottom-0 cursor-pointer " | |||||
| onClick={handleClick} | |||||
| > | |||||
| <Plus size={50} fontWeight={700} /> | |||||
| <div>{t('flow.createAgent')}</div> | |||||
| </div> | |||||
| )} | |||||
| {!isCreate && ( | |||||
| <> | |||||
| <div className="flex justify-start items-center gap-4 mb-4"> | |||||
| <RAGFlowAvatar | |||||
| className="w-7 h-7" | |||||
| avatar={ | |||||
| data.avatar ? data.avatar : 'https://github.com/shadcn.png' | |||||
| } | |||||
| name={data?.title || 'CN'} | |||||
| ></RAGFlowAvatar> | |||||
| <div className="text-[18px] font-bold ">{data.title}</div> | |||||
| </div> | |||||
| <p className="break-words">{data.description}</p> | |||||
| <div className="group-hover:bg-gradient-to-t from-black/70 from-10% via-black/0 via-50% to-black/0 w-full h-full group-hover:block absolute top-0 left-0 hidden rounded-xl"> | |||||
| <Button | |||||
| variant="default" | |||||
| className="w-1/3 absolute bottom-4 right-4 left-4 justify-center text-center m-auto" | |||||
| onClick={handleClick} | |||||
| > | |||||
| {t('flow.useTemplate')} | |||||
| </Button> | |||||
| </div> | |||||
| </> | |||||
| )} | |||||
| </CardContent> | </CardContent> | ||||
| </Card> | </Card> | ||||
| ); | ); |
| import { Button } from '@/components/ui/button'; | |||||
| import { useSecondPathName } from '@/hooks/route-hook'; | |||||
| import { cn } from '@/lib/utils'; | |||||
| import { Banknote, LayoutGrid, User } from 'lucide-react'; | |||||
| const menuItems = [ | |||||
| { | |||||
| section: 'All Templates', | |||||
| items: [ | |||||
| { icon: User, label: 'Assistant', key: 'Assistant' }, | |||||
| { icon: LayoutGrid, label: 'chatbot', key: 'chatbot' }, | |||||
| { icon: Banknote, label: 'generator', key: 'generator' }, | |||||
| { icon: Banknote, label: 'Intel', key: 'Intel' }, | |||||
| ], | |||||
| }, | |||||
| ]; | |||||
| export function SideBar({ change }: { change: (keyword: string) => void }) { | |||||
| const pathName = useSecondPathName(); | |||||
| const handleMenuClick = (key: string) => { | |||||
| change(key); | |||||
| }; | |||||
| return ( | |||||
| <aside className="w-[303px] bg-background border-r flex flex-col"> | |||||
| <div className="flex-1 overflow-auto"> | |||||
| {menuItems.map((section, idx) => ( | |||||
| <div key={idx}> | |||||
| <h2 | |||||
| className="p-6 text-sm font-semibold hover:bg-muted/50 cursor-pointer" | |||||
| onClick={() => handleMenuClick('')} | |||||
| > | |||||
| {section.section} | |||||
| </h2> | |||||
| {section.items.map((item, itemIdx) => { | |||||
| const active = pathName === item.key; | |||||
| return ( | |||||
| <Button | |||||
| key={itemIdx} | |||||
| variant={active ? 'secondary' : 'ghost'} | |||||
| className={cn('w-full justify-start gap-2.5 p-6 relative')} | |||||
| onClick={() => handleMenuClick(item.key)} | |||||
| > | |||||
| <item.icon className="w-6 h-6" /> | |||||
| <span>{item.label}</span> | |||||
| {active && ( | |||||
| <div className="absolute right-0 w-[5px] h-[66px] bg-primary rounded-l-xl shadow-[0_0_5.94px_#7561ff,0_0_11.88px_#7561ff,0_0_41.58px_#7561ff,0_0_83.16px_#7561ff,0_0_142.56px_#7561ff,0_0_249.48px_#7561ff]" /> | |||||
| )} | |||||
| </Button> | |||||
| ); | |||||
| })} | |||||
| </div> | |||||
| ))} | |||||
| </div> | |||||
| </aside> | |||||
| ); | |||||
| } |
| DropdownMenuItem, | DropdownMenuItem, | ||||
| DropdownMenuTrigger, | DropdownMenuTrigger, | ||||
| } from '@/components/ui/dropdown-menu'; | } from '@/components/ui/dropdown-menu'; | ||||
| import { | |||||
| HoverCard, | |||||
| HoverCardContent, | |||||
| HoverCardTrigger, | |||||
| } from '@/components/ui/hover-card'; | |||||
| import { Progress } from '@/components/ui/progress'; | import { Progress } from '@/components/ui/progress'; | ||||
| import { Separator } from '@/components/ui/separator'; | import { Separator } from '@/components/ui/separator'; | ||||
| import { IDocumentInfo } from '@/interfaces/database/document'; | import { IDocumentInfo } from '@/interfaces/database/document'; | ||||
| import { useCallback } from 'react'; | import { useCallback } from 'react'; | ||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| import { RunningStatus } from './constant'; | import { RunningStatus } from './constant'; | ||||
| import { ParsingCard } from './parsing-card'; | |||||
| import { ParsingCard, PopoverContent } from './parsing-card'; | |||||
| import { UseChangeDocumentParserShowType } from './use-change-document-parser'; | import { UseChangeDocumentParserShowType } from './use-change-document-parser'; | ||||
| import { useHandleRunDocumentByIds } from './use-run-document'; | import { useHandleRunDocumentByIds } from './use-run-document'; | ||||
| import { UseSaveMetaShowType } from './use-save-meta'; | import { UseSaveMetaShowType } from './use-save-meta'; | ||||
| import { isParserRunning } from './utils'; | import { isParserRunning } from './utils'; | ||||
| const IconMap = { | const IconMap = { | ||||
| [RunningStatus.UNSTART]: <Play />, | [RunningStatus.UNSTART]: <Play />, | ||||
| [RunningStatus.RUNNING]: <CircleX />, | [RunningStatus.RUNNING]: <CircleX />, | ||||
| </Button> | </Button> | ||||
| </ConfirmDeleteDialog> | </ConfirmDeleteDialog> | ||||
| {isParserRunning(run) ? ( | {isParserRunning(run) ? ( | ||||
| <div className="flex items-center gap-1"> | |||||
| <Progress value={p} className="h-1 flex-1 min-w-10" /> | |||||
| {p}% | |||||
| </div> | |||||
| <HoverCard> | |||||
| <HoverCardTrigger asChild> | |||||
| <div className="flex items-center gap-1"> | |||||
| <Progress value={p} className="h-1 flex-1 min-w-10" /> | |||||
| {p}% | |||||
| </div> | |||||
| </HoverCardTrigger> | |||||
| <HoverCardContent className="w-[40vw]"> | |||||
| <PopoverContent record={record}></PopoverContent> | |||||
| </HoverCardContent> | |||||
| </HoverCard> | |||||
| ) : ( | ) : ( | ||||
| <ParsingCard record={record}></ParsingCard> | <ParsingCard record={record}></ParsingCard> | ||||
| )} | )} |
| knowledgeDetails.avatar, | knowledgeDetails.avatar, | ||||
| ); | ); | ||||
| console.log('🚀 ~ useEffect ~ fileList:', fileList); | |||||
| form.reset({ | |||||
| ...pick(knowledgeDetails, [ | |||||
| console.log('🚀 ~ useEffect ~ fileList:', fileList, knowledgeDetails); | |||||
| const parser_config = { | |||||
| ...form.formState?.defaultValues?.parser_config, | |||||
| ...knowledgeDetails.parser_config, | |||||
| }; | |||||
| const formValues = { | |||||
| ...pick({ ...knowledgeDetails, parser_config: parser_config }, [ | |||||
| 'description', | 'description', | ||||
| 'name', | 'name', | ||||
| 'permission', | 'permission', | ||||
| 'parser_config', | 'parser_config', | ||||
| 'pagerank', | 'pagerank', | ||||
| ]), | ]), | ||||
| }; | |||||
| form.reset({ | |||||
| ...formValues, | |||||
| avatar: fileList, | avatar: fileList, | ||||
| }); | }); | ||||
| }, [form, knowledgeDetails]); | }, [form, knowledgeDetails]); |
| topn_tags: 3, | topn_tags: 3, | ||||
| raptor: { | raptor: { | ||||
| use_raptor: false, | use_raptor: false, | ||||
| max_token: 256, | |||||
| threshold: 0.1, | |||||
| max_cluster: 64, | |||||
| random_seed: 0, | |||||
| }, | }, | ||||
| graphrag: { | graphrag: { | ||||
| use_graphrag: false, | use_graphrag: false, |
| import { MoreButton } from '@/components/more-button'; | import { MoreButton } from '@/components/more-button'; | ||||
| import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; | |||||
| import { RAGFlowAvatar } from '@/components/ragflow-avatar'; | |||||
| import { Badge } from '@/components/ui/badge'; | import { Badge } from '@/components/ui/badge'; | ||||
| import { Card, CardContent } from '@/components/ui/card'; | import { Card, CardContent } from '@/components/ui/card'; | ||||
| import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | ||||
| <CardContent className="p-2.5 pt-2 group"> | <CardContent className="p-2.5 pt-2 group"> | ||||
| <section className="flex justify-between mb-2"> | <section className="flex justify-between mb-2"> | ||||
| <div className="flex gap-2 items-center"> | <div className="flex gap-2 items-center"> | ||||
| <Avatar className="size-6 rounded-lg"> | |||||
| <AvatarImage src={dataset.avatar} /> | |||||
| <AvatarFallback className="rounded-lg ">CN</AvatarFallback> | |||||
| </Avatar> | |||||
| <RAGFlowAvatar | |||||
| className="size-6 rounded-lg" | |||||
| avatar={dataset.avatar} | |||||
| name={dataset.name || 'CN'} | |||||
| ></RAGFlowAvatar> | |||||
| {owner && ( | {owner && ( | ||||
| <Badge className="h-5 rounded-sm px-1 bg-background-badge text-text-badge"> | <Badge className="h-5 rounded-sm px-1 bg-background-badge text-text-badge"> | ||||
| {owner} | {owner} |
| export const useSubmitOllama = () => { | export const useSubmitOllama = () => { | ||||
| const [selectedLlmFactory, setSelectedLlmFactory] = useState<string>(''); | const [selectedLlmFactory, setSelectedLlmFactory] = useState<string>(''); | ||||
| const [editMode, setEditMode] = useState(false); | const [editMode, setEditMode] = useState(false); | ||||
| const [initialValues, setInitialValues] = useState<Partial<IAddLlmRequestBody> | undefined>(); | |||||
| const [initialValues, setInitialValues] = useState< | |||||
| Partial<IAddLlmRequestBody> | undefined | |||||
| >(); | |||||
| const [originalModelName, setOriginalModelName] = useState<string>(''); | const [originalModelName, setOriginalModelName] = useState<string>(''); | ||||
| const { addLlm, loading } = useAddLlm(); | const { addLlm, loading } = useAddLlm(); | ||||
| const { | const { | ||||
| if (!cleanedPayload.api_key || cleanedPayload.api_key.trim() === '') { | if (!cleanedPayload.api_key || cleanedPayload.api_key.trim() === '') { | ||||
| delete cleanedPayload.api_key; | delete cleanedPayload.api_key; | ||||
| } | } | ||||
| const ret = await addLlm(cleanedPayload); | const ret = await addLlm(cleanedPayload); | ||||
| if (ret === 0) { | if (ret === 0) { | ||||
| hideLlmAddingModal(); | hideLlmAddingModal(); | ||||
| [hideLlmAddingModal, addLlm], | [hideLlmAddingModal, addLlm], | ||||
| ); | ); | ||||
| const handleShowLlmAddingModal = (llmFactory: string, isEdit = false, modelData?: any, detailedData?: any) => { | |||||
| const handleShowLlmAddingModal = ( | |||||
| llmFactory: string, | |||||
| isEdit = false, | |||||
| modelData?: any, | |||||
| detailedData?: any, | |||||
| ) => { | |||||
| setSelectedLlmFactory(llmFactory); | setSelectedLlmFactory(llmFactory); | ||||
| setEditMode(isEdit); | setEditMode(isEdit); | ||||
| if (isEdit && detailedData) { | if (isEdit && detailedData) { | ||||
| const initialVals = { | const initialVals = { | ||||
| llm_name: getRealModelName(detailedData.name), | llm_name: getRealModelName(detailedData.name), |
| import { useTheme } from '@/components/theme-provider'; | import { useTheme } from '@/components/theme-provider'; | ||||
| import { LLMFactory } from '@/constants/llm'; | import { LLMFactory } from '@/constants/llm'; | ||||
| import { useSetModalState, useTranslate } from '@/hooks/common-hooks'; | import { useSetModalState, useTranslate } from '@/hooks/common-hooks'; | ||||
| import { LlmItem, useSelectLlmList, useFetchMyLlmListDetailed } from '@/hooks/llm-hooks'; | |||||
| import { | |||||
| LlmItem, | |||||
| useFetchMyLlmListDetailed, | |||||
| useSelectLlmList, | |||||
| } from '@/hooks/llm-hooks'; | |||||
| import { getRealModelName } from '@/utils/llm-util'; | import { getRealModelName } from '@/utils/llm-util'; | ||||
| import { CloseCircleOutlined, EditOutlined, SettingOutlined } from '@ant-design/icons'; | |||||
| import { | |||||
| CloseCircleOutlined, | |||||
| EditOutlined, | |||||
| SettingOutlined, | |||||
| } from '@ant-design/icons'; | |||||
| import { | import { | ||||
| Button, | Button, | ||||
| Card, | Card, | ||||
| <Tag color="#b8b8b8">{model.type}</Tag> | <Tag color="#b8b8b8">{model.type}</Tag> | ||||
| {isLocalLlmFactory(item.name) && ( | {isLocalLlmFactory(item.name) && ( | ||||
| <Tooltip title={t('edit', { keyPrefix: 'common' })}> | <Tooltip title={t('edit', { keyPrefix: 'common' })}> | ||||
| <Button type={'text'} onClick={() => handleEditModel(model, item)}> | |||||
| <Button | |||||
| type={'text'} | |||||
| onClick={() => handleEditModel(model, item)} | |||||
| > | |||||
| <EditOutlined style={{ color: '#1890ff' }} /> | <EditOutlined style={{ color: '#1890ff' }} /> | ||||
| </Button> | </Button> | ||||
| </Tooltip> | </Tooltip> | ||||
| (model: any, factory: LlmItem) => { | (model: any, factory: LlmItem) => { | ||||
| if (factory) { | if (factory) { | ||||
| const detailedFactory = detailedLlmList[factory.name]; | const detailedFactory = detailedLlmList[factory.name]; | ||||
| const detailedModel = detailedFactory?.llm?.find((m: any) => m.name === model.name); | |||||
| const detailedModel = detailedFactory?.llm?.find( | |||||
| (m: any) => m.name === model.name, | |||||
| ); | |||||
| const editData = { | const editData = { | ||||
| llm_factory: factory.name, | llm_factory: factory.name, | ||||
| llm_name: model.name, | llm_name: model.name, | ||||
| model_type: model.type | |||||
| model_type: model.type, | |||||
| }; | }; | ||||
| if (isLocalLlmFactory(factory.name)) { | if (isLocalLlmFactory(factory.name)) { | ||||
| showLlmAddingModal(factory.name, true, editData, detailedModel); | showLlmAddingModal(factory.name, true, editData, detailedModel); | ||||
| } else if (factory.name in ModalMap) { | } else if (factory.name in ModalMap) { | ||||
| grid={{ gutter: 16, column: 1 }} | grid={{ gutter: 16, column: 1 }} | ||||
| dataSource={llmList} | dataSource={llmList} | ||||
| renderItem={(item) => ( | renderItem={(item) => ( | ||||
| <ModelCard item={item} clickApiKey={handleAddModel} handleEditModel={handleEditModel}></ModelCard> | |||||
| <ModelCard | |||||
| item={item} | |||||
| clickApiKey={handleAddModel} | |||||
| handleEditModel={handleEditModel} | |||||
| ></ModelCard> | |||||
| )} | )} | ||||
| /> | /> | ||||
| ), | ), |
| llmFactory, | llmFactory, | ||||
| editMode = false, | editMode = false, | ||||
| initialValues, | initialValues, | ||||
| }: IModalProps<IAddLlmRequestBody> & { | |||||
| llmFactory: string; | |||||
| }: IModalProps<IAddLlmRequestBody> & { | |||||
| llmFactory: string; | |||||
| editMode?: boolean; | editMode?: boolean; | ||||
| initialValues?: Partial<IAddLlmRequestBody>; | initialValues?: Partial<IAddLlmRequestBody>; | ||||
| }) => { | }) => { | ||||
| form.resetFields(); | form.resetFields(); | ||||
| } | } | ||||
| }, [visible, editMode, initialValues, form]); | }, [visible, editMode, initialValues, form]); | ||||
| const url = | const url = | ||||
| llmFactoryToUrlMap[llmFactory as LlmFactory] || | llmFactoryToUrlMap[llmFactory as LlmFactory] || | ||||
| 'https://github.com/infiniflow/ragflow/blob/main/docs/guides/models/deploy_local_llm.mdx'; | 'https://github.com/infiniflow/ragflow/blob/main/docs/guides/models/deploy_local_llm.mdx'; | ||||
| }; | }; | ||||
| return ( | return ( | ||||
| <Modal | <Modal | ||||
| title={editMode ? t('editLlmTitle', { name: llmFactory }) : t('addLlmTitle', { name: llmFactory })} | |||||
| title={ | |||||
| editMode | |||||
| ? t('editLlmTitle', { name: llmFactory }) | |||||
| : t('addLlmTitle', { name: llmFactory }) | |||||
| } | |||||
| open={visible} | open={visible} | ||||
| onOk={handleOk} | onOk={handleOk} | ||||
| onCancel={hideModal} | onCancel={hideModal} | ||||
| name="api_key" | name="api_key" | ||||
| rules={[{ required: false, message: t('apiKeyMessage') }]} | rules={[{ required: false, message: t('apiKeyMessage') }]} | ||||
| > | > | ||||
| <Input | |||||
| placeholder={t('apiKeyMessage')} | |||||
| onKeyDown={handleKeyDown} | |||||
| /> | |||||
| <Input placeholder={t('apiKeyMessage')} onKeyDown={handleKeyDown} /> | |||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item<FieldType> | <Form.Item<FieldType> | ||||
| label={t('maxTokens')} | label={t('maxTokens')} |