Skip to content

Commit

Permalink
feat: add search tool
Browse files Browse the repository at this point in the history
  • Loading branch information
JohannLai committed Jan 10, 2024
1 parent 30673f1 commit 6b77845
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 0 deletions.
48 changes: 48 additions & 0 deletions tools/aggregatedSearch.ts
Original file line number Diff line number Diff line change
@@ -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<typeof paramsSchema>) => {
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 };
45 changes: 45 additions & 0 deletions tools/bingCustomSearch.ts
Original file line number Diff line number Diff line change
@@ -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<typeof paramsSchema>) => {
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 };
42 changes: 42 additions & 0 deletions tools/serpApiCustomSearch.ts
Original file line number Diff line number Diff line change
@@ -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<typeof paramsSchema>) => {
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 };
50 changes: 50 additions & 0 deletions tools/serperCustomSearch.ts
Original file line number Diff line number Diff line change
@@ -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<typeof paramsSchema>) => {
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 };

0 comments on commit 6b77845

Please sign in to comment.