diff --git a/docs/Makefile b/docs/Makefile index 689b2fbe9a2..2d429e03f6d 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -69,9 +69,9 @@ md-sync: generate-references: $(PYTHON) scripts/generate_api_reference_links.py --docs_dir $(OUTPUT_NEW_DOCS_DIR) -build: install-py-deps generate-files copy-infra render md-sync generate-references +build: install-py-deps generate-files copy-infra render md-sync -vercel-build: install-vercel-deps build +vercel-build: install-vercel-deps build generate-references rm -rf docs mv $(OUTPUT_NEW_DOCS_DIR) docs rm -rf build diff --git a/docs/docs/how_to/binding.ipynb b/docs/docs/how_to/binding.ipynb index e71fd2cba35..744998ff6d7 100644 --- a/docs/docs/how_to/binding.ipynb +++ b/docs/docs/how_to/binding.ipynb @@ -16,7 +16,7 @@ "id": "711752cb-4f15-42a3-9838-a0c67f397771", "metadata": {}, "source": [ - "# How to attach runtime arguments to a Runnable\n", + "# How to add default invocation args to a Runnable\n", "\n", ":::info Prerequisites\n", "\n", diff --git a/docs/docs/how_to/dynamic_chain.ipynb b/docs/docs/how_to/dynamic_chain.ipynb new file mode 100644 index 00000000000..b513d7ee3b8 --- /dev/null +++ b/docs/docs/how_to/dynamic_chain.ipynb @@ -0,0 +1,200 @@ +{ + "cells": [ + { + "cell_type": "raw", + "id": "77bf57fb-e990-45f2-8b5f-c76388b05966", + "metadata": {}, + "source": [ + "---\n", + "keywords: [LCEL]\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "50d57bf2-7104-4570-b3e5-90fd71e1bea1", + "metadata": {}, + "source": [ + "# How to create a dynamic (self-constructing) chain\n", + "\n", + ":::info Prerequisites\n", + "\n", + "This guide assumes familiarity with the following:\n", + "- [LangChain Expression Language (LCEL)](/docs/concepts/#langchain-expression-language)\n", + "- [How to turn any function into a runnable](/docs/how_to/functions)\n", + "\n", + ":::\n", + "\n", + "Sometimes we want to construct parts of a chain at runtime, depending on the chain inputs ([routing](/docs/how_to/routing/) is the most common example of this). We can create dynamic chains like this using a very useful property of RunnableLambda's, which is that if a RunnableLambda returns a Runnable, that Runnable is itself invoked. Let's see an example.\n", + "\n", + "```{=mdx}\n", + "import ChatModelTabs from \"@theme/ChatModelTabs\";\n", + "\n", + "\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "406bffc2-86d0-4cb9-9262-5c1e3442397a", + "metadata": {}, + "outputs": [], + "source": [ + "# | echo: false\n", + "\n", + "from langchain_anthropic import ChatAnthropic\n", + "\n", + "llm = ChatAnthropic(model=\"claude-3-sonnet-20240229\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "0ae6692b-983e-40b8-aa2a-6c078d945b9e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"According to the context provided, Egypt's population in 2024 is estimated to be about 111 million.\"" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.output_parsers import StrOutputParser\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_core.runnables import Runnable, RunnablePassthrough, chain\n", + "\n", + "contextualize_instructions = \"\"\"Convert the latest user question into a standalone question given the chat history. Don't answer the question, return the question and nothing else (no descriptive text).\"\"\"\n", + "contextualize_prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\"system\", contextualize_instructions),\n", + " (\"placeholder\", \"{chat_history}\"),\n", + " (\"human\", \"{question}\"),\n", + " ]\n", + ")\n", + "contextualize_question = contextualize_prompt | llm | StrOutputParser()\n", + "\n", + "qa_instructions = (\n", + " \"\"\"Answer the user question given the following context:\\n\\n{context}.\"\"\"\n", + ")\n", + "qa_prompt = ChatPromptTemplate.from_messages(\n", + " [(\"system\", qa_instructions), (\"human\", \"{question}\")]\n", + ")\n", + "\n", + "\n", + "@chain\n", + "def contextualize_if_needed(input_: dict) -> Runnable:\n", + " if input_.get(\"chat_history\"):\n", + " # NOTE: This is returning another Runnable, not an actual output.\n", + " return contextualize_question\n", + " else:\n", + " return RunnablePassthrough()\n", + "\n", + "\n", + "@chain\n", + "def fake_retriever(input_: dict) -> str:\n", + " return \"egypt's population in 2024 is about 111 million\"\n", + "\n", + "\n", + "full_chain = (\n", + " RunnablePassthrough.assign(question=contextualize_if_needed).assign(\n", + " context=fake_retriever\n", + " )\n", + " | qa_prompt\n", + " | llm\n", + " | StrOutputParser()\n", + ")\n", + "\n", + "full_chain.invoke(\n", + " {\n", + " \"question\": \"what about egypt\",\n", + " \"chat_history\": [\n", + " (\"human\", \"what's the population of indonesia\"),\n", + " (\"ai\", \"about 276 million\"),\n", + " ],\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "5076ddb4-4a99-47ad-b549-8ac27ca3e2c6", + "metadata": {}, + "source": [ + "The key here is that `contextualize_if_needed` returns another Runnable and not an actual output. This returned Runnable is itself run when the full chain is executed.\n", + "\n", + "Looking at the trace we can see that, since we passed in chat_history, we executed the contextualize_question chain as part of the full chain: https://smith.langchain.com/public/9e0ae34c-4082-4f3f-beed-34a2a2f4c991/r" + ] + }, + { + "cell_type": "markdown", + "id": "4fe6ca44-a643-4859-a290-be68403f51f0", + "metadata": {}, + "source": [ + "Note that the streaming, batching, etc. capabilities of the returned Runnable are all preserved" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "6def37fa-5105-4090-9b07-77cb488ecd9c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "What\n", + " is\n", + " the\n", + " population\n", + " of\n", + " Egypt\n", + "?\n" + ] + } + ], + "source": [ + "for chunk in contextualize_if_needed.stream(\n", + " {\n", + " \"question\": \"what about egypt\",\n", + " \"chat_history\": [\n", + " (\"human\", \"what's the population of indonesia\"),\n", + " (\"ai\", \"about 276 million\"),\n", + " ],\n", + " }\n", + "):\n", + " print(chunk)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "poetry-venv-2", + "language": "python", + "name": "poetry-venv-2" + }, + "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.9.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/how_to/fallbacks.ipynb b/docs/docs/how_to/fallbacks.ipynb index 0c29961c6ed..6156bad0d3a 100644 --- a/docs/docs/how_to/fallbacks.ipynb +++ b/docs/docs/how_to/fallbacks.ipynb @@ -1,11 +1,21 @@ { "cells": [ + { + "cell_type": "raw", + "id": "018f3868-e60d-4db6-a1c6-c6633c66b1f4", + "metadata": {}, + "source": [ + "---\n", + "keywords: [LCEL, fallbacks]\n", + "---" + ] + }, { "cell_type": "markdown", "id": "19c9cbd6", "metadata": {}, "source": [ - "# Fallbacks\n", + "# How to add fallbacks to a runnable\n", "\n", "When working with language models, you may often encounter issues from the underlying APIs, whether these be rate limiting or downtime. Therefore, as you go to move your LLM applications into production it becomes more and more important to safeguard against these. That's why we've introduced the concept of fallbacks. \n", "\n", @@ -447,7 +457,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.5" + "version": "3.9.1" } }, "nbformat": 4, diff --git a/docs/docs/how_to/index.mdx b/docs/docs/how_to/index.mdx index 9958c3f842f..447cc29e3e2 100644 --- a/docs/docs/how_to/index.mdx +++ b/docs/docs/how_to/index.mdx @@ -19,27 +19,29 @@ For comprehensive descriptions of every class and function see the [API Referenc This highlights functionality that is core to using LangChain. -- [How to: return structured data from an LLM](/docs/how_to/structured_output/) -- [How to: use a chat model to call tools](/docs/how_to/tool_calling/) +- [How to: return structured data from a model](/docs/how_to/structured_output/) +- [How to: use a model to call tools](/docs/how_to/tool_calling/) - [How to: stream runnables](/docs/how_to/streaming) - [How to: debug your LLM apps](/docs/how_to/debugging/) ## LangChain Expression Language (LCEL) -LangChain Expression Language is a way to create arbitrary custom chains. It is built on the [Runnable](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html) protocol. +[LangChain Expression Language](/docs/concepts/#langchain-expression-language-lcel) is a way to create arbitrary custom chains. It is built on the [Runnable](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html) protocol. + +[**LCEL cheatsheet**](/docs/how_to/lcel_cheatsheet/): For a quick overview of how to use the main LCEL primitives. - [How to: chain runnables](/docs/how_to/sequence) - [How to: stream runnables](/docs/how_to/streaming) - [How to: invoke runnables in parallel](/docs/how_to/parallel/) -- [How to: attach runtime arguments to a runnable](/docs/how_to/binding/) -- [How to: run custom functions](/docs/how_to/functions) -- [How to: pass through arguments from one step to the next](/docs/how_to/passthrough) -- [How to: add values to a chain's state](/docs/how_to/assign) -- [How to: configure a chain at runtime](/docs/how_to/configure) -- [How to: add message history](/docs/how_to/message_history) -- [How to: route execution within a chain](/docs/how_to/routing) +- [How to: add default invocation args to runnables](/docs/how_to/binding/) +- [How to: turn any function into a runnable](/docs/how_to/functions) +- [How to: pass through inputs from one chain step to the next](/docs/how_to/passthrough) +- [How to: configure runnable behavior at runtime](/docs/how_to/configure) +- [How to: add message history (memory) to a chain](/docs/how_to/message_history) +- [How to: route between sub-chains](/docs/how_to/routing) +- [How to: create a dynamic (self-constructing) chain](/docs/how_to/dynamic_chain/) - [How to: inspect runnables](/docs/how_to/inspect) -- [How to: add fallbacks](/docs/how_to/fallbacks) +- [How to: add fallbacks to a runnable](/docs/how_to/fallbacks) ## Components diff --git a/docs/docs/how_to/lcel_cheatsheet.ipynb b/docs/docs/how_to/lcel_cheatsheet.ipynb new file mode 100644 index 00000000000..c1f5addf4c8 --- /dev/null +++ b/docs/docs/how_to/lcel_cheatsheet.ipynb @@ -0,0 +1,1142 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f26a4ba9-f29b-452a-85c0-3b2a188f2348", + "metadata": {}, + "source": [ + "# LangChain Expression Language Cheatsheet\n", + "\n", + "This is a quick reference for all the most important LCEL primitives. For more advanced usage see the [LCEL how-to guides](/docs/how_to/#langchain-expression-language-lcel) and the [full API reference](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html).\n", + "\n", + "### Invoke a runnable\n", + "#### [Runnable.invoke()](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.invoke) / [Runnable.ainvoke()](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.ainvoke)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b3ac3ad1-3c0e-4279-8fde-125809af9d2a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'5'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda\n", + "\n", + "runnable = RunnableLambda(lambda x: str(x))\n", + "runnable.invoke(5)\n", + "\n", + "# Async variant:\n", + "# await runnable.ainvoke(5)" + ] + }, + { + "cell_type": "markdown", + "id": "aa74c79a-1bf6-4015-84bc-d0df6e6a8433", + "metadata": {}, + "source": [ + "### Batch a runnable\n", + "#### [Runnable.batch()](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.batch) / [Runnable.abatch()](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.abatch)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "3a184890-da09-4ff7-92f9-0d29ca571ae4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['7', '8', '9']" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda\n", + "\n", + "runnable = RunnableLambda(lambda x: str(x))\n", + "runnable.batch([7, 8, 9])\n", + "\n", + "# Async variant:\n", + "# await runnable.abatch([7, 8, 9])" + ] + }, + { + "cell_type": "markdown", + "id": "b716b97f-cb58-447a-bef5-96202563cd2d", + "metadata": {}, + "source": [ + "### Stream a runnable\n", + "#### [Runnable.stream()](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.stream) / [Runnable.astream()](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.astream)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "983aa18b-e44d-4603-aaea-94e1e2339001", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "1\n", + "2\n", + "3\n", + "4\n" + ] + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda\n", + "\n", + "\n", + "def func(x):\n", + " for y in x:\n", + " yield str(y)\n", + "\n", + "\n", + "runnable = RunnableLambda(func)\n", + "\n", + "for chunk in runnable.stream(range(5)):\n", + " print(chunk)\n", + "\n", + "# Async variant:\n", + "# async for chunk in await runnable.astream(range(5)):\n", + "# print(chunk)" + ] + }, + { + "cell_type": "markdown", + "id": "6dc6bd08-98f3-4df6-9758-08b443e4328b", + "metadata": {}, + "source": [ + "### Compose runnables\n", + "#### Pipe operator `|`" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "4f744b0b-16ae-43f5-9856-789973457c96", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'foo': 2}, {'foo': 2}]" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda\n", + "\n", + "runnable1 = RunnableLambda(lambda x: {\"foo\": x})\n", + "runnable2 = RunnableLambda(lambda x: [x] * 2)\n", + "\n", + "chain = runnable1 | runnable2\n", + "\n", + "chain.invoke(2)" + ] + }, + { + "cell_type": "markdown", + "id": "b01e8694-f820-4457-a27a-7220f789bb2c", + "metadata": {}, + "source": [ + "### Invoke runnables in parallel\n", + "#### [RunnableParallel](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.RunnableParallel.html)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "89e509e5-a9a5-4e56-b6dd-c4f23543c3c8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'first': {'foo': 2}, 'second': [2, 2]}" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda, RunnableParallel\n", + "\n", + "runnable1 = RunnableLambda(lambda x: {\"foo\": x})\n", + "runnable2 = RunnableLambda(lambda x: [x] * 2)\n", + "\n", + "chain = RunnableParallel(first=runnable1, second=runnable2)\n", + "\n", + "chain.invoke(2)" + ] + }, + { + "cell_type": "markdown", + "id": "103f350b-84b3-421f-b64f-c01575a422bf", + "metadata": {}, + "source": [ + "### Turn any function into a runnable\n", + "#### [RunnableLambda](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.RunnableLambda.html)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "a9c0e43a-8eb5-4985-ad95-43f12c3e05e9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda\n", + "\n", + "\n", + "def func(x):\n", + " return x + 5\n", + "\n", + "\n", + "runnable = RunnableLambda(func)\n", + "runnable.invoke(2)" + ] + }, + { + "cell_type": "markdown", + "id": "20399b82-e417-403d-a53b-00f02a6ef2c6", + "metadata": {}, + "source": [ + "### Merge input and output dicts\n", + "#### [RunnablePassthrough.assign](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.passthrough.RunnablePassthrough.html)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "ab05d376-9abb-4f26-916a-9aea062e4817", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'foo': 10, 'bar': 17}" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda, RunnablePassthrough\n", + "\n", + "runnable1 = RunnableLambda(lambda x: x[\"foo\"] + 7)\n", + "\n", + "chain = RunnablePassthrough.assign(bar=runnable1)\n", + "\n", + "chain.invoke({\"foo\": 10})" + ] + }, + { + "cell_type": "markdown", + "id": "7f680f48-654a-44d1-86e3-13e1eb0955cb", + "metadata": {}, + "source": [ + "### Include input dict in output dict\n", + "#### [RunnablePassthrough](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.passthrough.RunnablePassthrough.html)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "3ec00283-e674-461e-a5d1-4876555a2a58", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'bar': 17, 'baz': {'foo': 10}}" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.runnables import (\n", + " RunnableLambda,\n", + " RunnableParallel,\n", + " RunnablePassthrough,\n", + ")\n", + "\n", + "runnable1 = RunnableLambda(lambda x: x[\"foo\"] + 7)\n", + "\n", + "chain = RunnableParallel(bar=runnable1, baz=RunnablePassthrough())\n", + "\n", + "chain.invoke({\"foo\": 10})" + ] + }, + { + "cell_type": "markdown", + "id": "baa4e967-fcfd-4176-a720-6916fe0df130", + "metadata": {}, + "source": [ + "### Add default invocation args\n", + "#### [Runnable.bind](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.bind)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "6c03ce55-5258-4361-857e-d6785c968624", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'bar': 'hello', 'foo': 'bye'}" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from typing import Optional\n", + "\n", + "from langchain_core.runnables import RunnableLambda\n", + "\n", + "\n", + "def func(main_arg: dict, other_arg: Optional[str] = None) -> dict:\n", + " if other_arg:\n", + " return {**main_arg, **{\"foo\": other_arg}}\n", + " return main_arg\n", + "\n", + "\n", + "runnable1 = RunnableLambda(func)\n", + "bound_runnable1 = runnable1.bind(other_arg=\"bye\")\n", + "\n", + "bound_runnable1.invoke({\"bar\": \"hello\"})" + ] + }, + { + "cell_type": "markdown", + "id": "cad5e0af-007e-47aa-93d4-f06bd837c3fb", + "metadata": {}, + "source": [ + "### Add fallbacks\n", + "#### [Runnable.with_fallbacks](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.with_fallbacks)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "5d132d96-802b-4952-a6cc-2caaf4612d0a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'5foo'" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda\n", + "\n", + "runnable1 = RunnableLambda(lambda x: x + \"foo\")\n", + "runnable2 = RunnableLambda(lambda x: str(x) + \"foo\")\n", + "\n", + "chain = runnable1.with_fallbacks([runnable2])\n", + "\n", + "chain.invoke(5)" + ] + }, + { + "cell_type": "markdown", + "id": "809f6437-4e20-48b9-bdcb-7f9ea6463a19", + "metadata": {}, + "source": [ + "### Add retries\n", + "#### [Runnable.with_retry](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.with_retry)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "49a75c66-d335-4115-9d8f-ca07d69223c4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "attempt with counter=0\n", + "attempt with counter=1\n" + ] + }, + { + "data": { + "text/plain": [ + "2.0" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda\n", + "\n", + "counter = -1\n", + "\n", + "\n", + "def func(x):\n", + " global counter\n", + " counter += 1\n", + " print(f\"attempt with {counter=}\")\n", + " return x / counter\n", + "\n", + "\n", + "chain = RunnableLambda(func).with_retry(stop_after_attempt=2)\n", + "\n", + "chain.invoke(2)" + ] + }, + { + "cell_type": "markdown", + "id": "741934ee-e97f-497d-b42f-371c79739a12", + "metadata": {}, + "source": [ + "### Configure runnable execution\n", + "#### [RunnableConfig](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.config.RunnableConfig.html)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "d85e357e-c125-4199-98d1-bd0db40e4ac0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'first': {'foo': 7}, 'second': [7, 7], 'third': '7'}" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda, RunnableParallel\n", + "\n", + "runnable1 = RunnableLambda(lambda x: {\"foo\": x})\n", + "runnable2 = RunnableLambda(lambda x: [x] * 2)\n", + "runnable3 = RunnableLambda(lambda x: str(x))\n", + "\n", + "chain = RunnableParallel(first=runnable1, second=runnable2, third=runnable3)\n", + "\n", + "chain.invoke(7, config={\"max_concurrency\": 2})" + ] + }, + { + "cell_type": "markdown", + "id": "8604edb4-4ffa-4cc7-89ba-e0c6947118ab", + "metadata": {}, + "source": [ + "### Add default config to runnable\n", + "#### [Runnable.with_config](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.with_config)" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "dfe8306c-d77c-479a-90ae-464db2b62605", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'first': {'foo': 7}, 'second': [7, 7], 'third': '7'}" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda, RunnableParallel\n", + "\n", + "runnable1 = RunnableLambda(lambda x: {\"foo\": x})\n", + "runnable2 = RunnableLambda(lambda x: [x] * 2)\n", + "runnable3 = RunnableLambda(lambda x: str(x))\n", + "\n", + "chain = RunnableParallel(first=runnable1, second=runnable2, third=runnable3)\n", + "configured_chain = chain.with_config(max_concurrency=2)\n", + "\n", + "chain.invoke(7)" + ] + }, + { + "cell_type": "markdown", + "id": "0f3d3bb1-b4b3-4acd-9dd8-da114e514fff", + "metadata": {}, + "source": [ + "### Make runnable attributes configurable\n", + "#### [Runnable.with_configurable_fields](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.RunnableSerializable.html#langchain_core.runnables.base.RunnableSerializable.configurable_fields)" + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "id": "ca265c51-6192-4b5d-bf4e-048b6630abc6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'not bar': 3}" + ] + }, + "execution_count": 110, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from typing import Any, Optional\n", + "\n", + "from langchain_core.runnables import (\n", + " ConfigurableField,\n", + " RunnableConfig,\n", + " RunnableSerializable,\n", + ")\n", + "\n", + "\n", + "class FooRunnable(RunnableSerializable[dict, dict]):\n", + " output_key: str\n", + "\n", + " def invoke(\n", + " self, input: Any, config: Optional[RunnableConfig] = None, **kwargs: Any\n", + " ) -> list:\n", + " return self._call_with_config(self.subtract_seven, input, config, **kwargs)\n", + "\n", + " def subtract_seven(self, input: dict) -> dict:\n", + " return {self.output_key: input[\"foo\"] - 7}\n", + "\n", + "\n", + "runnable1 = FooRunnable(output_key=\"bar\")\n", + "configurable_runnable1 = runnable1.configurable_fields(\n", + " output_key=ConfigurableField(id=\"output_key\")\n", + ")\n", + "\n", + "configurable_runnable1.invoke(\n", + " {\"foo\": 10}, config={\"configurable\": {\"output_key\": \"not bar\"}}\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "id": "e1cf0b01-dc03-40b7-9e0b-629895daa8e5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'bar': 3}" + ] + }, + "execution_count": 111, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "configurable_runnable1.invoke({\"foo\": 10})" + ] + }, + { + "cell_type": "markdown", + "id": "c7b86f34-4098-4c43-9cde-407dc0e03c0d", + "metadata": {}, + "source": [ + "### Make chain components configurable\n", + "#### [Runnable.with_configurable_alternatives](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.RunnableSerializable.html#langchain_core.runnables.base.RunnableSerializable.configurable_alternatives)" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "id": "98acdc84-b395-4dee-a9c7-d2f88a2486e3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"{'foo': 7}\"" + ] + }, + "execution_count": 106, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from typing import Any, Optional\n", + "\n", + "from langchain_core.runnables import RunnableConfig, RunnableLambda, RunnableParallel\n", + "\n", + "\n", + "class ListRunnable(RunnableSerializable[Any, list]):\n", + " def invoke(\n", + " self, input: Any, config: Optional[RunnableConfig] = None, **kwargs: Any\n", + " ) -> list:\n", + " return self._call_with_config(self.listify, input, config, **kwargs)\n", + "\n", + " def listify(self, input: Any) -> list:\n", + " return [input]\n", + "\n", + "\n", + "class StrRunnable(RunnableSerializable[Any, str]):\n", + " def invoke(\n", + " self, input: Any, config: Optional[RunnableConfig] = None, **kwargs: Any\n", + " ) -> list:\n", + " return self._call_with_config(self.strify, input, config, **kwargs)\n", + "\n", + " def strify(self, input: Any) -> str:\n", + " return str(input)\n", + "\n", + "\n", + "runnable1 = RunnableLambda(lambda x: {\"foo\": x})\n", + "\n", + "configurable_runnable = ListRunnable().configurable_alternatives(\n", + " ConfigurableField(id=\"second_step\"), default_key=\"list\", string=StrRunnable()\n", + ")\n", + "chain = runnable1 | configurable_runnable\n", + "\n", + "chain.invoke(7, config={\"configurable\": {\"second_step\": \"string\"}})" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "id": "6e76f8dd-96e2-4b69-8e20-5a0f82c60c9f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'foo': 7}]" + ] + }, + "execution_count": 107, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke(7)" + ] + }, + { + "cell_type": "markdown", + "id": "33249dee-14dc-4c72-b6ba-a3f10678ab9c", + "metadata": {}, + "source": [ + "### Build a chain dynamically based on input" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "id": "4611b7ad-c29c-446c-8886-324bfd00eb41", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'foo': 7}" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda, RunnableParallel\n", + "\n", + "runnable1 = RunnableLambda(lambda x: {\"foo\": x})\n", + "runnable2 = RunnableLambda(lambda x: [x] * 2)\n", + "\n", + "chain = RunnableLambda(lambda x: runnable1 if x > 6 else runnable2)\n", + "\n", + "chain.invoke(7)" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "id": "9655f494-2fba-4c26-a5d8-5683cc61e80d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[5, 5]" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke(5)" + ] + }, + { + "cell_type": "markdown", + "id": "f1263fe5-6561-419b-a936-687f8b274a5d", + "metadata": {}, + "source": [ + "### Generate a stream of events\n", + "#### [Runnable.astream_events](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.astream_events)" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "id": "b921264a-29e2-41eb-949c-f1bbabe63e54", + "metadata": {}, + "outputs": [], + "source": [ + "# | echo: false\n", + "\n", + "import nest_asyncio\n", + "\n", + "nest_asyncio.apply()" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "id": "1988b1b2-b189-43c9-8ffd-d7f275881065", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "event=on_chain_start | name=RunnableSequence | data={'input': 'bar'}\n", + "event=on_chain_start | name=first | data={}\n", + "event=on_chain_stream | name=first | data={'chunk': {'foo': 'bar'}}\n", + "event=on_chain_start | name=second | data={}\n", + "event=on_chain_end | name=first | data={'output': {'foo': 'bar'}, 'input': 'bar'}\n", + "event=on_chain_stream | name=second | data={'chunk': {'foo': 'bar'}}\n", + "event=on_chain_stream | name=RunnableSequence | data={'chunk': {'foo': 'bar'}}\n", + "event=on_chain_stream | name=second | data={'chunk': {'foo': 'bar'}}\n", + "event=on_chain_stream | name=RunnableSequence | data={'chunk': {'foo': 'bar'}}\n", + "event=on_chain_stream | name=second | data={'chunk': {'foo': 'bar'}}\n", + "event=on_chain_stream | name=RunnableSequence | data={'chunk': {'foo': 'bar'}}\n", + "event=on_chain_stream | name=second | data={'chunk': {'foo': 'bar'}}\n", + "event=on_chain_stream | name=RunnableSequence | data={'chunk': {'foo': 'bar'}}\n", + "event=on_chain_stream | name=second | data={'chunk': {'foo': 'bar'}}\n", + "event=on_chain_stream | name=RunnableSequence | data={'chunk': {'foo': 'bar'}}\n", + "event=on_chain_end | name=second | data={'output': {'foo': 'bar'}, 'input': {'foo': 'bar'}}\n", + "event=on_chain_end | name=RunnableSequence | data={'output': {'foo': 'bar'}}\n" + ] + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda, RunnableParallel\n", + "\n", + "runnable1 = RunnableLambda(lambda x: {\"foo\": x}, name=\"first\")\n", + "\n", + "\n", + "async def func(x):\n", + " for _ in range(5):\n", + " yield x\n", + "\n", + "\n", + "runnable2 = RunnableLambda(func, name=\"second\")\n", + "\n", + "chain = runnable1 | runnable2\n", + "\n", + "async for event in chain.astream_events(\"bar\", version=\"v2\"):\n", + " print(f\"event={event['event']} | name={event['name']} | data={event['data']}\")" + ] + }, + { + "cell_type": "markdown", + "id": "a7575005-ffbd-4a52-a280-a521799fed5d", + "metadata": {}, + "source": [ + "### Yield batched outputs as they complete\n", + "#### [Runnable.batch_as_completed](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.batch_as_completed) / [Runnable.abatch_as_completed](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.abatch_as_completed)" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "id": "826dedad-d654-4f85-b8b4-a53eda2f2837", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "slept 1\n", + "1 None\n", + "slept 5\n", + "0 None\n" + ] + } + ], + "source": [ + "import time\n", + "\n", + "from langchain_core.runnables import RunnableLambda, RunnableParallel\n", + "\n", + "runnable1 = RunnableLambda(lambda x: time.sleep(x) or print(f\"slept {x}\"))\n", + "\n", + "for idx, result in runnable1.batch_as_completed([5, 1]):\n", + " print(idx, result)\n", + "\n", + "# Async variant:\n", + "# async for idx, result in runnable1.abatch_as_completed([5, 1]):\n", + "# print(idx, result)" + ] + }, + { + "cell_type": "markdown", + "id": "cc1cacde-b35e-474a-a14a-ed9e8a858ba8", + "metadata": {}, + "source": [ + "### Return subset of output dict\n", + "#### [Runnable.pick](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.pick)" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "id": "78dd3ed5-5095-4698-ba4f-d0b73f12a608", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'foo': 7, 'bar': 'hi'}" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda, RunnablePassthrough\n", + "\n", + "runnable1 = RunnableLambda(lambda x: x[\"baz\"] + 5)\n", + "chain = RunnablePassthrough.assign(foo=runnable1).pick([\"foo\", \"bar\"])\n", + "\n", + "chain.invoke({\"bar\": \"hi\", \"baz\": 2})" + ] + }, + { + "cell_type": "markdown", + "id": "5587dbda-d6d7-4480-b2e9-7541af076d36", + "metadata": {}, + "source": [ + "### Declaratively make a batched version of a runnable\n", + "#### [Runnable.map](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.map)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "fd020416-faf0-4343-945c-823d879d8431", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[5, 6, 7]" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda\n", + "\n", + "runnable1 = RunnableLambda(lambda x: list(range(x)))\n", + "runnable2 = RunnableLambda(lambda x: x + 5)\n", + "\n", + "chain = runnable1 | runnable2.map()\n", + "\n", + "chain.invoke(3)" + ] + }, + { + "cell_type": "markdown", + "id": "1e4ed5c3-244e-4491-adbe-83af3fc14265", + "metadata": {}, + "source": [ + "### Get a graph representation of a runnable\n", + "#### [Runnable.get_graph](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.get_graph)" + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "id": "11ef1419-a2ee-41bd-a74f-c057a6d737ac", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " +-------------+ \n", + " | LambdaInput | \n", + " +-------------+ \n", + " * \n", + " * \n", + " * \n", + " +------------------------------+ \n", + " | Lambda(lambda x: {'foo': x}) | \n", + " +------------------------------+ \n", + " * \n", + " * \n", + " * \n", + " +-----------------------------+ \n", + " | ParallelInput | \n", + " +-----------------------------+ \n", + " **** *** \n", + " **** **** \n", + " ** ** \n", + "+---------------------------+ +--------------------------+ \n", + "| Lambda(lambda x: [x] * 2) | | Lambda(lambda x: str(x)) | \n", + "+---------------------------+ +--------------------------+ \n", + " **** *** \n", + " **** **** \n", + " ** ** \n", + " +------------------------------+ \n", + " | ParallelOutput | \n", + " +------------------------------+ \n" + ] + } + ], + "source": [ + "from langchain_core.runnables import RunnableLambda, RunnableParallel\n", + "\n", + "runnable1 = RunnableLambda(lambda x: {\"foo\": x})\n", + "runnable2 = RunnableLambda(lambda x: [x] * 2)\n", + "runnable3 = RunnableLambda(lambda x: str(x))\n", + "\n", + "chain = runnable1 | RunnableParallel(second=runnable2, third=runnable3)\n", + "\n", + "chain.get_graph().print_ascii()" + ] + }, + { + "cell_type": "markdown", + "id": "a2728a5e-e5b4-452d-9e90-afb00f06df44", + "metadata": {}, + "source": [ + "### Get all prompts in a chain\n", + "#### [Runnable.get_prompts](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.get_prompts)" + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "id": "9c35a8ec-e0ac-4cdd-a921-19161a66f5bf", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "**prompt i=0**\n", + "\n", + "================================ System Message ================================\n", + "\n", + "good ai\n", + "\n", + "================================ Human Message =================================\n", + "\n", + "{input}\n", + "\n", + "\n", + "\n", + "\n", + "**prompt i=1**\n", + "\n", + "================================ System Message ================================\n", + "\n", + "really good ai\n", + "\n", + "================================ Human Message =================================\n", + "\n", + "{input}\n", + "\n", + "================================== AI Message ==================================\n", + "\n", + "{ai_output}\n", + "\n", + "================================ Human Message =================================\n", + "\n", + "{input2}\n", + "\n", + "\n", + "\n", + "\n" + ] + } + ], + "source": [ + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_core.runnables import RunnableLambda\n", + "\n", + "prompt1 = ChatPromptTemplate.from_messages(\n", + " [(\"system\", \"good ai\"), (\"human\", \"{input}\")]\n", + ")\n", + "prompt2 = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\"system\", \"really good ai\"),\n", + " (\"human\", \"{input}\"),\n", + " (\"ai\", \"{ai_output}\"),\n", + " (\"human\", \"{input2}\"),\n", + " ]\n", + ")\n", + "fake_llm = RunnableLambda(lambda prompt: \"i am good ai\")\n", + "chain = prompt1.assign(ai_output=fake_llm) | prompt2 | fake_llm\n", + "\n", + "for i, prompt in enumerate(chain.get_prompts()):\n", + " print(f\"**prompt {i=}**\\n\")\n", + " print(prompt.pretty_repr())\n", + " print(\"\\n\" * 3)" + ] + }, + { + "cell_type": "markdown", + "id": "e5add050-b8e0-48eb-94cb-74afd88ed1a8", + "metadata": {}, + "source": [ + "### Add lifecycle listeners\n", + "#### [Runnable.with_listeners](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.with_listeners)" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "id": "faa37955-d9ce-46a7-bc39-301bf84421d6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "start_time: 2024-05-17 23:04:00.951065+00:00\n", + "end_time: 2024-05-17 23:04:02.958765+00:00\n" + ] + } + ], + "source": [ + "import time\n", + "\n", + "from langchain_core.runnables import RunnableLambda\n", + "from langchain_core.tracers.schemas import Run\n", + "\n", + "\n", + "def on_start(run_obj: Run):\n", + " print(\"start_time:\", run_obj.start_time)\n", + "\n", + "\n", + "def on_end(run_obj: Run):\n", + " print(\"end_time:\", run_obj.end_time)\n", + "\n", + "\n", + "runnable1 = RunnableLambda(lambda x: time.sleep(x))\n", + "chain = runnable1.with_listeners(on_start=on_start, on_end=on_end)\n", + "chain.invoke(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7186123c-99ce-45ed-a64f-9c627b09f92d", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "poetry-venv-2", + "language": "python", + "name": "poetry-venv-2" + }, + "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.9.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/how_to/qa_chat_history_how_to.ipynb b/docs/docs/how_to/qa_chat_history_how_to.ipynb index d0bc3316e6c..52d1c311f8c 100644 --- a/docs/docs/how_to/qa_chat_history_how_to.ipynb +++ b/docs/docs/how_to/qa_chat_history_how_to.ipynb @@ -941,7 +941,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.4" + "version": "3.9.1" } }, "nbformat": 4, diff --git a/docs/docs/how_to/routing.ipynb b/docs/docs/how_to/routing.ipynb index 7929cda3155..6fd2e24258e 100644 --- a/docs/docs/how_to/routing.ipynb +++ b/docs/docs/how_to/routing.ipynb @@ -16,7 +16,7 @@ "id": "4b47436a", "metadata": {}, "source": [ - "# How to route execution within a chain\n", + "# How to route between sub-chains\n", "\n", ":::info Prerequisites\n", "\n", diff --git a/docs/docs/how_to/sequence.ipynb b/docs/docs/how_to/sequence.ipynb index 1b34aef88bd..8df1dcab7cc 100644 --- a/docs/docs/how_to/sequence.ipynb +++ b/docs/docs/how_to/sequence.ipynb @@ -30,7 +30,7 @@ "\n", "The resulting [`RunnableSequence`](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.RunnableSequence.html) is itself a runnable, which means it can be invoked, streamed, or further chained just like any other runnable. Advantages of chaining runnables in this way are efficient streaming (the sequence will stream output as soon as it is available), and debugging and tracing with tools like [LangSmith](/docs/how_to/debugging).\n", "\n", - "## The pipe operator\n", + "## The pipe operator: `|`\n", "\n", "To show off how this works, let's go through an example. We'll walk through a common pattern in LangChain: using a [prompt template](/docs/how_to#prompt-templates) to format input into a [chat model](/docs/how_to#chat-models), and finally converting the chat message output into a string with an [output parser](/docs/how_to#output-parsers).\n", "\n", @@ -230,11 +230,28 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Next steps\n", + "Or the abbreviated:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "composed_chain_with_pipe = RunnableParallel({\"joke\": chain}).pipe(\n", + " analysis_prompt, model, StrOutputParser()\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Related\n", "\n", - "You now know some ways to chain two runnables together.\n", - "\n", - "To learn more, see the other how-to guides on runnables in this section." + "- [Streaming](/docs/how_to/streaming/): Check out the streaming guide to understand the streaming behavior of a chain\n", + "- " ] } ], diff --git a/docs/docs/how_to/streaming.ipynb b/docs/docs/how_to/streaming.ipynb index 3bcabb6ed21..13a06637e6d 100644 --- a/docs/docs/how_to/streaming.ipynb +++ b/docs/docs/how_to/streaming.ipynb @@ -1524,7 +1524,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.4" + "version": "3.9.1" } }, "nbformat": 4, diff --git a/docs/docs/how_to/tool_calling.ipynb b/docs/docs/how_to/tool_calling.ipynb index ccdc8b88e95..81ae98cf639 100644 --- a/docs/docs/how_to/tool_calling.ipynb +++ b/docs/docs/how_to/tool_calling.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# How to use a chat model to call tools\n", + "# How to use a model to call tools\n", "\n", ":::info Prerequisites\n", "\n", diff --git a/libs/core/langchain_core/runnables/base.py b/libs/core/langchain_core/runnables/base.py index 904ab1047d9..14fb672d180 100644 --- a/libs/core/langchain_core/runnables/base.py +++ b/libs/core/langchain_core/runnables/base.py @@ -95,7 +95,7 @@ if TYPE_CHECKING: RunLog, RunLogPatch, ) - from langchain_core.tracers.root_listeners import Listener + from langchain_core.tracers.schemas import Run Other = TypeVar("Other") @@ -1258,9 +1258,15 @@ class Runnable(Generic[Input, Output], ABC): def with_listeners( self, *, - on_start: Optional[Listener] = None, - on_end: Optional[Listener] = None, - on_error: Optional[Listener] = None, + on_start: Optional[ + Union[Callable[[Run], None], Callable[[Run, RunnableConfig], None]] + ] = None, + on_end: Optional[ + Union[Callable[[Run], None], Callable[[Run, RunnableConfig], None]] + ] = None, + on_error: Optional[ + Union[Callable[[Run], None], Callable[[Run, RunnableConfig], None]] + ] = None, ) -> Runnable[Input, Output]: """ Bind lifecycle listeners to a Runnable, returning a new Runnable. @@ -1276,22 +1282,26 @@ class Runnable(Generic[Input, Output], ABC): Example: .. code-block:: python + from langchain_core.runnables import RunnableLambda + from langchain_core.tracers.schemas import Run + import time def test_runnable(time_to_sleep : int): time.sleep(time_to_sleep) - def fn_start(run_obj : Runnable): + def fn_start(run_obj: Run): print("start_time:", run_obj.start_time) - def fn_end(run_obj : Runnable): + def fn_end(run_obj: Run): print("end_time:", run_obj.end_time) - RunnableLambda(test_runnable).with_listeners( + chain = RunnableLambda(test_runnable).with_listeners( on_start=fn_start, on_end=fn_end - ).invoke(2) + ) + chain.invoke(2) """ from langchain_core.tracers.root_listeners import RootListenersTracer @@ -1339,6 +1349,7 @@ class Runnable(Generic[Input, Output], ABC): Example: .. code-block:: python + from langchain_core.runnables import RunnableLambda count = 0 @@ -4239,9 +4250,15 @@ class RunnableEach(RunnableEachBase[Input, Output]): def with_listeners( self, *, - on_start: Optional[Listener] = None, - on_end: Optional[Listener] = None, - on_error: Optional[Listener] = None, + on_start: Optional[ + Union[Callable[[Run], None], Callable[[Run, RunnableConfig], None]] + ] = None, + on_end: Optional[ + Union[Callable[[Run], None], Callable[[Run, RunnableConfig], None]] + ] = None, + on_error: Optional[ + Union[Callable[[Run], None], Callable[[Run, RunnableConfig], None]] + ] = None, ) -> RunnableEach[Input, Output]: """ Bind lifecycle listeners to a Runnable, returning a new Runnable. @@ -4729,9 +4746,15 @@ class RunnableBinding(RunnableBindingBase[Input, Output]): def with_listeners( self, *, - on_start: Optional[Listener] = None, - on_end: Optional[Listener] = None, - on_error: Optional[Listener] = None, + on_start: Optional[ + Union[Callable[[Run], None], Callable[[Run, RunnableConfig], None]] + ] = None, + on_end: Optional[ + Union[Callable[[Run], None], Callable[[Run, RunnableConfig], None]] + ] = None, + on_error: Optional[ + Union[Callable[[Run], None], Callable[[Run, RunnableConfig], None]] + ] = None, ) -> Runnable[Input, Output]: """Bind lifecycle listeners to a Runnable, returning a new Runnable.