Added tavily search/extract blocks/tools

This commit is contained in:
Waleed Latif
2025-02-04 20:19:51 -08:00
parent 7e3d7bd8de
commit 13ed31740d
7 changed files with 325 additions and 5 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
import { BlockConfig } from '../types'
import { SearchIcon } from '@/components/icons'
import { SerperIcon } from '@/components/icons'
import { SearchResponse } from '@/tools/serper/search'
export const SerperBlock: BlockConfig<SearchResponse> = {
@@ -8,7 +8,7 @@ export const SerperBlock: BlockConfig<SearchResponse> = {
title: 'Web Search',
description: 'Search the web',
bgColor: '#4285F4', // Google blue
icon: SearchIcon,
icon: SerperIcon,
category: 'tools',
},
tools: {
+112
View File
@@ -0,0 +1,112 @@
import { BlockConfig } from '../types'
import { TavilyIcon } from '@/components/icons'
import { SearchResponse } from '@/tools/tavily/search'
import { ExtractResponse } from '@/tools/tavily/extract'
export const TavilySearchBlock: BlockConfig<SearchResponse> = {
type: 'tavily_search',
toolbar: {
title: 'Tavily Search',
description: 'Search the web using Tavily AI',
bgColor: '#0066FF',
icon: TavilyIcon,
category: 'tools',
},
tools: {
access: ['tavily_search']
},
workflow: {
inputs: {
query: { type: 'string', required: true },
apiKey: { type: 'string', required: true },
max_results: { type: 'number', required: false }
},
outputs: {
response: {
type: {
query: 'string',
results: 'json',
response_time: 'number'
}
}
},
subBlocks: [
{
id: 'query',
title: 'Search Query',
type: 'short-input',
layout: 'full',
placeholder: 'Enter your search query...'
},
{
id: 'max_results',
title: 'Max Results',
type: 'dropdown',
layout: 'half',
options: ['5', '10', '15', '20']
},
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
layout: 'full',
placeholder: 'Enter your Tavily API key',
password: true
}
]
}
}
export const TavilyExtractBlock: BlockConfig<ExtractResponse> = {
type: 'tavily_extract',
toolbar: {
title: 'Tavily Extract',
description: 'Scrape website content',
bgColor: '#0066FF',
icon: TavilyIcon,
category: 'tools',
},
tools: {
access: ['tavily_extract']
},
workflow: {
inputs: {
urls: { type: 'string', required: true },
apiKey: { type: 'string', required: true },
extract_depth: { type: 'string', required: false }
},
outputs: {
response: {
type: {
results: 'json',
failed_results: 'any',
response_time: 'number'
}
}
},
subBlocks: [
{
id: 'urls',
title: 'URL',
type: 'short-input',
layout: 'full',
placeholder: 'Enter URL to extract content from...'
},
{
id: 'extract_depth',
title: 'Extract Depth',
type: 'dropdown',
layout: 'half',
options: ['basic', 'advanced']
},
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
layout: 'full',
placeholder: 'Enter your Tavily API key',
password: true
}
]
}
}
+7 -2
View File
@@ -12,6 +12,7 @@ import { SlackMessageBlock } from './blocks/slack'
import { GitHubBlock } from './blocks/github'
import { ConditionBlock } from './blocks/condition'
import { SerperBlock } from './blocks/serper'
import { TavilySearchBlock, TavilyExtractBlock } from './blocks/tavily'
// Export blocks for ease of use
export {
@@ -25,7 +26,9 @@ export {
SlackMessageBlock,
GitHubBlock,
ConditionBlock,
SerperBlock
SerperBlock,
TavilySearchBlock,
TavilyExtractBlock
}
// Registry of all block configurations
@@ -40,7 +43,9 @@ const blocks: Record<string, BlockConfig> = {
slack_message: SlackMessageBlock,
github_repo_info: GitHubBlock,
condition: ConditionBlock,
serper_search: SerperBlock
serper_search: SerperBlock,
tavily_search: TavilySearchBlock,
tavily_extract: TavilyExtractBlock
}
// Build a reverse mapping of tools to block types
+14
View File
@@ -1016,3 +1016,17 @@ export const GithubIcon = (props: SVGProps<SVGSVGElement>) => (
/>
</svg>
)
export const SerperIcon = (props: SVGProps<SVGSVGElement>) => (
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
<path d="M12 24c6.627 0 12-5.373 12-12S18.627 0 12 0 0 5.373 0 12s5.373 12 12 12zm0-22.5c5.802 0 10.5 4.698 10.5 10.5s-4.698 10.5-10.5 10.5S1.5 17.802 1.5 12 6.198 1.5 12 1.5z" fill="currentColor" />
<path d="M12 24c6.627 0 12-5.373 12-12S18.627 0 12 0 0 5.373 0 12s5.373 12 12 12zm0-22.5c5.802 0 10.5 4.698 10.5 10.5s-4.698 10.5-10.5 10.5S1.5 17.802 1.5 12 6.198 1.5 12 1.5z" fill="currentColor" />
</svg>
)
export const TavilyIcon = (props: SVGProps<SVGSVGElement>) => (
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
<path d="M12 24c6.627 0 12-5.373 12-12S18.627 0 12 0 0 5.373 0 12s5.373 12 12 12zm0-22.5c5.802 0 10.5 4.698 10.5 10.5s-4.698 10.5-10.5 10.5S1.5 17.802 1.5 12 6.198 1.5 12 1.5z" fill="currentColor" />
<path d="M12 24c6.627 0 12-5.373 12-12S18.627 0 12 0 0 5.373 0 12s5.373 12 12 12zm0-22.5c5.802 0 10.5 4.698 10.5 10.5s-4.698 10.5-10.5 10.5S1.5 17.802 1.5 12 6.198 1.5 12 1.5z" fill="currentColor" />
</svg>
)
+5 -1
View File
@@ -15,6 +15,8 @@ import { readUrlTool } from './jina/reader'
import { slackMessageTool } from './slack/message'
import { repoInfoTool } from './github/repo'
import { searchTool as serperSearch } from './serper/search'
import { searchTool as tavilySearch } from './tavily/search'
import { extractTool as tavilyExtract } from './tavily/extract'
// Registry of all available tools
export const tools: Record<string, ToolConfig> = {
@@ -43,7 +45,9 @@ export const tools: Record<string, ToolConfig> = {
// GitHub Tools
'github_repoinfo': repoInfoTool,
// Search Tools
'serper_search': serperSearch
'serper_search': serperSearch,
'tavily_search': tavilySearch,
'tavily_extract': tavilyExtract
}
// Get a tool by its ID
+90
View File
@@ -0,0 +1,90 @@
import { ToolConfig, ToolResponse } from '../types'
interface ExtractParams {
urls: string | string[]
apiKey: string
extract_depth?: 'basic' | 'advanced'
}
interface ExtractResult {
url: string
raw_content: string
}
export interface ExtractResponse extends ToolResponse {
output: {
results: ExtractResult[]
failed_results?: Array<{
url: string
error: string
}>
response_time: number
}
}
export const extractTool: ToolConfig<ExtractParams, ExtractResponse> = {
id: 'tavily.extract',
name: 'Tavily Extract',
description: 'Extract web page content from URLs using Tavily Extract',
version: '1.0.0',
params: {
urls: {
type: 'string',
required: true,
description: 'URL or array of URLs to extract content from'
},
apiKey: {
type: 'string',
required: true,
description: 'Tavily API Key',
requiredForToolCall: true
},
extract_depth: {
type: 'string',
required: false,
description: 'The depth of extraction (basic=1 credit/5 URLs, advanced=2 credits/5 URLs)'
}
},
request: {
url: 'https://api.tavily.com/extract',
method: 'POST',
headers: (params) => ({
'Authorization': `Bearer ${params.apiKey}`,
'Content-Type': 'application/json'
}),
body: (params) => {
const body: Record<string, any> = {
urls: typeof params.urls === 'string' ? [params.urls] : params.urls
}
if (params.extract_depth) body.extract_depth = params.extract_depth
return body
}
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.message || 'Failed to extract content')
}
return {
success: true,
output: {
results: data.results || [],
...(data.failed_results && { failed_results: data.failed_results }),
response_time: data.response_time
}
}
},
transformError: (error) => {
return error instanceof Error
? error.message
: 'An error occurred while extracting content'
}
}
+95
View File
@@ -0,0 +1,95 @@
import { ToolConfig, ToolResponse } from '../types'
interface SearchParams {
query: string
apiKey: string
max_results?: number
}
interface SearchResult {
title: string
url: string
snippet: string
raw_content?: string
}
export interface SearchResponse extends ToolResponse {
output: {
query: string
results: SearchResult[]
response_time: number
}
}
export const searchTool: ToolConfig<SearchParams, SearchResponse> = {
id: 'tavily_search',
name: 'Tavily Search',
description: 'Search the web using Tavily AI Search API',
version: '1.0.0',
params: {
query: {
type: 'string',
required: true,
description: 'The search query to execute'
},
max_results: {
type: 'number',
required: false,
description: 'Maximum number of results (1-20)'
},
apiKey: {
type: 'string',
required: true,
requiredForToolCall: true,
description: 'Tavily API Key'
}
},
request: {
url: 'https://api.tavily.com/search',
method: 'POST',
headers: (params) => ({
'Authorization': `Bearer ${params.apiKey}`,
'Content-Type': 'application/json'
}),
body: (params) => {
const body: Record<string, any> = {
query: params.query
}
// Only include optional parameters if explicitly set
if (params.max_results) body.max_results = params.max_results
return body
}
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.message || 'Failed to perform search')
}
return {
success: true,
output: {
query: data.query,
results: data.results.map((result: any) => ({
title: result.title,
url: result.url,
snippet: result.snippet,
...(result.raw_content && { raw_content: result.raw_content })
})),
response_time: data.response_time
}
}
},
transformError: (error) => {
return error instanceof Error
? error.message
: 'An error occurred while performing the search'
}
}