Feat: Add Tavily operator #3221 (#8400)

### What problem does this PR solve?

Feat: Add Tavily operator #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-06-23 09:51:09 +08:00
committed by GitHub
parent 887651e5fa
commit db9e91152d
14 changed files with 226 additions and 93 deletions

View File

@ -13,9 +13,9 @@ import GoogleForm from '../google-form';
import GoogleScholarForm from '../google-scholar-form';
import PubMedForm from '../pubmed-form';
import RetrievalForm from '../retrieval-form/next';
import TavilyForm from '../tavily-form';
import WikipediaForm from '../wikipedia-form';
import YahooFinanceForm from '../yahoo-finance-form';
import TavilyForm from './tavily-form';
export const ToolFormConfigMap = {
[Operator.Retrieval]: RetrievalForm,

View File

@ -0,0 +1,60 @@
import { FormContainer } from '@/components/form-container';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { useValues } from '../use-values';
import { useWatchFormChange } from '../use-watch-change';
const TavilyForm = () => {
const values = useValues();
const FormSchema = z.object({
api_key: z.string(),
});
const form = useForm<z.infer<typeof FormSchema>>({
defaultValues: values,
resolver: zodResolver(FormSchema),
});
useWatchFormChange(form);
return (
<Form {...form}>
<form
className="space-y-5 px-5 "
autoComplete="off"
onSubmit={(e) => {
e.preventDefault();
}}
>
<FormContainer>
<FormField
control={form.control}
name="api_key"
render={({ field }) => (
<FormItem>
<FormLabel>Api Key</FormLabel>
<FormControl>
<Input type="password" {...field}></Input>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</FormContainer>
</form>
</Form>
);
};
export default TavilyForm;

View File

@ -0,0 +1,43 @@
import { isEmpty } from 'lodash';
import { useMemo } from 'react';
import useGraphStore from '../../store';
import { getAgentNodeTools } from '../../utils';
export enum SearchDepth {
Basic = 'basic',
Advanced = 'advanced',
}
export enum Topic {
News = 'news',
General = 'general',
}
export const defaultValues = {
api_key: '',
};
export function useValues() {
const { clickedToolId, clickedNodeId, findUpstreamNodeById } = useGraphStore(
(state) => state,
);
const values = useMemo(() => {
const agentNode = findUpstreamNodeById(clickedNodeId);
const tools = getAgentNodeTools(agentNode);
const formData = tools.find(
(x) => x.component_name === clickedToolId,
)?.params;
if (isEmpty(formData)) {
return defaultValues;
}
return {
...formData,
};
}, [clickedNodeId, clickedToolId, findUpstreamNodeById]);
return values;
}

View File

@ -0,0 +1,39 @@
import { useEffect } from 'react';
import { UseFormReturn, useWatch } from 'react-hook-form';
import useGraphStore from '../../store';
import { getAgentNodeTools } from '../../utils';
export function useWatchFormChange(form?: UseFormReturn<any>) {
let values = useWatch({ control: form?.control });
const { clickedToolId, clickedNodeId, findUpstreamNodeById, updateNodeForm } =
useGraphStore((state) => state);
useEffect(() => {
const agentNode = findUpstreamNodeById(clickedNodeId);
// Manually triggered form updates are synchronized to the canvas
if (agentNode && form?.formState.isDirty) {
const agentNodeId = agentNode?.id;
const tools = getAgentNodeTools(agentNode);
values = form?.getValues();
const nextTools = tools.map((x) => {
if (x.component_name === clickedToolId) {
return {
...x,
params: {
...values,
},
};
}
return x;
});
const nextValues = {
...(agentNode?.data?.form ?? {}),
tools: nextTools,
};
updateNodeForm(agentNodeId, nextValues);
}
}, [form?.formState.isDirty, updateNodeForm, values]);
}