diff --git a/docs/core_docs/docs/integrations/tools/google_trends.mdx b/docs/core_docs/docs/integrations/tools/google_trends.mdx index 14e833047f06..9c8214b90866 100644 --- a/docs/core_docs/docs/integrations/tools/google_trends.mdx +++ b/docs/core_docs/docs/integrations/tools/google_trends.mdx @@ -6,12 +6,12 @@ import CodeBlock from "@theme/CodeBlock"; # Google Trends Tool -The Google Trends Tool allows your agent to utilize the Google Trends API from Serp API to retrieve and analyze search interest data. +The Google Trends Tool allows your agent to utilize the Google Trends API from Serp API to retrieve and analyze search interest data. This can be useful for understanding trending topics, regional search interest, and historical popularity of search terms. ## Setup -To use this tool, you'll need to configure access to the Google Trends API. The integration relies on the unofficial +To use this tool, you'll need to configure access to the Google Trends API. The integration relies on the unofficial Google Trends library, as Google does not provide a formal API for accessing trends data. Get an API key from [SERPApi](https://serpapi.com/users/sign_in) @@ -34,4 +34,4 @@ import ToolExample from "@examples/tools/google_trends.ts"; ## Related -- Tool [https://serpapi.com/google-trends-api] \ No newline at end of file +- Tool [https://serpapi.com/google-trends-api] diff --git a/examples/src/tools/google_trends.ts b/examples/src/tools/google_trends.ts index af669c993846..2934b9a1a7f7 100644 --- a/examples/src/tools/google_trends.ts +++ b/examples/src/tools/google_trends.ts @@ -1,10 +1,9 @@ import { GoogleTrendsAPI } from "@langchain/community/tools/google_trends"; export async function run() { - - const tool = new GoogleTrendsAPI(); + const tool = new GoogleTrendsAPI(); const res = await tool._call("Monster"); console.log(res); -} \ No newline at end of file +} diff --git a/libs/langchain-community/src/tools/google_trends.ts b/libs/langchain-community/src/tools/google_trends.ts index 2ebc9a940a2c..461dd9594661 100644 --- a/libs/langchain-community/src/tools/google_trends.ts +++ b/libs/langchain-community/src/tools/google_trends.ts @@ -5,113 +5,137 @@ import { Tool } from "@langchain/core/tools"; * Interface for parameters required by GoogleTrendsAPI class. */ export interface GoogleTrendsAPIParams { - apiKey?: string; - } - + apiKey?: string; +} /** - * Tool that queries the Google Trends API + * Tool that queries the Google Trends API. Uses default interest over time. */ export class GoogleTrendsAPI extends Tool { - static lc_name() { - return "GoogleTrendsAPI"; - } - - get lc_secrets(): { [key: string]: string } | undefined { - return { - apiKey: "SERPAPI_API_KEY", - }; - } - - name = "google_trends"; - - protected apiKey: string; + static lc_name() { + return "GoogleTrendsAPI"; + } - description = `A wrapper around Google Trends API. Useful for analyzing and retrieving trending search data based on keywords, + get lc_secrets(): { [key: string]: string } | undefined { + return { + apiKey: "SERPAPI_API_KEY", + }; + } + + name = "google_trends"; + + protected apiKey: string; + + description = `A wrapper around Google Trends API. Useful for analyzing and retrieving trending search data based on keywords, categories, or regions. Input should be a search query or specific parameters for trends analysis.`; - constructor(fields?: GoogleTrendsAPIParams) { - super(...arguments); - const apiKey = - fields?.apiKey ?? getEnvironmentVariable("SERPAPI_API_KEY"); - if (apiKey === undefined) { - throw new Error( - `Google Trends API key not set. You can set it as "SERPAPI_API_KEY" in your environment variables.` - ); + constructor(fields?: GoogleTrendsAPIParams) { + super(...arguments); + const apiKey = fields?.apiKey ?? getEnvironmentVariable("SERPAPI_API_KEY"); + if (apiKey === undefined) { + throw new Error( + `Google Trends API key not set. You can set it as "SERPAPI_API_KEY" in your environment variables.` + ); + } + this.apiKey = apiKey; + } + + async _call(query: string): Promise { + /** + * Related queries only accepts one at a time, and multiple + * queries at once on interest over time (default) is effectively the same as + * each query one by one. + */ + if (query.split(",").length > 1) { + throw new Error("Please do one query at a time"); + } + const serpapiApiKey = this.apiKey; + const params = new URLSearchParams({ + engine: "google_trends", + api_key: serpapiApiKey, + q: query, + }); + + const res = await fetch( + `https://serpapi.com/search.json?${params.toString()}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, } - this.apiKey = apiKey; + ); + + if (!res.ok) { + throw new Error(`Error fetching data from SerpAPI: ${res.statusText}`); + } + + const clientDict = await res.json(); + const totalResults = clientDict.interest_over_time?.timeline_data ?? []; + + if (totalResults.length === 0) { + return "No good Trend Result was found"; } - async _call(query: string): Promise { - const serpapiApiKey = this.apiKey; - const params = new URLSearchParams({ - engine: "google_trends", - api_key: serpapiApiKey, - q: query, - }); - - const res = await fetch(`https://serpapi.com/search.json?${params.toString()}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - - if (!res.ok) { - throw new Error(`Error fetching data from SerpAPI: ${res.statusText}`); - } - - const clientDict = await res.json(); - const totalResults = clientDict.interest_over_time?.timeline_data ?? []; - - if (totalResults.length === 0) { - return "No good Trend Result was found"; - } - - const startDate = totalResults[0].date.split(" "); - const endDate = totalResults[totalResults.length - 1].date.split(" "); - const values = totalResults.map((result: any) => result.values[0].extracted_value); - const minValue = Math.min(...values); - const maxValue = Math.max(...values); - const avgValue = values.reduce((a: number, b: number) => a + b, 0) / values.length; - const percentageChange = ((values[values.length - 1] - values[0]) / (values[0] || 1)) * 100; - - // const relatedParams = new URLSearchParams({ - // engine: "google_trends", - // api_key: serpapiApiKey, - // data_type: "RELATED_TOPICS", - // q: query, - // }); - - // const relatedRes = await fetch(`https://serpapi.com/search.json?${relatedParams.toString()}`, { - // method: "GET", - // headers: { - // "Content-Type": "application/json", - // }, - // }); - - // if (!relatedRes.ok) { - // throw new Error(`Error fetching related queries from SerpAPI: ${relatedRes.statusText}`); - // } - - // const relatedDict = await relatedRes.json(); - // const rising = relatedDict.related_queries?.rising?.map((result: any) => result.query) ?? []; - // const top = relatedDict.related_queries?.top?.map((result: any) => result.query) ?? []; - - const doc = [ - `Query: ${query}`, - `Date From: ${startDate[0]} ${startDate[1]}, ${startDate[2]}`, - `Date To: ${endDate[0]} ${endDate[1]} ${endDate[2]}`, - `Min Value: ${minValue}`, - `Max Value: ${maxValue}`, - `Average Value: ${avgValue}`, - `Percent Change: ${percentageChange.toFixed(2)}%`, - // `Trend values: ${values.join(", ")}`, - // `Rising Related Queries: ${rising.join(", ")}`, - // `Top Related Queries: ${top.join(", ")}`, - ]; - - return doc.join("\n\n"); + const startDate = totalResults[0].date.split(" "); + const endDate = totalResults[totalResults.length - 1].date.split(" "); + const values = totalResults.map( + (result: any) => result.values[0].extracted_value + ); + const minValue = Math.min(...values); + const maxValue = Math.max(...values); + const avgValue = + values.reduce((a: number, b: number) => a + b, 0) / values.length; + const percentageChange = + ((values[values.length - 1] - values[0]) / (values[0] || 1)) * 100; + + const relatedParams = new URLSearchParams({ + engine: "google_trends", + api_key: serpapiApiKey, + data_type: "RELATED_QUERIES", + q: query, + }); + + const relatedRes = await fetch( + `https://serpapi.com/search.json?${relatedParams.toString()}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, } + ); + + let rising = []; + let top = []; + if (!relatedRes.ok) { + console.error( + `Error fetching related queries from SerpAPI: ${relatedRes.statusText}` + ); + } else { + const relatedDict = await relatedRes.json(); + rising = + relatedDict.related_queries?.rising?.map( + (result: any) => result.query + ) ?? []; + top = + relatedDict.related_queries?.top?.map((result: any) => result.query) ?? + []; + } + + const doc = [ + `Query: ${query}`, + `Date From: ${startDate[0]} ${startDate[1]}, ${startDate[2]}`, + `Date To: ${endDate[0]} ${endDate[1]} ${endDate[2]}`, + `Min Value: ${minValue}`, + `Max Value: ${maxValue}`, + `Average Value: ${avgValue}`, + `Percent Change: ${percentageChange.toFixed(2)}%`, + `Trend values: ${values.join(", ")}`, + `Rising Related Queries: ${rising.join(", ")}`, + `Top Related Queries: ${top.join(", ")}`, + ]; + return doc.join("\n\n"); + } } diff --git a/libs/langchain-community/src/tools/tests/google_trends.int.test.ts b/libs/langchain-community/src/tools/tests/google_trends.int.test.ts index fcfbf4e60476..e51175be998b 100644 --- a/libs/langchain-community/src/tools/tests/google_trends.int.test.ts +++ b/libs/langchain-community/src/tools/tests/google_trends.int.test.ts @@ -19,15 +19,15 @@ describe("GoogleTrendsAPI", () => { expect(result).toContain("Max Value:"); expect(result).toContain("Average Value:"); expect(result).toContain("Percent Change:"); - // expect(result).toContain("Trend values:"); - // expect(result).toContain("Rising Related Queries:"); - // expect(result).toContain("Top Related Queries:"); + expect(result).toContain("Trend values:"); + expect(result).toContain("Rising Related Queries:"); + expect(result).toContain("Top Related Queries:"); }); test("GoogleTrendsAPI returns 'No good Trend Result was found' for a non-existent query", async () => { const tool = new GoogleTrendsAPI(); - const result = await tool._call("hhdhbfsjbfwl"); + const result = await tool._call("earghajgpajrpgjaprgag"); expect(result).toBe("No good Trend Result was found"); });