diff --git a/tools/aggregatedSearch.ts b/tools/aggregatedSearch.ts new file mode 100644 index 0000000..ff6b81d --- /dev/null +++ b/tools/aggregatedSearch.ts @@ -0,0 +1,48 @@ +import { Tool } from './tool'; +import { z } from 'zod'; +import { createGoogleCustomSearch } from './googleCustomSearch'; +import { createBingCustomSearch } from './bingCustomSearch'; +import { createSerpApiCustomSearch } from './serpApiCustomSearch'; +import { createSerperCustomSearch } from './serperCustomSearch'; + +function createAggregatedSearchTool({ googleApiKey, googleCSEId, bingApiKey, serpApiApiKey, serperApiKey }: { googleApiKey: string, googleCSEId: string, bingApiKey: string, serpApiApiKey: string, serperApiKey: string }) { + const paramsSchema = z.object({ + input: z.string(), + }); + const name = 'aggregatedSearch'; + const description = 'Aggregated search tool. Input should be a search query. Outputs a JSON array of results from multiple search engines.'; + + const execute = async ({ input }: z.infer) => { + try { + // Create instances of each search tool + const [googleCustomSearch] = createGoogleCustomSearch({ apiKey: googleApiKey, googleCSEId }); + const [bingSearch] = createBingCustomSearch({ apiKey: bingApiKey }); + const [serpApiSearch] = createSerpApiCustomSearch({ apiKey: serpApiApiKey }); + const [serperSearch] = createSerperCustomSearch({ apiKey: serperApiKey }); + + // Perform all the searches in parallel + const results = await Promise.all([ + googleCustomSearch({ input }), + bingSearch({ input }), + serpApiSearch({ input }), + serperSearch({ input }), + ]); + + // Parse the results into a combined object + const combinedResults = { + google: JSON.parse(results[0]), + bing: JSON.parse(results[1]), + serpApi: JSON.parse(results[2]), + serper: JSON.parse(results[3]), + }; + + return JSON.stringify(combinedResults); + } catch (error) { + throw new Error(`Error in AggregatedSearchTool: ${error}`); + } + }; + + return new Tool(paramsSchema, name, description, execute).tool; +} + +export { createAggregatedSearchTool }; diff --git a/tools/bingCustomSearch.ts b/tools/bingCustomSearch.ts new file mode 100644 index 0000000..9e76b3c --- /dev/null +++ b/tools/bingCustomSearch.ts @@ -0,0 +1,45 @@ +import { Tool } from './tool'; +import { z } from 'zod'; + +function createBingCustomSearch({ apiKey }: { apiKey: string }) { + if (!apiKey) { + throw new Error('Bing API key must be set.'); + } + + const paramsSchema = z.object({ + input: z.string(), + }); + const name = 'bingCustomSearch'; + const description = 'Bing search engine. Input should be a search query. Outputs a JSON array of results.'; + + const execute = async ({ input }: z.infer) => { + try { + const res = await fetch( + `https://api.bing.microsoft.com/v7.0/search?q=${encodeURIComponent(input)}`, + { + headers: { "Ocp-Apim-Subscription-Key": apiKey } + } + ); + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`); + } + + const data = await res.json(); + + const results = + data.webPages?.value.map((item: any) => ({ + title: item.name, + link: item.url, + snippet: item.snippet, + })) ?? []; + return JSON.stringify(results); + } catch (error) { + throw new Error(`Error in BingCustomSearch: ${error}`); + } + }; + + return new Tool(paramsSchema, name, description, execute).tool; +} + +export { createBingCustomSearch }; diff --git a/tools/serpApiCustomSearch.ts b/tools/serpApiCustomSearch.ts new file mode 100644 index 0000000..4b219b7 --- /dev/null +++ b/tools/serpApiCustomSearch.ts @@ -0,0 +1,42 @@ +import { Tool } from './tool'; +import { z } from 'zod'; + +function createSerpApiCustomSearch({ apiKey }: { apiKey: string }) { + if (!apiKey) { + throw new Error('SerpApi key must be set.'); + } + + const paramsSchema = z.object({ + input: z.string(), + }); + const name = 'serpApiCustomSearch'; + const description = 'SerpApi search engine. Input should be a search query. Outputs a JSON array of results.'; + + const execute = async ({ input }: z.infer) => { + try { + const res = await fetch( + `https://serpapi.com/search?api_key=${apiKey}&engine=google&q=${encodeURIComponent(input)}&google_domain=google.com` + ); + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`); + } + + const data = await res.json(); + + const results = + data.organic_results?.map((item: any) => ({ + title: item.title, + link: item.link, + snippet: item.snippet, + })) ?? []; + return JSON.stringify(results); + } catch (error) { + throw new Error(`Error in SerpApiCustomSearch: ${error}`); + } + }; + + return new Tool(paramsSchema, name, description, execute).tool; +} + +export { createSerpApiCustomSearch }; diff --git a/tools/serperCustomSearch.ts b/tools/serperCustomSearch.ts new file mode 100644 index 0000000..18d1a14 --- /dev/null +++ b/tools/serperCustomSearch.ts @@ -0,0 +1,50 @@ +import { Tool } from './tool'; +import { z } from 'zod'; + +function createSerperCustomSearch({ apiKey }: { apiKey: string }) { + if (!apiKey) { + throw new Error('Serper key must be set.'); + } + + const paramsSchema = z.object({ + input: z.string(), + }); + const name = 'serperCustomSearch'; + const description = 'Serper search engine. Input should be a search query. Outputs a JSON array of results.'; + + const execute = async ({ input }: z.infer) => { + try { + const res = await fetch( + 'https://google.serper.dev/search', + { + method: 'POST', + headers: { + 'X-API-KEY': apiKey, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ q: input, gl: "cn", hl: "zh-cn" }) + } + ); + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`); + } + + const data = await res.json(); + + const results = + data.organic?.map((item: any) => ({ + title: item.title, + link: item.link, + snippet: item.snippet, + })) ?? []; + return JSON.stringify(results); + } catch (error) { + throw new Error(`Error in SerperCustomSearch: ${error}`); + } + }; + + return new Tool(paramsSchema, name, description, execute).tool; +} + +export { createSerperCustomSearch };