{ "cells": [ { "cell_type": "markdown", "id": "562ddb82", "metadata": {}, "source": [ "# How to stream LLM tokens from your graph\n", "\n", "In this example, we will stream tokens from the language model powering an\n", "agent. We will use a ReAct agent as an example. The tl;dr is to use\n", "[streamEvents](https://js.langchain.com/v0.2/docs/how_to/chat_streaming/#stream-events)\n", "([API Ref](https://api.js.langchain.com/classes/langchain_core_runnables.Runnable.html#streamEvents)).\n", "\n", "
\n", "

Note

\n", "

\n", " If you are using a version of @langchain/core < 0.2.3, when calling chat models or LLMs you need to call await model.stream() within your nodes to get token-by-token streaming events, and aggregate final outputs if needed to update the graph state. In later versions of @langchain/core, this occurs automatically, and you can call await model.invoke().\n", "
\n", " For more on how to upgrade @langchain/core, check out the instructions here.\n", "

\n", "
\n", "\n", "This how-to guide closely follows the others in this directory, showing how to\n", "incorporate the functionality into a prototypical agent in LangGraph.\n", "\n", "
\n", "

Streaming Support

\n", "

\n", " Token streaming is supported by many, but not all chat models. Check to see if your LLM integration supports token streaming here (doc). Note that some integrations may support general token streaming but lack support for streaming tool calls.\n", "

\n", "
\n", "\n", "
\n", "

Note

\n", "

\n", " In this how-to, we will create our agent from scratch to be transparent (but verbose). You can accomplish similar functionality using the createReactAgent({ llm, tools }) (API doc) constructor. This may be more appropriate if you are used to LangChain's AgentExecutor class.\n", "

\n", "
\n", "\n", "## Setup\n", "\n", "This guide will use OpenAI's GPT-4o model. We will optionally set our API key\n", "for [LangSmith tracing](https://smith.langchain.com/), which will give us\n", "best-in-class observability.\n", "\n", "---" ] }, { "cell_type": "code", "execution_count": 1, "id": "8e76833b", "metadata": { "lines_to_next_cell": 2 }, "outputs": [], "source": [ "// process.env.OPENAI_API_KEY = \"sk_...\";\n", "\n", "// Optional, add tracing in LangSmith\n", "// process.env.LANGCHAIN_API_KEY = \"ls__...\";\n", "// process.env.LANGCHAIN_CALLBACKS_BACKGROUND = \"true\";\n", "// process.env.LANGCHAIN_TRACING = \"true\";\n", "// process.env.LANGCHAIN_PROJECT = \"Stream Tokens: LangGraphJS\";" ] }, { "cell_type": "markdown", "id": "ab95dc97", "metadata": {}, "source": [ "## Define the state\n", "\n", "The state is the interface for all of the nodes in our graph.\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "1648124b", "metadata": { "lines_to_next_cell": 2 }, "outputs": [], "source": [ "import { Annotation } from \"@langchain/langgraph\";\n", "import { BaseMessage } from \"@langchain/core/messages\";\n", "\n", "const StateAnnotation = Annotation.Root({\n", " messages: Annotation({\n", " reducer: (x, y) => x.concat(y),\n", " }),\n", "});" ] }, { "cell_type": "markdown", "id": "da50fbd8", "metadata": {}, "source": [ "## Set up the tools\n", "\n", "First define the tools you want to use. For this simple example, we'll create a placeholder search engine, but see the documentation [here](https://js.langchain.com/v0.2/docs/how_to/custom_tools) on how to create your own custom tools." ] }, { "cell_type": "code", "execution_count": 3, "id": "a8f1ae1c", "metadata": { "lines_to_next_cell": 2 }, "outputs": [], "source": [ "import { tool } from \"@langchain/core/tools\";\n", "import { z } from \"zod\";\n", "\n", "const searchTool = tool((_) => {\n", " // This is a placeholder for the actual implementation\n", " return \"Cold, with a low of 3℃\";\n", "}, {\n", " name: \"search\",\n", " description:\n", " \"Use to surf the web, fetch current information, check the weather, and retrieve other information.\",\n", " schema: z.object({\n", " query: z.string().describe(\"The query to use in your search.\"),\n", " }),\n", "});\n", "\n", "await searchTool.invoke({ query: \"What's the weather like?\" });\n", "\n", "const tools = [searchTool];" ] }, { "cell_type": "markdown", "id": "19b27cb3", "metadata": {}, "source": [ "We can now wrap these tools in a prebuilt\n", "[ToolNode](/langgraphjs/reference/classes/langgraph_prebuilt.ToolNode.html).\n", "This object will actually run the tools (functions) whenever they are invoked by\n", "our LLM." ] }, { "cell_type": "code", "execution_count": 4, "id": "f02278b1", "metadata": { "lines_to_next_cell": 2 }, "outputs": [], "source": [ "import { ToolNode } from \"@langchain/langgraph/prebuilt\";\n", "\n", "const toolNode = new ToolNode(tools);" ] }, { "cell_type": "markdown", "id": "dd55ee5a", "metadata": {}, "source": [ "## Set up the model\n", "\n", "Now load the [chat model](https://js.langchain.com/v0.2/docs/concepts/#chat-models).\n", "\n", "1. It should work with messages. We will represent all agent state in the form\n", " of messages, so it needs to be able to work well with them.\n", "2. It should work with\n", " [tool calling](https://js.langchain.com/v0.2/docs/how_to/tool_calling/#passing-tools-to-llms),\n", " meaning it can return function arguments in its response.\n", "\n", "
\n", "

Note

\n", "

\n", " These model requirements are not general requirements for using LangGraph - they are just requirements for this one example.\n", "

\n", "
" ] }, { "cell_type": "code", "execution_count": 5, "id": "9c7210e7", "metadata": { "lines_to_next_cell": 2 }, "outputs": [], "source": [ "import { ChatOpenAI } from \"@langchain/openai\";\n", "\n", "const model = new ChatOpenAI({ model: \"gpt-4o\", temperature: 0 });" ] }, { "cell_type": "markdown", "id": "73e59248", "metadata": {}, "source": [ "After you've done this, we should make sure the model knows that it has these\n", "tools available to call. We can do this by calling\n", "[bindTools](https://v01.api.js.langchain.com/classes/langchain_core_language_models_chat_models.BaseChatModel.html#bindTools)." ] }, { "cell_type": "code", "execution_count": 6, "id": "b4ff23ee", "metadata": { "lines_to_next_cell": 2 }, "outputs": [], "source": [ "const boundModel = model.bindTools(tools);" ] }, { "cell_type": "markdown", "id": "dbe67356", "metadata": {}, "source": [ "## Define the graph\n", "\n", "We can now put it all together." ] }, { "cell_type": "code", "execution_count": 7, "id": "0ba603bb", "metadata": {}, "outputs": [], "source": [ "import { StateGraph, END } from \"@langchain/langgraph\";\n", "import { AIMessage } from \"@langchain/core/messages\";\n", "\n", "const routeMessage = (state: typeof StateAnnotation.State) => {\n", " const { messages } = state;\n", " const lastMessage = messages[messages.length - 1] as AIMessage;\n", " // If no tools are called, we can finish (respond to the user)\n", " if (!lastMessage?.tool_calls?.length) {\n", " return END;\n", " }\n", " // Otherwise if there is, we continue and call the tools\n", " return \"tools\";\n", "};\n", "\n", "const callModel = async (\n", " state: typeof StateAnnotation.State,\n", ") => {\n", " // For versions of @langchain/core < 0.2.3, you must call `.stream()`\n", " // and aggregate the message from chunks instead of calling `.invoke()`.\n", " const { messages } = state;\n", " const responseMessage = await boundModel.invoke(messages);\n", " return { messages: [responseMessage] };\n", "};\n", "\n", "const workflow = new StateGraph(StateAnnotation)\n", " .addNode(\"agent\", callModel)\n", " .addNode(\"tools\", toolNode)\n", " .addEdge(\"__start__\", \"agent\")\n", " .addConditionalEdges(\"agent\", routeMessage)\n", " .addEdge(\"tools\", \"agent\");\n", "\n", "const agent = workflow.compile();" ] }, { "cell_type": "code", "execution_count": 8, "id": "a88cf20a", "metadata": {}, "outputs": [ { "data": { "image/png": "/9j/4AAQSkZJRgABAQAAAQABAAD/4gHYSUNDX1BST0ZJTEUAAQEAAAHIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAEAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAACRyWFlaAAABFAAAABRnWFlaAAABKAAAABRiWFlaAAABPAAAABR3dHB0AAABUAAAABRyVFJDAAABZAAAAChnVFJDAAABZAAAAChiVFJDAAABZAAAAChjcHJ0AAABjAAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAAAAAAACSgAAAPhAAAts9YWVogAAAAAAAA9tYAAQAAAADTLXBhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADb/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCADaAMcDASIAAhEBAxEB/8QAHQABAAMBAAMBAQAAAAAAAAAAAAUGBwgCAwQJAf/EAE8QAAEDBAADAwYIBw0HBQAAAAECAwQABQYRBxIhEzFVCBYiQZTRFBUXMlFhk+EJN0JxdbO0IyQ0NkNSYnN2gaHB0hhUVpGSlbElM0Vyov/EABsBAQACAwEBAAAAAAAAAAAAAAACAwEEBQYH/8QANREAAgECAQgIBQUBAQAAAAAAAAECAxExBBITIUFRUpEFFBVhcaGxwSIyM2LRQnKB4fA0Y//aAAwDAQACEQMRAD8A/VOlKUApSlAK+SbdoNtKBMmx4pX1SH3Uo5vzbNfXWZ5/Cjzs/tSJMdqQkWyQQl1AUAe1a+mjlGEZTlgk2XUaelmoXxLx51WXxiB7Sj3086rL4xA9pR76zvzetfhsP7BHup5vWvw2H9gj3Vye1cn4Jc0dPs77vI0TzqsvjED2lHvp51WXxiB7Sj31nfm9a/DYf2CPdTzetfhsP7BHup2rk/BLmh2d93kaJ51WXxiB7Sj3086rL4xA9pR76zvzetfhsP7BHup5vWvw2H9gj3U7VyfglzQ7O+7yNE86rL4xA9pR76edVl8Yge0o99Z35vWvw2H9gj3U83rX4bD+wR7qdq5PwS5odnfd5GiedVl8Yge0o99eTWS2h91Dbd1hOOLISlCZCCVE9wA3Wc+b1r8Nh/YI91Rl/s1visW91mDGZdTdbfpbbKUqH78Z9YFX0OkKFetCiotZzSxW12IyyDNi5Z2BtdKUrfOQKUpQClKUApSlAKUpQClKUApSlAKznNfxg2v9FyP1rVaNWc5r+MG1/ouR+taqqt9Cp+1m5kn1onjSlK8IenILMs4snD6zi6X+cIENTqI6FBtbq3HVHSUIQgFS1HrpKQT0P0VQMr8pDHsemYQY7U242zJH5DZlsW+WtbCGW3CSGkslal9ogJKNBQHMrWgTUxxztlrueHxhdLbkE4MT2ZEaRjDCnp0B9IUUSEJTs+j1B9FXztFJBNZeZmcO2LhZmGT2O73WRYr5NMtuLbv/AFBcNxiQwxIcit9UrIU2VoSOm+4dQNulThKN5d+3u1GtUnJOy7vU1jJuOeEYbdmbder0q3yXG23SXYb/AGbSXOiC64G+Rrf9Mpr6cl4w4liWRjH7lcnU3tUduWmBGhSJLqmVqUhKwlptWxtCt6+boE6BG8H41NZRnxzu3ybTm0iPPs7Qxe22pl2PDV2kbbhmKSUjtEulQU08e5ICUqJrQ+HlonO8ZxfH7VOjRXcGtcdMmXFW1yu9u+txklQGnACgqQeo6bFSdKEYKT3b/DuIqpNyzUTnDjjjbeIWX5Tj7cObElWe4uQ2lLhSQ282httSlqcU0lCFcy1AIKuYgBQ2FA1plY9wzfnYjxTz+xXCx3dKb3e1XaFdWoS1wFsqiMpIU+PRQoKZUnlVo7I1vdbDVFVRUvhwsi6m21rFRGTfwOB+lLf+2M1L1EZN/A4H6Ut/7YzW10d/20f3R9UKv05eDNfpSlewPIilKUApSlAKUpQClKUApSlAKUpQCs5zX8YNr/Rcj9a1WjVXMlwaDk8+NNfkzYsmO0plK4b/AGe0qIJB6HfVIrEoqpCUG7XTRfQqKlUU2ZzlfD3GM6VGOR4/bL6YvMGDcIqHuy5tc3LzA63yp3r6BUB/s/cMt78wMb/7Wz/prUvkqg+MXv237qfJVB8Yvftv3VxV0XNKyrep1nltB63EpWLcOMVwd997HcctdjdkJCHV2+IhkuJB2AopA2BVjqS+SqD4xe/bfup8lUHxi9+2/dUX0S5O7qrkySy+ktSTI2lZpxkizcJ4ncI7HbL3dEQMlu78O4B2RzKU2hnnTynXonfrrXfkqg+MXv237qx2P/6rkzPaFLcyvXyxW7JrVItl2gx7nbpAAdiy2g404AQRzJPQ9QD/AHVUEcAeGjZ2nAccSdEbFsZHQjRHzforUPkqg+MXv237qfJVB8Yvftv3VNdFSjqVZcmReXUXjEzi18E+H9juMa4W/CrDBnRlh1mTHtzSHG1juUlQTsEfTU9k38DgfpS3/tjNWn5KoPjF79t+6v6nhRbO3juO3G7SUsPtyEtPS+ZBW2sLTsa6jmSD/dWxk/R7pV6dadW+a08HsdyEstpOLjFWuXWlKV0ziClKUApSlAKUpQClKUApSlAKUpQClKUApSlAKUpQHO/lI/jx8nn+0Mv9mNdEVzv5SP48fJ5/tDL/AGY10RQClKUApSlAKUpQClKUApSlAKUpQClKUApSlAKUpQClKUApSlAKUpQHO/lI/jx8nn+0Mv8AZjXRFc7+Uj+PHyef7Qy/2Y10RQClKUApSlAKUpQClKUApSlAKUpQClKUApSlAKUpQClKUApSoTJMvgYyG0P9rJmvAlmDFTzvOgd5A2AlPcOZRCRsbPUVKMXJ2iZScnZE3Xy3S2Rb1bZdvnx25cGWyuO+w6NodbUkpUlQ9YIJB/PVDczzJZJKmLPboTfXlEmWt1z6thKAAfqCj+evX555d/u1k/6nqt0W+S5m11Ws/wBJ+OnlF8G5fArjBfsRfClxWHu2t76v5eKv0mlb9Z16Kv6SVD1V+r3kU8F3+B/AOz2qehbV6ujirxcWXO9p51CAG9eopbQ2kj+clX01UOLnBg8Zs/wrLL5EtIn4w/2qW2u05ZiAoLQ07sbKErHMB/SWPyumueeeXf7tZP8AqepolxLmOqVtxpVKzdGa5Ykgrh2Zwb+al11HT8/Kf/FS1n4jsvyGot5hKskh1QQ26p0OxnFE6CQ7oaJOgAtKdkgDZ6VjRN/K0/B+2JCWT1YK7iXKlKVSa4pSlAKUpQClKUApSlAKUpQClKUApSlAKUpQEVlF+RjNhl3FbfbKaSEtsg6LrqlBLaAfUVLUlP8AfWew4zqFOyZbpk3CQQuQ+T3n1JT9CE7ISn1D6ySZ7istXwbHGv5J27oDn0aSy8tP/wC0IqKq2fwU4pbdfsl5M7OQwWa57RSudOPPFHJ8ZvGSSMQv1zlOY3ARMnWqJZYz0GOeQualSHVJX6aBvlaPMkddHYr08TuL2QfHOTohZfFwNiy41HvUCPIjMPLuzrqXVFO3QSUJLaG9NgK5l9/cK1bG660VdHSNK5me4ncQslvbGO2hrIYr9msdtk3J61wLdIlOy5LJWQ8JTjSUpHLrTaNlXP1SAAZm0ZRxLyrLsRxq63M4RcZeOS7hc2o0OM+6HmZbbSFo5u0QgrSsKI2sAKI79KCxlVU8Ezf1uJb5eZQTzHlGzrZ+ivF9huSy4y82l1pxJQttYBSpJGiCD3g1yreLtknEqzcHJUzI37ZeGcunWp+XAisacdYRMaTICHELAVytH0fm/uqunROuqIbLkeIw06+uU6hCUrfcSlKnCBoqISAAT39AB16Cs4ayUJ599RO4BfHkSpNgmOrfcjtiREfeXzLcYKtFKiepLatDZ6lKkbJPMau1ZXblqa4gY2pHznBKaXrv7Mtcx/u5kI/wrVK2qmtRnvXu17XODlUFCq0hSlKpNQUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgK7ntjfv2NvtRE80+OtEuMknl5nG1BQRv1BYBQT9CzVNhTG7hFbkNE8ix3KGlJI6FJHqIIII9RBFapWP53fbHbOKNpxa1TixmV7juTfiv4K4uM80gHbzziEkMElJSHDvZ0ClXokWq045jdrYfg38lyhUm4ywZRcu4BWPMLtf5b92vkCLkDSGrtbbfMDUaaUt9mlaxyFYPIEpPKpIUEgKB67zzirwnyQ5JZn8etuR3ddstLEKJdI92tiOR1vm0txqSwSgn0SpbOubp6I5RXQjhvcMlEvGLilQ36cUtPtq+sFK+b/mkH6q9fxhP/wCHL17J99Y6vV2LzR03KjJapGbp4KyciYsl9vWQ3SxZ2m1swbtdMZkIYTNKRtSVpW2pJAUVEKCUkb6aGgLZb+G9vt+V2jIRMuEi4WyzrsjZkvh0OMqW2srcURzKc20n0ubrs7BJ3Xvu2dQ7DcLZAucSXbp1zcLMCLLDbTstY1tLSVLBWRsdE7PUVK/GE/8A4cvXsn306vV3E1Oitq5lEk8BLC/iUWxNXG7QzDvD18h3KM+hEuNKcdccUUK5OXl/dnE8qkn0T12etaBaoKrZbIkNUp+cqOyhoyZSgp14pAHOsgAFR1skAdT3V4ImXJ0hKMbvKlE60phKP8VLAqTtuH3u/KHxmn4it5+eyy8Fy3B/NK07S2PUSkqV1Oik6VTQSXz2S8fbEi61GmrpnswW3qu2TSLwRuFBaXCjq3tLjqlDtlD/AOnIlG/pLg6aO9Eqk8OOJWLZrJv9jx3to7+MSvi2bAehORTHUNhHKFJAKFBJKSn1a7t1dqTkpNJYLD/eZwatR1ZubFKUqsqFKUoBSlKAUpSgFKUoBSlKAUpSgFfwkDvOvz1HT8hgQLgzbFTIxvEllx+LblPoQ/ISjXMUJJ2QNjZ7hsbrNIWEz+OuKY3cOJmPycVuFtupujFit95WpBCFEx/hJb5QpSfRXoHopAOwCpFAfdcciu3FRzPcMtEfI8GdtyG4jOXLiIShx5Q5l/BkrO1gJ5RzgD550UkJJvWM4+nGrDbLaZsu6uwYrcX4wuKw5KfCQBzOLAHMo62TrqetStKAUpUbklkTkuO3W0LlyoCLhFdiGXCWEPshaCnnbUQQFp3sEggEDoaA/ILyy/KMmcT/ACiF3ewXBTVrxR8RLJIjr/LaXzKkJPdtTg2FfzUo+iv1L8n/AIvQ+OXCWwZfE5W3pjPJMjp/kJKPRdR9OuYEjfekpPrriHi3+D/4e4FxI4V4/b7zkz0PKro/CmuSZUdTjaEM84LRSwADvv5goa9VdreT/wCT/j3k4YbMxrGplznQJU9dxW5dXW3HQ4pttsgFttA5dNJ9W9k9e7QGmUpSgKvxFwCJxIw+6Y/Jn3CzonpRzT7PIMaU0pCgpCkuD1gpHfsEdKhY96ynFM0xXEW8bnZBi7lt7OTmD89tTrMltJ/99s+krnCUnnH5S+6tCpQEXjmUWfMLYLjY7pDu8ArU18JhPJdb50nSk7SSNg9CKlKzDKOFdxx3DrhE4Pu2TAb3LuKbk8tdtS5GlL6BaFpTrk5wlIKkgkAHQBOxLw+LFrVxTc4dyY9xayBu2puSJSoDiIcpvYDhac6j0CUbBOgVgAkg6AvFKUoBSlKAUpSgFKUoBSlKAVnWY57OvreX4rw6uFqd4i2VqMXI14Q6iPFD/pIcUQn0/wBz5lDl2NgA67q0Ws0uU5GN8d7NGhYIqQvJbe/8YZfGbJ+D/BgC2w8Qg6Srm9EqWOvQA+oCdtPDazoyO35fdrXbZudtW1u3v3xmNyKIAJX2YJVyJKlL9ZOiEkkCrdSlAKUpQClKw7jX5Q0jGsgZ4fcPLajLuKE9vmbgJV+9rW2dfviYsfMSNghOwVbHdzJ2BX/KPuURzyhvJ6tiJLS7im9S5KoiVguhr4OR2hT3hOwRvu6H6DXSNY7wL8nmPwxkzcoyO5Ly/iVeBzXTJJY2ob/kI6f5JlOgAABvQ3oBKU7FQClKUApSlAK9E2G3cIj8Z3nDbzamlFpam1hKho8qkkFJ+sEEeqvfSgMgj4vkPAPBccsOAWeZncBq59lKbvV51KjRHFHRaWtPKUtcydJ6aQg95JUNStV7t19aedts+LcGmXVMOLivJdShxPRSFFJOlD1g9RX21lPk6ysJl41kqsFhzYUBORz0TkTiSpc4LHbqTtSvQJ1ru/MKA1alKUApSlAKUpQClKUAr8+/KJ/CQXPF81h47jGK3exSrHdWlXtu9LjIclIbWsPQwlAeSlCwGyH0Ob79JI0T+gD8hqK2XHnUNIHepxQSP+Zri7y8vJnsfGSyO5ticy3jOLaz++IzUhG7pHSPmaB6upA9E96h6J36OpKMpYIFz8hzym808pa35fOyq1We3RbU7FZhOWlh1sOrWHS6F9o6vfKEta1r5x7/AFdRVyl+Dyx6Dw48nC3qucli23O9TZFzfjS3EtuoBIab2lWiAUNJWPqXv110z51WXxiB7Sj31LRz4WZsyUpXzQ7nDuG/gstiTrqexcC//BrmfJuIuVeVFkU/C+F8yRj2BQnVRb/nqElLj6h0XFt++9XqLvq3sdOXng01qZgluJfHTIc/zGXww4MdjLyBj0L5lrqe0gWBB2CAe52R0OkDYBHXelcui8FOBePcD7A/EtfbXG8T1/CLrfp6u0mXF87JcdWeutk6TvQ2e8kkzfDLhfjfCDEYmN4rbW7bbI/UhPVx5Z+c44vvWs66k/UBoAAWusAUpSgFKUoBSvkm3aDbSgTJseKV9Uh91KOb82zXzedVl8Yge0o99TUJNXSM2ZKUqL86rL4xA9pR76edVl8Yge0o99Z0c+FizMY8rPyn5vkv2XH7s3hispt9zkOxXnxcfgiYriUpU2k/uTnMVjtCO7XZnv3XO/Cf8Jffs3yy24rC4UQpd2vFx7GMIV3VHQhK1DRcBYXspGypewNAnQ1XV/HnD8Y438KMhw+Zd7ahc6OTEkLko/e8lPpNOdDvQUBvXekqHrrjn8G5wOZxbJ8izzLixbbhbXXLPbI0x1CFJc7pDwBPqGmwobB5nB6qaOfCxZn6NUqL86rL4xA9pR76edVl8Yge0o99NHPhYsyUpUX51WXxiB7Sj30TlFmUoAXeCSegAko6/wCNNHPhYsyUpSlVmBVQy7Ln4ksWm0hBuBSFvyXBzNxEHu6flOK/JT3AAqV05Urtch9EWO684dNtpK1H6gNmshxpbku1N3F/Rl3I/DX1DfVSwCB19SU8qR9SRVsbRi6j2YeJu5LRVWfxYI/i8agy3u3uLZvEsjRk3HTyz130BHKkfUkAfVXu837WP/jYf2CPdVO4wcXYnCOJj78qHImC63Vi3nsGHnS0hSvTc02hZUoDuR0Kj3b0RX0ZFxsw3FI1sdul0djKuUb4ZHjCBJXJ7HptxbKWy42kb6laU6OwdEGq3WqSxkzuJwjq1KxafN+1+Gw/sE+6nm/a/DYf2CfdVdv/ABgw/G7PaLnMvbS4l4Tz24wmnJTktPLzFTbbSVLUACCSBobG9VDSuLzFyyjhszjkiFdLBlLs5DkwBRUAxHW4OTqOVXOjlUFAkaI0DUdJPiZlyii7PYrZ3lBZtsZDqSFJdabDbiSO4hSdEf3GpbGr+7hfZw5jhkWNbhAkKSO1irWvZU4ofPbKlElZ9JJJUoqBKkU2wcXMTyjJ5WP2q6mbc4y3W3EojPBrmbOnEpeKOzUUnoQlRIq3PMokMradQlxtaSlSFDYUD0INWRrSwm7r/YbiqpShWjY1ClVPhjcHJmKNx33C6/b3nYKlkklSW1ENkk9SS3yEk+vff31bKTjmScdx5yUXFuLFKUqBEUpSgMzz+FHnZ/akSY7UhItkghLqAoA9q19NfH5vWvw2H9gj3VJZr+MG1/ouR+tarxrn5fUnGcUm1qXqzxfS0pLKWk9iI/zetfhsP7BHup5vWvw2H9gj3VIVGZLk1rw+ySrvepzVutsYAuyHjoDZAAHrJJIAA2SSAASa5ulqP9T5nHU5t2TZ5+b1r8Nh/YI91PN61+Gw/sEe6qjD474LNsV2vCb8lmFaezM/4VFeYdjJcUEoUtpxCXAlRPRXLroevQ1IYrxYxXNJc+Larr2kmCymS+1JjuxlBlW+V1IdSnnbOj6adp+us59ZbX5ljVZJtp6vEnvN61+Gw/sEe6nm9a/DYf2CPdWVRvKOsmUcTsLxvFJ0e6wruuaJj64j6PQZYUtCmHFBKFpK06Kk8419HfWy0lOrHGT5mJqrTtn3VyP83rX4bD+wR7qhc0sluYxa4uNQIrbiW9pWhlIIOx3HVWqoLOf4pXP+q/zFbOSVajyims5/MtveWZPOWmhr2r1NlpSldg+inzXKILhbpUUnQfaU3v6Ngj/OslxVxS8btoWlSHW2EsuIUNFK0DlWD+ZSSK2Os6yqwu45cZN1iMKetUtZdmNtDa4zpABdCfW2rXpa6pV6WiFKKLorPg6axxX4/wBusdDI6qpzaltMm8oK23GTjmOXK322Xd/iTI7fdZMSA2XZC2GnP3QtoHVagFb5R1OjVWVkcvFeK9yzl7E8mulnyGxxY0X4HaXHZcR1h17mYdY1ztBfaJUCoBOwdkVusaSzMYQ/HdQ+y4OZDjagpKh9II6GvZWq9WpnYcLvOTOWuHWJZDwZl4JkV9xy53GKmx3C3yIVmjGa9anX53wttPZo2op5D2RUgHRQN6FeeM4pkmP3vC8vmY1c24UjMLxc3bZHY7STb485lbbKnW0np6Wlr1vl5zvuNdRUrFyCopWs8P6/BgOAfGti4xfFuK2fJrbh0uRPfvUG+wC3CivbKkPwnj1IdcJJbSpSdKJ0kjVb9SvlhxnswkLt9scIjBXJMuKN8jKd6UhtQ6F0jYAHzPnK/JSuyEHUfdte4k3GjFuT1Fk4URyMdlzNEJn3CRIRsaJQFdmk/mIbBH1EVdK9EKGxbobESM0liMw2lpppA0lCEjQA+oACvfVtSWfNyR5ucs+TlvFKUqsgKUpQGc5r+MG1/ouR+tarxryzX8YNr/Rcj9a1Vcyvh7jGdKinI8ftt9MXmDBuEVD3Zc2ubl5gdb5U719ArmdIW0kb7l7niulbda17kWGsj8pfErrlWGWR61xJ1y+Jr7EusuBa5CmJcmO3zhxLK0qSQ4OcLTpQJKBo71U5/s+8Mt/xAxv/ALWz/pqdxXhviuDPvvY7jlrsbshIQ6u3xEMlxIOwFFIGwK5yai7o5kJRpyU4t3Xd/Zz3mWE23JeFmd3PHMZzpd9ehxbeheTKnPyZLQkodLbLT61r0ggknlA6nW+tWjjXgN/zTiDkEazxZCPjDh7cLa1N5FJYMhUlooZU5rlClDm6E70VHu3W/wBKlpWixZTJNNbL46934OdLJe5uYcRODoj4RkePR7C1OanfGFqcYjxCYRbSgOa5VJ5hpKh6J6ddnVdF181xt0W8W+TBnR2pkKS2pl+O+gLQ6hQ0pKknoQQSCDVJHk/8MwQRgGOAjuItjP8ApqLkpY6iE5wqWvqt/O1vf3l/qCzn+KVz/qv8xUBG4DcN4chp9jBMdZfaUFtuItjIUlQOwQeXoQan85/ilc/6r/MVsZJbrNO3EvUzQUdNDNe1evibLSlK7h9GFKUoCr3PhvYbnJckiM7BkuHa3bfIcjlZ3slQQQFHfrIJr4PkogeL3r237qu9KvVeov1FiqzjqUmUj5KIHi969t+6nyUQPF717b91XelZ09Tf6EtNU4mU5nhVYwoGUu4XJIIPZy5zimzr6UAhJ/MQRVriRGIEZuPGZbjx2khKGmkBKEAdwAHQCvdSq5VJz1SZXKUpfM7ilKVWRFKUoBSlKArmS4NByefGmvyZsWTHaUylcN/s9pUQSD0O+qRUZ8lUHxi9+2/dV2pVmklZL2RXKnCTvKKf8FJ+SqD4xe/bfup8lUHxi9+2/dV2pTSPu5IjoaXAuSKT8lUHxi9+2/dT5KoPjF79t+6rtSmkfdyQ0NLgXJFJ+SqD4xe/bfup8lUHxi9+2/dV2pTSPu5IaGlwLkik/JVB8Yvftv3V65HCG1y2lNSLneH2VfObXM2lQ+g9KvVKyqsk7r0RlUaSd1FckKUpVRaf/9k=" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import * as tslab from \"tslab\";\n", "\n", "const runnableGraph = agent.getGraph();\n", "const image = await runnableGraph.drawMermaidPng();\n", "const arrayBuffer = await image.arrayBuffer();\n", "\n", "await tslab.display.png(new Uint8Array(arrayBuffer));" ] }, { "cell_type": "markdown", "id": "055aacad", "metadata": {}, "source": [ "## How to stream tool calls\n", "\n", "You can now run your agent. Let's first look at an example of streaming back intermediate tool calls. This is not supported by all providers, but some support token-level streaming of tool invocations.\n", "\n", "To get the partially populated tool calls, you can access the message chunks' `tool_call_chunks` property:" ] }, { "cell_type": "code", "execution_count": 9, "id": "c704d23c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[\n", " {\n", " name: 'search',\n", " args: '',\n", " id: 'call_ziGo5u8fYyqQ78SdLZTEC9Vg',\n", " index: 0,\n", " type: 'tool_call_chunk'\n", " }\n", "]\n", "[\n", " {\n", " name: undefined,\n", " args: '{\"',\n", " id: undefined,\n", " index: 0,\n", " type: 'tool_call_chunk'\n", " }\n", "]\n", "[\n", " {\n", " name: undefined,\n", " args: 'query',\n", " id: undefined,\n", " index: 0,\n", " type: 'tool_call_chunk'\n", " }\n", "]\n", "[\n", " {\n", " name: undefined,\n", " args: '\":\"',\n", " id: undefined,\n", " index: 0,\n", " type: 'tool_call_chunk'\n", " }\n", "]\n", "[\n", " {\n", " name: undefined,\n", " args: 'current',\n", " id: undefined,\n", " index: 0,\n", " type: 'tool_call_chunk'\n", " }\n", "]\n", "[\n", " {\n", " name: undefined,\n", " args: ' weather',\n", " id: undefined,\n", " index: 0,\n", " type: 'tool_call_chunk'\n", " }\n", "]\n", "[\n", " {\n", " name: undefined,\n", " args: '\"}',\n", " id: undefined,\n", " index: 0,\n", " type: 'tool_call_chunk'\n", " }\n", "]\n" ] } ], "source": [ "import type { AIMessageChunk } from \"@langchain/core/messages\";\n", "\n", "const eventStream = await agent.streamEvents(\n", " { messages: [{role: \"user\", content: \"What's the weather like today?\" }] },\n", " {\n", " version: \"v2\",\n", " },\n", ");\n", "\n", "for await (const { event, data } of eventStream) {\n", " if (event === \"on_chat_model_stream\") {\n", " const msg = data.chunk as AIMessageChunk;\n", " if (msg.tool_call_chunks !== undefined && msg.tool_call_chunks.length > 0) {\n", " console.log(msg.tool_call_chunks);\n", " }\n", " }\n", "}" ] }, { "cell_type": "markdown", "id": "1d9b168b", "metadata": {}, "source": [ "Because this is a ReAct-style agent, this will only log intermediate steps and not the final response because the model generates a final response with no tool calls when it no longer needs to gather more information from calling tools.\n", "\n", "## Streaming final responses\n", "\n", "### ReAct agents\n", "\n", "For ReAct-style agents, you know that as soon as you start message chunks with no `tool_call_chunks`, the model is responding directly to the user. So we can flip the conditional like this to only log tokens from the final response:" ] }, { "cell_type": "code", "execution_count": 10, "id": "86f843bb", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "The\n", " weather\n", " today\n", " is\n", " cold\n", ",\n", " with\n", " a\n", " low\n", " of\n", " \n", "3\n", "℃\n", ".\n", "\n" ] } ], "source": [ "const eventStreamFinalRes = await agent.streamEvents(\n", " { messages: [{ role: \"user\", content: \"What's the weather like today?\" }] },\n", " { version: \"v2\" });\n", "\n", "for await (const { event, data } of eventStreamFinalRes) {\n", " if (event === \"on_chat_model_stream\") {\n", " const msg = data.chunk as AIMessageChunk;\n", " if (!msg.tool_call_chunks?.length) {\n", " console.log(msg.content);\n", " }\n", " }\n", "}" ] }, { "cell_type": "markdown", "id": "f13b4790", "metadata": {}, "source": [ "### Other graphs\n", "\n", "If your graph has multiple model calls in multiple nodes and there's one that will always be called last, you can distinguish that model by assigning it a run name or a tag. To illustrate this, declare a new graph like this:" ] }, { "cell_type": "code", "execution_count": 11, "id": "0fea2f20", "metadata": {}, "outputs": [], "source": [ "const OtherGraphAnnotation = Annotation.Root({\n", " messages: Annotation({\n", " reducer: (x, y) => x.concat(y),\n", " }),\n", "});\n", "\n", "const respond = async (state: typeof OtherGraphAnnotation.State) => {\n", " const { messages } = state;\n", " const model = new ChatOpenAI({ model: \"gpt-4o\", temperature: 0 });\n", " const responseMessage = await model.invoke(messages);\n", " return {\n", " messages: [responseMessage],\n", " }\n", "};\n", "\n", "const summarize = async (state: typeof OtherGraphAnnotation.State) => {\n", " const { messages } = state;\n", " // Assign the final model call a run name\n", " const model = new ChatOpenAI({\n", " model: \"gpt-4o\",\n", " temperature: 0\n", " }).withConfig({ runName: \"Summarizer\" });\n", " const userMessage = { role: \"human\", content: \"Now, summarize the above messages\" };\n", " const responseMessage = await model.invoke([\n", " ...messages,\n", " userMessage,\n", " ]);\n", " return { \n", " messages: [userMessage, responseMessage]\n", " };\n", "}\n", "\n", "const otherWorkflow = new StateGraph(OtherGraphAnnotation)\n", " .addNode(\"respond\", respond)\n", " .addNode(\"summarize\", summarize)\n", " .addEdge(\"__start__\", \"respond\")\n", " .addEdge(\"respond\", \"summarize\")\n", " .addEdge(\"summarize\", \"__end__\");\n", "\n", "const otherGraph = otherWorkflow.compile();" ] }, { "cell_type": "code", "execution_count": 12, "id": "2149f527", "metadata": {}, "outputs": [ { "data": { "image/png": "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" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "const otherRunnableGraph = otherGraph.getGraph();\n", "const otherImage = await otherRunnableGraph.drawMermaidPng();\n", "const otherArrayBuffer = await otherImage.arrayBuffer();\n", "\n", "await tslab.display.png(new Uint8Array(otherArrayBuffer));" ] }, { "cell_type": "markdown", "id": "5ff9d991", "metadata": {}, "source": [ "Now when we call `streamEvents`, we can see that we can now filter on run name to only see the final summary generation of the current chat history:" ] }, { "cell_type": "code", "execution_count": 13, "id": "51381303", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "You\n", " asked\n", " about\n", " the\n", " capital\n", " of\n", " Nepal\n", ",\n", " and\n", " I\n", " responded\n", " that\n", " it\n", " is\n", " Kathmandu\n", ".\n", "\n" ] } ], "source": [ "const otherEventStream = await otherGraph.streamEvents(\n", " { messages: [{ role: \"user\", content: \"What's the capital of Nepal?\" }] },\n", " { version: \"v2\" },\n", " { includeNames: [\"Summarizer\"] }\n", ");\n", "\n", "for await (const { event, data } of otherEventStream) {\n", " if (event === \"on_chat_model_stream\") {\n", " console.log(data.chunk.content);\n", " }\n", "}" ] }, { "cell_type": "markdown", "id": "46998967", "metadata": {}, "source": [ "And you can see the resulting chunks are only ones from the final summary model call.\n", "\n", "## Next steps\n", "\n", "You've now seen some ways to stream LLM tokens from within your graph. Next, check out some of the other how-tos around streaming by going [to this page](/langgraphjs/how-tos/#streaming)." ] } ], "metadata": { "kernelspec": { "display_name": "TypeScript", "language": "typescript", "name": "tslab" }, "language_info": { "codemirror_mode": { "mode": "typescript", "name": "javascript", "typescript": true }, "file_extension": ".ts", "mimetype": "text/typescript", "name": "typescript", "version": "3.7.2" } }, "nbformat": 4, "nbformat_minor": 5 }