feat(tools): added google search and linkup search tools/blocks (#283)

This commit is contained in:
Waleed Latif
2025-04-19 19:04:48 -07:00
committed by GitHub
parent 0c696887a0
commit 4800397c67
11 changed files with 414 additions and 1 deletions

111
sim/blocks/blocks/google.ts Normal file
View File

@@ -0,0 +1,111 @@
import { GoogleIcon } from '@/components/icons'
import { BlockConfig } from '../types'
import { ToolResponse } from '@/tools/types'
interface GoogleSearchResponse extends ToolResponse {
output: {
items: Array<{
title: string
link: string
snippet: string
displayLink?: string
pagemap?: Record<string, any>
}>
searchInformation: {
totalResults: string
searchTime: number
formattedSearchTime: string
formattedTotalResults: string
}
}
}
export const GoogleSearchBlock: BlockConfig<GoogleSearchResponse> = {
type: 'google_search',
name: 'Google Search',
description: 'Search the web',
longDescription: 'Searches the web using Google\'s Custom Search API, which provides high-quality search results from the entire internet or a specific site defined by a custom search engine ID.',
category: 'tools',
bgColor: '#E0E0E0',
icon: GoogleIcon,
subBlocks: [
{
id: 'query',
title: 'Search Query',
type: 'long-input',
layout: 'full',
placeholder: 'Enter your search query',
},
{
id: 'searchEngineId',
title: 'Custom Search Engine ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter your Custom Search Engine ID',
description: 'Required Custom Search Engine ID',
},
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
layout: 'full',
placeholder: 'Enter your Google API key',
description: 'Required API Key for Google Search',
password: true,
},
{
id: 'num',
title: 'Number of Results',
type: 'short-input',
layout: 'half',
placeholder: '10',
description: 'Number of search results to return (max: 10)',
}
],
tools: {
access: ['google_search'],
config: {
tool: () => 'google_search',
params: (params) => ({
query: params.query,
apiKey: params.apiKey,
searchEngineId: params.searchEngineId,
num: params.num || undefined,
}),
},
},
inputs: {
query: {
type: 'string',
required: true,
description: 'The search query to execute',
},
apiKey: {
type: 'string',
required: true,
description: 'Google API key',
},
searchEngineId: {
type: 'string',
required: true,
description: 'Custom Search Engine ID',
},
num: {
type: 'string',
required: false,
description: 'Number of results to return (default: 10, max: 10)',
}
},
outputs: {
response: {
type: {
items: 'json',
searchInformation: 'json',
} as any,
},
},
}

View File

@@ -0,0 +1,65 @@
import { LinkupIcon } from '@/components/icons'
import { BlockConfig } from '../types'
import { LinkupSearchToolResponse } from '@/tools/linkup/types'
export const LinkupBlock: BlockConfig<LinkupSearchToolResponse> = {
type: 'linkup',
name: 'Linkup',
description: 'Search the web with Linkup',
longDescription: 'Linkup Search allows you to search and retrieve up-to-date information from the web with source attribution.',
category: 'tools',
bgColor: '#EAEADC',
icon: LinkupIcon,
subBlocks: [
{
id: 'q',
title: 'Search Query',
type: 'long-input',
layout: 'full',
placeholder: 'Enter your search query',
},
{
id: 'outputType',
title: 'Output Type',
type: 'dropdown',
layout: 'half',
options: [ { label: 'Answer', id: 'sourcedAnswer' }, { label: 'Search', id: 'searchResults' }],
},
{
id: 'depth',
title: 'Search Depth',
type: 'dropdown',
layout: 'half',
options: [ { label: 'Standard', id: 'standard' }, { label: 'Deep', id: 'deep' }],
},
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
layout: 'full',
placeholder: 'Enter your Linkup API key',
password: true,
},
],
tools: {
access: ['linkup_search'],
},
inputs: {
q: { type: 'string', required: true },
apiKey: { type: 'string', required: true },
depth: { type: 'string', required: true },
outputType: { type: 'string', required: true }
},
outputs: {
response: {
type: {
answer: 'string',
sources: 'json',
},
},
},
}

View File

@@ -16,10 +16,11 @@ import { FirecrawlBlock } from './blocks/firecrawl'
import { FunctionBlock } from './blocks/function'
import { GitHubBlock } from './blocks/github'
import { GmailBlock } from './blocks/gmail'
import { GoogleSearchBlock } from './blocks/google'
// import { GuestyBlock } from './blocks/guesty'
import { ImageGeneratorBlock } from './blocks/image-generator'
import { JinaBlock } from './blocks/jina'
import { LinkupBlock } from './blocks/linkup'
import { MistralParseBlock } from './blocks/mistral-parse'
import { NotionBlock } from './blocks/notion'
import { OpenAIBlock } from './blocks/openai'
@@ -61,7 +62,9 @@ export {
FirecrawlBlock,
// GuestyBlock,
FileBlock,
GoogleSearchBlock,
JinaBlock,
LinkupBlock,
TranslateBlock,
SlackBlock,
GitHubBlock,
@@ -113,10 +116,12 @@ const blocks: Record<string, BlockConfig> = {
gmail: GmailBlock,
google_docs: GoogleDocsBlock,
google_drive: GoogleDriveBlock,
google_search: GoogleSearchBlock,
google_sheets: GoogleSheetsBlock,
// guesty: GuestyBlock,
image_generator: ImageGeneratorBlock,
jina: JinaBlock,
linkup: LinkupBlock,
mem0: Mem0Block,
mistral_parse: MistralParseBlock,
notion: NotionBlock,

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
import { searchTool } from './search'
export { searchTool }

View File

@@ -0,0 +1,85 @@
import { ToolConfig } from '../types'
import { GoogleSearchParams, GoogleSearchResponse } from './types'
export const searchTool: ToolConfig<GoogleSearchParams, GoogleSearchResponse> = {
id: 'google_search',
name: 'Google Search',
description: 'Search the web with the Custom Search API',
version: '1.0.0',
params: {
query: {
type: 'string',
required: true,
description: 'The search query to execute',
},
apiKey: {
type: 'string',
required: true,
description: 'Google API key',
requiredForToolCall: true,
},
searchEngineId: {
type: 'string',
required: true,
description: 'Custom Search Engine ID',
requiredForToolCall: true,
},
num: {
type: 'string', // Treated as string for compatibility with tool interfaces
required: false,
description: 'Number of results to return (default: 10, max: 10)',
}
},
request: {
url: (params: GoogleSearchParams) => {
const baseUrl = 'https://www.googleapis.com/customsearch/v1'
const searchParams = new URLSearchParams()
// Add required parameters
searchParams.append('key', params.apiKey)
searchParams.append('q', params.query)
searchParams.append('cx', params.searchEngineId)
// Add optional parameter
if (params.num) {
searchParams.append('num', params.num.toString())
}
return `${baseUrl}?${searchParams.toString()}`
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.error?.message || 'Failed to perform Google search')
}
const data = await response.json()
return {
success: true,
output: {
items: data.items || [],
searchInformation: data.searchInformation || {
totalResults: '0',
searchTime: 0,
formattedSearchTime: '0',
formattedTotalResults: '0',
},
},
}
},
transformError: (error) => {
return error instanceof Error
? error.message
: 'An error occurred while performing the Google search'
},
}

26
sim/tools/google/types.ts Normal file
View File

@@ -0,0 +1,26 @@
import { ToolResponse } from "../types"
export interface GoogleSearchParams {
query: string
apiKey: string
searchEngineId: string
num?: number | string
}
export interface GoogleSearchResponse extends ToolResponse {
output: {
items: Array<{
title: string
link: string
snippet: string
displayLink?: string
pagemap?: Record<string, any>
}>
searchInformation: {
totalResults: string
searchTime: number
formattedSearchTime: string
formattedTotalResults: string
}
}
}

View File

@@ -0,0 +1,3 @@
import { searchTool } from './search'
export const linkupSearchTool = searchTool

View File

@@ -0,0 +1,74 @@
import { ToolConfig } from '../types'
import { LinkupSearchParams, LinkupSearchResponse, LinkupSearchToolResponse } from './types'
export const searchTool: ToolConfig<LinkupSearchParams, LinkupSearchToolResponse> = {
id: 'linkup_search',
name: 'Linkup Search',
description: 'Search the web for information using Linkup',
version: '1.0.0',
params: {
q: {
type: 'string',
required: true,
description: 'The search query',
},
apiKey: {
type: 'string',
required: true,
description: 'Enter your Linkup API key',
requiredForToolCall: true,
},
depth: {
type: 'string',
required: true,
description: 'Search depth (has to either be "standard" or "deep")',
},
outputType: {
type: 'string',
required: true,
description: 'Type of output to return (has to either be "sourcedAnswer" or "searchResults")',
},
},
request: {
url: 'https://api.linkup.so/v1/search',
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
'Authorization': `Bearer ${params.apiKey}`,
}),
body: (params) => {
const body: Record<string, any> = {
q: params.q,
}
if (params.depth) body.depth = params.depth
if (params.outputType) body.outputType = params.outputType
body.includeImages = false
return body
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Linkup API error: ${response.status} ${errorText}`)
}
const data: LinkupSearchResponse = await response.json()
return {
success: true,
output: {
answer: data.answer,
sources: data.sources,
},
}
},
transformError: (error) => {
return `Error searching with Linkup: ${error.message}`
},
}

23
sim/tools/linkup/types.ts Normal file
View File

@@ -0,0 +1,23 @@
import { ToolResponse } from "../types"
export interface LinkupSource {
name: string
url: string
snippet: string
}
export interface LinkupSearchParams {
q: string
apiKey: string
depth?: 'standard' | 'deep'
outputType?: 'sourcedAnswer' | 'searchResults'
}
export interface LinkupSearchResponse {
answer: string
sources: LinkupSource[]
}
export interface LinkupSearchToolResponse extends ToolResponse {
output: LinkupSearchResponse
}

View File

@@ -11,9 +11,11 @@ import { functionExecuteTool } from './function'
import { githubCommentTool, githubLatestCommitTool, githubPrTool, githubRepoInfoTool } from './github'
import { gmailReadTool, gmailSearchTool, gmailSendTool } from './gmail'
import { guestyGuestTool, guestyReservationTool } from './guesty'
import { searchTool as googleSearchTool } from './google'
import { requestTool as httpRequest } from './http/request'
import { contactsTool as hubspotContacts } from './hubspot/contacts'
import { readUrlTool } from './jina/reader'
import { linkupSearchTool } from './linkup'
import { mem0AddMemoriesTool, mem0SearchMemoriesTool, mem0GetMemoriesTool } from './mem0'
import { mistralParserTool } from './mistral'
import { notionReadTool, notionWriteTool } from './notion'
@@ -51,7 +53,9 @@ export const tools: Record<string, ToolConfig> = {
vision_tool: visionTool,
file_parser: fileParseTool,
firecrawl_scrape: scrapeTool,
google_search: googleSearchTool,
jina_readurl: readUrlTool,
linkup_search: linkupSearchTool,
slack_message: slackMessageTool,
github_repoinfo: githubRepoInfoTool,
github_latest_commit: githubLatestCommitTool,