@langchain/community
Version:
Third-party integrations for LangChain.js
1 lines • 14.7 kB
Source Map (JSON)
{"version":3,"file":"dataforseo_api_search.cjs","names":["Tool"],"sources":["../../src/tools/dataforseo_api_search.ts"],"sourcesContent":["import { getEnvironmentVariable } from \"@langchain/core/utils/env\";\nimport { Tool } from \"@langchain/core/tools\";\n\n/**\n * @interface DataForSeoApiConfig\n * @description Represents the configuration object used to set up a DataForSeoAPISearch instance.\n */\nexport interface DataForSeoApiConfig {\n /**\n * @property apiLogin\n * @type {string}\n * @description The API login credential for DataForSEO. If not provided, it will be fetched from environment variables.\n */\n apiLogin?: string;\n\n /**\n * @property apiPassword\n * @type {string}\n * @description The API password credential for DataForSEO. If not provided, it will be fetched from environment variables.\n */\n apiPassword?: string;\n\n /**\n * @property params\n * @type {Record<string, string | number | boolean>}\n * @description Additional parameters to customize the API request.\n */\n params?: Record<string, string | number | boolean>;\n\n /**\n * @property useJsonOutput\n * @type {boolean}\n * @description Determines if the output should be in JSON format.\n */\n useJsonOutput?: boolean;\n\n /**\n * @property jsonResultTypes\n * @type {Array<string>}\n * @description Specifies the types of results to include in the output.\n */\n jsonResultTypes?: Array<string>;\n\n /**\n * @property jsonResultFields\n * @type {Array<string>}\n * @description Specifies the fields to include in each result object.\n */\n jsonResultFields?: Array<string>;\n\n /**\n * @property topCount\n * @type {number}\n * @description Specifies the maximum number of results to return.\n */\n topCount?: number;\n}\n\n/**\n * Represents a task in the API response.\n */\ntype Task = {\n id: string;\n status_code: number;\n status_message: string;\n time: string;\n result: Result[];\n};\n\n/**\n * Represents a result in the API response.\n */\ntype Result = {\n keyword: string;\n check_url: string;\n datetime: string;\n spell?: string;\n item_types: string[];\n se_results_count: number;\n items_count: number;\n // oxlint-disable-next-line typescript/no-explicit-any\n items: any[];\n};\n\n/**\n * Represents the API response.\n */\ntype ApiResponse = {\n status_code: number;\n status_message: string;\n tasks: Task[];\n};\n\n/**\n * @class DataForSeoAPISearch\n * @extends {Tool}\n * @description Represents a wrapper class to work with DataForSEO SERP API.\n */\nexport class DataForSeoAPISearch extends Tool {\n static lc_name() {\n return \"DataForSeoAPISearch\";\n }\n\n name = \"dataforseo-api-wrapper\";\n\n description =\n \"A robust Google Search API provided by DataForSeo. This tool is handy when you need information about trending topics or current events.\";\n\n protected apiLogin: string;\n\n protected apiPassword: string;\n\n /**\n * @property defaultParams\n * @type {Record<string, string | number | boolean>}\n * @description These are the default parameters to be used when making an API request.\n */\n protected defaultParams: Record<string, string | number | boolean> = {\n location_name: \"United States\",\n language_code: \"en\",\n depth: 10,\n se_name: \"google\",\n se_type: \"organic\",\n };\n\n protected params: Record<string, string | number | boolean> = {};\n\n protected jsonResultTypes: Array<string> | undefined;\n\n protected jsonResultFields: Array<string> | undefined;\n\n protected topCount: number | undefined;\n\n protected useJsonOutput = false;\n\n /**\n * @constructor\n * @param {DataForSeoApiConfig} config\n * @description Sets up the class, throws an error if the API login/password isn't provided.\n */\n constructor(config: DataForSeoApiConfig = {}) {\n super();\n const apiLogin =\n config.apiLogin ?? getEnvironmentVariable(\"DATAFORSEO_LOGIN\");\n const apiPassword =\n config.apiPassword ?? getEnvironmentVariable(\"DATAFORSEO_PASSWORD\");\n const params = config.params ?? {};\n if (!apiLogin || !apiPassword) {\n throw new Error(\n \"DataForSEO login or password not set. You can set it as DATAFORSEO_LOGIN and DATAFORSEO_PASSWORD in your .env file, or pass it to DataForSeoAPISearch.\"\n );\n }\n this.params = { ...this.defaultParams, ...params };\n this.apiLogin = apiLogin;\n this.apiPassword = apiPassword;\n this.jsonResultTypes = config.jsonResultTypes;\n this.jsonResultFields = config.jsonResultFields;\n this.useJsonOutput = config.useJsonOutput ?? false;\n this.topCount = config.topCount;\n }\n\n /**\n * @method _call\n * @param {string} keyword\n * @returns {Promise<string>}\n * @description Initiates a call to the API and processes the response.\n */\n async _call(keyword: string): Promise<string> {\n return this.useJsonOutput\n ? JSON.stringify(await this.results(keyword))\n : this.processResponse(await this.getResponseJson(keyword));\n }\n\n /**\n * @method results\n * @param {string} keyword\n * @returns {Promise<Array<any>>}\n * @description Fetches the results from the API for the given keyword.\n */\n // oxlint-disable-next-line typescript/no-explicit-any\n async results(keyword: string): Promise<Array<any>> {\n const res = await this.getResponseJson(keyword);\n return this.filterResults(res, this.jsonResultTypes);\n }\n\n /**\n * @method prepareRequest\n * @param {string} keyword\n * @returns {{url: string; headers: HeadersInit; data: BodyInit}}\n * @description Prepares the request details for the API call.\n */\n protected prepareRequest(keyword: string): {\n url: string;\n headers: HeadersInit;\n data: BodyInit;\n } {\n if (this.apiLogin === undefined || this.apiPassword === undefined) {\n throw new Error(\"api_login or api_password is not provided\");\n }\n\n const credentials = Buffer.from(\n `${this.apiLogin}:${this.apiPassword}`,\n \"utf-8\"\n ).toString(\"base64\");\n const headers = {\n Authorization: `Basic ${credentials}`,\n \"Content-Type\": \"application/json\",\n };\n\n const params = { ...this.params };\n params.keyword ??= keyword;\n const data = [params];\n\n return {\n url: `https://api.dataforseo.com/v3/serp/${params.se_name}/${params.se_type}/live/advanced`,\n headers,\n data: JSON.stringify(data),\n };\n }\n\n /**\n * @method getResponseJson\n * @param {string} keyword\n * @returns {Promise<ApiResponse>}\n * @description Executes a POST request to the provided URL and returns a parsed JSON response.\n */\n protected async getResponseJson(keyword: string): Promise<ApiResponse> {\n const requestDetails = this.prepareRequest(keyword);\n const response = await fetch(requestDetails.url, {\n method: \"POST\",\n headers: requestDetails.headers,\n body: requestDetails.data,\n });\n\n if (!response.ok) {\n throw new Error(\n `Got ${response.status} error from DataForSEO: ${response.statusText}`\n );\n }\n\n const result: ApiResponse = await response.json();\n return this.checkResponse(result);\n }\n\n /**\n * @method checkResponse\n * @param {ApiResponse} response\n * @returns {ApiResponse}\n * @description Checks the response status code.\n */\n private checkResponse(response: ApiResponse): ApiResponse {\n if (response.status_code !== 20000) {\n throw new Error(\n `Got error from DataForSEO SERP API: ${response.status_message}`\n );\n }\n for (const task of response.tasks) {\n if (task.status_code !== 20000) {\n throw new Error(\n `Got error from DataForSEO SERP API: ${task.status_message}`\n );\n }\n }\n return response;\n }\n\n /* oxlint-disable typescript/no-explicit-any */\n /**\n * @method filterResults\n * @param {ApiResponse} res\n * @param {Array<string> | undefined} types\n * @returns {Array<any>}\n * @description Filters the results based on the specified result types.\n */\n private filterResults(\n res: ApiResponse,\n types: Array<string> | undefined\n ): Array<any> {\n const output: Array<any> = [];\n for (const task of res.tasks || []) {\n for (const result of task.result || []) {\n for (const item of result.items || []) {\n if (\n types === undefined ||\n types.length === 0 ||\n types.includes(item.type)\n ) {\n const newItem = this.cleanupUnnecessaryItems(item);\n if (Object.keys(newItem).length !== 0) {\n output.push(newItem);\n }\n }\n if (this.topCount !== undefined && output.length >= this.topCount) {\n break;\n }\n }\n }\n }\n return output;\n }\n\n /* oxlint-disable typescript/no-explicit-any */\n /**\n * @method cleanupUnnecessaryItems\n * @param {any} d\n * @description Removes unnecessary items from the response.\n */\n private cleanupUnnecessaryItems(d: any): any {\n if (Array.isArray(d)) {\n return d.map((item) => this.cleanupUnnecessaryItems(item));\n }\n\n const toRemove = [\"xpath\", \"position\", \"rectangle\"];\n if (typeof d === \"object\" && d !== null) {\n return Object.keys(d).reduce((newObj: any, key: string) => {\n if (\n (this.jsonResultFields === undefined ||\n this.jsonResultFields.includes(key)) &&\n !toRemove.includes(key)\n ) {\n if (typeof d[key] === \"object\" && d[key] !== null) {\n newObj[key] = this.cleanupUnnecessaryItems(d[key]);\n } else {\n newObj[key] = d[key];\n }\n }\n return newObj;\n }, {});\n }\n\n return d;\n }\n\n /**\n * @method processResponse\n * @param {ApiResponse} res\n * @returns {string}\n * @description Processes the response to extract meaningful data.\n */\n protected processResponse(res: ApiResponse): string {\n let returnValue = \"No good search result found\";\n for (const task of res.tasks || []) {\n for (const result of task.result || []) {\n const { item_types } = result;\n const items = result.items || [];\n if (item_types.includes(\"answer_box\")) {\n returnValue = items.find(\n (item: { type: string; text: string }) => item.type === \"answer_box\"\n ).text;\n } else if (item_types.includes(\"knowledge_graph\")) {\n returnValue = items.find(\n (item: { type: string; description: string }) =>\n item.type === \"knowledge_graph\"\n ).description;\n } else if (item_types.includes(\"featured_snippet\")) {\n returnValue = items.find(\n (item: { type: string; description: string }) =>\n item.type === \"featured_snippet\"\n ).description;\n } else if (item_types.includes(\"shopping\")) {\n returnValue = items.find(\n (item: { type: string; price: string }) => item.type === \"shopping\"\n ).price;\n } else if (item_types.includes(\"organic\")) {\n returnValue = items.find(\n (item: { type: string; description: string }) =>\n item.type === \"organic\"\n ).description;\n }\n if (returnValue) {\n break;\n }\n }\n }\n return returnValue;\n }\n}\n"],"mappings":"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"}