### What problem does this PR solve? Feat: Add retrieval tool #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.20.0
| @@ -10,7 +10,6 @@ import { | |||
| FormItem, | |||
| FormLabel, | |||
| } from '@/components/ui/form'; | |||
| import { Textarea } from '@/components/ui/textarea'; | |||
| import { zodResolver } from '@hookform/resolvers/zod'; | |||
| import { Position } from '@xyflow/react'; | |||
| import { useContext, useMemo } from 'react'; | |||
| @@ -22,6 +21,7 @@ import { AgentInstanceContext } from '../../context'; | |||
| import { INextOperatorForm } from '../../interface'; | |||
| import useGraphStore from '../../store'; | |||
| import { isBottomSubAgent } from '../../utils'; | |||
| import { DescriptionField } from '../components/description-field'; | |||
| import { Output } from '../components/output'; | |||
| import { PromptEditor } from '../components/prompt-editor'; | |||
| import { AgentTools } from './agent-tools'; | |||
| @@ -85,20 +85,7 @@ const AgentForm = ({ node }: INextOperatorForm) => { | |||
| }} | |||
| > | |||
| <FormContainer> | |||
| {isSubAgent && ( | |||
| <FormField | |||
| control={form.control} | |||
| name={`description`} | |||
| render={({ field }) => ( | |||
| <FormItem className="flex-1"> | |||
| <FormLabel>Description</FormLabel> | |||
| <FormControl> | |||
| <Textarea {...field}></Textarea> | |||
| </FormControl> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| )} | |||
| {isSubAgent && <DescriptionField></DescriptionField>} | |||
| <LargeModelFormField></LargeModelFormField> | |||
| <FormField | |||
| control={form.control} | |||
| @@ -42,6 +42,7 @@ const Menus = [ | |||
| Operator.Invoke, | |||
| Operator.Crawler, | |||
| Operator.Code, | |||
| Operator.Retrieval, | |||
| ], | |||
| }, | |||
| ]; | |||
| @@ -1,4 +1,5 @@ | |||
| import { IAgentForm } from '@/interfaces/database/agent'; | |||
| import { DefaultAgentToolValuesMap } from '@/pages/agent/constant'; | |||
| import { AgentFormContext } from '@/pages/agent/context'; | |||
| import useGraphStore from '@/pages/agent/store'; | |||
| import { get } from 'lodash'; | |||
| @@ -23,7 +24,17 @@ export function useUpdateAgentNodeTools() { | |||
| if (node?.id) { | |||
| const nextValue = value.reduce<IAgentForm['tools']>((pre, cur) => { | |||
| const tool = tools.find((x) => x.component_name === cur); | |||
| pre.push(tool ? tool : { component_name: cur, params: {} }); | |||
| pre.push( | |||
| tool | |||
| ? tool | |||
| : { | |||
| component_name: cur, | |||
| params: | |||
| DefaultAgentToolValuesMap[ | |||
| cur as keyof typeof DefaultAgentToolValuesMap | |||
| ] || {}, | |||
| }, | |||
| ); | |||
| return pre; | |||
| }, []); | |||
| @@ -1,11 +1,11 @@ | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { Form, Input, Select } from 'antd'; | |||
| import { useMemo } from 'react'; | |||
| import { IOperatorForm } from '../../interface'; | |||
| import { | |||
| BaiduFanyiDomainOptions, | |||
| BaiduFanyiSourceLangOptions, | |||
| } from '../../constant'; | |||
| import { IOperatorForm } from '../../interface'; | |||
| } from '../../options'; | |||
| import DynamicInputVariable from '../components/dynamic-input-variable'; | |||
| const BaiduFanyiForm = ({ onValuesChange, form, node }: IOperatorForm) => { | |||
| @@ -2,8 +2,8 @@ import TopNItem from '@/components/top-n-item'; | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { Form, Input, Select } from 'antd'; | |||
| import { useMemo } from 'react'; | |||
| import { BingCountryOptions, BingLanguageOptions } from '../../constant'; | |||
| import { IOperatorForm } from '../../interface'; | |||
| import { BingCountryOptions, BingLanguageOptions } from '../../options'; | |||
| import DynamicInputVariable from '../components/dynamic-input-variable'; | |||
| const BingForm = ({ onValuesChange, form, node }: IOperatorForm) => { | |||
| @@ -0,0 +1,26 @@ | |||
| import { | |||
| FormControl, | |||
| FormField, | |||
| FormItem, | |||
| FormLabel, | |||
| } from '@/components/ui/form'; | |||
| import { Textarea } from '@/components/ui/textarea'; | |||
| import { useFormContext } from 'react-hook-form'; | |||
| export function DescriptionField() { | |||
| const form = useFormContext(); | |||
| return ( | |||
| <FormField | |||
| control={form.control} | |||
| name={`description`} | |||
| render={({ field }) => ( | |||
| <FormItem className="flex-1"> | |||
| <FormLabel>Description</FormLabel> | |||
| <FormControl> | |||
| <Textarea {...field}></Textarea> | |||
| </FormControl> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| ); | |||
| } | |||
| @@ -1,8 +1,8 @@ | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { Form, Input, Select } from 'antd'; | |||
| import { useMemo } from 'react'; | |||
| import { CrawlerResultOptions } from '../../constant'; | |||
| import { IOperatorForm } from '../../interface'; | |||
| import { CrawlerResultOptions } from '../../options'; | |||
| import DynamicInputVariable from '../components/dynamic-input-variable'; | |||
| const CrawlerForm = ({ onValuesChange, form, node }: IOperatorForm) => { | |||
| const { t } = useTranslate('flow'); | |||
| @@ -1,9 +1,9 @@ | |||
| import TopNItem from '@/components/top-n-item'; | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { Form, Select } from 'antd'; | |||
| import { DeepLSourceLangOptions, DeepLTargetLangOptions } from '../../constant'; | |||
| import { useBuildSortOptions } from '../../form-hooks'; | |||
| import { IOperatorForm } from '../../interface'; | |||
| import { DeepLSourceLangOptions, DeepLTargetLangOptions } from '../../options'; | |||
| import DynamicInputVariable from '../components/dynamic-input-variable'; | |||
| const DeepLForm = ({ onValuesChange, form, node }: IOperatorForm) => { | |||
| @@ -4,8 +4,8 @@ import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { useTestDbConnect } from '@/hooks/flow-hooks'; | |||
| import { Button, Flex, Form, Input, InputNumber, Select } from 'antd'; | |||
| import { useCallback } from 'react'; | |||
| import { ExeSQLOptions } from '../../constant'; | |||
| import { IOperatorForm } from '../../interface'; | |||
| import { ExeSQLOptions } from '../../options'; | |||
| import DynamicInputVariable from '../components/dynamic-input-variable'; | |||
| const ExeSQLForm = ({ onValuesChange, form, node }: IOperatorForm) => { | |||
| @@ -1,8 +1,8 @@ | |||
| import TopNItem from '@/components/top-n-item'; | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { Form, Input, Select } from 'antd'; | |||
| import { GoogleCountryOptions, GoogleLanguageOptions } from '../../constant'; | |||
| import { IOperatorForm } from '../../interface'; | |||
| import { GoogleCountryOptions, GoogleLanguageOptions } from '../../options'; | |||
| import DynamicInputVariable from '../components/dynamic-input-variable'; | |||
| const GoogleForm = ({ onValuesChange, form, node }: IOperatorForm) => { | |||
| @@ -1,6 +1,7 @@ | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { Form, Input, Select } from 'antd'; | |||
| import { useMemo } from 'react'; | |||
| import { IOperatorForm } from '../../interface'; | |||
| import { | |||
| Jin10CalendarDatashapeOptions, | |||
| Jin10CalendarTypeOptions, | |||
| @@ -8,8 +9,7 @@ import { | |||
| Jin10SymbolsDatatypeOptions, | |||
| Jin10SymbolsTypeOptions, | |||
| Jin10TypeOptions, | |||
| } from '../../constant'; | |||
| import { IOperatorForm } from '../../interface'; | |||
| } from '../../options'; | |||
| import DynamicInputVariable from '../components/dynamic-input-variable'; | |||
| const Jin10Form = ({ onValuesChange, form, node }: IOperatorForm) => { | |||
| @@ -10,13 +10,13 @@ import { Input } from '@/components/ui/input'; | |||
| import { RAGFlowSelect } from '@/components/ui/select'; | |||
| import { useCallback, useMemo } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { INextOperatorForm } from '../../interface'; | |||
| import { | |||
| QWeatherLangOptions, | |||
| QWeatherTimePeriodOptions, | |||
| QWeatherTypeOptions, | |||
| QWeatherUserTypeOptions, | |||
| } from '../../constant'; | |||
| import { INextOperatorForm } from '../../interface'; | |||
| } from '../../options'; | |||
| import { DynamicInputVariable } from '../components/next-dynamic-input-variable'; | |||
| enum FormFieldName { | |||
| @@ -14,7 +14,7 @@ import { | |||
| import { Textarea } from '@/components/ui/textarea'; | |||
| import { zodResolver } from '@hookform/resolvers/zod'; | |||
| import { useMemo } from 'react'; | |||
| import { useForm } from 'react-hook-form'; | |||
| import { useForm, useFormContext } from 'react-hook-form'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { z } from 'zod'; | |||
| import { initialRetrievalValues } from '../../constant'; | |||
| @@ -24,8 +24,7 @@ import { Output } from '../components/output'; | |||
| import { QueryVariable } from '../components/query-variable'; | |||
| import { useValues } from './use-values'; | |||
| const FormSchema = z.object({ | |||
| query: z.string().optional(), | |||
| export const RetrievalPartialSchema = { | |||
| similarity_threshold: z.coerce.number(), | |||
| keywords_similarity_weight: z.coerce.number(), | |||
| top_n: z.coerce.number(), | |||
| @@ -33,11 +32,40 @@ const FormSchema = z.object({ | |||
| kb_ids: z.array(z.string()), | |||
| rerank_id: z.string(), | |||
| empty_response: z.string(), | |||
| }; | |||
| export const FormSchema = z.object({ | |||
| query: z.string().optional(), | |||
| ...RetrievalPartialSchema, | |||
| }); | |||
| const RetrievalForm = ({ node }: INextOperatorForm) => { | |||
| export function EmptyResponseField() { | |||
| const { t } = useTranslation(); | |||
| const form = useFormContext(); | |||
| return ( | |||
| <FormField | |||
| control={form.control} | |||
| name="empty_response" | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormLabel>{t('chat.emptyResponse')}</FormLabel> | |||
| <FormControl> | |||
| <Textarea | |||
| placeholder={t('common.namePlaceholder')} | |||
| {...field} | |||
| autoComplete="off" | |||
| rows={4} | |||
| /> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| ); | |||
| } | |||
| const RetrievalForm = ({ node }: INextOperatorForm) => { | |||
| const outputList = useMemo(() => { | |||
| return [ | |||
| { | |||
| @@ -75,25 +103,7 @@ const RetrievalForm = ({ node }: INextOperatorForm) => { | |||
| ></SimilaritySliderFormField> | |||
| <TopNFormField></TopNFormField> | |||
| <RerankFormFields></RerankFormFields> | |||
| <FormField | |||
| control={form.control} | |||
| name="empty_response" | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormLabel>{t('chat.emptyResponse')}</FormLabel> | |||
| <FormControl> | |||
| <Textarea | |||
| placeholder={t('common.namePlaceholder')} | |||
| {...field} | |||
| autoComplete="off" | |||
| rows={4} | |||
| /> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| <EmptyResponseField></EmptyResponseField> | |||
| </FormContainer> | |||
| <Output list={outputList}></Output> | |||
| </form> | |||
| @@ -10,8 +10,8 @@ import { | |||
| } from '@/components/ui/form'; | |||
| import { RAGFlowSelect } from '@/components/ui/select'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { GoogleLanguageOptions } from '../../constant'; | |||
| import { INextOperatorForm } from '../../interface'; | |||
| import { GoogleLanguageOptions } from '../../options'; | |||
| const RewriteQuestionForm = ({ form }: INextOperatorForm) => { | |||
| const { t } = useTranslation(); | |||
| @@ -12,9 +12,9 @@ import GithubForm from '../github-form'; | |||
| import GoogleForm from '../google-form'; | |||
| import GoogleScholarForm from '../google-scholar-form'; | |||
| import PubMedForm from '../pubmed-form'; | |||
| import RetrievalForm from '../retrieval-form/next'; | |||
| import WikipediaForm from '../wikipedia-form'; | |||
| import YahooFinanceForm from '../yahoo-finance-form'; | |||
| import RetrievalForm from './retrieval-form'; | |||
| import TavilyForm from './tavily-form'; | |||
| export const ToolFormConfigMap = { | |||
| @@ -0,0 +1,59 @@ | |||
| import { FormContainer } from '@/components/form-container'; | |||
| import { KnowledgeBaseFormField } from '@/components/knowledge-base-item'; | |||
| import { RerankFormFields } from '@/components/rerank'; | |||
| import { SimilaritySliderFormField } from '@/components/similarity-slider'; | |||
| import { TopNFormField } from '@/components/top-n-item'; | |||
| import { Form } from '@/components/ui/form'; | |||
| import { zodResolver } from '@hookform/resolvers/zod'; | |||
| import { useForm } from 'react-hook-form'; | |||
| import { z } from 'zod'; | |||
| import { DescriptionField } from '../../components/description-field'; | |||
| import { | |||
| EmptyResponseField, | |||
| RetrievalPartialSchema, | |||
| } from '../../retrieval-form/next'; | |||
| import { useValues } from '../use-values'; | |||
| import { useWatchFormChange } from '../use-watch-change'; | |||
| export const FormSchema = z.object({ | |||
| ...RetrievalPartialSchema, | |||
| description: z.string().optional(), | |||
| }); | |||
| const RetrievalForm = () => { | |||
| const defaultValues = useValues(); | |||
| const form = useForm({ | |||
| defaultValues: defaultValues, | |||
| resolver: zodResolver(FormSchema), | |||
| }); | |||
| useWatchFormChange(form); | |||
| return ( | |||
| <Form {...form}> | |||
| <form | |||
| className="space-y-6 p-4" | |||
| onSubmit={(e) => { | |||
| e.preventDefault(); | |||
| }} | |||
| > | |||
| <FormContainer> | |||
| <DescriptionField></DescriptionField> | |||
| <KnowledgeBaseFormField></KnowledgeBaseFormField> | |||
| </FormContainer> | |||
| <FormContainer> | |||
| <SimilaritySliderFormField | |||
| vectorSimilarityWeightName="keywords_similarity_weight" | |||
| isTooltipShown | |||
| ></SimilaritySliderFormField> | |||
| <TopNFormField></TopNFormField> | |||
| <RerankFormFields></RerankFormFields> | |||
| <EmptyResponseField></EmptyResponseField> | |||
| </FormContainer> | |||
| </form> | |||
| </Form> | |||
| ); | |||
| }; | |||
| export default RetrievalForm; | |||
| @@ -1,5 +1,6 @@ | |||
| import { isEmpty } from 'lodash'; | |||
| import { useMemo } from 'react'; | |||
| import { DefaultAgentToolValuesMap } from '../../constant'; | |||
| import useGraphStore from '../../store'; | |||
| import { getAgentNodeTools } from '../../utils'; | |||
| @@ -13,10 +14,6 @@ export enum Topic { | |||
| General = 'general', | |||
| } | |||
| export const defaultValues = { | |||
| api_key: '', | |||
| }; | |||
| export function useValues() { | |||
| const { clickedToolId, clickedNodeId, findUpstreamNodeById } = useGraphStore( | |||
| (state) => state, | |||
| @@ -31,6 +28,11 @@ export function useValues() { | |||
| )?.params; | |||
| if (isEmpty(formData)) { | |||
| const defaultValues = | |||
| DefaultAgentToolValuesMap[ | |||
| clickedToolId as keyof typeof DefaultAgentToolValuesMap | |||
| ]; | |||
| return defaultValues; | |||
| } | |||
| @@ -2,8 +2,8 @@ import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { DatePicker, DatePickerProps, Form, Input, Select } from 'antd'; | |||
| import dayjs from 'dayjs'; | |||
| import { useCallback, useMemo } from 'react'; | |||
| import { TuShareSrcOptions } from '../../constant'; | |||
| import { IOperatorForm } from '../../interface'; | |||
| import { TuShareSrcOptions } from '../../options'; | |||
| import DynamicInputVariable from '../components/dynamic-input-variable'; | |||
| const DateTimePicker = ({ | |||
| @@ -10,8 +10,8 @@ import { | |||
| import { RAGFlowSelect } from '@/components/ui/select'; | |||
| import { useMemo } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { WenCaiQueryTypeOptions } from '../../constant'; | |||
| import { INextOperatorForm } from '../../interface'; | |||
| import { WenCaiQueryTypeOptions } from '../../options'; | |||
| import { DynamicInputVariable } from '../components/next-dynamic-input-variable'; | |||
| const WenCaiForm = ({ form, node }: INextOperatorForm) => { | |||
| @@ -9,8 +9,8 @@ import { | |||
| } from '@/components/ui/form'; | |||
| import { RAGFlowSelect } from '@/components/ui/select'; | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { LanguageOptions } from '../../constant'; | |||
| import { INextOperatorForm } from '../../interface'; | |||
| import { LanguageOptions } from '../../options'; | |||
| import { DynamicInputVariable } from '../components/next-dynamic-input-variable'; | |||
| const WikipediaForm = ({ form, node }: INextOperatorForm) => { | |||
| @@ -186,11 +186,9 @@ function useAddChildEdge() { | |||
| } | |||
| function useAddToolNode() { | |||
| const addNode = useGraphStore((state) => state.addNode); | |||
| const getNode = useGraphStore((state) => state.getNode); | |||
| const addEdge = useGraphStore((state) => state.addEdge); | |||
| const edges = useGraphStore((state) => state.edges); | |||
| const nodes = useGraphStore((state) => state.nodes); | |||
| const { nodes, edges, addEdge, getNode, addNode } = useGraphStore( | |||
| (state) => state, | |||
| ); | |||
| const addToolNode = useCallback( | |||
| (newNode: Node<any>, nodeId?: string) => { | |||