-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(community): Added Google Trends Integration (#7248)
Co-authored-by: Roger Zhao <[email protected]> Co-authored-by: nishus24 <[email protected]>
- Loading branch information
1 parent
3dfaa37
commit f8175ef
Showing
8 changed files
with
265 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
--- | ||
hide_table_of_contents: true | ||
--- | ||
|
||
import CodeBlock from "@theme/CodeBlock"; | ||
|
||
# Google Trends Tool | ||
|
||
The Google Trends Tool allows your agent to utilize the Google Trends API from SerpApi to retrieve and analyze search interest data. | ||
This can be useful for understanding trending topics, regional search interest, and historical popularity of search terms. | ||
|
||
For API details see [here](https://serpapi.com/google-trends-api) | ||
|
||
SerpApi caches queries, so the first query will be slower while subsequent identical queries will be fast. | ||
Occasionally, related queries will not work while interest over time will be fine. You can check your query [here](https://serpapi.com/playground?engine=google_trends&q=monster&data_type=RELATED_QUERIES). | ||
|
||
## Setup | ||
|
||
To use this tool, you will need to configure access to the Google Trends API from SerpApi. | ||
|
||
Get an API key from [SerpApi](https://serpapi.com/users/sign_in) | ||
|
||
Then, set your API key as `process.env.SERPAPI_API_KEY` or pass it in as an `apiKey` constructor argument. | ||
|
||
## Usage | ||
|
||
import IntegrationInstallTooltip from "@mdx_components/integration_install_tooltip.mdx"; | ||
|
||
<IntegrationInstallTooltip></IntegrationInstallTooltip> | ||
|
||
```bash npm2yarn | ||
npm install @langchain/openai @langchain/community @langchain/core | ||
``` | ||
|
||
import ToolExample from "@examples/tools/google_trends.ts"; | ||
|
||
<CodeBlock language="typescript">{ToolExample}</CodeBlock> | ||
|
||
## Related | ||
|
||
- Tool [conceptual guide](/docs/concepts/tools) | ||
- Tool [how-to guides](/docs/how_to/#tools) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { SERPGoogleTrendsTool } from "@langchain/community/tools/google_trends"; | ||
|
||
export async function run() { | ||
const tool = new SERPGoogleTrendsTool(); | ||
|
||
const res = await tool.invoke("Monster"); | ||
|
||
console.log(res); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
import { getEnvironmentVariable } from "@langchain/core/utils/env"; | ||
import { Tool } from "@langchain/core/tools"; | ||
|
||
/** | ||
* Interfaces for the response from the SerpApi Google Trends API. | ||
*/ | ||
interface TimelineValue { | ||
query: string; | ||
value: string; | ||
extracted_value: number; | ||
} | ||
|
||
interface TimelineData { | ||
date: string; | ||
timestamp: string; | ||
values: TimelineValue[]; | ||
} | ||
|
||
/** | ||
* Interface for parameters required by SERPGoogleTrendsTool class. | ||
*/ | ||
export interface SERPGoogleTrendsToolParams { | ||
apiKey?: string; | ||
} | ||
|
||
/** | ||
* Tool that queries the Google Trends API. Uses default interest over time. | ||
*/ | ||
export class SERPGoogleTrendsTool extends Tool { | ||
static lc_name() { | ||
return "SERPGoogleTrendsTool"; | ||
} | ||
|
||
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?: SERPGoogleTrendsToolParams) { | ||
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<string> { | ||
/** | ||
* 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. | ||
* | ||
* SerpApi caches queries, so the first time will be slower | ||
* and subsequent identical queries will be very fast. | ||
*/ | ||
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", | ||
}, | ||
} | ||
); | ||
|
||
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: TimelineData) => 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) { | ||
// Sometimes related queries from SerpAPI will fail, but interest over time will be fine | ||
console.error( | ||
`Error fetching related queries from SerpAPI: ${relatedRes.statusText}` | ||
); | ||
} else { | ||
const relatedDict = await relatedRes.json(); | ||
rising = | ||
relatedDict.related_queries?.rising?.map( | ||
(result: { query: string }) => result.query | ||
) ?? []; | ||
top = | ||
relatedDict.related_queries?.top?.map( | ||
(result: { query: string }) => 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"); | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
libs/langchain-community/src/tools/tests/google_trends.int.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { expect, describe } from "@jest/globals"; | ||
import { SERPGoogleTrendsTool } from "../google_trends.js"; | ||
|
||
describe("SERPGoogleTrendsTool", () => { | ||
test("should be setup with correct parameters", async () => { | ||
const instance = new SERPGoogleTrendsTool(); | ||
expect(instance.name).toBe("google_trends"); | ||
}); | ||
|
||
test("SERPGoogleTrendsTool returns expected result for valid query", async () => { | ||
const tool = new SERPGoogleTrendsTool(); | ||
|
||
const result = await tool._call("Coffee"); | ||
|
||
expect(result).toContain("Query: Coffee"); | ||
expect(result).toContain("Date From:"); | ||
expect(result).toContain("Date To:"); | ||
expect(result).toContain("Min Value:"); | ||
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:"); | ||
}); | ||
|
||
test("SERPGoogleTrendsTool returns 'No good Trend Result was found' for a non-existent query", async () => { | ||
const tool = new SERPGoogleTrendsTool(); | ||
|
||
const result = await tool._call("earghajgpajrpgjaprgag"); | ||
|
||
expect(result).toBe("No good Trend Result was found"); | ||
}); | ||
}); |