diff --git a/docs/core_docs/docs/integrations/tools/google_finance.ipynb b/docs/core_docs/docs/integrations/tools/google_finance.ipynb new file mode 100644 index 000000000000..22027fbf945b --- /dev/null +++ b/docs/core_docs/docs/integrations/tools/google_finance.ipynb @@ -0,0 +1,265 @@ +{ + "cells": [ + { + "cell_type": "raw", + "id": "10238e62-3465-4973-9279-606cbb7ccf16", + "metadata": { + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "---\n", + "sidebar_label: Google Finance Tool\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "a6f91f20", + "metadata": {}, + "source": [ + "# Google Finance Tool\n", + "\n", + "This notebook provides a quick overview for getting started with [`Google Finance Tool`](/docs/integrations/tools/). For detailed documentation of all `Google Finance Tool` features and configurations head to the [API reference](https://v03.api.js.langchain.com/modules/_langchain_community.tools_google_finance.html).\n", + "\n", + "This tool uses the [SerpApi Google Finance API](https://serpapi.com/google-finance-api), so you will need a [SerpApi API key](https://serpapi.com/dashboard).\n", + "\n", + "## Overview\n", + "\n", + "### Integration details\n", + "\n", + "| Class | Package | [PY support](https://python.langchain.com/docs/integrations/tools/google_finance/) | Package latest |\n", + "| :--- | :--- | :---: | :---: |\n", + "| [`SERPGoogleFinanceAPITool`](https://v03.api.js.langchain.com/modules/_langchain_community.tools_google_finance.html) | [`@langchain/community`](https://www.npmjs.com/package/@langchain/community) | ✅ | ![NPM - Version](https://img.shields.io/npm/v/@langchain/community?style=flat-square&label=%20&) |\n", + "\n", + "## Setup\n", + "\n", + "The integration lives in the `@langchain/community` package.\n", + "\n", + "```{=mdx}\n", + "import IntegrationInstallTooltip from \"@mdx_components/integration_install_tooltip.mdx\";\n", + "import Npm2Yarn from \"@theme/Npm2Yarn\";\n", + "\n", + "\n", + "\n", + "\n", + " @langchain/community @langchain/core\n", + "\n", + "```\n", + "\n", + "### Credentials\n", + "\n", + "```typescript\n", + "process.env.SERPAPI_API_KEY=\"your-serpapi-api-key\"\n", + "```\n", + "\n", + "It's also helpful (but not needed) to set up [LangSmith](https://smith.langchain.com/) for best-in-class observability:\n", + "\n", + "```typescript\n", + "process.env.LANGCHAIN_TRACING_V2=\"true\"\n", + "process.env.LANGCHAIN_API_KEY=\"your-api-key\"\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "1c97218f-f366-479d-8bf7-fe9f2f6df73f", + "metadata": {}, + "source": [ + "## Instantiation\n", + "\n", + "You can import and instantiate an instance of the `SERPGoogleFinanceAPITool` tool like this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b3ddfe9-ca79-494c-a7ab-1f56d9407a64", + "metadata": {}, + "outputs": [], + "source": [ + "import { SERPGoogleFinanceAPITool } from \"@langchain/community/tools/google_finance\";\n", + "\n", + "const tool = new SERPGoogleFinanceAPITool({\n", + " // optional, either pass in your api key here or set it as an environment variable\n", + " apiKey: \"your-serpapi-api-key\",\n", + "});" + ] + }, + { + "cell_type": "markdown", + "id": "74147a1a", + "metadata": {}, + "source": [ + "## Invocation\n", + "\n", + "### [Invoke directly with args](/docs/concepts/#invoke-with-just-the-arguments)\n", + "\n", + "The input is a query string for anything you might search for on Google Finance (e.g. GOOG:NASDAQ):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "65310a8b-eb0c-4d9e-a618-4f4abe2414fc", + "metadata": {}, + "outputs": [], + "source": [ + "await tool.invoke(\"GOOG:NASDAQ\")" + ] + }, + { + "cell_type": "markdown", + "id": "d6e73897", + "metadata": {}, + "source": [ + "### [Invoke with ToolCall](/docs/concepts/#invoke-with-toolcall)\n", + "\n", + "We can also invoke the tool with a model-generated `ToolCall`, in which case a `ToolMessage` will be returned:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f90e33a7", + "metadata": {}, + "outputs": [], + "source": [ + "// This is usually generated by a model, but we'll create a tool call directly for demo purposes.\n", + "const modelGeneratedToolCall = {\n", + " args: {\n", + " input: \"What is the price of GOOG:NASDAQ?\"\n", + " },\n", + " id: \"1\",\n", + " name: tool.name,\n", + " type: \"tool_call\",\n", + "}\n", + "await tool.invoke(modelGeneratedToolCall)" + ] + }, + { + "cell_type": "markdown", + "id": "659f9fbd-6fcf-445f-aa8c-72d8e60154bd", + "metadata": {}, + "source": [ + "## Chaining\n", + "\n", + "We can use our tool in a chain by first binding it to a [tool-calling model](/docs/how_to/tool_calling/) and then calling it:\n", + "\n", + "```{=mdx}\n", + "import ChatModelTabs from \"@theme/ChatModelTabs\";\n", + "\n", + "\n", + "```\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "af3123ad-7a02-40e5-b58e-7d56e23e5830", + "metadata": {}, + "outputs": [], + "source": [ + "// @lc-docs-hide-cell\n", + "\n", + "import { ChatOpenAI } from \"@langchain/openai\"\n", + "\n", + "const llm = new ChatOpenAI({\n", + " model: \"gpt-4o-mini\",\n", + "})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fdbf35b5-3aaf-4947-9ec6-48c21533fb95", + "metadata": {}, + "outputs": [], + "source": [ + "import { HumanMessage } from \"@langchain/core/messages\";\n", + "import { ChatPromptTemplate } from \"@langchain/core/prompts\";\n", + "import { RunnableLambda } from \"@langchain/core/runnables\";\n", + "\n", + "const prompt = ChatPromptTemplate.fromMessages(\n", + " [\n", + " [\"system\", \"You are a helpful assistant.\"],\n", + " [\"placeholder\", \"{messages}\"],\n", + " ]\n", + ")\n", + "\n", + "const llmWithTools = llm.bindTools([tool]);\n", + "\n", + "const chain = prompt.pipe(llmWithTools);\n", + "\n", + "const toolChain = RunnableLambda.from(\n", + " async (userInput: string, config) => {\n", + " const humanMessage = new HumanMessage(userInput,);\n", + " const aiMsg = await chain.invoke({\n", + " messages: [new HumanMessage(userInput)],\n", + " }, config);\n", + " const toolMsgs = await tool.batch(aiMsg.tool_calls, config);\n", + " return chain.invoke({\n", + " messages: [humanMessage, aiMsg, ...toolMsgs],\n", + " }, config);\n", + " }\n", + ");\n", + "\n", + "const toolChainResult = await toolChain.invoke(\"What is the price of GOOG:NASDAQ?\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9ac188a2", + "metadata": {}, + "outputs": [], + "source": [ + "const { tool_calls, content } = toolChainResult;\n", + "\n", + "console.log(\"AIMessage\", JSON.stringify({\n", + " tool_calls,\n", + " content,\n", + "}, null, 2));" + ] + }, + { + "cell_type": "markdown", + "id": "93848b02", + "metadata": {}, + "source": [ + "## Agents\n", + "\n", + "For guides on how to use LangChain tools in agents, see the [LangGraph.js](https://langchain-ai.github.io/langgraphjs/) docs." + ] + }, + { + "cell_type": "markdown", + "id": "4ac8146c", + "metadata": {}, + "source": [ + "## API reference\n", + "\n", + "For detailed documentation of all `Google Finance Tool` features and configurations, head to the [API reference](https://v03.api.js.langchain.com/modules/_langchain_community.tools_google_finance.html)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Deno", + "language": "typescript", + "name": "deno" + }, + "language_info": { + "codemirror_mode": "typescript", + "file_extension": ".ts", + "mimetype": "text/x.typescript", + "name": "typescript", + "nbconvert_exporter": "script", + "pygments_lexer": "typescript", + "version": "5.6.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/core_docs/docs/integrations/tools/google_finance.mdx b/docs/core_docs/docs/integrations/tools/google_finance.mdx new file mode 100644 index 000000000000..af47a7b48fb4 --- /dev/null +++ b/docs/core_docs/docs/integrations/tools/google_finance.mdx @@ -0,0 +1,202 @@ +--- +sidebar_label: Google Finance Tool +title: Google Finance Tool +--- + +export const quartoRawHtml = [ + ` + ++++++ + + + + + + + + + + + + + + + + +
ClassPackagePY supportPackage latest
SERPGoogleFinanceAPITool@langchain/communityNPM - Version
+`, +]; + +This notebook provides a quick overview for getting started with +[`Google Finance Tool`](../../../docs/integrations/tools/). For detailed +documentation of all `Google Finance Tool` features and configurations +head to the [API +reference](https://v03.api.js.langchain.com/modules/_langchain_community.tools_google_finance.html). + +This tool uses the [SerpApi Google Finance +API](https://serpapi.com/google-finance-api), so you will need a +[SerpApi API key](https://serpapi.com/dashboard). + +## Overview + +### Integration details + +
+ +## Setup + +The integration lives in the `@langchain/community` package. + +```mdx-code-block +import IntegrationInstallTooltip from "@mdx_components/integration_install_tooltip.mdx"; +import Npm2Yarn from "@theme/Npm2Yarn"; + + + + + @langchain/community @langchain/core + +``` + +### Credentials + +```typescript +process.env.SERPAPI_API_KEY = "your-serpapi-api-key"; +``` + +It’s also helpful (but not needed) to set up +[LangSmith](https://smith.langchain.com/) for best-in-class +observability: + +```typescript +process.env.LANGCHAIN_TRACING_V2 = "true"; +process.env.LANGCHAIN_API_KEY = "your-api-key"; +``` + +## Instantiation + +You can import and instantiate an instance of the +`SERPGoogleFinanceAPITool` tool like this: + +```typescript +import { SERPGoogleFinanceAPITool } from "@langchain/community/tools/google_finance"; + +const tool = new SERPGoogleFinanceAPITool({ + // optional, either pass in your api key here or set it as an environment variable + apiKey: "your-serpapi-api-key", +}); +``` + +## Invocation + +### [Invoke directly with args](../../../docs/concepts/#invoke-with-just-the-arguments) + +The input is a query string for anything you might search for on Google +Finance (e.g. GOOG:NASDAQ): + +```typescript +await tool.invoke("GOOG:NASDAQ"); +``` + +### [Invoke with ToolCall](../../../docs/concepts/#invoke-with-toolcall) + +We can also invoke the tool with a model-generated `ToolCall`, in which +case a `ToolMessage` will be returned: + +```typescript +// This is usually generated by a model, but we'll create a tool call directly for demo purposes. +const modelGeneratedToolCall = { + args: { + input: "What is the price of GOOG:NASDAQ?", + }, + id: "1", + name: tool.name, + type: "tool_call", +}; +await tool.invoke(modelGeneratedToolCall); +``` + +## Chaining + +We can use our tool in a chain by first binding it to a [tool-calling +model](../../../docs/how_to/tool_calling/) and then calling it: + +```mdx-code-block +import ChatModelTabs from "@theme/ChatModelTabs"; + + +``` + +```typescript +import { HumanMessage } from "@langchain/core/messages"; +import { ChatPromptTemplate } from "@langchain/core/prompts"; +import { RunnableLambda } from "@langchain/core/runnables"; + +const prompt = ChatPromptTemplate.fromMessages([ + ["system", "You are a helpful assistant."], + ["placeholder", "{messages}"], +]); + +const llmWithTools = llm.bindTools([tool]); + +const chain = prompt.pipe(llmWithTools); + +const toolChain = RunnableLambda.from(async (userInput: string, config) => { + const humanMessage = new HumanMessage(userInput); + const aiMsg = await chain.invoke( + { + messages: [new HumanMessage(userInput)], + }, + config + ); + const toolMsgs = await tool.batch(aiMsg.tool_calls, config); + return chain.invoke( + { + messages: [humanMessage, aiMsg, ...toolMsgs], + }, + config + ); +}); + +const toolChainResult = await toolChain.invoke( + "What is the price of GOOG:NASDAQ?" +); +``` + +```typescript +const { tool_calls, content } = toolChainResult; + +console.log( + "AIMessage", + JSON.stringify( + { + tool_calls, + content, + }, + null, + 2 + ) +); +``` + +## Agents + +For guides on how to use LangChain tools in agents, see the +[LangGraph.js](https://langchain-ai.github.io/langgraphjs/) docs. + +## API reference + +For detailed documentation of all `Google Finance Tool` features and +configurations, head to the [API +reference](https://v03.api.js.langchain.com/modules/_langchain_community.tools_google_finance.html). + + +## Related + +- Tool [conceptual guide](/docs/concepts/#tools) +- Tool [how-to guides](/docs/how_to/#tools) diff --git a/examples/src/tools/google_finance.ts b/examples/src/tools/google_finance.ts new file mode 100644 index 000000000000..8962c1bd85ab --- /dev/null +++ b/examples/src/tools/google_finance.ts @@ -0,0 +1,29 @@ +import { SERPGoogleFinanceAPITool } from "@langchain/community/tools/google_finance"; +import { OpenAI } from "@langchain/openai"; +import { createReactAgent } from "@langchain/langgraph/prebuilt"; + +export async function run() { + const model = new OpenAI({ + temperature: 0, + apiKey: process.env.OPENAI_API_KEY, + }); + + const tools = [ + new SERPGoogleFinanceAPITool({ apiKey: process.env.SERPAPI_API_KEY }), + ]; + + const financeAgent = createReactAgent({ + llm: model, + tools: tools, + }); + + const inputs = { + messages: [{ role: "user", content: "what is the price of GOOG:NASDAQ?" }], + }; + + const stream = await financeAgent.stream(inputs, { streamMode: "values" }); + + for await (const { messages } of stream) { + console.log(messages); + } +} diff --git a/libs/langchain-community/langchain.config.js b/libs/langchain-community/langchain.config.js index 4a402c6941e8..b00648a66bb6 100644 --- a/libs/langchain-community/langchain.config.js +++ b/libs/langchain-community/langchain.config.js @@ -50,6 +50,7 @@ export const config = { "tools/gmail": "tools/gmail/index", "tools/google_calendar": "tools/google_calendar/index", "tools/google_custom_search": "tools/google_custom_search", + "tools/google_finance": "tools/google_finance", "tools/google_places": "tools/google_places", "tools/google_routes": "tools/google_routes", "tools/ifttt": "tools/ifttt", diff --git a/libs/langchain-community/src/tools/google_finance.ts b/libs/langchain-community/src/tools/google_finance.ts new file mode 100644 index 000000000000..7216fcc20433 --- /dev/null +++ b/libs/langchain-community/src/tools/google_finance.ts @@ -0,0 +1,114 @@ +import { Tool } from "@langchain/core/tools"; +import { getEnvironmentVariable } from "@langchain/core/utils/env"; + +/** + * Interface for parameters required by the SERPGoogleFinanceAPITool class. + */ +export interface SERPGoogleFinanceAPIToolParams { + /** + * Optional API key for accessing the SerpApi service. + */ + apiKey?: string; +} + +/** + * Tool for querying Google Finance using the SerpApi service. + */ +export class SERPGoogleFinanceAPITool extends Tool { + static lc_name() { + return "GoogleFinanceAPI"; + } + + /** + * Returns a mapping of secret environment variable names to their usage in the tool. + * @returns {object} Mapping of secret names to their environment variable counterparts. + */ + get lc_secrets(): { [key: string]: string } | undefined { + return { + apiKey: "SERPAPI_API_KEY", + }; + } + + // Name of the tool, used for logging or identification within LangChain. + name = "google_finance"; + + // The API key used for making requests to SerpApi. + protected apiKey: string; + + /** + * Description of the tool for usage documentation. + */ + description = `A wrapper around Google Finance Search. + Useful for when you need to get information about + google search Finance from Google Finance. + Input should be a search query that includes a stock ticker + (e.g. GOOG:NASDAQ). It provides detailed information on: + - Stock summary + - Markets + - Graph (price per minute) + - Knowledge graph + - News articles + - Financials + - Related searches that may be of interest `; + + /** + * Constructs a new instance of SERPGoogleFinanceAPITool. + * @param fields - Optional parameters including an API key. + */ + constructor(fields?: SERPGoogleFinanceAPIToolParams) { + super(...arguments); + + // Retrieve API key from fields or environment variables. + const apiKey = fields?.apiKey ?? getEnvironmentVariable("SERPAPI_API_KEY"); + + // Throw an error if no API key is found. + if (!apiKey) { + throw new Error( + `SerpApi key not set. You can set it as "SERPAPI_API_KEY" in your environment variables.` + ); + } + this.apiKey = apiKey; + } + + /** + * Makes a request to SerpApi for Google Finance results. + * @param input - Search query string. + * @returns A JSON string containing the search results. + * @throws Error if the API request fails or returns an error. + */ + async _call(input: string): Promise { + // Construct the URL for the API request. + const url = `https://serpapi.com/search.json?q=${encodeURIComponent( + input + )}&engine=google_finance&api_key=${encodeURIComponent(this.apiKey)}`; + + // Make an HTTP GET request to the SerpApi service. + const response = await fetch(url); + + // Handle non-OK responses by extracting the error message. + if (!response.ok) { + let message; + try { + const json = await response.json(); + message = json.error; + } catch (error) { + message = + "Unable to parse error message: SerpApi did not return a JSON response."; + } + // Throw an error with detailed information about the failure. + throw new Error( + `Got ${response.status}: ${response.statusText} error from SerpApi: ${message}` + ); + } + + // Parse the JSON response from SerpApi. + const json = await response.json(); + + // Remove metadata and search parameters from result. + if (json.search_metadata) delete json.search_metadata; + if (json.search_parameters) delete json.search_parameters; + + // Return the results as a formatted JSON string. + return JSON.stringify(json, null, 2); + } +} diff --git a/libs/langchain-community/src/tools/tests/google_finance.int.test.ts b/libs/langchain-community/src/tools/tests/google_finance.int.test.ts new file mode 100644 index 000000000000..60a67e440dbb --- /dev/null +++ b/libs/langchain-community/src/tools/tests/google_finance.int.test.ts @@ -0,0 +1,27 @@ +import { expect, describe } from "@jest/globals"; +import { SERPGoogleFinanceAPITool } from "../google_finance.js"; + +describe("SERPGoogleFinanceAPITool", () => { + test("should be setup with correct parameters", async () => { + const instance = new SERPGoogleFinanceAPITool(); + expect(instance.name).toBe("google_finance"); + }); + + test("SERPGoogleFinanceAPITool returns expected result for valid query", async () => { + const tool = new SERPGoogleFinanceAPITool(); + + const result = await tool.invoke("GOOG:NASDAQ"); + + expect(result).toContain("Alphabet Inc."); + expect(result).toContain("GOOG"); + expect(result).toContain("NASDAQ"); + }); + + test("SERPGoogleFinanceAPITool returns '' for query on a non-existent ticker symbol", async () => { + const tool = new SERPGoogleFinanceAPITool(); + + const result = await tool.invoke("mkvdfmvkdmvkdovkam"); + + expect(result).toContain(""); + }); +});