### 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
| @@ -102,7 +102,7 @@ function AccordionOperators() { | |||
| Operator.TavilySearch, | |||
| Operator.TavilyExtract, | |||
| Operator.ExeSQL, | |||
| Operator.Bing, | |||
| Operator.Google, | |||
| ]} | |||
| ></OperatorItemList> | |||
| </AccordionContent> | |||
| @@ -368,11 +368,23 @@ export const initialArXivValues = { | |||
| }; | |||
| export const initialGoogleValues = { | |||
| q: AgentGlobals.SysQuery, | |||
| start: 0, | |||
| num: 12, | |||
| top_n: 10, | |||
| api_key: 'YOUR_API_KEY (obtained from https://serpapi.com/manage-api-key)', | |||
| api_key: '', | |||
| country: 'cn', | |||
| language: 'en', | |||
| ...initialQueryBaseValues, | |||
| outputs: { | |||
| formalized_content: { | |||
| value: '', | |||
| type: 'string', | |||
| }, | |||
| json: { | |||
| value: [], | |||
| type: 'Array<Object>', | |||
| }, | |||
| }, | |||
| }; | |||
| export const initialBingValues = { | |||
| @@ -19,6 +19,7 @@ import { memo } from 'react'; | |||
| import { useForm } from 'react-hook-form'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { buildOutputList } from '../../utils/build-output-list'; | |||
| import { FormWrapper } from '../components/form-wrapper'; | |||
| import { Output } from '../components/output'; | |||
| import { | |||
| DynamicInputVariable, | |||
| @@ -57,12 +58,7 @@ function CodeForm({ node }: INextOperatorForm) { | |||
| return ( | |||
| <Form {...form}> | |||
| <form | |||
| className="p-5 space-y-5" | |||
| onSubmit={(e) => { | |||
| e.preventDefault(); | |||
| }} | |||
| > | |||
| <FormWrapper> | |||
| <DynamicInputVariable | |||
| node={node} | |||
| title={t('flow.input')} | |||
| @@ -159,7 +155,7 @@ function CodeForm({ node }: INextOperatorForm) { | |||
| </FormContainer> | |||
| </div> | |||
| )} | |||
| </form> | |||
| </FormWrapper> | |||
| <div className="p-5"> | |||
| <Output list={buildOutputList(formData.outputs)}></Output> | |||
| </div> | |||
| @@ -0,0 +1,31 @@ | |||
| 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> | |||
| )} | |||
| /> | |||
| ); | |||
| } | |||
| @@ -1,32 +1,122 @@ | |||
| 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 { 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 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 defaultValues = useFormValues(initialGoogleValues, node); | |||
| const form = useForm<z.infer<typeof FormSchema>>({ | |||
| defaultValues, | |||
| resolver: zodResolver(FormSchema), | |||
| }); | |||
| 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> | |||
| ); | |||
| }; | |||
| @@ -23,9 +23,10 @@ import { useFormValues } from '../../hooks/use-form-values'; | |||
| import { useWatchFormChange } from '../../hooks/use-watch-form-change'; | |||
| import { INextOperatorForm } from '../../interface'; | |||
| 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 { TavilyApiKeyField, TavilyFormSchema } from '../tavily-form'; | |||
| import { TavilyFormSchema } from '../tavily-form'; | |||
| const outputList = buildOutputList(initialTavilyExtractValues.outputs); | |||
| @@ -53,7 +54,7 @@ function TavilyExtractForm({ node }: INextOperatorForm) { | |||
| <Form {...form}> | |||
| <FormWrapper> | |||
| <FormContainer> | |||
| <TavilyApiKeyField></TavilyApiKeyField> | |||
| <ApiKeyField></ApiKeyField> | |||
| </FormContainer> | |||
| <FormContainer> | |||
| <FormField | |||
| @@ -13,7 +13,7 @@ import { Switch } from '@/components/ui/switch'; | |||
| import { buildOptions } from '@/utils/form'; | |||
| import { zodResolver } from '@hookform/resolvers/zod'; | |||
| import { memo, useMemo } from 'react'; | |||
| import { useForm, useFormContext } from 'react-hook-form'; | |||
| import { useForm } from 'react-hook-form'; | |||
| import { z } from 'zod'; | |||
| import { | |||
| TavilySearchDepth, | |||
| @@ -21,6 +21,7 @@ import { | |||
| initialTavilyValues, | |||
| } from '../../constant'; | |||
| import { INextOperatorForm } from '../../interface'; | |||
| import { ApiKeyField } from '../components/api-key-field'; | |||
| import { FormWrapper } from '../components/form-wrapper'; | |||
| import { Output, OutputType } from '../components/output'; | |||
| import { QueryVariable } from '../components/query-variable'; | |||
| @@ -28,25 +29,6 @@ import { DynamicDomain } from './dynamic-domain'; | |||
| import { useValues } from './use-values'; | |||
| 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 = { | |||
| api_key: z.string(), | |||
| }; | |||
| @@ -90,7 +72,7 @@ function TavilyForm({ node }: INextOperatorForm) { | |||
| <Form {...form}> | |||
| <FormWrapper> | |||
| <FormContainer> | |||
| <TavilyApiKeyField></TavilyApiKeyField> | |||
| <ApiKeyField></ApiKeyField> | |||
| </FormContainer> | |||
| <FormContainer> | |||
| <QueryVariable></QueryVariable> | |||
| @@ -5,7 +5,6 @@ import DeepLForm from '../deepl-form'; | |||
| import DuckDuckGoForm from '../duckduckgo-form'; | |||
| import EmailForm from '../email-form'; | |||
| import GithubForm from '../github-form'; | |||
| import GoogleForm from '../google-form'; | |||
| import GoogleScholarForm from '../google-scholar-form'; | |||
| import PubMedForm from '../pubmed-form'; | |||
| import WikipediaForm from '../wikipedia-form'; | |||
| @@ -23,7 +22,7 @@ export const ToolFormConfigMap = { | |||
| [Operator.Wikipedia]: WikipediaForm, | |||
| [Operator.PubMed]: PubMedForm, | |||
| [Operator.ArXiv]: ArXivForm, | |||
| [Operator.Google]: GoogleForm, | |||
| [Operator.Google]: TavilyForm, | |||
| [Operator.Bing]: BingForm, | |||
| [Operator.GoogleScholar]: GoogleScholarForm, | |||
| [Operator.DeepL]: DeepLForm, | |||
| @@ -3,8 +3,9 @@ import { Form } from '@/components/ui/form'; | |||
| import { zodResolver } from '@hookform/resolvers/zod'; | |||
| import { useForm } from 'react-hook-form'; | |||
| import { z } from 'zod'; | |||
| import { ApiKeyField } from '../../components/api-key-field'; | |||
| import { FormWrapper } from '../../components/form-wrapper'; | |||
| import { TavilyApiKeyField, TavilyFormSchema } from '../../tavily-form'; | |||
| import { TavilyFormSchema } from '../../tavily-form'; | |||
| import { useValues } from '../use-values'; | |||
| import { useWatchFormChange } from '../use-watch-change'; | |||
| @@ -24,7 +25,7 @@ const TavilyForm = () => { | |||
| <Form {...form}> | |||
| <FormWrapper> | |||
| <FormContainer> | |||
| <TavilyApiKeyField></TavilyApiKeyField> | |||
| <ApiKeyField></ApiKeyField> | |||
| </FormContainer> | |||
| </FormWrapper> | |||
| </Form> | |||
| @@ -16,7 +16,7 @@ export function useAgentToolInitialValues() { | |||
| ...omit(initialValues, 'query'), | |||
| description: '', | |||
| }; | |||
| case (Operator.TavilySearch, Operator.TavilyExtract): | |||
| case (Operator.TavilySearch, Operator.TavilyExtract, Operator.Google): | |||
| return { | |||
| api_key: '', | |||
| }; | |||