Просмотр исходного кода

Feat: Add Email and DuckDuckGo and Wikipedia Operator #3221 (#9090)

### What problem does this PR solve?

Feat: Add Email and DuckDuckGo and Wikipedia Operator #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
tags/v0.20.0
balibabu 3 месяцев назад
Родитель
Сommit
b9d3846bb4
Аккаунт пользователя с таким Email не найден

+ 1
- 2
web/src/pages/agent/canvas/index.tsx Просмотреть файл

@@ -41,7 +41,6 @@ import { RagNode } from './node';
import { AgentNode } from './node/agent-node';
import { BeginNode } from './node/begin-node';
import { CategorizeNode } from './node/categorize-node';
import { EmailNode } from './node/email-node';
import { GenerateNode } from './node/generate-node';
import { InvokeNode } from './node/invoke-node';
import { IterationNode, IterationStartNode } from './node/iteration-node';
@@ -71,7 +70,7 @@ export const nodeTypes: NodeTypes = {
keywordNode: KeywordNode,
invokeNode: InvokeNode,
templateNode: TemplateNode,
emailNode: EmailNode,
// emailNode: EmailNode,
group: IterationNode,
iterationStartNode: IterationStartNode,
agentNode: AgentNode,

+ 3
- 0
web/src/pages/agent/canvas/node/dropdown/next-step-dropdown.tsx Просмотреть файл

@@ -104,6 +104,9 @@ function AccordionOperators() {
Operator.ExeSQL,
Operator.Google,
Operator.YahooFinance,
Operator.Email,
Operator.DuckDuckGo,
Operator.Wikipedia,
]}
></OperatorItemList>
</AccordionContent>

+ 26
- 4
web/src/pages/agent/constant.tsx Просмотреть файл

@@ -341,7 +341,17 @@ export const initialKeywordExtractValues = {
export const initialDuckValues = {
top_n: 10,
channel: Channel.Text,
...initialQueryBaseValues,
query: AgentGlobals.SysQuery,
outputs: {
formalized_content: {
value: '',
type: 'string',
},
json: {
value: [],
type: 'Array<Object>',
},
},
};

export const initialBaiduValues = {
@@ -352,7 +362,13 @@ export const initialBaiduValues = {
export const initialWikipediaValues = {
top_n: 10,
language: 'en',
...initialQueryBaseValues,
query: AgentGlobals.SysQuery,
outputs: {
formalized_content: {
value: '',
type: 'string',
},
},
};

export const initialPubMedValues = {
@@ -526,7 +542,7 @@ export const initialTemplateValues = {

export const initialEmailValues = {
smtp_server: '',
smtp_port: 587,
smtp_port: 465,
email: '',
password: '',
sender_name: '',
@@ -534,6 +550,12 @@ export const initialEmailValues = {
cc_email: '',
subject: '',
content: '',
outputs: {
success: {
value: true,
type: 'boolean',
},
},
};

export const initialIterationValues = {
@@ -815,7 +837,7 @@ export const NodeMap = {
[Operator.Crawler]: 'ragNode',
[Operator.Invoke]: 'invokeNode',
[Operator.Template]: 'templateNode',
[Operator.Email]: 'emailNode',
[Operator.Email]: 'ragNode',
[Operator.Iteration]: 'group',
[Operator.IterationStart]: 'iterationStartNode',
[Operator.Code]: 'ragNode',

+ 64
- 28
web/src/pages/agent/form/duckduckgo-form/index.tsx Просмотреть файл

@@ -1,3 +1,4 @@
import { FormContainer } from '@/components/form-container';
import { TopNFormField } from '@/components/top-n-item';
import {
Form,
@@ -9,44 +10,79 @@ import {
} from '@/components/ui/form';
import { RAGFlowSelect } from '@/components/ui/select';
import { useTranslate } from '@/hooks/common-hooks';
import { useMemo } from 'react';
import { Channel } from '../../constant';
import { zodResolver } from '@hookform/resolvers/zod';
import { memo, useMemo } from 'react';
import { useForm, useFormContext } from 'react-hook-form';
import { z } from 'zod';
import { Channel, initialDuckValues } from '../../constant';
import { useFormValues } from '../../hooks/use-form-values';
import { INextOperatorForm } from '../../interface';
import { DynamicInputVariable } from '../components/next-dynamic-input-variable';
import { buildOutputList } from '../../utils/build-output-list';
import { FormWrapper } from '../components/form-wrapper';
import { Output } from '../components/output';
import { QueryVariable } from '../components/query-variable';

const DuckDuckGoForm = ({ form, node }: INextOperatorForm) => {
export const DuckDuckGoFormPartialSchema = {
top_n: z.string(),
channel: z.string(),
};

const FormSchema = z.object({
query: z.string(),
...DuckDuckGoFormPartialSchema,
});

export function DuckDuckGoWidgets() {
const { t } = useTranslate('flow');
const form = useFormContext();

const options = useMemo(() => {
return Object.values(Channel).map((x) => ({ value: x, label: t(x) }));
}, [t]);

return (
<>
<TopNFormField></TopNFormField>
<FormField
control={form.control}
name={'channel'}
render={({ field }) => (
<FormItem>
<FormLabel tooltip={t('channelTip')}>{t('channel')}</FormLabel>
<FormControl>
<RAGFlowSelect {...field} options={options} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</>
);
}

const outputList = buildOutputList(initialDuckValues.outputs);

function DuckDuckGoForm({ node }: INextOperatorForm) {
const defaultValues = useFormValues(initialDuckValues, node);

const form = useForm<z.infer<typeof FormSchema>>({
defaultValues,
resolver: zodResolver(FormSchema),
});

return (
<Form {...form}>
<form
className="space-y-6"
onSubmit={(e) => {
e.preventDefault();
}}
>
<DynamicInputVariable node={node}></DynamicInputVariable>
<TopNFormField></TopNFormField>
<FormField
control={form.control}
name="channel"
render={({ field }) => (
<FormItem>
<FormLabel tooltip={t('channelTip')}>{t('channel')}</FormLabel>
<FormControl>
<RAGFlowSelect {...field} options={options} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</form>
<FormWrapper>
<FormContainer>
<QueryVariable></QueryVariable>
<DuckDuckGoWidgets></DuckDuckGoWidgets>
</FormContainer>
</FormWrapper>
<div className="p-5">
<Output list={outputList}></Output>
</div>
</Form>
);
};
}

export default DuckDuckGoForm;
export default memo(DuckDuckGoForm);

+ 111
- 42
web/src/pages/agent/form/email-form/index.tsx Просмотреть файл

@@ -1,50 +1,119 @@
import { FormContainer } from '@/components/form-container';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { useTranslate } from '@/hooks/common-hooks';
import { Form, Input } from 'antd';
import { IOperatorForm } from '../../interface';
import DynamicInputVariable from '../components/dynamic-input-variable';
import { zodResolver } from '@hookform/resolvers/zod';
import { ReactNode } from 'react';
import { useForm, useFormContext } from 'react-hook-form';
import { z } from 'zod';
import { initialEmailValues } from '../../constant';
import { useFormValues } from '../../hooks/use-form-values';
import { INextOperatorForm } from '../../interface';
import { buildOutputList } from '../../utils/build-output-list';
import { FormWrapper } from '../components/form-wrapper';
import { Output } from '../components/output';

const EmailForm = ({ onValuesChange, form, node }: IOperatorForm) => {
interface InputFormFieldProps {
name: string;
label: ReactNode;
type?: string;
}

function InputFormField({ name, label, type }: InputFormFieldProps) {
const form = useFormContext();

return (
<FormField
control={form.control}
name={name}
render={({ field }) => (
<FormItem>
<FormLabel>{label}</FormLabel>
<FormControl>
<Input {...field} type={type}></Input>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
);
}

export function EmailFormWidgets() {
const { t } = useTranslate('flow');

return (
<Form
name="basic"
autoComplete="off"
form={form}
onValuesChange={onValuesChange}
layout={'vertical'}
>
<DynamicInputVariable node={node}></DynamicInputVariable>

{/* SMTP服务器配置 */}
<Form.Item label={t('smtpServer')} name={'smtp_server'}>
<Input placeholder="smtp.example.com" />
</Form.Item>
<Form.Item label={t('smtpPort')} name={'smtp_port'}>
<Input type="number" placeholder="587" />
</Form.Item>
<Form.Item label={t('senderEmail')} name={'email'}>
<Input placeholder="sender@example.com" />
</Form.Item>
<Form.Item label={t('authCode')} name={'password'}>
<Input.Password placeholder="your_password" />
</Form.Item>
<Form.Item label={t('senderName')} name={'sender_name'}>
<Input placeholder="Sender Name" />
</Form.Item>

{/* 动态参数说明 */}
<div style={{ marginBottom: 24 }}>
<h4>{t('dynamicParameters')}</h4>
<div>{t('jsonFormatTip')}</div>
<pre style={{ background: '#f5f5f5', padding: 12, borderRadius: 4 }}>
{`{
"to_email": "recipient@example.com",
"cc_email": "cc@example.com",
"subject": "Email Subject",
"content": "Email Content"
}`}
</pre>
<>
<InputFormField
name="smtp_server"
label={t('smtpServer')}
></InputFormField>
<InputFormField
name="smtp_port"
label={t('smtpPort')}
type="number"
></InputFormField>
<InputFormField name="email" label={t('senderEmail')}></InputFormField>
<InputFormField
name="password"
label={t('authCode')}
type="password"
></InputFormField>
<InputFormField
name="sender_name"
label={t('senderName')}
></InputFormField>
</>
);
}

export const EmailFormPartialSchema = {
smtp_server: z.string(),
smtp_port: z.number(),
email: z.string(),
password: z.string(),
sender_name: z.string(),
};

const FormSchema = z.object({
to_email: z.string(),
cc_email: z.string(),
content: z.string(),
subject: z.string(),
...EmailFormPartialSchema,
});

const outputList = buildOutputList(initialEmailValues.outputs);

const EmailForm = ({ node }: INextOperatorForm) => {
const { t } = useTranslate('flow');
const defaultValues = useFormValues(initialEmailValues, node);

const form = useForm<z.infer<typeof FormSchema>>({
defaultValues,
resolver: zodResolver(FormSchema),
});

return (
<Form {...form}>
<FormWrapper>
<FormContainer>
<InputFormField name="to_email" label={t('toEmail')}></InputFormField>
<InputFormField name="cc_email" label={t('ccEmail')}></InputFormField>
<InputFormField name="content" label={t('content')}></InputFormField>
<InputFormField name="subject" label={t('subject')}></InputFormField>
<EmailFormWidgets></EmailFormWidgets>
</FormContainer>
</FormWrapper>
<div className="p-5">
<Output list={outputList}></Output>
</div>
</Form>
);

+ 3
- 3
web/src/pages/agent/form/tool-form/constant.tsx Просмотреть файл

@@ -2,17 +2,17 @@ import { Operator } from '../../constant';
import AkShareForm from '../akshare-form';
import ArXivForm from '../arxiv-form';
import DeepLForm from '../deepl-form';
import DuckDuckGoForm from '../duckduckgo-form';
import EmailForm from '../email-form';
import GithubForm from '../github-form';
import GoogleScholarForm from '../google-scholar-form';
import PubMedForm from '../pubmed-form';
import WikipediaForm from '../wikipedia-form';
import BingForm from './bing-form';
import CrawlerForm from './crawler-form';
import DuckDuckGoForm from './duckduckgo-form';
import EmailForm from './email-form';
import ExeSQLForm from './exesql-form';
import RetrievalForm from './retrieval-form';
import TavilyForm from './tavily-form';
import WikipediaForm from './wikipedia-form';
import YahooFinanceForm from './yahoo-finance-form';

export const ToolFormConfigMap = {

+ 38
- 0
web/src/pages/agent/form/tool-form/duckduckgo-form/index.tsx Просмотреть файл

@@ -0,0 +1,38 @@
import { FormContainer } from '@/components/form-container';
import { Form } from '@/components/ui/form';
import { zodResolver } from '@hookform/resolvers/zod';
import { memo } from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { FormWrapper } from '../../components/form-wrapper';
import {
DuckDuckGoFormPartialSchema,
DuckDuckGoWidgets,
} from '../../duckduckgo-form';
import { useValues } from '../use-values';
import { useWatchFormChange } from '../use-watch-change';

function DuckDuckGoForm() {
const values = useValues();

const FormSchema = z.object(DuckDuckGoFormPartialSchema);

const form = useForm<z.infer<typeof FormSchema>>({
defaultValues: values,
resolver: zodResolver(FormSchema),
});

useWatchFormChange(form);

return (
<Form {...form}>
<FormWrapper>
<FormContainer>
<DuckDuckGoWidgets></DuckDuckGoWidgets>
</FormContainer>
</FormWrapper>
</Form>
);
}

export default memo(DuckDuckGoForm);

+ 35
- 0
web/src/pages/agent/form/tool-form/email-form/index.tsx Просмотреть файл

@@ -0,0 +1,35 @@
import { FormContainer } from '@/components/form-container';
import { Form } from '@/components/ui/form';
import { zodResolver } from '@hookform/resolvers/zod';
import { memo } from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { FormWrapper } from '../../components/form-wrapper';
import { EmailFormPartialSchema, EmailFormWidgets } from '../../email-form';
import { useValues } from '../use-values';
import { useWatchFormChange } from '../use-watch-change';

function EmailForm() {
const values = useValues();

const FormSchema = z.object(EmailFormPartialSchema);

const form = useForm<z.infer<typeof FormSchema>>({
defaultValues: values,
resolver: zodResolver(FormSchema),
});

useWatchFormChange(form);

return (
<Form {...form}>
<FormWrapper>
<FormContainer>
<EmailFormWidgets></EmailFormWidgets>
</FormContainer>
</FormWrapper>
</Form>
);
}

export default memo(EmailForm);

+ 4
- 3
web/src/pages/agent/form/tool-form/tavily-form/index.tsx Просмотреть файл

@@ -1,6 +1,7 @@
import { FormContainer } from '@/components/form-container';
import { Form } from '@/components/ui/form';
import { zodResolver } from '@hookform/resolvers/zod';
import { memo } from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { ApiKeyField } from '../../components/api-key-field';
@@ -9,7 +10,7 @@ import { TavilyFormSchema } from '../../tavily-form';
import { useValues } from '../use-values';
import { useWatchFormChange } from '../use-watch-change';

const TavilyForm = () => {
function TavilyForm() {
const values = useValues();

const FormSchema = z.object(TavilyFormSchema);
@@ -30,6 +31,6 @@ const TavilyForm = () => {
</FormWrapper>
</Form>
);
};
}

export default TavilyForm;
export default memo(TavilyForm);

+ 38
- 0
web/src/pages/agent/form/tool-form/wikipedia-form/index.tsx Просмотреть файл

@@ -0,0 +1,38 @@
import { FormContainer } from '@/components/form-container';
import { Form } from '@/components/ui/form';
import { zodResolver } from '@hookform/resolvers/zod';
import { memo } from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { FormWrapper } from '../../components/form-wrapper';
import {
WikipediaFormPartialSchema,
WikipediaFormWidgets,
} from '../../wikipedia-form';
import { useValues } from '../use-values';
import { useWatchFormChange } from '../use-watch-change';

function WikipediaForm() {
const values = useValues();

const FormSchema = z.object(WikipediaFormPartialSchema);

const form = useForm<z.infer<typeof FormSchema>>({
defaultValues: values,
resolver: zodResolver(FormSchema),
});

useWatchFormChange(form);

return (
<Form {...form}>
<FormWrapper>
<FormContainer>
<WikipediaFormWidgets></WikipediaFormWidgets>
</FormContainer>
</FormWrapper>
</Form>
);
}

export default memo(WikipediaForm);

+ 65
- 28
web/src/pages/agent/form/wikipedia-form/index.tsx Просмотреть файл

@@ -1,3 +1,5 @@
import { FormContainer } from '@/components/form-container';
import { SelectWithSearch } from '@/components/originui/select-with-search';
import { TopNFormField } from '@/components/top-n-item';
import {
Form,
@@ -7,42 +9,77 @@ import {
FormLabel,
FormMessage,
} from '@/components/ui/form';
import { RAGFlowSelect } from '@/components/ui/select';
import { useTranslate } from '@/hooks/common-hooks';
import { zodResolver } from '@hookform/resolvers/zod';
import { memo } from 'react';
import { useForm, useFormContext } from 'react-hook-form';
import { z } from 'zod';
import { initialWikipediaValues } from '../../constant';
import { useFormValues } from '../../hooks/use-form-values';
import { INextOperatorForm } from '../../interface';
import { LanguageOptions } from '../../options';
import { DynamicInputVariable } from '../components/next-dynamic-input-variable';
import { buildOutputList } from '../../utils/build-output-list';
import { FormWrapper } from '../components/form-wrapper';
import { Output } from '../components/output';
import { QueryVariable } from '../components/query-variable';

const WikipediaForm = ({ form, node }: INextOperatorForm) => {
export const WikipediaFormPartialSchema = {
top_n: z.string(),
language: z.string(),
};

const FormSchema = z.object({
query: z.string(),
...WikipediaFormPartialSchema,
});

export function WikipediaFormWidgets() {
const { t } = useTranslate('common');
const form = useFormContext();

return (
<>
<TopNFormField></TopNFormField>
<FormField
control={form.control}
name="language"
render={({ field }) => (
<FormItem>
<FormLabel>{t('language')}</FormLabel>
<FormControl>
<SelectWithSearch {...field} options={LanguageOptions} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</>
);
}

const outputList = buildOutputList(initialWikipediaValues.outputs);

function WikipediaForm({ node }: INextOperatorForm) {
const defaultValues = useFormValues(initialWikipediaValues, node);

const form = useForm<z.infer<typeof FormSchema>>({
defaultValues,
resolver: zodResolver(FormSchema),
});

return (
<Form {...form}>
<form
className="space-y-6"
onSubmit={(e) => {
e.preventDefault();
}}
>
<DynamicInputVariable node={node}></DynamicInputVariable>
<TopNFormField></TopNFormField>

<FormField
control={form.control}
name="language"
render={({ field }) => (
<FormItem>
<FormLabel>{t('language')}</FormLabel>
<FormControl>
<RAGFlowSelect {...field} options={LanguageOptions} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</form>
<FormWrapper>
<FormContainer>
<QueryVariable></QueryVariable>
<WikipediaFormWidgets></WikipediaFormWidgets>
</FormContainer>
</FormWrapper>
<div className="p-5">
<Output list={outputList}></Output>
</div>
</Form>
);
};
}

export default WikipediaForm;
export default memo(WikipediaForm);

+ 17
- 1
web/src/pages/agent/hooks/use-agent-tool-initial-values.ts Просмотреть файл

@@ -1,4 +1,4 @@
import { omit } from 'lodash';
import { omit, pick } from 'lodash';
import { useCallback } from 'react';
import { Operator } from '../constant';
import { useInitializeOperatorParams } from './use-add-node';
@@ -27,6 +27,22 @@ export function useAgentToolInitialValues() {
case Operator.YahooFinance:
return omit(initialValues, 'stock_code');

case Operator.Email:
return pick(
initialValues,
'smtp_server',
'smtp_port',
'email',
'password',
'sender_name',
);

case Operator.DuckDuckGo:
return pick(initialValues, 'top_n', 'channel');

case Operator.Wikipedia:
return pick(initialValues, 'top_n', 'language');

default:
return initialValues;
}

Загрузка…
Отмена
Сохранить