diff --git a/docs/docs/how-tos/streaming-content.ipynb b/docs/docs/how-tos/streaming-content.ipynb index f4fa01623..a5c7261c3 100644 --- a/docs/docs/how-tos/streaming-content.ipynb +++ b/docs/docs/how-tos/streaming-content.ipynb @@ -7,13 +7,44 @@ "source": [ "# How to stream custom data\n", "\n", - "The most common use case for streaming from inside a node is to stream LLM tokens, but you may also want to stream custom data. For example, you might have some long-running streaming functions you may wish to render for the user. \n", + "
Prerequisites
\n", + "\n", + " This guide assumes familiarity with the following:\n", + "
ASYNC IN PYTHON<=3.10
\n", "\n", @@ -178,7 +246,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 19, "id": "486a01a0", "metadata": {}, "outputs": [], @@ -229,7 +297,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 20, "id": "ce773a40", "metadata": {}, "outputs": [ @@ -270,7 +338,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.11.4" } }, "nbformat": 4, diff --git a/docs/docs/how-tos/streaming-events-from-within-tools-without-langchain.ipynb b/docs/docs/how-tos/streaming-events-from-within-tools-without-langchain.ipynb index 2d702c1f2..d0404adf0 100644 --- a/docs/docs/how-tos/streaming-events-from-within-tools-without-langchain.ipynb +++ b/docs/docs/how-tos/streaming-events-from-within-tools-without-langchain.ipynb @@ -1,26 +1,49 @@ { "cells": [ { + "attachments": {}, "cell_type": "markdown", - "id": "b23ced4e-dc29-43be-9f94-0c36bb181b8a", - "metadata": {}, - "source": [ - "# How to stream events from within a tool (without LangChain LLMs / tools)" - ] - }, - { - "cell_type": "markdown", - "id": "7044eeb8-4074-4f9c-8a62-962488744557", - "metadata": {}, - "source": [ - "In this example we will stream tokens from within tools that an agent is using. We'll also be using OpenAI client library directly, without using LangChain chat models. We will use a ReAct agent as an example." - ] - }, - { - "cell_type": "markdown", - "id": "a37f60af-43ea-4aa6-847a-df8cc47065f5", + "id": "18e6e213-b398-4a7e-b342-ba225e97b424", "metadata": {}, "source": [ + "# How to stream events from within a tool (without LangChain LLMs / tools)\n", + "\n", + "\n", + "
Prerequisites
\n", + "\n", + " This guide assumes familiarity with the following:\n", + "
Prerequisites
\n", + "\n", + " This guide assumes familiarity with the following:\n", + "
ASYNC IN PYTHON<=3.10
\n", "\n", - "Any Langchain RunnableLambda, a RunnableGenerator, or Tool that invokes other runnables and is running async in python<=3.10, will have to propagate callbacks to child objects manually. This is because LangChain cannot automatically propagate callbacks to child objects in this case.\n", + "Any Langchain `RunnableLambda`, a `RunnableGenerator`, or `Tool` that invokes other runnables and is running async in python<=3.10, will have to propagate callbacks to child objects **manually**. This is because LangChain cannot automatically propagate callbacks to child objects in this case.\n", " \n", "This is a common reason why you may fail to see events being emitted from custom runnables or tools.\n", "
\n", @@ -117,84 +123,61 @@ }, { "cell_type": "code", - "execution_count": 14, - "id": "2cb38dd9-74d8-456d-9e39-4655f2bf3f37", + "execution_count": 5, + "id": "f1975577-a485-42bd-b0f1-d3e987faf52b", "metadata": {}, "outputs": [], "source": [ + "from langchain_core.callbacks import Callbacks\n", + "from langchain_core.messages import HumanMessage\n", + "from langchain_core.tools import tool\n", + "\n", + "from langgraph.prebuilt import create_react_agent\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "\n", "@tool\n", "async def get_items(\n", - " place: str, callbacks: Callbacks\n", - ") -> str: # <--- Accept callbacks (Python <= 3.10)\n", + " place: str,\n", + " callbacks: Callbacks, # <--- Manually accept callbacks (needed for Python <= 3.10)\n", + ") -> str:\n", " \"\"\"Use this tool to look up which items are in the given place.\"\"\"\n", - " template = ChatPromptTemplate.from_messages(\n", + " # Attention when using async, you should be invoking the LLM using ainvoke!\n", + " # If you fail to do so, streaming will not WORK.\n", + " return await llm.ainvoke(\n", " [\n", - " (\n", - " \"human\",\n", - " \"Can you tell me what kind of items i might find in the following place: '{place}'. \"\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": f\"Can you tell me what kind of items i might find in the following place: '{place}'. \"\n", " \"List at least 3 such items separating them by a comma. And include a brief description of each item..\",\n", - " )\n", - " ]\n", + " }\n", + " ],\n", + " {\"callbacks\": callbacks},\n", " )\n", - " chain = template | llm.with_config(\n", - " {\n", - " \"run_name\": \"Get Items LLM\",\n", - " \"tags\": [\"tool_llm\"],\n", - " \"callbacks\": callbacks, # <-- Propagate callbacks (Python <= 3.10)\n", - " }\n", - " )\n", - " chunks = [chunk async for chunk in chain.astream({\"place\": place})]\n", - " return \"\".join(chunk.content for chunk in chunks)" - ] - }, - { - "cell_type": "markdown", - "id": "17279b8a-049d-483d-af63-8a875098e71f", - "metadata": {}, - "source": [ - "We're adding a custom tag (`tool_llm`) to our LLM runnable within the tool. This will allow us to filter events that we'll stream from the compiled graph (`agent`) Runnable below" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "7254310e-7016-45f7-9795-6d52a1160086", - "metadata": {}, - "outputs": [], - "source": [ - "llm = ChatOpenAI(model_name=\"gpt-3.5-turbo\")\n", + "\n", + "\n", + "llm = ChatOpenAI(model_name=\"gpt-4o\")\n", "tools = [get_items]\n", "agent = create_react_agent(llm, tools=tools)" ] }, { "cell_type": "markdown", - "id": "b7d88960-a66b-4699-adee-c12d40b4318a", + "id": "15cb55cc-b59d-4743-b6a3-13db75414d2c", "metadata": {}, "source": [ - "## Stream events from the graph" + "## Using stream_mode=\"messages\"\n", + "\n", + "Using `stream_mode=\"messages\"` is a good option if you don't have any complex LCEL logic inside of nodes (or you don't need super granular progress from within the LCEL chain)." ] }, { "cell_type": "code", - "execution_count": 25, - "id": "ec461f66", + "execution_count": 6, + "id": "4c9cdad3-3e9a-444f-9d9d-eae20b8d3486", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1|.| Books| -| A| collection| of| written| or| printed| works| bound| together| and| typically| held| upright| on| a| shelf| for| easy| access| and| storage|.\n", - "|2|.| Picture| frames| -| Decor|ative| frames| used| to| display| photographs| or| artwork| on| a| shelf|,| adding| a| personal| touch| to| the| space|.\n", - "|3|.| Decor|ative| figur|ines| -| Small| sculptures| or| statues| that| are| placed| on| a| shelf| for| decorative| purposes|,| adding| visual| interest| and| personality| to| the| room|.|" - ] - } - ], + "outputs": [], "source": [ - "from langchain_core.messages import HumanMessage\n", - "\n", - "inputs = [HumanMessage(content=\"what is the weather in sf\")]\n", "final_message = \"\"\n", "async for msg, metadata in agent.astream(\n", " {\"messages\": [(\"human\", \"what items are on the shelf?\")]}, stream_mode=\"messages\"\n", @@ -213,32 +196,58 @@ ] }, { - "cell_type": "code", - "execution_count": 26, - "id": "1b35d72f", + "attachments": {}, + "cell_type": "markdown", + "id": "81656193-1cbf-4721-a8df-0e316fd510e5", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'The items on the shelf are:\\n1. Books\\n2. Picture frames\\n3. Decorative figurines'" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ - "final_message" + "## Using stream events API\n", + "\n", + "For simplicity, the `get_items` tool doesn't use any complex LCEL logic inside it -- it only invokes an LLM.\n", + "\n", + "However, if the tool were more complex (e.g., using a RAG chain inside it), and you wanted to see more granular events from within the chain, then you can use the astream events API.\n", + "\n", + "The example below only illustrates how to invoke the API.\n", + "\n", + "Use async for the astream events API
\n", + "\n", + " You should generally be using `async` code (e.g., using `ainvoke` to invoke the llm) to be able to leverage the astream events API properly.\n", + "
\n", + "