From 2b8510af7f92e54b526a06d1bde9a17ec2c98839 Mon Sep 17 00:00:00 2001 From: Arvind Chavan Date: Sat, 26 Oct 2024 14:58:05 +0530 Subject: [PATCH] Add OpenAPI tool to framework Related to #105 Add support for `apiKey` and `http_proxy_url` parameters to various tools. * **DuckDuckGoSearchTool**: - Add `apiKey` and `http_proxy_url` parameters to `DuckDuckGoSearchToolOptions`. - Update `_run` method to use `http_proxy_url` for all requests. - Update `_run` method to include `apiKey` in headers if provided. * **GoogleSearchTool**: - Add `http_proxy_url` parameter to `GoogleSearchToolOptions`. - Update `_run` method to use `http_proxy_url` for all requests. * **OpenMeteoTool**: - Add `http_proxy_url` parameter to `ToolOptions`. - Update `_run` method to use `http_proxy_url` for all requests. - Update `_geocode` method to use `http_proxy_url` for all requests. --- src/tools/search/duckDuckGoSearch.ts | 19 ++++++++++++++++-- src/tools/search/googleSearch.ts | 29 ++++++++++++++++------------ src/tools/weather/openMeteo.ts | 28 ++++++++++++++++++++------- 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/src/tools/search/duckDuckGoSearch.ts b/src/tools/search/duckDuckGoSearch.ts index d1743c30..56722e9c 100644 --- a/src/tools/search/duckDuckGoSearch.ts +++ b/src/tools/search/duckDuckGoSearch.ts @@ -28,6 +28,7 @@ import { HeaderGenerator } from "header-generator"; import type { NeedleOptions } from "needle"; import { z } from "zod"; import { Cache } from "@/cache/decoratorCache.js"; +import { HttpsProxyAgent } from "https-proxy-agent"; export { SafeSearchType as DuckDuckGoSearchToolSearchType }; @@ -36,6 +37,8 @@ export interface DuckDuckGoSearchToolOptions extends SearchToolOptions { throttle?: ThrottleOptions | false; httpClientOptions?: NeedleOptions; maxResultsPerPage: number; + apiKey?: string; + http_proxy_url?: string; } export interface DuckDuckGoSearchToolRunOptions extends SearchToolRunOptions { @@ -121,6 +124,19 @@ export class DuckDuckGoSearchTool extends Tool< ) { const headers = new HeaderGenerator().getHeaders(); + if (this.options.apiKey) { + headers["Authorization"] = `Bearer ${this.options.apiKey}`; + } + + const httpClientOptions = { + ...this.options?.httpClientOptions, + ...options?.httpClientOptions, + }; + + if (this.options.http_proxy_url) { + httpClientOptions.agent = new HttpsProxyAgent(this.options.http_proxy_url); + } + const { results } = await this.client( input, { @@ -132,8 +148,7 @@ export class DuckDuckGoSearchTool extends Tool< { headers, user_agent: headers["user-agent"], - ...this.options?.httpClientOptions, - ...options?.httpClientOptions, + ...httpClientOptions, }, ); diff --git a/src/tools/search/googleSearch.ts b/src/tools/search/googleSearch.ts index fbd98136..ff45ba4d 100644 --- a/src/tools/search/googleSearch.ts +++ b/src/tools/search/googleSearch.ts @@ -28,11 +28,13 @@ import { ValueError } from "@/errors.js"; import { ValidationError } from "ajv"; import { parseEnv } from "@/internals/env.js"; import { RunContext } from "@/context.js"; +import { HttpsProxyAgent } from "https-proxy-agent"; export interface GoogleSearchToolOptions extends SearchToolOptions { apiKey?: string; cseId?: string; maxResultsPerPage: number; + http_proxy_url?: string; } type GoogleSearchToolRunOptions = SearchToolRunOptions; @@ -129,18 +131,21 @@ export class GoogleSearchTool extends Tool< run: RunContext, ) { const startIndex = (page - 1) * this.options.maxResultsPerPage + 1; - const response = await this.client.cse.list( - { - cx: this.cseId, - q: input, - num: this.options.maxResultsPerPage, - start: startIndex, - safe: "active", - }, - { - signal: run.signal, - }, - ); + const requestOptions: GoogleSearchAPI.Params$Resource$Cse$List = { + cx: this.cseId, + q: input, + num: this.options.maxResultsPerPage, + start: startIndex, + safe: "active", + }; + + if (this.options.http_proxy_url) { + requestOptions.agent = new HttpsProxyAgent(this.options.http_proxy_url); + } + + const response = await this.client.cse.list(requestOptions, { + signal: run.signal, + }); const results = response.data.items || []; diff --git a/src/tools/weather/openMeteo.ts b/src/tools/weather/openMeteo.ts index c8b9d0d7..f8a6144d 100644 --- a/src/tools/weather/openMeteo.ts +++ b/src/tools/weather/openMeteo.ts @@ -27,8 +27,9 @@ import { createURLParams } from "@/internals/fetcher.js"; import { isNullish, pick, pickBy } from "remeda"; import { Cache } from "@/cache/decoratorCache.js"; import { RunContext } from "@/context.js"; +import { HttpsProxyAgent } from "https-proxy-agent"; -type ToolOptions = { apiKey?: string } & BaseToolOptions; +type ToolOptions = { apiKey?: string; http_proxy_url?: string } & BaseToolOptions; type ToolRunOptions = BaseToolRunOptions; interface Location { @@ -107,7 +108,7 @@ export class OpenMeteoTool extends Tool< _options: BaseToolRunOptions | undefined, run: RunContext, ) { - const { apiKey } = this.options; + const { apiKey, http_proxy_url } = this.options; const prepareParams = async () => { const extractLocation = async (): Promise => { @@ -141,14 +142,20 @@ export class OpenMeteoTool extends Tool< }; const params = await prepareParams(); - const response = await fetch(`https://api.open-meteo.com/v1/forecast?${params}`, { + const fetchOptions: RequestInit = { headers: { ...(apiKey && { Authorization: `Bearer ${apiKey}`, }), }, signal: run.signal, - }); + }; + + if (http_proxy_url) { + fetchOptions.agent = new HttpsProxyAgent(http_proxy_url); + } + + const response = await fetch(`https://api.open-meteo.com/v1/forecast?${params}`, fetchOptions); if (!response.ok) { throw new ToolError("Request to OpenMeteo API has failed!", [ @@ -162,7 +169,7 @@ export class OpenMeteoTool extends Tool< @Cache() protected async _geocode(location: LocationSearch, signal: AbortSignal) { - const { apiKey } = this.options; + const { apiKey, http_proxy_url } = this.options; const params = createURLParams({ name: location.name, @@ -171,14 +178,21 @@ export class OpenMeteoTool extends Tool< format: "json", count: 1, }); - const response = await fetch(`https://geocoding-api.open-meteo.com/v1/search?${params}`, { + + const fetchOptions: RequestInit = { headers: { ...(apiKey && { Authorization: `Bearer ${apiKey}`, }), }, signal, - }); + }; + + if (http_proxy_url) { + fetchOptions.agent = new HttpsProxyAgent(http_proxy_url); + } + + const response = await fetch(`https://geocoding-api.open-meteo.com/v1/search?${params}`, fetchOptions); if (!response.ok) { throw new ToolError(`Failed to GeoCode provided location (${location.name}).`, [ new Error(await response.text()),