### What problem does this PR solve? Feat: Add Google operator #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.20.0
| Operator.TavilySearch, | Operator.TavilySearch, | ||||
| Operator.TavilyExtract, | Operator.TavilyExtract, | ||||
| Operator.ExeSQL, | Operator.ExeSQL, | ||||
| Operator.Bing, | |||||
| Operator.Google, | |||||
| ]} | ]} | ||||
| ></OperatorItemList> | ></OperatorItemList> | ||||
| </AccordionContent> | </AccordionContent> |
| }; | }; | ||||
| export const initialGoogleValues = { | export const initialGoogleValues = { | ||||
| q: AgentGlobals.SysQuery, | |||||
| start: 0, | |||||
| num: 12, | |||||
| top_n: 10, | top_n: 10, | ||||
| api_key: 'YOUR_API_KEY (obtained from https://serpapi.com/manage-api-key)', | |||||
| api_key: '', | |||||
| country: 'cn', | country: 'cn', | ||||
| language: 'en', | language: 'en', | ||||
| ...initialQueryBaseValues, | |||||
| outputs: { | |||||
| formalized_content: { | |||||
| value: '', | |||||
| type: 'string', | |||||
| }, | |||||
| json: { | |||||
| value: [], | |||||
| type: 'Array<Object>', | |||||
| }, | |||||
| }, | |||||
| }; | }; | ||||
| export const initialBingValues = { | export const initialBingValues = { |
| import { useForm } from 'react-hook-form'; | import { useForm } from 'react-hook-form'; | ||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| import { buildOutputList } from '../../utils/build-output-list'; | import { buildOutputList } from '../../utils/build-output-list'; | ||||
| import { FormWrapper } from '../components/form-wrapper'; | |||||
| import { Output } from '../components/output'; | import { Output } from '../components/output'; | ||||
| import { | import { | ||||
| DynamicInputVariable, | DynamicInputVariable, | ||||
| return ( | return ( | ||||
| <Form {...form}> | <Form {...form}> | ||||
| <form | |||||
| className="p-5 space-y-5" | |||||
| onSubmit={(e) => { | |||||
| e.preventDefault(); | |||||
| }} | |||||
| > | |||||
| <FormWrapper> | |||||
| <DynamicInputVariable | <DynamicInputVariable | ||||
| node={node} | node={node} | ||||
| title={t('flow.input')} | title={t('flow.input')} | ||||
| </FormContainer> | </FormContainer> | ||||
| </div> | </div> | ||||
| )} | )} | ||||
| </form> | |||||
| </FormWrapper> | |||||
| <div className="p-5"> | <div className="p-5"> | ||||
| <Output list={buildOutputList(formData.outputs)}></Output> | <Output list={buildOutputList(formData.outputs)}></Output> | ||||
| </div> | </div> |
| import { | |||||
| FormControl, | |||||
| FormField, | |||||
| FormItem, | |||||
| FormLabel, | |||||
| FormMessage, | |||||
| } from '@/components/ui/form'; | |||||
| import { Input } from '@/components/ui/input'; | |||||
| import { useFormContext } from 'react-hook-form'; | |||||
| interface IApiKeyFieldProps { | |||||
| placeholder?: string; | |||||
| } | |||||
| export function ApiKeyField({ placeholder }: IApiKeyFieldProps) { | |||||
| const form = useFormContext(); | |||||
| return ( | |||||
| <FormField | |||||
| control={form.control} | |||||
| name="api_key" | |||||
| render={({ field }) => ( | |||||
| <FormItem> | |||||
| <FormLabel>Api Key</FormLabel> | |||||
| <FormControl> | |||||
| <Input type="password" {...field} placeholder={placeholder}></Input> | |||||
| </FormControl> | |||||
| <FormMessage /> | |||||
| </FormItem> | |||||
| )} | |||||
| /> | |||||
| ); | |||||
| } |
| import TopNItem from '@/components/top-n-item'; | |||||
| import { FormContainer } from '@/components/form-container'; | |||||
| import NumberInput from '@/components/originui/number-input'; | |||||
| import { SelectWithSearch } from '@/components/originui/select-with-search'; | |||||
| import { TopNFormField } from '@/components/top-n-item'; | |||||
| import { | |||||
| Form, | |||||
| FormControl, | |||||
| FormField, | |||||
| FormItem, | |||||
| FormLabel, | |||||
| FormMessage, | |||||
| } from '@/components/ui/form'; | |||||
| import { useTranslate } from '@/hooks/common-hooks'; | import { useTranslate } from '@/hooks/common-hooks'; | ||||
| import { Form, Input, Select } from 'antd'; | |||||
| import { IOperatorForm } from '../../interface'; | |||||
| import { zodResolver } from '@hookform/resolvers/zod'; | |||||
| import { useForm } from 'react-hook-form'; | |||||
| import { z } from 'zod'; | |||||
| import { initialGoogleValues } from '../../constant'; | |||||
| import { useFormValues } from '../../hooks/use-form-values'; | |||||
| import { INextOperatorForm } from '../../interface'; | |||||
| import { GoogleCountryOptions, GoogleLanguageOptions } from '../../options'; | import { GoogleCountryOptions, GoogleLanguageOptions } from '../../options'; | ||||
| import DynamicInputVariable from '../components/dynamic-input-variable'; | |||||
| import { buildOutputList } from '../../utils/build-output-list'; | |||||
| import { ApiKeyField } from '../components/api-key-field'; | |||||
| import { FormWrapper } from '../components/form-wrapper'; | |||||
| import { Output } from '../components/output'; | |||||
| import { QueryVariable } from '../components/query-variable'; | |||||
| const GoogleForm = ({ onValuesChange, form, node }: IOperatorForm) => { | |||||
| const outputList = buildOutputList(initialGoogleValues.outputs); | |||||
| export const FormSchema = z.object({ | |||||
| top_n: z.number(), | |||||
| api_key: z.string(), | |||||
| country: z.string(), | |||||
| language: z.string(), | |||||
| q: z.string(), | |||||
| start: z.number(), | |||||
| num: z.number(), | |||||
| }); | |||||
| const GoogleForm = ({ node }: INextOperatorForm) => { | |||||
| const { t } = useTranslate('flow'); | const { t } = useTranslate('flow'); | ||||
| const defaultValues = useFormValues(initialGoogleValues, node); | |||||
| const form = useForm<z.infer<typeof FormSchema>>({ | |||||
| defaultValues, | |||||
| resolver: zodResolver(FormSchema), | |||||
| }); | |||||
| return ( | return ( | ||||
| <Form | |||||
| name="basic" | |||||
| autoComplete="off" | |||||
| form={form} | |||||
| onValuesChange={onValuesChange} | |||||
| layout={'vertical'} | |||||
| > | |||||
| <DynamicInputVariable node={node}></DynamicInputVariable> | |||||
| <TopNItem initialValue={10}></TopNItem> | |||||
| <Form.Item label={t('apiKey')} name={'api_key'}> | |||||
| <Input></Input> | |||||
| </Form.Item> | |||||
| <Form.Item label={t('country')} name={'country'}> | |||||
| <Select options={GoogleCountryOptions}></Select> | |||||
| </Form.Item> | |||||
| <Form.Item label={t('language')} name={'language'}> | |||||
| <Select options={GoogleLanguageOptions}></Select> | |||||
| </Form.Item> | |||||
| <Form {...form}> | |||||
| <FormWrapper> | |||||
| <FormContainer> | |||||
| <QueryVariable name="q"></QueryVariable> | |||||
| </FormContainer> | |||||
| <FormContainer> | |||||
| <ApiKeyField placeholder="YOUR_API_KEY (obtained from https://serpapi.com/manage-api-key)"></ApiKeyField> | |||||
| <FormField | |||||
| control={form.control} | |||||
| name={`start`} | |||||
| render={({ field }) => ( | |||||
| <FormItem> | |||||
| <FormLabel>{t('start')}</FormLabel> | |||||
| <FormControl> | |||||
| <NumberInput {...field} className="w-full"></NumberInput> | |||||
| </FormControl> | |||||
| <FormMessage /> | |||||
| </FormItem> | |||||
| )} | |||||
| /> | |||||
| <FormField | |||||
| control={form.control} | |||||
| name={`num`} | |||||
| render={({ field }) => ( | |||||
| <FormItem> | |||||
| <FormLabel>{t('num')}</FormLabel> | |||||
| <FormControl> | |||||
| <NumberInput {...field} className="w-full"></NumberInput> | |||||
| </FormControl> | |||||
| <FormMessage /> | |||||
| </FormItem> | |||||
| )} | |||||
| /> | |||||
| <TopNFormField></TopNFormField> | |||||
| <FormField | |||||
| control={form.control} | |||||
| name={`country`} | |||||
| render={({ field }) => ( | |||||
| <FormItem className="flex-1"> | |||||
| <FormLabel>{t('country')}</FormLabel> | |||||
| <FormControl> | |||||
| <SelectWithSearch | |||||
| {...field} | |||||
| options={GoogleCountryOptions} | |||||
| ></SelectWithSearch> | |||||
| </FormControl> | |||||
| <FormMessage /> | |||||
| </FormItem> | |||||
| )} | |||||
| /> | |||||
| <FormField | |||||
| control={form.control} | |||||
| name={`language`} | |||||
| render={({ field }) => ( | |||||
| <FormItem className="flex-1"> | |||||
| <FormLabel>{t('language')}</FormLabel> | |||||
| <FormControl> | |||||
| <SelectWithSearch | |||||
| {...field} | |||||
| options={GoogleLanguageOptions} | |||||
| ></SelectWithSearch> | |||||
| </FormControl> | |||||
| <FormMessage /> | |||||
| </FormItem> | |||||
| )} | |||||
| /> | |||||
| </FormContainer> | |||||
| </FormWrapper> | |||||
| <div className="p-5"> | |||||
| <Output list={outputList}></Output> | |||||
| </div> | |||||
| </Form> | </Form> | ||||
| ); | ); | ||||
| }; | }; |
| import { useWatchFormChange } from '../../hooks/use-watch-form-change'; | import { useWatchFormChange } from '../../hooks/use-watch-form-change'; | ||||
| import { INextOperatorForm } from '../../interface'; | import { INextOperatorForm } from '../../interface'; | ||||
| import { buildOutputList } from '../../utils/build-output-list'; | import { buildOutputList } from '../../utils/build-output-list'; | ||||
| import { ApiKeyField } from '../components/api-key-field'; | |||||
| import { FormWrapper } from '../components/form-wrapper'; | import { FormWrapper } from '../components/form-wrapper'; | ||||
| import { Output } from '../components/output'; | import { Output } from '../components/output'; | ||||
| import { TavilyApiKeyField, TavilyFormSchema } from '../tavily-form'; | |||||
| import { TavilyFormSchema } from '../tavily-form'; | |||||
| const outputList = buildOutputList(initialTavilyExtractValues.outputs); | const outputList = buildOutputList(initialTavilyExtractValues.outputs); | ||||
| <Form {...form}> | <Form {...form}> | ||||
| <FormWrapper> | <FormWrapper> | ||||
| <FormContainer> | <FormContainer> | ||||
| <TavilyApiKeyField></TavilyApiKeyField> | |||||
| <ApiKeyField></ApiKeyField> | |||||
| </FormContainer> | </FormContainer> | ||||
| <FormContainer> | <FormContainer> | ||||
| <FormField | <FormField |
| import { buildOptions } from '@/utils/form'; | import { buildOptions } from '@/utils/form'; | ||||
| import { zodResolver } from '@hookform/resolvers/zod'; | import { zodResolver } from '@hookform/resolvers/zod'; | ||||
| import { memo, useMemo } from 'react'; | import { memo, useMemo } from 'react'; | ||||
| import { useForm, useFormContext } from 'react-hook-form'; | |||||
| import { useForm } from 'react-hook-form'; | |||||
| import { z } from 'zod'; | import { z } from 'zod'; | ||||
| import { | import { | ||||
| TavilySearchDepth, | TavilySearchDepth, | ||||
| initialTavilyValues, | initialTavilyValues, | ||||
| } from '../../constant'; | } from '../../constant'; | ||||
| import { INextOperatorForm } from '../../interface'; | import { INextOperatorForm } from '../../interface'; | ||||
| import { ApiKeyField } from '../components/api-key-field'; | |||||
| import { FormWrapper } from '../components/form-wrapper'; | import { FormWrapper } from '../components/form-wrapper'; | ||||
| import { Output, OutputType } from '../components/output'; | import { Output, OutputType } from '../components/output'; | ||||
| import { QueryVariable } from '../components/query-variable'; | import { QueryVariable } from '../components/query-variable'; | ||||
| import { useValues } from './use-values'; | import { useValues } from './use-values'; | ||||
| import { useWatchFormChange } from './use-watch-change'; | import { useWatchFormChange } from './use-watch-change'; | ||||
| export function TavilyApiKeyField() { | |||||
| const form = useFormContext(); | |||||
| return ( | |||||
| <FormField | |||||
| control={form.control} | |||||
| name="api_key" | |||||
| render={({ field }) => ( | |||||
| <FormItem> | |||||
| <FormLabel>Api Key</FormLabel> | |||||
| <FormControl> | |||||
| <Input type="password" {...field}></Input> | |||||
| </FormControl> | |||||
| <FormMessage /> | |||||
| </FormItem> | |||||
| )} | |||||
| /> | |||||
| ); | |||||
| } | |||||
| export const TavilyFormSchema = { | export const TavilyFormSchema = { | ||||
| api_key: z.string(), | api_key: z.string(), | ||||
| }; | }; | ||||
| <Form {...form}> | <Form {...form}> | ||||
| <FormWrapper> | <FormWrapper> | ||||
| <FormContainer> | <FormContainer> | ||||
| <TavilyApiKeyField></TavilyApiKeyField> | |||||
| <ApiKeyField></ApiKeyField> | |||||
| </FormContainer> | </FormContainer> | ||||
| <FormContainer> | <FormContainer> | ||||
| <QueryVariable></QueryVariable> | <QueryVariable></QueryVariable> |
| import DuckDuckGoForm from '../duckduckgo-form'; | import DuckDuckGoForm from '../duckduckgo-form'; | ||||
| import EmailForm from '../email-form'; | import EmailForm from '../email-form'; | ||||
| import GithubForm from '../github-form'; | import GithubForm from '../github-form'; | ||||
| import GoogleForm from '../google-form'; | |||||
| import GoogleScholarForm from '../google-scholar-form'; | import GoogleScholarForm from '../google-scholar-form'; | ||||
| import PubMedForm from '../pubmed-form'; | import PubMedForm from '../pubmed-form'; | ||||
| import WikipediaForm from '../wikipedia-form'; | import WikipediaForm from '../wikipedia-form'; | ||||
| [Operator.Wikipedia]: WikipediaForm, | [Operator.Wikipedia]: WikipediaForm, | ||||
| [Operator.PubMed]: PubMedForm, | [Operator.PubMed]: PubMedForm, | ||||
| [Operator.ArXiv]: ArXivForm, | [Operator.ArXiv]: ArXivForm, | ||||
| [Operator.Google]: GoogleForm, | |||||
| [Operator.Google]: TavilyForm, | |||||
| [Operator.Bing]: BingForm, | [Operator.Bing]: BingForm, | ||||
| [Operator.GoogleScholar]: GoogleScholarForm, | [Operator.GoogleScholar]: GoogleScholarForm, | ||||
| [Operator.DeepL]: DeepLForm, | [Operator.DeepL]: DeepLForm, |
| import { zodResolver } from '@hookform/resolvers/zod'; | import { zodResolver } from '@hookform/resolvers/zod'; | ||||
| import { useForm } from 'react-hook-form'; | import { useForm } from 'react-hook-form'; | ||||
| import { z } from 'zod'; | import { z } from 'zod'; | ||||
| import { ApiKeyField } from '../../components/api-key-field'; | |||||
| import { FormWrapper } from '../../components/form-wrapper'; | import { FormWrapper } from '../../components/form-wrapper'; | ||||
| import { TavilyApiKeyField, TavilyFormSchema } from '../../tavily-form'; | |||||
| import { TavilyFormSchema } from '../../tavily-form'; | |||||
| import { useValues } from '../use-values'; | import { useValues } from '../use-values'; | ||||
| import { useWatchFormChange } from '../use-watch-change'; | import { useWatchFormChange } from '../use-watch-change'; | ||||
| <Form {...form}> | <Form {...form}> | ||||
| <FormWrapper> | <FormWrapper> | ||||
| <FormContainer> | <FormContainer> | ||||
| <TavilyApiKeyField></TavilyApiKeyField> | |||||
| <ApiKeyField></ApiKeyField> | |||||
| </FormContainer> | </FormContainer> | ||||
| </FormWrapper> | </FormWrapper> | ||||
| </Form> | </Form> |
| ...omit(initialValues, 'query'), | ...omit(initialValues, 'query'), | ||||
| description: '', | description: '', | ||||
| }; | }; | ||||
| case (Operator.TavilySearch, Operator.TavilyExtract): | |||||
| case (Operator.TavilySearch, Operator.TavilyExtract, Operator.Google): | |||||
| return { | return { | ||||
| api_key: '', | api_key: '', | ||||
| }; | }; |