mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-10 23:48:09 -05:00
feat(tools): added google search and linkup search tools/blocks (#283)
This commit is contained in:
111
sim/blocks/blocks/google.ts
Normal file
111
sim/blocks/blocks/google.ts
Normal 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,
|
||||
},
|
||||
},
|
||||
}
|
||||
65
sim/blocks/blocks/linkup.ts
Normal file
65
sim/blocks/blocks/linkup.ts
Normal 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',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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
3
sim/tools/google/index.ts
Normal file
3
sim/tools/google/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { searchTool } from './search'
|
||||
|
||||
export { searchTool }
|
||||
85
sim/tools/google/search.ts
Normal file
85
sim/tools/google/search.ts
Normal 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
26
sim/tools/google/types.ts
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
3
sim/tools/linkup/index.ts
Normal file
3
sim/tools/linkup/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { searchTool } from './search'
|
||||
|
||||
export const linkupSearchTool = searchTool
|
||||
74
sim/tools/linkup/search.ts
Normal file
74
sim/tools/linkup/search.ts
Normal 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
23
sim/tools/linkup/types.ts
Normal 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
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user