mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-22 07:32:25 +02:00
582 lines
35 KiB
Plaintext
582 lines
35 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Async\n",
|
|
"\n",
|
|
"In this example we will build a chat executor with native async implementations of the core logic. This enables taking advantage of Chat Models which have async clients, removing the need for calling the model in a separate thread."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "7cbd446a-808f-4394-be92-d45ab818953c",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Setup\n",
|
|
"\n",
|
|
"First we need to install the packages required"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"\n",
|
|
"\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.3.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.3.2\u001b[0m\n",
|
|
"\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"%%capture --no-stderr\n",
|
|
"%pip install --quiet -U langchain langchain_openai tavily-python"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d",
|
|
"metadata": {},
|
|
"source": [
|
|
"Next, we need to set API keys for OpenAI (the LLM we will use) and Tavily (the search tool we will use)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"OpenAI API Key: ········\n",
|
|
"Tavily API Key: ········\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"import os\n",
|
|
"import getpass\n",
|
|
"\n",
|
|
"os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n",
|
|
"os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c",
|
|
"metadata": {},
|
|
"source": [
|
|
"Optionally, we can set API key for [LangSmith tracing](https://smith.langchain.com/), which will give us best-in-class observability."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
|
|
"os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass(\"LangSmith API Key:\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "21ac643b-cb06-4724-a80c-2862ba4773f1",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Set up the tools\n",
|
|
"\n",
|
|
"We will first define the tools we want to use.\n",
|
|
"For this simple example, we will use a built-in search tool via Tavily.\n",
|
|
"However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"id": "d7ef57dd-5d6e-4ad3-9377-a92201c1310e",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
|
|
"\n",
|
|
"tools = [TavilySearchResults(max_results=1)]"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "01885785-b71a-44d1-b1d6-7b5b14d53b58",
|
|
"metadata": {},
|
|
"source": [
|
|
"We can now wrap these tools in a simple ToolExecutor.\n",
|
|
"This is a real simple class that takes in a ToolInvocation and calls that tool, returning the output.\n",
|
|
"A ToolInvocation is any class with `tool` and `tool_input` attribute.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"id": "5cf3331e-ccb3-41c8-aeb9-a840a94d41e7",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from langgraph.prebuilt import ToolExecutor\n",
|
|
"\n",
|
|
"tool_executor = ToolExecutor(tools)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "5497ed70-fce3-47f1-9cad-46f912bad6a5",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Set up the model\n",
|
|
"\n",
|
|
"Now we need to load the chat model we want to use.\n",
|
|
"Importantly, this should satisfy two criteria:\n",
|
|
"\n",
|
|
"1. It should work with messages. We will represent all agent state in the form of messages, so it needs to be able to work well with them.\n",
|
|
"2. It should work with OpenAI function calling. This means it should either be an OpenAI model or a model that exposes a similar interface.\n",
|
|
"\n",
|
|
"Note: these model requirements are not requirements for using LangGraph - they are just requirements for this one example.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"id": "892b54b9-75f0-4804-9ed0-88b5e5532989",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from langchain_openai import ChatOpenAI\n",
|
|
"\n",
|
|
"# We will set streaming=True so that we can stream tokens\n",
|
|
"# See the streaming section for more information on this.\n",
|
|
"model = ChatOpenAI(temperature=0, streaming=True)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "a77995c0-bae2-4cee-a036-8688a90f05b9",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n",
|
|
"After we've done this, we should make sure the model knows that it has these tools available to call.\n",
|
|
"We can do this by converting the LangChain tools into the format for OpenAI function calling, and then bind them to the model class.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"id": "cd3cbae5-d92c-4559-a4aa-44721b80d107",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"model = model.bind_tools(tools)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "8e8b9211-93d0-4ad5-aa7a-9c09099c53ff",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Define the agent state\n",
|
|
"\n",
|
|
"The main type of graph in `langgraph` is the `StatefulGraph`.\n",
|
|
"This graph is parameterized by a state object that it passes around to each node.\n",
|
|
"Each node then returns operations to update that state.\n",
|
|
"These operations can either SET specific attributes on the state (e.g. overwrite the existing values) or ADD to the existing attribute.\n",
|
|
"Whether to set or add is denoted by annotating the state object you construct the graph with.\n",
|
|
"\n",
|
|
"For this example, the state we will track will just be a list of messages.\n",
|
|
"We want each node to just add messages to that list.\n",
|
|
"Therefore, we will use a `TypedDict` with one key (`messages`) and annotate it so that the `messages` attribute is always added to.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"id": "ea793afa-2eab-4901-910d-6eed90cd6564",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from typing import TypedDict, Annotated, Sequence\n",
|
|
"from langchain_core.messages import BaseMessage\n",
|
|
"\n",
|
|
"\n",
|
|
"def add_messages(left: list | None, right: list | None) -> list:\n",
|
|
" if not left:\n",
|
|
" left = []\n",
|
|
" if not right:\n",
|
|
" right = []\n",
|
|
" return left + right\n",
|
|
"\n",
|
|
"\n",
|
|
"class AgentState(TypedDict):\n",
|
|
" messages: Annotated[Sequence[BaseMessage], add_messages]"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "e03c5094-9297-4d19-a04e-3eedc75cefb4",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Define the nodes\n",
|
|
"\n",
|
|
"We now need to define a few different nodes in our graph.\n",
|
|
"In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n",
|
|
"There are two main nodes we need for this:\n",
|
|
"\n",
|
|
"1. The agent: responsible for deciding what (if any) actions to take.\n",
|
|
"2. A function to invoke tools: if the agent decides to take an action, this node will then execute that action.\n",
|
|
"\n",
|
|
"We will also need to define some edges.\n",
|
|
"Some of these edges may be conditional.\n",
|
|
"The reason they are conditional is that based on the output of a node, one of several paths may be taken.\n",
|
|
"The path that is taken is not known until that node is run (the LLM decides).\n",
|
|
"\n",
|
|
"1. Conditional Edge: after the agent is called, we should either:\n",
|
|
" a. If the agent said to take an action, then the function to invoke tools should be called\n",
|
|
" b. If the agent said that it was finished, then it should finish\n",
|
|
"2. Normal Edge: after the tools are invoked, it should always go back to the agent to decide what to do next\n",
|
|
"\n",
|
|
"Let's define the nodes, as well as a function to decide how what conditional edge to take.\n",
|
|
"\n",
|
|
"**MODIFICATION**\n",
|
|
"\n",
|
|
"We define each node as an async function."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 22,
|
|
"id": "3b541bb9-900c-40d0-964d-7b5dfee30667",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from typing import Literal\n",
|
|
"from langgraph.prebuilt import ToolInvocation\n",
|
|
"from langchain_core.messages import ToolMessage\n",
|
|
"\n",
|
|
"\n",
|
|
"# Define the function that determines whether to continue or not\n",
|
|
"def should_continue(state) -> Literal[\"end\", \"continue\"]:\n",
|
|
" messages = state[\"messages\"]\n",
|
|
" last_message = messages[-1]\n",
|
|
" # If there is no tool call, then we finish\n",
|
|
" if not last_message.tool_calls:\n",
|
|
" return \"end\"\n",
|
|
" # Otherwise if there is, we continue\n",
|
|
" else:\n",
|
|
" return \"continue\"\n",
|
|
"\n",
|
|
"\n",
|
|
"# Define the function that calls the model\n",
|
|
"async def call_model(state):\n",
|
|
" messages = state[\"messages\"]\n",
|
|
" response = await model.ainvoke(messages)\n",
|
|
" # We return a list, because this will get added to the existing list\n",
|
|
" return {\"messages\": [response]}\n",
|
|
"\n",
|
|
"\n",
|
|
"# Define the function to execute tools\n",
|
|
"async def call_tool(state):\n",
|
|
" messages = state[\"messages\"]\n",
|
|
" # Based on the continue condition\n",
|
|
" # we know the last message involves a function call\n",
|
|
" last_message = messages[-1]\n",
|
|
" # We construct an ToolInvocation from the function_call\n",
|
|
" action = ToolInvocation(\n",
|
|
" tool=last_message.tool_calls[0][\"name\"],\n",
|
|
" tool_input=last_message.tool_calls[0][\"args\"],\n",
|
|
" )\n",
|
|
" # We call the tool_executor and get back a response\n",
|
|
" response = await tool_executor.ainvoke(action)\n",
|
|
" # We use the response to create a FunctionMessage\n",
|
|
" function_message = ToolMessage(\n",
|
|
" content=str(response),\n",
|
|
" name=action.tool,\n",
|
|
" tool_call_id=last_message.tool_calls[0][\"id\"],\n",
|
|
" )\n",
|
|
" # We return a list, because this will get added to the existing list\n",
|
|
" return {\"messages\": [function_message]}"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ffd6e892-946c-4899-8cc0-7c9291c1f73b",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Define the graph\n",
|
|
"\n",
|
|
"We can now put it all together and define the graph!"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 23,
|
|
"id": "813ae66c-3b58-4283-a02a-36da72a2ab90",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from langgraph.graph import StateGraph, END\n",
|
|
"\n",
|
|
"# Define a new graph\n",
|
|
"workflow = StateGraph(AgentState)\n",
|
|
"\n",
|
|
"# Define the two nodes we will cycle between\n",
|
|
"workflow.add_node(\"agent\", call_model)\n",
|
|
"workflow.add_node(\"action\", call_tool)\n",
|
|
"\n",
|
|
"# Set the entrypoint as `agent`\n",
|
|
"# This means that this node is the first one called\n",
|
|
"workflow.set_entry_point(\"agent\")\n",
|
|
"\n",
|
|
"# We now add a conditional edge\n",
|
|
"workflow.add_conditional_edges(\n",
|
|
" # First, we define the start node. We use `agent`.\n",
|
|
" # This means these are the edges taken after the `agent` node is called.\n",
|
|
" \"agent\",\n",
|
|
" # Next, we pass in the function that will determine which node is called next.\n",
|
|
" should_continue,\n",
|
|
" # Finally we pass in a mapping.\n",
|
|
" # The keys are strings, and the values are other nodes.\n",
|
|
" # END is a special node marking that the graph should finish.\n",
|
|
" # What will happen is we will call `should_continue`, and then the output of that\n",
|
|
" # will be matched against the keys in this mapping.\n",
|
|
" # Based on which one it matches, that node will then be called.\n",
|
|
" {\n",
|
|
" # If `tools`, then we call the tool node.\n",
|
|
" \"continue\": \"action\",\n",
|
|
" # Otherwise we finish.\n",
|
|
" \"end\": END,\n",
|
|
" },\n",
|
|
")\n",
|
|
"\n",
|
|
"# We now add a normal edge from `tools` to `agent`.\n",
|
|
"# This means that after `tools` is called, `agent` node is called next.\n",
|
|
"workflow.add_edge(\"action\", \"agent\")\n",
|
|
"\n",
|
|
"# Finally, we compile it!\n",
|
|
"# This compiles it into a LangChain Runnable,\n",
|
|
"# meaning you can use it as you would any other runnable\n",
|
|
"app = workflow.compile()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 24,
|
|
"id": "4b369a6f",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"image/jpeg": "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",
|
|
"text/plain": [
|
|
"<IPython.core.display.Image object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"from IPython.display import Image, display\n",
|
|
"\n",
|
|
"try:\n",
|
|
" display(Image(app.get_graph(xray=True).draw_mermaid_png()))\n",
|
|
"except:\n",
|
|
" # This requires some extra dependencies and is optional\n",
|
|
" pass"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "547c3931-3dae-4281-ad4e-4b51305594d4",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Use it!\n",
|
|
"\n",
|
|
"We can now use it!\n",
|
|
"This now exposes the [same interface](https://python.langchain.com/docs/expression_language/) as all other LangChain runnables."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 25,
|
|
"id": "8edb04b9-40b6-46f1-a7a8-4b2d8aba7752",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"{'messages': [HumanMessage(content='what is the weather in sf'),\n",
|
|
" AIMessage(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_UCKe4ydjxDCQPaawAbIWAuwQ', 'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls'}, id='run-f0d86d19-0bdd-46b5-9e14-fd3c9649eac3-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'weather in San Francisco'}, 'id': 'call_UCKe4ydjxDCQPaawAbIWAuwQ'}]),\n",
|
|
" ToolMessage(content='[{\\'url\\': \\'https://www.weatherapi.com/\\', \\'content\\': \"{\\'location\\': {\\'name\\': \\'San Francisco\\', \\'region\\': \\'California\\', \\'country\\': \\'United States of America\\', \\'lat\\': 37.78, \\'lon\\': -122.42, \\'tz_id\\': \\'America/Los_Angeles\\', \\'localtime_epoch\\': 1714807578, \\'localtime\\': \\'2024-05-04 0:26\\'}, \\'current\\': {\\'last_updated_epoch\\': 1714806900, \\'last_updated\\': \\'2024-05-04 00:15\\', \\'temp_c\\': 12.8, \\'temp_f\\': 55.0, \\'is_day\\': 0, \\'condition\\': {\\'text\\': \\'Overcast\\', \\'icon\\': \\'//cdn.weatherapi.com/weather/64x64/night/122.png\\', \\'code\\': 1009}, \\'wind_mph\\': 11.9, \\'wind_kph\\': 19.1, \\'wind_degree\\': 240, \\'wind_dir\\': \\'WSW\\', \\'pressure_mb\\': 1013.0, \\'pressure_in\\': 29.9, \\'precip_mm\\': 0.0, \\'precip_in\\': 0.0, \\'humidity\\': 96, \\'cloud\\': 100, \\'feelslike_c\\': 11.4, \\'feelslike_f\\': 52.4, \\'vis_km\\': 16.0, \\'vis_miles\\': 9.0, \\'uv\\': 1.0, \\'gust_mph\\': 14.9, \\'gust_kph\\': 23.9}}\"}]', name='tavily_search_results_json', tool_call_id='call_UCKe4ydjxDCQPaawAbIWAuwQ'),\n",
|
|
" AIMessage(content='The current weather in San Francisco is as follows:\\n- Temperature: 55.0°F (12.8°C)\\n- Condition: Overcast\\n- Wind: 11.9 mph (19.1 kph) from WSW\\n- Humidity: 96%\\n- Cloud Cover: 100%\\n- Feels like: 52.4°F (11.4°C)\\n- Visibility: 9.0 miles (16.0 km)\\n- UV Index: 1.0\\n\\nFor more detailed information, you can visit [Weather API](https://www.weatherapi.com/).', response_metadata={'finish_reason': 'stop'}, id='run-0415f86c-579a-404d-a1f8-cd9224f8b7bb-0')]}"
|
|
]
|
|
},
|
|
"execution_count": 25,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"from langchain_core.messages import HumanMessage\n",
|
|
"\n",
|
|
"inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf\")]}\n",
|
|
"await app.ainvoke(inputs)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "5a9e8155-70c5-4973-912c-dc55104b2acf",
|
|
"metadata": {},
|
|
"source": [
|
|
"This may take a little bit - it's making a few calls behind the scenes.\n",
|
|
"In order to start seeing some intermediate results as they happen, we can use streaming - see below for more information on that.\n",
|
|
"\n",
|
|
"## Streaming\n",
|
|
"\n",
|
|
"LangGraph has support for several different types of streaming.\n",
|
|
"\n",
|
|
"### Streaming Node Output\n",
|
|
"\n",
|
|
"One of the benefits of using LangGraph is that it is easy to stream output as it's produced by each node.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 26,
|
|
"id": "f544977e-31f7-41f0-88c4-ec9c27b8cecb",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Output from node 'agent':\n",
|
|
"---\n",
|
|
"{'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_os6sSAwICFGXtN8z5Isgnp63', 'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls'}, id='run-3498553b-4ca0-4920-bd9a-0780632d4607-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'weather in San Francisco'}, 'id': 'call_os6sSAwICFGXtN8z5Isgnp63'}])]}\n",
|
|
"\n",
|
|
"---\n",
|
|
"\n",
|
|
"Output from node 'action':\n",
|
|
"---\n",
|
|
"{'messages': [ToolMessage(content='[{\\'url\\': \\'https://www.weatherapi.com/\\', \\'content\\': \"{\\'location\\': {\\'name\\': \\'San Francisco\\', \\'region\\': \\'California\\', \\'country\\': \\'United States of America\\', \\'lat\\': 37.78, \\'lon\\': -122.42, \\'tz_id\\': \\'America/Los_Angeles\\', \\'localtime_epoch\\': 1714807578, \\'localtime\\': \\'2024-05-04 0:26\\'}, \\'current\\': {\\'last_updated_epoch\\': 1714806900, \\'last_updated\\': \\'2024-05-04 00:15\\', \\'temp_c\\': 12.8, \\'temp_f\\': 55.0, \\'is_day\\': 0, \\'condition\\': {\\'text\\': \\'Overcast\\', \\'icon\\': \\'//cdn.weatherapi.com/weather/64x64/night/122.png\\', \\'code\\': 1009}, \\'wind_mph\\': 11.9, \\'wind_kph\\': 19.1, \\'wind_degree\\': 240, \\'wind_dir\\': \\'WSW\\', \\'pressure_mb\\': 1013.0, \\'pressure_in\\': 29.9, \\'precip_mm\\': 0.0, \\'precip_in\\': 0.0, \\'humidity\\': 96, \\'cloud\\': 100, \\'feelslike_c\\': 11.4, \\'feelslike_f\\': 52.4, \\'vis_km\\': 16.0, \\'vis_miles\\': 9.0, \\'uv\\': 1.0, \\'gust_mph\\': 14.9, \\'gust_kph\\': 23.9}}\"}]', name='tavily_search_results_json', tool_call_id='call_os6sSAwICFGXtN8z5Isgnp63')]}\n",
|
|
"\n",
|
|
"---\n",
|
|
"\n",
|
|
"Output from node 'agent':\n",
|
|
"---\n",
|
|
"{'messages': [AIMessage(content='The current weather in San Francisco is as follows:\\n- Temperature: 55.0°F (12.8°C)\\n- Condition: Overcast\\n- Wind: 11.9 mph (19.1 kph) from WSW\\n- Humidity: 96%\\n- Cloud Cover: 100%\\n- Feels like: 52.4°F (11.4°C)\\n- Visibility: 9.0 miles (16.0 km)\\n- UV Index: 1.0\\n\\nFor more details, you can visit [Weather API](https://www.weatherapi.com/).', response_metadata={'finish_reason': 'stop'}, id='run-bc828ced-d6f4-45ca-babd-75282b71af82-0')]}\n",
|
|
"\n",
|
|
"---\n",
|
|
"\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf\")]}\n",
|
|
"async for output in app.astream(inputs):\n",
|
|
" # stream() yields dictionaries with output keyed by node name\n",
|
|
" for key, value in output.items():\n",
|
|
" print(f\"Output from node '{key}':\")\n",
|
|
" print(\"---\")\n",
|
|
" print(value)\n",
|
|
" print(\"\\n---\\n\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "2a1b56c5-bd61-4192-8bdb-458a1e9f0159",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Streaming LLM Tokens\n",
|
|
"\n",
|
|
"You can also access the LLM tokens as they are produced by each node. \n",
|
|
"In this case only the \"agent\" node produces LLM tokens.\n",
|
|
"In order for this to work properly, you must be using an LLM that supports streaming as well as have set it when constructing the LLM (e.g. `ChatOpenAI(model=\"gpt-3.5-turbo-1106\", streaming=True)`)\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 29,
|
|
"id": "cfd140f0-a5a6-4697-8115-322242f197b5",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"|||||||||||The| current| weather| in| San| Francisco| is| as| follows|:\n",
|
|
"|-| Temperature|:| |55|.|0|°F| (|12|.|8|°C|)\n",
|
|
"|-| Condition|:| Over|cast|\n",
|
|
"|-| Wind|:| |11|.|9| mph| from| W|SW|\n",
|
|
"|-| Hum|idity|:| |96|%\n",
|
|
"|-| Cloud| Cover|:| |100|%\n",
|
|
"|-| Visibility|:| |9|.|0| miles|\n",
|
|
"\n",
|
|
"|For| more| detailed| information|,| you| can| visit| [|Weather| API|](|https|://|www|.weather|api|.com|/|).||"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf\")]}\n",
|
|
"async for output in app.astream_log(inputs, include_types=[\"llm\"]):\n",
|
|
" # astream_log() yields the requested logs (here LLMs) in JSONPatch format\n",
|
|
" for op in output.ops:\n",
|
|
" if op[\"path\"] == \"/streamed_output/-\":\n",
|
|
" # this is the output from .stream()\n",
|
|
" ...\n",
|
|
" elif op[\"path\"].startswith(\"/logs/\") and op[\"path\"].endswith(\n",
|
|
" \"/streamed_output/-\"\n",
|
|
" ):\n",
|
|
" # because we chose to only include LLMs, these are LLM tokens\n",
|
|
" print(op[\"value\"].content, end=\"|\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "08ae8246-11d5-40e1-8567-361e5bef8917",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 3 (ipykernel)",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.11.2"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|