Skip to content

Commit b5b929e

Browse files
authored
Feat/Exa Search Tool (FlowiseAI#2524)
* add exa search tool * add exa svg
1 parent 7706b34 commit b5b929e

File tree

5 files changed

+287
-0
lines changed

5 files changed

+287
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { INodeParams, INodeCredential } from '../src/Interface'
2+
3+
class ExaSearchApi implements INodeCredential {
4+
label: string
5+
name: string
6+
version: number
7+
description: string
8+
inputs: INodeParams[]
9+
10+
constructor() {
11+
this.label = 'Exa Search API'
12+
this.name = 'exaSearchApi'
13+
this.version = 1.0
14+
this.description =
15+
'Refer to <a target="_blank" href="https://docs.exa.ai/reference/getting-started#getting-access">official guide</a> on how to get an API Key from Exa'
16+
this.inputs = [
17+
{
18+
label: 'ExaSearch Api Key',
19+
name: 'exaSearchApiKey',
20+
type: 'password'
21+
}
22+
]
23+
}
24+
}
25+
26+
module.exports = { credClass: ExaSearchApi }
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
import { ExaSearchResults } from '@langchain/exa'
2+
import Exa from 'exa-js'
3+
import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
4+
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
5+
6+
const DESC = `A wrapper around Exa Search. Input should be an Exa-optimized query. Output is a JSON array of the query results`
7+
8+
class ExaSearch_Tools implements INode {
9+
label: string
10+
name: string
11+
version: number
12+
description: string
13+
type: string
14+
icon: string
15+
category: string
16+
baseClasses: string[]
17+
credential: INodeParams
18+
inputs: INodeParams[]
19+
20+
constructor() {
21+
this.label = 'Exa Search'
22+
this.name = 'exaSearch'
23+
this.version = 1.0
24+
this.type = 'ExaSearch'
25+
this.icon = 'exa.svg'
26+
this.category = 'Tools'
27+
this.description = 'Wrapper around Exa Search API - search engine fully designed for use by LLMs'
28+
this.inputs = [
29+
{
30+
label: 'Tool Description',
31+
name: 'description',
32+
type: 'string',
33+
description: 'Description of what the tool does. This is for LLM to determine when to use this tool.',
34+
rows: 4,
35+
additionalParams: true,
36+
default: DESC
37+
},
38+
{
39+
label: 'Num of Results',
40+
name: 'numResults',
41+
type: 'number',
42+
optional: true,
43+
step: 1,
44+
additionalParams: true,
45+
description: 'Number of search results to return. Default 10. Max 10 for basic plans. Up to thousands for custom plans.'
46+
},
47+
{
48+
label: 'Search Type',
49+
name: 'type',
50+
type: 'options',
51+
options: [
52+
{
53+
label: 'keyword',
54+
name: 'keyword'
55+
},
56+
{
57+
label: 'neural',
58+
name: 'neural'
59+
},
60+
{
61+
label: 'magic',
62+
name: 'magic',
63+
description: 'decides between keyword and neural'
64+
}
65+
],
66+
optional: true,
67+
additionalParams: true
68+
},
69+
{
70+
label: 'Use Auto Prompt',
71+
name: 'useAutoprompt',
72+
type: 'boolean',
73+
optional: true,
74+
additionalParams: true,
75+
description: 'If true, your query will be converted to a Exa query. Default false.'
76+
},
77+
{
78+
label: 'Category (Beta)',
79+
name: 'category',
80+
type: 'options',
81+
description:
82+
'A data category to focus on, with higher comprehensivity and data cleanliness. Categories right now include company, research paper, news, github, tweet, movie, song, personal site, and pdf',
83+
options: [
84+
{
85+
label: 'company',
86+
name: 'company'
87+
},
88+
{
89+
label: 'research paper',
90+
name: 'research paper'
91+
},
92+
{
93+
label: 'news',
94+
name: 'news'
95+
},
96+
{
97+
label: 'github',
98+
name: 'github'
99+
},
100+
{
101+
label: 'tweet',
102+
name: 'tweet'
103+
},
104+
{
105+
label: 'movie',
106+
name: 'movie'
107+
},
108+
{
109+
label: 'song',
110+
name: 'song'
111+
},
112+
{
113+
label: 'pdf',
114+
name: 'pdf'
115+
},
116+
{
117+
label: 'personal site',
118+
name: 'personal site'
119+
}
120+
],
121+
optional: true,
122+
additionalParams: true
123+
},
124+
{
125+
label: 'Include Domains',
126+
name: 'includeDomains',
127+
type: 'string',
128+
rows: 4,
129+
optional: true,
130+
additionalParams: true,
131+
description:
132+
'List of domains to include in the search, separated by comma. If specified, results will only come from these domains.'
133+
},
134+
{
135+
label: 'Exclude Domains',
136+
name: 'excludeDomains',
137+
type: 'string',
138+
rows: 4,
139+
optional: true,
140+
additionalParams: true,
141+
description:
142+
'List of domains to exclude in the search, separated by comma. If specified, results will not include any from these domains.'
143+
},
144+
{
145+
label: 'Start Crawl Date',
146+
name: 'startCrawlDate',
147+
type: 'string',
148+
optional: true,
149+
additionalParams: true,
150+
placeholder: '2023-01-01T00:00:00.000Z',
151+
description:
152+
'Crawl date refers to the date that Exa discovered a link. Results will include links that were crawled after this date. Must be specified in ISO 8601 format.'
153+
},
154+
{
155+
label: 'End Crawl Date',
156+
name: 'endCrawlDate',
157+
type: 'string',
158+
optional: true,
159+
additionalParams: true,
160+
placeholder: '2023-12-31T00:00:00.000Z',
161+
description:
162+
'Crawl date refers to the date that Exa discovered a link. Results will include links that were crawled before this date. Must be specified in ISO 8601 format.'
163+
},
164+
{
165+
label: 'Start Published Date',
166+
name: 'startPublishedDate',
167+
type: 'string',
168+
optional: true,
169+
additionalParams: true,
170+
placeholder: '2023-01-01T00:00:00.000Z',
171+
description: 'Only links with a published date after this will be returned. Must be specified in ISO 8601 format.'
172+
},
173+
{
174+
label: 'End Published Date',
175+
name: 'endPublishedDate',
176+
type: 'string',
177+
optional: true,
178+
additionalParams: true,
179+
placeholder: '2023-12-31T00:00:00.000Z',
180+
description: 'Only links with a published date before this will be returned. Must be specified in ISO 8601 format.'
181+
}
182+
]
183+
this.credential = {
184+
label: 'Connect Credential',
185+
name: 'credential',
186+
type: 'credential',
187+
credentialNames: ['exaSearchApi']
188+
}
189+
this.baseClasses = [this.type, ...getBaseClasses(ExaSearchResults)]
190+
}
191+
192+
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
193+
const description = nodeData.inputs?.description as string
194+
const numResults = nodeData.inputs?.numResults as string
195+
const type = nodeData.inputs?.type as string
196+
const useAutoprompt = nodeData.inputs?.useAutoprompt as boolean
197+
const category = nodeData.inputs?.category as string
198+
const includeDomains = nodeData.inputs?.includeDomains as string
199+
const excludeDomains = nodeData.inputs?.excludeDomains as string
200+
const startCrawlDate = nodeData.inputs?.startCrawlDate as string
201+
const endCrawlDate = nodeData.inputs?.endCrawlDate as string
202+
const startPublishedDate = nodeData.inputs?.startPublishedDate as string
203+
const endPublishedDate = nodeData.inputs?.endPublishedDate as string
204+
205+
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
206+
const exaSearchApiKey = getCredentialParam('exaSearchApiKey', credentialData, nodeData)
207+
208+
const tool = new ExaSearchResults({
209+
client: new Exa(exaSearchApiKey),
210+
searchArgs: {
211+
numResults: numResults ? parseFloat(numResults) : undefined,
212+
type: type || undefined,
213+
useAutoprompt: useAutoprompt || undefined,
214+
category: category || undefined,
215+
includeDomains: includeDomains ? includeDomains.split(',') : undefined,
216+
excludeDomains: excludeDomains ? excludeDomains.split(',') : undefined,
217+
startCrawlDate: startCrawlDate || undefined,
218+
endCrawlDate: endCrawlDate || undefined,
219+
startPublishedDate: startPublishedDate || undefined,
220+
endPublishedDate: endPublishedDate || undefined
221+
}
222+
})
223+
224+
if (description) tool.description = description
225+
226+
return tool
227+
}
228+
}
229+
230+
module.exports = { nodeClass: ExaSearch_Tools }
Lines changed: 3 additions & 0 deletions
Loading

packages/components/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"@langchain/cohere": "^0.0.7",
3939
"@langchain/community": "^0.0.43",
4040
"@langchain/core": "^0.1.63",
41+
"@langchain/exa": "^0.0.5",
4142
"@langchain/google-genai": "^0.0.10",
4243
"@langchain/google-vertexai": "^0.0.5",
4344
"@langchain/groq": "^0.0.8",
@@ -68,6 +69,7 @@
6869
"css-what": "^6.1.0",
6970
"d3-dsv": "2",
7071
"dotenv": "^16.0.0",
72+
"exa-js": "^1.0.12",
7173
"express": "^4.17.3",
7274
"faiss-node": "^0.5.1",
7375
"fast-json-patch": "^3.1.1",

pnpm-lock.yaml

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)