From 48ac815f8e7f49f41ab359cb1a19ad3b648b34e3 Mon Sep 17 00:00:00 2001 From: Henry Heng Date: Wed, 29 May 2024 23:39:11 +0100 Subject: [PATCH] Bugfix/Restore Requests Tool (#2513) restore requests tool --- .../nodes/tools/RequestsGet/RequestsGet.ts | 72 ++++++++++++++++ .../nodes/tools/RequestsGet/core.ts | 46 ++++++++++ .../nodes/tools/RequestsGet/requestsget.svg | 6 ++ .../nodes/tools/RequestsPost/RequestsPost.ts | 86 +++++++++++++++++++ .../nodes/tools/RequestsPost/core.ts | 72 ++++++++++++++++ .../nodes/tools/RequestsPost/requestspost.svg | 7 ++ 6 files changed, 289 insertions(+) create mode 100644 packages/components/nodes/tools/RequestsGet/RequestsGet.ts create mode 100644 packages/components/nodes/tools/RequestsGet/core.ts create mode 100644 packages/components/nodes/tools/RequestsGet/requestsget.svg create mode 100644 packages/components/nodes/tools/RequestsPost/RequestsPost.ts create mode 100644 packages/components/nodes/tools/RequestsPost/core.ts create mode 100644 packages/components/nodes/tools/RequestsPost/requestspost.svg diff --git a/packages/components/nodes/tools/RequestsGet/RequestsGet.ts b/packages/components/nodes/tools/RequestsGet/RequestsGet.ts new file mode 100644 index 000000000..91cff5000 --- /dev/null +++ b/packages/components/nodes/tools/RequestsGet/RequestsGet.ts @@ -0,0 +1,72 @@ +import { INode, INodeData, INodeParams } from '../../../src/Interface' +import { getBaseClasses } from '../../../src/utils' +import { desc, RequestParameters, RequestsGetTool } from './core' + +class RequestsGet_Tools implements INode { + label: string + name: string + version: number + description: string + type: string + icon: string + category: string + baseClasses: string[] + inputs: INodeParams[] + + constructor() { + this.label = 'Requests Get' + this.name = 'requestsGet' + this.version = 1.0 + this.type = 'RequestsGet' + this.icon = 'requestsget.svg' + this.category = 'Tools' + this.description = 'Execute HTTP GET requests' + this.baseClasses = [this.type, ...getBaseClasses(RequestsGetTool)] + this.inputs = [ + { + label: 'URL', + name: 'url', + type: 'string', + description: + 'Agent will make call to this exact URL. If not specified, agent will try to figure out itself from AIPlugin if provided', + additionalParams: true, + optional: true + }, + { + label: 'Description', + name: 'description', + type: 'string', + rows: 4, + default: desc, + description: 'Acts like a prompt to tell agent when it should use this tool', + additionalParams: true, + optional: true + }, + { + label: 'Headers', + name: 'headers', + type: 'json', + additionalParams: true, + optional: true + } + ] + } + + async init(nodeData: INodeData): Promise { + const headers = nodeData.inputs?.headers as string + const url = nodeData.inputs?.url as string + const description = nodeData.inputs?.description as string + + const obj: RequestParameters = {} + if (url) obj.url = url + if (description) obj.description = description + if (headers) { + const parsedHeaders = typeof headers === 'object' ? headers : JSON.parse(headers) + obj.headers = parsedHeaders + } + + return new RequestsGetTool(obj) + } +} + +module.exports = { nodeClass: RequestsGet_Tools } diff --git a/packages/components/nodes/tools/RequestsGet/core.ts b/packages/components/nodes/tools/RequestsGet/core.ts new file mode 100644 index 000000000..ea97cdf22 --- /dev/null +++ b/packages/components/nodes/tools/RequestsGet/core.ts @@ -0,0 +1,46 @@ +import fetch from 'node-fetch' +import { Tool } from '@langchain/core/tools' + +export const desc = `A portal to the internet. Use this when you need to get specific content from a website. +Input should be a url (i.e. https://www.google.com). The output will be the text response of the GET request.` + +export interface Headers { + [key: string]: string +} + +export interface RequestParameters { + headers?: Headers + url?: string + description?: string + maxOutputLength?: number +} + +export class RequestsGetTool extends Tool { + name = 'requests_get' + url = '' + description = desc + maxOutputLength = 2000 + headers = {} + + constructor(args?: RequestParameters) { + super() + this.url = args?.url ?? this.url + this.headers = args?.headers ?? this.headers + this.description = args?.description ?? this.description + this.maxOutputLength = args?.maxOutputLength ?? this.maxOutputLength + } + + /** @ignore */ + async _call(input: string) { + const inputUrl = !this.url ? input : this.url + + if (process.env.DEBUG === 'true') console.info(`Making GET API call to ${inputUrl}`) + + const res = await fetch(inputUrl, { + headers: this.headers + }) + + const text = await res.text() + return text.slice(0, this.maxOutputLength) + } +} diff --git a/packages/components/nodes/tools/RequestsGet/requestsget.svg b/packages/components/nodes/tools/RequestsGet/requestsget.svg new file mode 100644 index 000000000..d92c5b51a --- /dev/null +++ b/packages/components/nodes/tools/RequestsGet/requestsget.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/components/nodes/tools/RequestsPost/RequestsPost.ts b/packages/components/nodes/tools/RequestsPost/RequestsPost.ts new file mode 100644 index 000000000..9ff3d1426 --- /dev/null +++ b/packages/components/nodes/tools/RequestsPost/RequestsPost.ts @@ -0,0 +1,86 @@ +import { INode, INodeData, INodeParams } from '../../../src/Interface' +import { getBaseClasses } from '../../../src/utils' +import { RequestParameters, desc, RequestsPostTool } from './core' + +class RequestsPost_Tools implements INode { + label: string + name: string + version: number + description: string + type: string + icon: string + category: string + baseClasses: string[] + inputs: INodeParams[] + + constructor() { + this.label = 'Requests Post' + this.name = 'requestsPost' + this.version = 1.0 + this.type = 'RequestsPost' + this.icon = 'requestspost.svg' + this.category = 'Tools' + this.description = 'Execute HTTP POST requests' + this.baseClasses = [this.type, ...getBaseClasses(RequestsPostTool)] + this.inputs = [ + { + label: 'URL', + name: 'url', + type: 'string', + description: + 'Agent will make call to this exact URL. If not specified, agent will try to figure out itself from AIPlugin if provided', + additionalParams: true, + optional: true + }, + { + label: 'Body', + name: 'body', + type: 'json', + description: + 'JSON body for the POST request. If not specified, agent will try to figure out itself from AIPlugin if provided', + additionalParams: true, + optional: true + }, + { + label: 'Description', + name: 'description', + type: 'string', + rows: 4, + default: desc, + description: 'Acts like a prompt to tell agent when it should use this tool', + additionalParams: true, + optional: true + }, + { + label: 'Headers', + name: 'headers', + type: 'json', + additionalParams: true, + optional: true + } + ] + } + + async init(nodeData: INodeData): Promise { + const headers = nodeData.inputs?.headers as string + const url = nodeData.inputs?.url as string + const description = nodeData.inputs?.description as string + const body = nodeData.inputs?.body as string + + const obj: RequestParameters = {} + if (url) obj.url = url + if (description) obj.description = description + if (headers) { + const parsedHeaders = typeof headers === 'object' ? headers : JSON.parse(headers) + obj.headers = parsedHeaders + } + if (body) { + const parsedBody = typeof body === 'object' ? body : JSON.parse(body) + obj.body = parsedBody + } + + return new RequestsPostTool(obj) + } +} + +module.exports = { nodeClass: RequestsPost_Tools } diff --git a/packages/components/nodes/tools/RequestsPost/core.ts b/packages/components/nodes/tools/RequestsPost/core.ts new file mode 100644 index 000000000..a380f1676 --- /dev/null +++ b/packages/components/nodes/tools/RequestsPost/core.ts @@ -0,0 +1,72 @@ +import { Tool } from '@langchain/core/tools' +import fetch from 'node-fetch' + +export const desc = `Use this when you want to POST to a website. +Input should be a json string with two keys: "url" and "data". +The value of "url" should be a string, and the value of "data" should be a dictionary of +key-value pairs you want to POST to the url as a JSON body. +Be careful to always use double quotes for strings in the json string +The output will be the text response of the POST request.` + +export interface Headers { + [key: string]: string +} + +export interface Body { + [key: string]: any +} + +export interface RequestParameters { + headers?: Headers + body?: Body + url?: string + description?: string + maxOutputLength?: number +} + +export class RequestsPostTool extends Tool { + name = 'requests_post' + url = '' + description = desc + maxOutputLength = Infinity + headers = {} + body = {} + + constructor(args?: RequestParameters) { + super() + this.url = args?.url ?? this.url + this.headers = args?.headers ?? this.headers + this.body = args?.body ?? this.body + this.description = args?.description ?? this.description + this.maxOutputLength = args?.maxOutputLength ?? this.maxOutputLength + } + + /** @ignore */ + async _call(input: string) { + try { + let inputUrl = '' + let inputBody = {} + if (Object.keys(this.body).length || this.url) { + if (this.url) inputUrl = this.url + if (Object.keys(this.body).length) inputBody = this.body + } else { + const { url, data } = JSON.parse(input) + inputUrl = url + inputBody = data + } + + if (process.env.DEBUG === 'true') console.info(`Making POST API call to ${inputUrl} with body ${JSON.stringify(inputBody)}`) + + const res = await fetch(inputUrl, { + method: 'POST', + headers: this.headers, + body: JSON.stringify(inputBody) + }) + + const text = await res.text() + return text.slice(0, this.maxOutputLength) + } catch (error) { + return `${error}` + } + } +} diff --git a/packages/components/nodes/tools/RequestsPost/requestspost.svg b/packages/components/nodes/tools/RequestsPost/requestspost.svg new file mode 100644 index 000000000..477b1baf3 --- /dev/null +++ b/packages/components/nodes/tools/RequestsPost/requestspost.svg @@ -0,0 +1,7 @@ + + + + + + +