From ce0a588ae60ae5a115f7693c0040d29d780df360 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Fri, 29 Mar 2024 14:23:55 -0700 Subject: [PATCH] docs[minor]: Add chat model tabs to docs pages (#19589) --- .../expression_language/get_started.ipynb | 44 +- docs/docs/expression_language/why.ipynb | 2349 +++++++++-------- .../model_io/chat/chat_model_caching.ipynb | 31 +- .../model_io/chat/function_calling.mdx | 300 +-- .../modules/model_io/chat/quick_start.ipynb | 60 +- .../question_answering/chat_history.ipynb | 2 +- .../question_answering/quickstart.mdx | 192 +- docs/src/theme/ChatModelTabs.js | 87 +- docs/vercel_build.sh | 6 +- 9 files changed, 1503 insertions(+), 1568 deletions(-) diff --git a/docs/docs/expression_language/get_started.ipynb b/docs/docs/expression_language/get_started.ipynb index a7c03adb58..16d10e379d 100644 --- a/docs/docs/expression_language/get_started.ipynb +++ b/docs/docs/expression_language/get_started.ipynb @@ -40,6 +40,33 @@ "%pip install --upgrade --quiet langchain-core langchain-community langchain-openai" ] }, + { + "cell_type": "markdown", + "id": "c3d54f72", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "import ChatModelTabs from \"@theme/ChatModelTabs\";\n", + "\n", + "\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f9eed8e8", + "metadata": {}, + "outputs": [], + "source": [ + "# | output: false\n", + "# | echo: false\n", + "\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "model = ChatOpenAI(model=\"gpt-4\")" + ] + }, { "cell_type": "code", "execution_count": 1, @@ -60,10 +87,8 @@ "source": [ "from langchain_core.output_parsers import StrOutputParser\n", "from langchain_core.prompts import ChatPromptTemplate\n", - "from langchain_openai import ChatOpenAI\n", "\n", "prompt = ChatPromptTemplate.from_template(\"tell me a short joke about {topic}\")\n", - "model = ChatOpenAI(model=\"gpt-4\")\n", "output_parser = StrOutputParser()\n", "\n", "chain = prompt | model | output_parser\n", @@ -324,6 +349,16 @@ "For our next example, we want to run a retrieval-augmented generation chain to add some context when responding to questions." ] }, + { + "cell_type": "markdown", + "id": "b8fe8eb4", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "```" + ] + }, { "cell_type": "code", "execution_count": null, @@ -338,7 +373,7 @@ "from langchain_core.output_parsers import StrOutputParser\n", "from langchain_core.prompts import ChatPromptTemplate\n", "from langchain_core.runnables import RunnableParallel, RunnablePassthrough\n", - "from langchain_openai import ChatOpenAI, OpenAIEmbeddings\n", + "from langchain_openai import OpenAIEmbeddings\n", "\n", "vectorstore = DocArrayInMemorySearch.from_texts(\n", " [\"harrison worked at kensho\", \"bears like to eat honey\"],\n", @@ -352,7 +387,6 @@ "Question: {question}\n", "\"\"\"\n", "prompt = ChatPromptTemplate.from_template(template)\n", - "model = ChatOpenAI()\n", "output_parser = StrOutputParser()\n", "\n", "setup_and_retrieval = RunnableParallel(\n", @@ -495,7 +529,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.4" + "version": "3.11.0" } }, "nbformat": 4, diff --git a/docs/docs/expression_language/why.ipynb b/docs/docs/expression_language/why.ipynb index f6fb1f12f6..be492c448d 100644 --- a/docs/docs/expression_language/why.ipynb +++ b/docs/docs/expression_language/why.ipynb @@ -1,1143 +1,1210 @@ { - "cells": [ - { - "cell_type": "raw", - "id": "bc346658-6820-413a-bd8f-11bd3082fe43", - "metadata": {}, - "source": [ - "---\n", - "sidebar_position: 0.5\n", - "title: Why use LCEL\n", - "---\n", - "\n", - "import { ColumnContainer, Column } from \\\"@theme/Columns\\\";" - ] - }, - { - "cell_type": "markdown", - "id": "919a5ae2-ed21-4923-b98f-723c111bac67", - "metadata": {}, - "source": [ - ":::{.callout-tip} \n", - "We recommend reading the LCEL [Get started](/docs/expression_language/get_started) section first.\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "id": "f331037f-be3f-4782-856f-d55dab952488", - "metadata": {}, - "source": [ - "LCEL makes it easy to build complex chains from basic components. It does this by providing:\n", - "1. **A unified interface**: Every LCEL object implements the `Runnable` interface, which defines a common set of invocation methods (`invoke`, `batch`, `stream`, `ainvoke`, ...). This makes it possible for chains of LCEL objects to also automatically support these invocations. That is, every chain of LCEL objects is itself an LCEL object.\n", - "2. **Composition primitives**: LCEL provides a number of primitives that make it easy to compose chains, parallelize components, add fallbacks, dynamically configure chain internal, and more.\n", - "\n", - "To better understand the value of LCEL, it's helpful to see it in action and think about how we might recreate similar functionality without it. In this walkthrough we'll do just that with our [basic example](/docs/expression_language/get_started#basic_example) from the get started section. We'll take our simple prompt + model chain, which under the hood already defines a lot of functionality, and see what it would take to recreate all of it." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b99b47ec", - "metadata": {}, - "outputs": [], - "source": [ - "%pip install --upgrade --quiet langchain-core langchain-openai langchain-anthropic" - ] - }, - { - "cell_type": "markdown", - "id": "e3621b62-a037-42b8-8faa-59575608bb8b", - "metadata": {}, - "source": [ - "## Invoke\n", - "In the simplest case, we just want to pass in a topic string and get back a joke string:\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "#### Without LCEL\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e628905c-430e-4e4a-9d7c-c91d2f42052e", - "metadata": {}, - "outputs": [], - "source": [ - "from typing import List\n", - "\n", - "import openai\n", - "\n", - "\n", - "prompt_template = \"Tell me a short joke about {topic}\"\n", - "client = openai.OpenAI()\n", - "\n", - "def call_chat_model(messages: List[dict]) -> str:\n", - " response = client.chat.completions.create(\n", - " model=\"gpt-3.5-turbo\", \n", - " messages=messages,\n", - " )\n", - " return response.choices[0].message.content\n", - "\n", - "def invoke_chain(topic: str) -> str:\n", - " prompt_value = prompt_template.format(topic=topic)\n", - " messages = [{\"role\": \"user\", \"content\": prompt_value}]\n", - " return call_chat_model(messages)\n", - "\n", - "invoke_chain(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "cdc3b527-c09e-4c77-9711-c3cc4506cd95", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0d2a7cf8-1bc7-405c-bb0d-f2ab2ba3b6ab", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_openai import ChatOpenAI\n", - "from langchain_core.prompts import ChatPromptTemplate\n", - "from langchain_core.output_parsers import StrOutputParser\n", - "from langchain_core.runnables import RunnablePassthrough\n", - "\n", - "\n", - "prompt = ChatPromptTemplate.from_template(\n", - " \"Tell me a short joke about {topic}\"\n", - ")\n", - "output_parser = StrOutputParser()\n", - "model = ChatOpenAI(model=\"gpt-3.5-turbo\")\n", - "chain = (\n", - " {\"topic\": RunnablePassthrough()} \n", - " | prompt\n", - " | model\n", - " | output_parser\n", - ")\n", - "\n", - "chain.invoke(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "3c0b0513-77b8-4371-a20e-3e487cec7e7f", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "## Stream\n", - "If we want to stream results instead, we'll need to change our function:\n", - "\n", - "\n", - "\n", - "\n", - "#### Without LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4f2cc6dc-d70a-4c13-9258-452f14290da6", - "metadata": {}, - "outputs": [], - "source": [ - "from typing import Iterator\n", - "\n", - "\n", - "def stream_chat_model(messages: List[dict]) -> Iterator[str]:\n", - " stream = client.chat.completions.create(\n", - " model=\"gpt-3.5-turbo\",\n", - " messages=messages,\n", - " stream=True,\n", - " )\n", - " for response in stream:\n", - " content = response.choices[0].delta.content\n", - " if content is not None:\n", - " yield content\n", - "\n", - "def stream_chain(topic: str) -> Iterator[str]:\n", - " prompt_value = prompt.format(topic=topic)\n", - " return stream_chat_model([{\"role\": \"user\", \"content\": prompt_value}])\n", - "\n", - "\n", - "for chunk in stream_chain(\"ice cream\"):\n", - " print(chunk, end=\"\", flush=True)" - ] - }, - { - "cell_type": "markdown", - "id": "f8e36b0e-c7dc-4130-a51b-189d4b756c7f", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "173e1a9c-2a18-4669-b0de-136f39197786", - "metadata": {}, - "outputs": [], - "source": [ - "for chunk in chain.stream(\"ice cream\"):\n", - " print(chunk, end=\"\", flush=True)" - ] - }, - { - "cell_type": "markdown", - "id": "b9b41e78-ddeb-44d0-a58b-a0ea0c99a761", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "## Batch\n", - "\n", - "If we want to run on a batch of inputs in parallel, we'll again need a new function:\n", - "\n", - "\n", - "\n", - "\n", - "#### Without LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6b492f13-73a6-48ed-8d4f-9ad634da9988", - "metadata": {}, - "outputs": [], - "source": [ - "from concurrent.futures import ThreadPoolExecutor\n", - "\n", - "\n", - "def batch_chain(topics: list) -> list:\n", - " with ThreadPoolExecutor(max_workers=5) as executor:\n", - " return list(executor.map(invoke_chain, topics))\n", - "\n", - "batch_chain([\"ice cream\", \"spaghetti\", \"dumplings\"])" - ] - }, - { - "cell_type": "markdown", - "id": "9b3e9d34-6775-43c1-93d8-684b58e341ab", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8f55b292-4e97-4d09-8e71-c71b4d853526", - "metadata": {}, - "outputs": [], - "source": [ - "chain.batch([\"ice cream\", \"spaghetti\", \"dumplings\"])" - ] - }, - { - "cell_type": "markdown", - "id": "cc5ba36f-eec1-4fc1-8cfe-fa242a7f7809", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "## Async\n", - "\n", - "If we need an asynchronous version:\n", - "\n", - "\n", - "\n", - "\n", - "#### Without LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "eabe6621-e815-41e3-9c9d-5aa561a69835", - "metadata": {}, - "outputs": [], - "source": [ - "async_client = openai.AsyncOpenAI()\n", - "\n", - "async def acall_chat_model(messages: List[dict]) -> str:\n", - " response = await async_client.chat.completions.create(\n", - " model=\"gpt-3.5-turbo\", \n", - " messages=messages,\n", - " )\n", - " return response.choices[0].message.content\n", - "\n", - "async def ainvoke_chain(topic: str) -> str:\n", - " prompt_value = prompt_template.format(topic=topic)\n", - " messages = [{\"role\": \"user\", \"content\": prompt_value}]\n", - " return await acall_chat_model(messages)\n", - "\n", - "\n", - "await ainvoke_chain(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "2f209290-498c-4c17-839e-ee9002919846", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - " \n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4d009781-7307-48a4-8439-f9d3dd015560", - "metadata": {}, - "outputs": [], - "source": [ - "await chain.ainvoke(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "1f282129-99a3-40f4-b67f-2d0718b1bea9", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "## Async Batch\n", - "\n", - "\n", - "\n", - "\n", - "#### Without LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1933f39d-7bd7-45fa-a6a5-5fb7be8e31ec", - "metadata": {}, - "outputs": [], - "source": [ - "import asyncio\n", - "import openai\n", - "\n", - "\n", - "async def abatch_chain(topics: list) -> list:\n", - " coros = map(ainvoke_chain, topics)\n", - " return await asyncio.gather(*coros)\n", - "\n", - "\n", - "await abatch_chain([\"ice cream\", \"spaghetti\", \"dumplings\"])" - ] - }, - { - "cell_type": "markdown", - "id": "90691048-17ae-479d-83c2-859e33ddf3eb", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "947dad23-3443-40eb-a03b-7840c261e261", - "metadata": {}, - "outputs": [], - "source": [ - "await chain.abatch([\"ice cream\", \"spaghetti\", \"dumplings\"])" - ] - }, - { - "cell_type": "markdown", - "id": "f6888245-1ebe-4768-a53b-e1fef6a8b379", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "## LLM instead of chat model\n", - "\n", - "If we want to use a completion endpoint instead of a chat endpoint: \n", - "\n", - "\n", - "\n", - "\n", - "#### Without LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9aca946b-acaa-4f7e-a3d0-ad8e3225e7f2", - "metadata": {}, - "outputs": [], - "source": [ - "def call_llm(prompt_value: str) -> str:\n", - " response = client.completions.create(\n", - " model=\"gpt-3.5-turbo-instruct\",\n", - " prompt=prompt_value,\n", - " )\n", - " return response.choices[0].text\n", - "\n", - "def invoke_llm_chain(topic: str) -> str:\n", - " prompt_value = prompt_template.format(topic=topic)\n", - " return call_llm(prompt_value)\n", - "\n", - "invoke_llm_chain(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "45342cd6-58c2-4543-9392-773e05ef06e7", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d56efc0c-88e0-4cf8-a46a-e8e9b9cd6805", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_openai import OpenAI\n", - "\n", - "llm = OpenAI(model=\"gpt-3.5-turbo-instruct\")\n", - "llm_chain = (\n", - " {\"topic\": RunnablePassthrough()} \n", - " | prompt\n", - " | llm\n", - " | output_parser\n", - ")\n", - "\n", - "llm_chain.invoke(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "ca115eaf-59ef-45c1-aac1-e8b0ce7db250", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "## Different model provider\n", - "\n", - "If we want to use Anthropic instead of OpenAI: \n", - "\n", - "\n", - "\n", - "\n", - "#### Without LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cde2ceb0-f65e-487b-9a32-137b0e9d79d5", - "metadata": {}, - "outputs": [], - "source": [ - "import anthropic\n", - "\n", - "anthropic_template = f\"Human:\\n\\n{prompt_template}\\n\\nAssistant:\"\n", - "anthropic_client = anthropic.Anthropic()\n", - "\n", - "def call_anthropic(prompt_value: str) -> str:\n", - " response = anthropic_client.completions.create(\n", - " model=\"claude-2\",\n", - " prompt=prompt_value,\n", - " max_tokens_to_sample=256,\n", - " )\n", - " return response.completion \n", - "\n", - "def invoke_anthropic_chain(topic: str) -> str:\n", - " prompt_value = anthropic_template.format(topic=topic)\n", - " return call_anthropic(prompt_value)\n", - "\n", - "invoke_anthropic_chain(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "52a0c9f8-e316-42e1-af85-cabeba4b7059", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b3b800d1-5954-41a4-80b0-f00a7908961e", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_anthropic import ChatAnthropic\n", - "\n", - "anthropic = ChatAnthropic(model=\"claude-2\")\n", - "anthropic_chain = (\n", - " {\"topic\": RunnablePassthrough()} \n", - " | prompt \n", - " | anthropic\n", - " | output_parser\n", - ")\n", - "\n", - "anthropic_chain.invoke(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "d7a91eee-d017-420d-b215-f663dcbf8ed2", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "## Runtime configurability\n", - "\n", - "If we wanted to make the choice of chat model or LLM configurable at runtime:\n", - "\n", - "\n", - "\n", - "\n", - "#### Without LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d0ef10e4-8e8e-463a-bd0f-59b0715e79b6", - "metadata": {}, - "outputs": [], - "source": [ - "def invoke_configurable_chain(\n", - " topic: str, \n", - " *, \n", - " model: str = \"chat_openai\"\n", - ") -> str:\n", - " if model == \"chat_openai\":\n", - " return invoke_chain(topic)\n", - " elif model == \"openai\":\n", - " return invoke_llm_chain(topic)\n", - " elif model == \"anthropic\":\n", - " return invoke_anthropic_chain(topic)\n", - " else:\n", - " raise ValueError(\n", - " f\"Received invalid model '{model}'.\"\n", - " \" Expected one of chat_openai, openai, anthropic\"\n", - " )\n", - "\n", - "def stream_configurable_chain(\n", - " topic: str, \n", - " *, \n", - " model: str = \"chat_openai\"\n", - ") -> Iterator[str]:\n", - " if model == \"chat_openai\":\n", - " return stream_chain(topic)\n", - " elif model == \"openai\":\n", - " # Note we haven't implemented this yet.\n", - " return stream_llm_chain(topic)\n", - " elif model == \"anthropic\":\n", - " # Note we haven't implemented this yet\n", - " return stream_anthropic_chain(topic)\n", - " else:\n", - " raise ValueError(\n", - " f\"Received invalid model '{model}'.\"\n", - " \" Expected one of chat_openai, openai, anthropic\"\n", - " )\n", - "\n", - "def batch_configurable_chain(\n", - " topics: List[str], \n", - " *, \n", - " model: str = \"chat_openai\"\n", - ") -> List[str]:\n", - " # You get the idea\n", - " ...\n", - "\n", - "async def abatch_configurable_chain(\n", - " topics: List[str], \n", - " *, \n", - " model: str = \"chat_openai\"\n", - ") -> List[str]:\n", - " ...\n", - "\n", - "invoke_configurable_chain(\"ice cream\", model=\"openai\")\n", - "stream = stream_configurable_chain(\n", - " \"ice_cream\", \n", - " model=\"anthropic\"\n", - ")\n", - "for chunk in stream:\n", - " print(chunk, end=\"\", flush=True)\n", - "\n", - "# batch_configurable_chain([\"ice cream\", \"spaghetti\", \"dumplings\"])\n", - "# await ainvoke_configurable_chain(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "d1530c5c-6635-4599-9483-6df357ca2d64", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "\n", - "#### With LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "76809d14-e77a-4125-a2ea-efbebf0b47cc", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_core.runnables import ConfigurableField\n", - "\n", - "\n", - "configurable_model = model.configurable_alternatives(\n", - " ConfigurableField(id=\"model\"), \n", - " default_key=\"chat_openai\", \n", - " openai=llm,\n", - " anthropic=anthropic,\n", - ")\n", - "configurable_chain = (\n", - " {\"topic\": RunnablePassthrough()} \n", - " | prompt \n", - " | configurable_model \n", - " | output_parser\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4a3d94d0-cd42-4195-80b8-ef2e12503d6f", - "metadata": {}, - "outputs": [], - "source": [ - "configurable_chain.invoke(\n", - " \"ice cream\", \n", - " config={\"model\": \"openai\"}\n", - ")\n", - "stream = configurable_chain.stream(\n", - " \"ice cream\", \n", - " config={\"model\": \"anthropic\"}\n", - ")\n", - "for chunk in stream:\n", - " print(chunk, end=\"\", flush=True)\n", - "\n", - "configurable_chain.batch([\"ice cream\", \"spaghetti\", \"dumplings\"])\n", - "\n", - "# await configurable_chain.ainvoke(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "370dd4d7-b825-40c4-ae3c-2693cba2f22a", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "## Logging\n", - "\n", - "If we want to log our intermediate results:\n", - "\n", - "\n", - "\n", - "\n", - "#### Without LCEL\n", - "\n", - "We'll `print` intermediate steps for illustrative purposes\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "383a3c51-926d-48c6-b9ae-42bf8f14ecc8", - "metadata": {}, - "outputs": [], - "source": [ - "def invoke_anthropic_chain_with_logging(topic: str) -> str:\n", - " print(f\"Input: {topic}\")\n", - " prompt_value = anthropic_template.format(topic=topic)\n", - " print(f\"Formatted prompt: {prompt_value}\")\n", - " output = call_anthropic(prompt_value)\n", - " print(f\"Output: {output}\")\n", - " return output\n", - "\n", - "invoke_anthropic_chain_with_logging(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "16bd20fd-43cd-4aaf-866f-a53d1f20312d", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "\n", - "#### LCEL\n", - "Every component has built-in integrations with LangSmith. If we set the following two environment variables, all chain traces are logged to LangSmith.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d6204f21-d2e7-4ac6-871f-b60b34e5bd36", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "\n", - "os.environ[\"LANGCHAIN_API_KEY\"] = \"...\"\n", - "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", - "\n", - "anthropic_chain.invoke(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "db37c922-e641-45e4-86fe-9ed7ef468fd8", - "metadata": {}, - "source": [ - "Here's what our LangSmith trace looks like: https://smith.langchain.com/public/e4de52f8-bcd9-4732-b950-deee4b04e313/r" - ] - }, - { - "cell_type": "markdown", - "id": "e25ce3c5-27a7-4954-9f0e-b94313597135", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "## Fallbacks\n", - "\n", - "If we wanted to add fallback logic, in case one model API is down:\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "#### Without LCEL\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2e49d512-bc83-4c5f-b56e-934b8343b0fe", - "metadata": {}, - "outputs": [], - "source": [ - "def invoke_chain_with_fallback(topic: str) -> str:\n", - " try:\n", - " return invoke_chain(topic)\n", - " except Exception:\n", - " return invoke_anthropic_chain(topic)\n", - "\n", - "async def ainvoke_chain_with_fallback(topic: str) -> str:\n", - " try:\n", - " return await ainvoke_chain(topic)\n", - " except Exception:\n", - " # Note: we haven't actually implemented this.\n", - " return await ainvoke_anthropic_chain(topic)\n", - "\n", - "async def batch_chain_with_fallback(topics: List[str]) -> str:\n", - " try:\n", - " return batch_chain(topics)\n", - " except Exception:\n", - " # Note: we haven't actually implemented this.\n", - " return batch_anthropic_chain(topics)\n", - "\n", - "invoke_chain_with_fallback(\"ice cream\")\n", - "# await ainvoke_chain_with_fallback(\"ice cream\")\n", - "batch_chain_with_fallback([\"ice cream\", \"spaghetti\", \"dumplings\"]))" - ] - }, - { - "cell_type": "markdown", - "id": "f7ef59b5-2ce3-479e-a7ac-79e1e2f30e9c", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3d0d8a0f-66eb-4c35-9529-74bec44ce4b8", - "metadata": {}, - "outputs": [], - "source": [ - "fallback_chain = chain.with_fallbacks([anthropic_chain])\n", - "\n", - "fallback_chain.invoke(\"ice cream\")\n", - "# await fallback_chain.ainvoke(\"ice cream\")\n", - "fallback_chain.batch([\"ice cream\", \"spaghetti\", \"dumplings\"])" - ] - }, - { - "cell_type": "markdown", - "id": "3af52d36-37c6-4d89-b515-95d7270bb96a", - "metadata": {}, - "source": [ - "\n", - "" - ] - }, - { - "cell_type": "markdown", - "id": "f58af836-26bd-4eab-97a0-76dd56d53430", - "metadata": {}, - "source": [ - "## Full code comparison\n", - "\n", - "Even in this simple case, our LCEL chain succinctly packs in a lot of functionality. As chains become more complex, this becomes especially valuable.\n", - "\n", - "\n", - "\n", - "\n", - "#### Without LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8684690a-e450-4ba7-8509-e9815a42ff1c", - "metadata": {}, - "outputs": [], - "source": [ - "from concurrent.futures import ThreadPoolExecutor\n", - "from typing import Iterator, List, Tuple\n", - "\n", - "import anthropic\n", - "import openai\n", - "\n", - "\n", - "prompt_template = \"Tell me a short joke about {topic}\"\n", - "anthropic_template = f\"Human:\\n\\n{prompt_template}\\n\\nAssistant:\"\n", - "client = openai.OpenAI()\n", - "async_client = openai.AsyncOpenAI()\n", - "anthropic_client = anthropic.Anthropic()\n", - "\n", - "def call_chat_model(messages: List[dict]) -> str:\n", - " response = client.chat.completions.create(\n", - " model=\"gpt-3.5-turbo\", \n", - " messages=messages,\n", - " )\n", - " return response.choices[0].message.content\n", - "\n", - "def invoke_chain(topic: str) -> str:\n", - " print(f\"Input: {topic}\")\n", - " prompt_value = prompt_template.format(topic=topic)\n", - " print(f\"Formatted prompt: {prompt_value}\")\n", - " messages = [{\"role\": \"user\", \"content\": prompt_value}]\n", - " output = call_chat_model(messages)\n", - " print(f\"Output: {output}\")\n", - " return output\n", - "\n", - "def stream_chat_model(messages: List[dict]) -> Iterator[str]:\n", - " stream = client.chat.completions.create(\n", - " model=\"gpt-3.5-turbo\",\n", - " messages=messages,\n", - " stream=True,\n", - " )\n", - " for response in stream:\n", - " content = response.choices[0].delta.content\n", - " if content is not None:\n", - " yield content\n", - "\n", - "def stream_chain(topic: str) -> Iterator[str]:\n", - " print(f\"Input: {topic}\")\n", - " prompt_value = prompt.format(topic=topic)\n", - " print(f\"Formatted prompt: {prompt_value}\")\n", - " stream = stream_chat_model([{\"role\": \"user\", \"content\": prompt_value}])\n", - " for chunk in stream:\n", - " print(f\"Token: {chunk}\", end=\"\")\n", - " yield chunk\n", - "\n", - "def batch_chain(topics: list) -> list:\n", - " with ThreadPoolExecutor(max_workers=5) as executor:\n", - " return list(executor.map(invoke_chain, topics))\n", - "\n", - "def call_llm(prompt_value: str) -> str:\n", - " response = client.completions.create(\n", - " model=\"gpt-3.5-turbo-instruct\",\n", - " prompt=prompt_value,\n", - " )\n", - " return response.choices[0].text\n", - "\n", - "def invoke_llm_chain(topic: str) -> str:\n", - " print(f\"Input: {topic}\")\n", - " prompt_value = promtp_template.format(topic=topic)\n", - " print(f\"Formatted prompt: {prompt_value}\")\n", - " output = call_llm(prompt_value)\n", - " print(f\"Output: {output}\")\n", - " return output\n", - "\n", - "def call_anthropic(prompt_value: str) -> str:\n", - " response = anthropic_client.completions.create(\n", - " model=\"claude-2\",\n", - " prompt=prompt_value,\n", - " max_tokens_to_sample=256,\n", - " )\n", - " return response.completion \n", - "\n", - "def invoke_anthropic_chain(topic: str) -> str:\n", - " print(f\"Input: {topic}\")\n", - " prompt_value = anthropic_template.format(topic=topic)\n", - " print(f\"Formatted prompt: {prompt_value}\")\n", - " output = call_anthropic(prompt_value)\n", - " print(f\"Output: {output}\")\n", - " return output\n", - "\n", - "async def ainvoke_anthropic_chain(topic: str) -> str:\n", - " ...\n", - "\n", - "def stream_anthropic_chain(topic: str) -> Iterator[str]:\n", - " ...\n", - "\n", - "def batch_anthropic_chain(topics: List[str]) -> List[str]:\n", - " ...\n", - "\n", - "def invoke_configurable_chain(\n", - " topic: str, \n", - " *, \n", - " model: str = \"chat_openai\"\n", - ") -> str:\n", - " if model == \"chat_openai\":\n", - " return invoke_chain(topic)\n", - " elif model == \"openai\":\n", - " return invoke_llm_chain(topic)\n", - " elif model == \"anthropic\":\n", - " return invoke_anthropic_chain(topic)\n", - " else:\n", - " raise ValueError(\n", - " f\"Received invalid model '{model}'.\"\n", - " \" Expected one of chat_openai, openai, anthropic\"\n", - " )\n", - "\n", - "def stream_configurable_chain(\n", - " topic: str, \n", - " *, \n", - " model: str = \"chat_openai\"\n", - ") -> Iterator[str]:\n", - " if model == \"chat_openai\":\n", - " return stream_chain(topic)\n", - " elif model == \"openai\":\n", - " # Note we haven't implemented this yet.\n", - " return stream_llm_chain(topic)\n", - " elif model == \"anthropic\":\n", - " # Note we haven't implemented this yet\n", - " return stream_anthropic_chain(topic)\n", - " else:\n", - " raise ValueError(\n", - " f\"Received invalid model '{model}'.\"\n", - " \" Expected one of chat_openai, openai, anthropic\"\n", - " )\n", - "\n", - "def batch_configurable_chain(\n", - " topics: List[str], \n", - " *, \n", - " model: str = \"chat_openai\"\n", - ") -> List[str]:\n", - " ...\n", - "\n", - "async def abatch_configurable_chain(\n", - " topics: List[str], \n", - " *, \n", - " model: str = \"chat_openai\"\n", - ") -> List[str]:\n", - " ...\n", - "\n", - "def invoke_chain_with_fallback(topic: str) -> str:\n", - " try:\n", - " return invoke_chain(topic)\n", - " except Exception:\n", - " return invoke_anthropic_chain(topic)\n", - "\n", - "async def ainvoke_chain_with_fallback(topic: str) -> str:\n", - " try:\n", - " return await ainvoke_chain(topic)\n", - " except Exception:\n", - " return await ainvoke_anthropic_chain(topic)\n", - "\n", - "async def batch_chain_with_fallback(topics: List[str]) -> str:\n", - " try:\n", - " return batch_chain(topics)\n", - " except Exception:\n", - " return batch_anthropic_chain(topics)" - ] - }, - { - "cell_type": "markdown", - "id": "9fb3d71d-8c69-4dc4-81b7-95cd46b271c2", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "715c469a-545e-434e-bd6e-99745dd880a7", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "\n", - "from langchain_anthropic import ChatAnthropic\n", - "from langchain_openai import ChatOpenAI\n", - "from langchain_openai import OpenAI\n", - "from langchain_core.output_parsers import StrOutputParser\n", - "from langchain_core.prompts import ChatPromptTemplate\n", - "from langchain_core.runnables import RunnablePassthrough, ConfigurableField\n", - "\n", - "os.environ[\"LANGCHAIN_API_KEY\"] = \"...\"\n", - "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", - "\n", - "prompt = ChatPromptTemplate.from_template(\n", - " \"Tell me a short joke about {topic}\"\n", - ")\n", - "chat_openai = ChatOpenAI(model=\"gpt-3.5-turbo\")\n", - "openai = OpenAI(model=\"gpt-3.5-turbo-instruct\")\n", - "anthropic = ChatAnthropic(model=\"claude-2\")\n", - "model = (\n", - " chat_openai\n", - " .with_fallbacks([anthropic])\n", - " .configurable_alternatives(\n", - " ConfigurableField(id=\"model\"),\n", - " default_key=\"chat_openai\",\n", - " openai=openai,\n", - " anthropic=anthropic,\n", - " )\n", - ")\n", - "\n", - "chain = (\n", - " {\"topic\": RunnablePassthrough()} \n", - " | prompt \n", - " | model \n", - " | StrOutputParser()\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "e3637d39", - "metadata": {}, - "source": [ - "\n", - "" - ] - }, - { - "cell_type": "markdown", - "id": "5e47e773-d0f1-42b5-b509-896807b65c9c", - "metadata": {}, - "source": [ - "## Next steps\n", - "\n", - "To continue learning about LCEL, we recommend:\n", - "- Reading up on the full LCEL [Interface](/docs/expression_language/interface), which we've only partially covered here.\n", - "- Exploring the [How-to](/docs/expression_language/how_to) section to learn about additional composition primitives that LCEL provides.\n", - "- Looking through the [Cookbook](/docs/expression_language/cookbook) section to see LCEL in action for common use cases. A good next use case to look at would be [Retrieval-augmented generation](/docs/expression_language/cookbook/retrieval)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 + "cells": [ + { + "cell_type": "raw", + "id": "bc346658-6820-413a-bd8f-11bd3082fe43", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 0.5\n", + "title: Why use LCEL\n", + "---\n", + "\n", + "```{=mdx}\n", + "import { ColumnContainer, Column } from \"@theme/Columns\";\n", + "```" + ] }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} + { + "cell_type": "markdown", + "id": "919a5ae2-ed21-4923-b98f-723c111bac67", + "metadata": {}, + "source": [ + ":::{.callout-tip} \n", + "We recommend reading the LCEL [Get started](/docs/expression_language/get_started) section first.\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "id": "f331037f-be3f-4782-856f-d55dab952488", + "metadata": {}, + "source": [ + "LCEL makes it easy to build complex chains from basic components. It does this by providing:\n", + "1. **A unified interface**: Every LCEL object implements the `Runnable` interface, which defines a common set of invocation methods (`invoke`, `batch`, `stream`, `ainvoke`, ...). This makes it possible for chains of LCEL objects to also automatically support these invocations. That is, every chain of LCEL objects is itself an LCEL object.\n", + "2. **Composition primitives**: LCEL provides a number of primitives that make it easy to compose chains, parallelize components, add fallbacks, dynamically configure chain internal, and more.\n", + "\n", + "To better understand the value of LCEL, it's helpful to see it in action and think about how we might recreate similar functionality without it. In this walkthrough we'll do just that with our [basic example](/docs/expression_language/get_started#basic_example) from the get started section. We'll take our simple prompt + model chain, which under the hood already defines a lot of functionality, and see what it would take to recreate all of it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b99b47ec", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install --upgrade --quiet langchain-core langchain-openai langchain-anthropic" + ] + }, + { + "cell_type": "markdown", + "id": "e3621b62-a037-42b8-8faa-59575608bb8b", + "metadata": {}, + "source": [ + "## Invoke\n", + "In the simplest case, we just want to pass in a topic string and get back a joke string:\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e628905c-430e-4e4a-9d7c-c91d2f42052e", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import List\n", + "\n", + "import openai\n", + "\n", + "\n", + "prompt_template = \"Tell me a short joke about {topic}\"\n", + "client = openai.OpenAI()\n", + "\n", + "def call_chat_model(messages: List[dict]) -> str:\n", + " response = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\", \n", + " messages=messages,\n", + " )\n", + " return response.choices[0].message.content\n", + "\n", + "def invoke_chain(topic: str) -> str:\n", + " prompt_value = prompt_template.format(topic=topic)\n", + " messages = [{\"role\": \"user\", \"content\": prompt_value}]\n", + " return call_chat_model(messages)\n", + "\n", + "invoke_chain(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "cdc3b527-c09e-4c77-9711-c3cc4506cd95", + "metadata": {}, + "source": [ + "\n", + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0d2a7cf8-1bc7-405c-bb0d-f2ab2ba3b6ab", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_openai import ChatOpenAI\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_core.output_parsers import StrOutputParser\n", + "from langchain_core.runnables import RunnablePassthrough\n", + "\n", + "\n", + "prompt = ChatPromptTemplate.from_template(\n", + " \"Tell me a short joke about {topic}\"\n", + ")\n", + "output_parser = StrOutputParser()\n", + "model = ChatOpenAI(model=\"gpt-3.5-turbo\")\n", + "chain = (\n", + " {\"topic\": RunnablePassthrough()} \n", + " | prompt\n", + " | model\n", + " | output_parser\n", + ")\n", + "\n", + "chain.invoke(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "3c0b0513-77b8-4371-a20e-3e487cec7e7f", + "metadata": {}, + "source": [ + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "## Stream\n", + "If we want to stream results instead, we'll need to change our function:\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4f2cc6dc-d70a-4c13-9258-452f14290da6", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Iterator\n", + "\n", + "\n", + "def stream_chat_model(messages: List[dict]) -> Iterator[str]:\n", + " stream = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=messages,\n", + " stream=True,\n", + " )\n", + " for response in stream:\n", + " content = response.choices[0].delta.content\n", + " if content is not None:\n", + " yield content\n", + "\n", + "def stream_chain(topic: str) -> Iterator[str]:\n", + " prompt_value = prompt.format(topic=topic)\n", + " return stream_chat_model([{\"role\": \"user\", \"content\": prompt_value}])\n", + "\n", + "\n", + "for chunk in stream_chain(\"ice cream\"):\n", + " print(chunk, end=\"\", flush=True)" + ] + }, + { + "cell_type": "markdown", + "id": "f8e36b0e-c7dc-4130-a51b-189d4b756c7f", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "173e1a9c-2a18-4669-b0de-136f39197786", + "metadata": {}, + "outputs": [], + "source": [ + "for chunk in chain.stream(\"ice cream\"):\n", + " print(chunk, end=\"\", flush=True)" + ] + }, + { + "cell_type": "markdown", + "id": "b9b41e78-ddeb-44d0-a58b-a0ea0c99a761", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "## Batch\n", + "\n", + "If we want to run on a batch of inputs in parallel, we'll again need a new function:\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b492f13-73a6-48ed-8d4f-9ad634da9988", + "metadata": {}, + "outputs": [], + "source": [ + "from concurrent.futures import ThreadPoolExecutor\n", + "\n", + "\n", + "def batch_chain(topics: list) -> list:\n", + " with ThreadPoolExecutor(max_workers=5) as executor:\n", + " return list(executor.map(invoke_chain, topics))\n", + "\n", + "batch_chain([\"ice cream\", \"spaghetti\", \"dumplings\"])" + ] + }, + { + "cell_type": "markdown", + "id": "9b3e9d34-6775-43c1-93d8-684b58e341ab", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8f55b292-4e97-4d09-8e71-c71b4d853526", + "metadata": {}, + "outputs": [], + "source": [ + "chain.batch([\"ice cream\", \"spaghetti\", \"dumplings\"])" + ] + }, + { + "cell_type": "markdown", + "id": "cc5ba36f-eec1-4fc1-8cfe-fa242a7f7809", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "## Async\n", + "\n", + "If we need an asynchronous version:\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eabe6621-e815-41e3-9c9d-5aa561a69835", + "metadata": {}, + "outputs": [], + "source": [ + "async_client = openai.AsyncOpenAI()\n", + "\n", + "async def acall_chat_model(messages: List[dict]) -> str:\n", + " response = await async_client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\", \n", + " messages=messages,\n", + " )\n", + " return response.choices[0].message.content\n", + "\n", + "async def ainvoke_chain(topic: str) -> str:\n", + " prompt_value = prompt_template.format(topic=topic)\n", + " messages = [{\"role\": \"user\", \"content\": prompt_value}]\n", + " return await acall_chat_model(messages)\n", + "\n", + "\n", + "await ainvoke_chain(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "2f209290-498c-4c17-839e-ee9002919846", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4d009781-7307-48a4-8439-f9d3dd015560", + "metadata": {}, + "outputs": [], + "source": [ + "await chain.ainvoke(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "1f282129-99a3-40f4-b67f-2d0718b1bea9", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "## Async Batch\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1933f39d-7bd7-45fa-a6a5-5fb7be8e31ec", + "metadata": {}, + "outputs": [], + "source": [ + "import asyncio\n", + "import openai\n", + "\n", + "\n", + "async def abatch_chain(topics: list) -> list:\n", + " coros = map(ainvoke_chain, topics)\n", + " return await asyncio.gather(*coros)\n", + "\n", + "\n", + "await abatch_chain([\"ice cream\", \"spaghetti\", \"dumplings\"])" + ] + }, + { + "cell_type": "markdown", + "id": "90691048-17ae-479d-83c2-859e33ddf3eb", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "947dad23-3443-40eb-a03b-7840c261e261", + "metadata": {}, + "outputs": [], + "source": [ + "await chain.abatch([\"ice cream\", \"spaghetti\", \"dumplings\"])" + ] + }, + { + "cell_type": "markdown", + "id": "f6888245-1ebe-4768-a53b-e1fef6a8b379", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "## LLM instead of chat model\n", + "\n", + "If we want to use a completion endpoint instead of a chat endpoint: \n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9aca946b-acaa-4f7e-a3d0-ad8e3225e7f2", + "metadata": {}, + "outputs": [], + "source": [ + "def call_llm(prompt_value: str) -> str:\n", + " response = client.completions.create(\n", + " model=\"gpt-3.5-turbo-instruct\",\n", + " prompt=prompt_value,\n", + " )\n", + " return response.choices[0].text\n", + "\n", + "def invoke_llm_chain(topic: str) -> str:\n", + " prompt_value = prompt_template.format(topic=topic)\n", + " return call_llm(prompt_value)\n", + "\n", + "invoke_llm_chain(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "45342cd6-58c2-4543-9392-773e05ef06e7", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d56efc0c-88e0-4cf8-a46a-e8e9b9cd6805", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_openai import OpenAI\n", + "\n", + "llm = OpenAI(model=\"gpt-3.5-turbo-instruct\")\n", + "llm_chain = (\n", + " {\"topic\": RunnablePassthrough()} \n", + " | prompt\n", + " | llm\n", + " | output_parser\n", + ")\n", + "\n", + "llm_chain.invoke(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "ca115eaf-59ef-45c1-aac1-e8b0ce7db250", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "## Different model provider\n", + "\n", + "If we want to use Anthropic instead of OpenAI: \n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cde2ceb0-f65e-487b-9a32-137b0e9d79d5", + "metadata": {}, + "outputs": [], + "source": [ + "import anthropic\n", + "\n", + "anthropic_template = f\"Human:\\n\\n{prompt_template}\\n\\nAssistant:\"\n", + "anthropic_client = anthropic.Anthropic()\n", + "\n", + "def call_anthropic(prompt_value: str) -> str:\n", + " response = anthropic_client.completions.create(\n", + " model=\"claude-2\",\n", + " prompt=prompt_value,\n", + " max_tokens_to_sample=256,\n", + " )\n", + " return response.completion \n", + "\n", + "def invoke_anthropic_chain(topic: str) -> str:\n", + " prompt_value = anthropic_template.format(topic=topic)\n", + " return call_anthropic(prompt_value)\n", + "\n", + "invoke_anthropic_chain(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "52a0c9f8-e316-42e1-af85-cabeba4b7059", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3b800d1-5954-41a4-80b0-f00a7908961e", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_anthropic import ChatAnthropic\n", + "\n", + "anthropic = ChatAnthropic(model=\"claude-2\")\n", + "anthropic_chain = (\n", + " {\"topic\": RunnablePassthrough()} \n", + " | prompt \n", + " | anthropic\n", + " | output_parser\n", + ")\n", + "\n", + "anthropic_chain.invoke(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "d7a91eee-d017-420d-b215-f663dcbf8ed2", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "## Runtime configurability\n", + "\n", + "If we wanted to make the choice of chat model or LLM configurable at runtime:\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0ef10e4-8e8e-463a-bd0f-59b0715e79b6", + "metadata": {}, + "outputs": [], + "source": [ + "def invoke_configurable_chain(\n", + " topic: str, \n", + " *, \n", + " model: str = \"chat_openai\"\n", + ") -> str:\n", + " if model == \"chat_openai\":\n", + " return invoke_chain(topic)\n", + " elif model == \"openai\":\n", + " return invoke_llm_chain(topic)\n", + " elif model == \"anthropic\":\n", + " return invoke_anthropic_chain(topic)\n", + " else:\n", + " raise ValueError(\n", + " f\"Received invalid model '{model}'.\"\n", + " \" Expected one of chat_openai, openai, anthropic\"\n", + " )\n", + "\n", + "def stream_configurable_chain(\n", + " topic: str, \n", + " *, \n", + " model: str = \"chat_openai\"\n", + ") -> Iterator[str]:\n", + " if model == \"chat_openai\":\n", + " return stream_chain(topic)\n", + " elif model == \"openai\":\n", + " # Note we haven't implemented this yet.\n", + " return stream_llm_chain(topic)\n", + " elif model == \"anthropic\":\n", + " # Note we haven't implemented this yet\n", + " return stream_anthropic_chain(topic)\n", + " else:\n", + " raise ValueError(\n", + " f\"Received invalid model '{model}'.\"\n", + " \" Expected one of chat_openai, openai, anthropic\"\n", + " )\n", + "\n", + "def batch_configurable_chain(\n", + " topics: List[str], \n", + " *, \n", + " model: str = \"chat_openai\"\n", + ") -> List[str]:\n", + " # You get the idea\n", + " ...\n", + "\n", + "async def abatch_configurable_chain(\n", + " topics: List[str], \n", + " *, \n", + " model: str = \"chat_openai\"\n", + ") -> List[str]:\n", + " ...\n", + "\n", + "invoke_configurable_chain(\"ice cream\", model=\"openai\")\n", + "stream = stream_configurable_chain(\n", + " \"ice_cream\", \n", + " model=\"anthropic\"\n", + ")\n", + "for chunk in stream:\n", + " print(chunk, end=\"\", flush=True)\n", + "\n", + "# batch_configurable_chain([\"ice cream\", \"spaghetti\", \"dumplings\"])\n", + "# await ainvoke_configurable_chain(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "d1530c5c-6635-4599-9483-6df357ca2d64", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### With LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76809d14-e77a-4125-a2ea-efbebf0b47cc", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.runnables import ConfigurableField\n", + "\n", + "\n", + "configurable_model = model.configurable_alternatives(\n", + " ConfigurableField(id=\"model\"), \n", + " default_key=\"chat_openai\", \n", + " openai=llm,\n", + " anthropic=anthropic,\n", + ")\n", + "configurable_chain = (\n", + " {\"topic\": RunnablePassthrough()} \n", + " | prompt \n", + " | configurable_model \n", + " | output_parser\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a3d94d0-cd42-4195-80b8-ef2e12503d6f", + "metadata": {}, + "outputs": [], + "source": [ + "configurable_chain.invoke(\n", + " \"ice cream\", \n", + " config={\"model\": \"openai\"}\n", + ")\n", + "stream = configurable_chain.stream(\n", + " \"ice cream\", \n", + " config={\"model\": \"anthropic\"}\n", + ")\n", + "for chunk in stream:\n", + " print(chunk, end=\"\", flush=True)\n", + "\n", + "configurable_chain.batch([\"ice cream\", \"spaghetti\", \"dumplings\"])\n", + "\n", + "# await configurable_chain.ainvoke(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "370dd4d7-b825-40c4-ae3c-2693cba2f22a", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "## Logging\n", + "\n", + "If we want to log our intermediate results:\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n", + "We'll `print` intermediate steps for illustrative purposes\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "383a3c51-926d-48c6-b9ae-42bf8f14ecc8", + "metadata": {}, + "outputs": [], + "source": [ + "def invoke_anthropic_chain_with_logging(topic: str) -> str:\n", + " print(f\"Input: {topic}\")\n", + " prompt_value = anthropic_template.format(topic=topic)\n", + " print(f\"Formatted prompt: {prompt_value}\")\n", + " output = call_anthropic(prompt_value)\n", + " print(f\"Output: {output}\")\n", + " return output\n", + "\n", + "invoke_anthropic_chain_with_logging(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "16bd20fd-43cd-4aaf-866f-a53d1f20312d", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### LCEL\n", + "Every component has built-in integrations with LangSmith. If we set the following two environment variables, all chain traces are logged to LangSmith.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6204f21-d2e7-4ac6-871f-b60b34e5bd36", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "os.environ[\"LANGCHAIN_API_KEY\"] = \"...\"\n", + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "\n", + "anthropic_chain.invoke(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "db37c922-e641-45e4-86fe-9ed7ef468fd8", + "metadata": {}, + "source": [ + "Here's what our LangSmith trace looks like: https://smith.langchain.com/public/e4de52f8-bcd9-4732-b950-deee4b04e313/r" + ] + }, + { + "cell_type": "markdown", + "id": "e25ce3c5-27a7-4954-9f0e-b94313597135", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "## Fallbacks\n", + "\n", + "If we wanted to add fallback logic, in case one model API is down:\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2e49d512-bc83-4c5f-b56e-934b8343b0fe", + "metadata": {}, + "outputs": [], + "source": [ + "def invoke_chain_with_fallback(topic: str) -> str:\n", + " try:\n", + " return invoke_chain(topic)\n", + " except Exception:\n", + " return invoke_anthropic_chain(topic)\n", + "\n", + "async def ainvoke_chain_with_fallback(topic: str) -> str:\n", + " try:\n", + " return await ainvoke_chain(topic)\n", + " except Exception:\n", + " # Note: we haven't actually implemented this.\n", + " return await ainvoke_anthropic_chain(topic)\n", + "\n", + "async def batch_chain_with_fallback(topics: List[str]) -> str:\n", + " try:\n", + " return batch_chain(topics)\n", + " except Exception:\n", + " # Note: we haven't actually implemented this.\n", + " return batch_anthropic_chain(topics)\n", + "\n", + "invoke_chain_with_fallback(\"ice cream\")\n", + "# await ainvoke_chain_with_fallback(\"ice cream\")\n", + "batch_chain_with_fallback([\"ice cream\", \"spaghetti\", \"dumplings\"]))" + ] + }, + { + "cell_type": "markdown", + "id": "f7ef59b5-2ce3-479e-a7ac-79e1e2f30e9c", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d0d8a0f-66eb-4c35-9529-74bec44ce4b8", + "metadata": {}, + "outputs": [], + "source": [ + "fallback_chain = chain.with_fallbacks([anthropic_chain])\n", + "\n", + "fallback_chain.invoke(\"ice cream\")\n", + "# await fallback_chain.ainvoke(\"ice cream\")\n", + "fallback_chain.batch([\"ice cream\", \"spaghetti\", \"dumplings\"])" + ] + }, + { + "cell_type": "markdown", + "id": "3af52d36-37c6-4d89-b515-95d7270bb96a", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "f58af836-26bd-4eab-97a0-76dd56d53430", + "metadata": {}, + "source": [ + "## Full code comparison\n", + "\n", + "Even in this simple case, our LCEL chain succinctly packs in a lot of functionality. As chains become more complex, this becomes especially valuable.\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8684690a-e450-4ba7-8509-e9815a42ff1c", + "metadata": {}, + "outputs": [], + "source": [ + "from concurrent.futures import ThreadPoolExecutor\n", + "from typing import Iterator, List, Tuple\n", + "\n", + "import anthropic\n", + "import openai\n", + "\n", + "\n", + "prompt_template = \"Tell me a short joke about {topic}\"\n", + "anthropic_template = f\"Human:\\n\\n{prompt_template}\\n\\nAssistant:\"\n", + "client = openai.OpenAI()\n", + "async_client = openai.AsyncOpenAI()\n", + "anthropic_client = anthropic.Anthropic()\n", + "\n", + "def call_chat_model(messages: List[dict]) -> str:\n", + " response = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\", \n", + " messages=messages,\n", + " )\n", + " return response.choices[0].message.content\n", + "\n", + "def invoke_chain(topic: str) -> str:\n", + " print(f\"Input: {topic}\")\n", + " prompt_value = prompt_template.format(topic=topic)\n", + " print(f\"Formatted prompt: {prompt_value}\")\n", + " messages = [{\"role\": \"user\", \"content\": prompt_value}]\n", + " output = call_chat_model(messages)\n", + " print(f\"Output: {output}\")\n", + " return output\n", + "\n", + "def stream_chat_model(messages: List[dict]) -> Iterator[str]:\n", + " stream = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=messages,\n", + " stream=True,\n", + " )\n", + " for response in stream:\n", + " content = response.choices[0].delta.content\n", + " if content is not None:\n", + " yield content\n", + "\n", + "def stream_chain(topic: str) -> Iterator[str]:\n", + " print(f\"Input: {topic}\")\n", + " prompt_value = prompt.format(topic=topic)\n", + " print(f\"Formatted prompt: {prompt_value}\")\n", + " stream = stream_chat_model([{\"role\": \"user\", \"content\": prompt_value}])\n", + " for chunk in stream:\n", + " print(f\"Token: {chunk}\", end=\"\")\n", + " yield chunk\n", + "\n", + "def batch_chain(topics: list) -> list:\n", + " with ThreadPoolExecutor(max_workers=5) as executor:\n", + " return list(executor.map(invoke_chain, topics))\n", + "\n", + "def call_llm(prompt_value: str) -> str:\n", + " response = client.completions.create(\n", + " model=\"gpt-3.5-turbo-instruct\",\n", + " prompt=prompt_value,\n", + " )\n", + " return response.choices[0].text\n", + "\n", + "def invoke_llm_chain(topic: str) -> str:\n", + " print(f\"Input: {topic}\")\n", + " prompt_value = promtp_template.format(topic=topic)\n", + " print(f\"Formatted prompt: {prompt_value}\")\n", + " output = call_llm(prompt_value)\n", + " print(f\"Output: {output}\")\n", + " return output\n", + "\n", + "def call_anthropic(prompt_value: str) -> str:\n", + " response = anthropic_client.completions.create(\n", + " model=\"claude-2\",\n", + " prompt=prompt_value,\n", + " max_tokens_to_sample=256,\n", + " )\n", + " return response.completion \n", + "\n", + "def invoke_anthropic_chain(topic: str) -> str:\n", + " print(f\"Input: {topic}\")\n", + " prompt_value = anthropic_template.format(topic=topic)\n", + " print(f\"Formatted prompt: {prompt_value}\")\n", + " output = call_anthropic(prompt_value)\n", + " print(f\"Output: {output}\")\n", + " return output\n", + "\n", + "async def ainvoke_anthropic_chain(topic: str) -> str:\n", + " ...\n", + "\n", + "def stream_anthropic_chain(topic: str) -> Iterator[str]:\n", + " ...\n", + "\n", + "def batch_anthropic_chain(topics: List[str]) -> List[str]:\n", + " ...\n", + "\n", + "def invoke_configurable_chain(\n", + " topic: str, \n", + " *, \n", + " model: str = \"chat_openai\"\n", + ") -> str:\n", + " if model == \"chat_openai\":\n", + " return invoke_chain(topic)\n", + " elif model == \"openai\":\n", + " return invoke_llm_chain(topic)\n", + " elif model == \"anthropic\":\n", + " return invoke_anthropic_chain(topic)\n", + " else:\n", + " raise ValueError(\n", + " f\"Received invalid model '{model}'.\"\n", + " \" Expected one of chat_openai, openai, anthropic\"\n", + " )\n", + "\n", + "def stream_configurable_chain(\n", + " topic: str, \n", + " *, \n", + " model: str = \"chat_openai\"\n", + ") -> Iterator[str]:\n", + " if model == \"chat_openai\":\n", + " return stream_chain(topic)\n", + " elif model == \"openai\":\n", + " # Note we haven't implemented this yet.\n", + " return stream_llm_chain(topic)\n", + " elif model == \"anthropic\":\n", + " # Note we haven't implemented this yet\n", + " return stream_anthropic_chain(topic)\n", + " else:\n", + " raise ValueError(\n", + " f\"Received invalid model '{model}'.\"\n", + " \" Expected one of chat_openai, openai, anthropic\"\n", + " )\n", + "\n", + "def batch_configurable_chain(\n", + " topics: List[str], \n", + " *, \n", + " model: str = \"chat_openai\"\n", + ") -> List[str]:\n", + " ...\n", + "\n", + "async def abatch_configurable_chain(\n", + " topics: List[str], \n", + " *, \n", + " model: str = \"chat_openai\"\n", + ") -> List[str]:\n", + " ...\n", + "\n", + "def invoke_chain_with_fallback(topic: str) -> str:\n", + " try:\n", + " return invoke_chain(topic)\n", + " except Exception:\n", + " return invoke_anthropic_chain(topic)\n", + "\n", + "async def ainvoke_chain_with_fallback(topic: str) -> str:\n", + " try:\n", + " return await ainvoke_chain(topic)\n", + " except Exception:\n", + " return await ainvoke_anthropic_chain(topic)\n", + "\n", + "async def batch_chain_with_fallback(topics: List[str]) -> str:\n", + " try:\n", + " return batch_chain(topics)\n", + " except Exception:\n", + " return batch_anthropic_chain(topics)" + ] + }, + { + "cell_type": "markdown", + "id": "9fb3d71d-8c69-4dc4-81b7-95cd46b271c2", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "715c469a-545e-434e-bd6e-99745dd880a7", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "from langchain_anthropic import ChatAnthropic\n", + "from langchain_openai import ChatOpenAI\n", + "from langchain_openai import OpenAI\n", + "from langchain_core.output_parsers import StrOutputParser\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_core.runnables import RunnablePassthrough, ConfigurableField\n", + "\n", + "os.environ[\"LANGCHAIN_API_KEY\"] = \"...\"\n", + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "\n", + "prompt = ChatPromptTemplate.from_template(\n", + " \"Tell me a short joke about {topic}\"\n", + ")\n", + "chat_openai = ChatOpenAI(model=\"gpt-3.5-turbo\")\n", + "openai = OpenAI(model=\"gpt-3.5-turbo-instruct\")\n", + "anthropic = ChatAnthropic(model=\"claude-2\")\n", + "model = (\n", + " chat_openai\n", + " .with_fallbacks([anthropic])\n", + " .configurable_alternatives(\n", + " ConfigurableField(id=\"model\"),\n", + " default_key=\"chat_openai\",\n", + " openai=openai,\n", + " anthropic=anthropic,\n", + " )\n", + ")\n", + "\n", + "chain = (\n", + " {\"topic\": RunnablePassthrough()} \n", + " | prompt \n", + " | model \n", + " | StrOutputParser()\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "e3637d39", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "5e47e773-d0f1-42b5-b509-896807b65c9c", + "metadata": {}, + "source": [ + "## Next steps\n", + "\n", + "To continue learning about LCEL, we recommend:\n", + "- Reading up on the full LCEL [Interface](/docs/expression_language/interface), which we've only partially covered here.\n", + "- Exploring the [How-to](/docs/expression_language/how_to) section to learn about additional composition primitives that LCEL provides.\n", + "- Looking through the [Cookbook](/docs/expression_language/cookbook) section to see LCEL in action for common use cases. A good next use case to look at would be [Retrieval-augmented generation](/docs/expression_language/cookbook/retrieval)." + ] + } + ], + "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.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 + } + \ No newline at end of file diff --git a/docs/docs/modules/model_io/chat/chat_model_caching.ipynb b/docs/docs/modules/model_io/chat/chat_model_caching.ipynb index ec4c413b72..a7f82b3857 100644 --- a/docs/docs/modules/model_io/chat/chat_model_caching.ipynb +++ b/docs/docs/modules/model_io/chat/chat_model_caching.ipynb @@ -12,19 +12,44 @@ "It can speed up your application by reducing the number of API calls you make to the LLM provider.\n" ] }, + { + "cell_type": "markdown", + "id": "289b31de", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "import ChatModelTabs from \"@theme/ChatModelTabs\";\n", + "\n", + "\n", + "```" + ] + }, { "cell_type": "code", - "execution_count": 1, - "id": "5472a032", + "execution_count": null, + "id": "c6641f37", "metadata": {}, "outputs": [], "source": [ - "from langchain.globals import set_llm_cache\n", + "# | output: false\n", + "# | echo: false\n", + "\n", "from langchain_openai import ChatOpenAI\n", "\n", "llm = ChatOpenAI()" ] }, + { + "cell_type": "code", + "execution_count": 1, + "id": "5472a032", + "metadata": {}, + "outputs": [], + "source": [ + "# \n", + "from langchain.globals import set_llm_cache" + ] + }, { "cell_type": "markdown", "id": "357b89a8", diff --git a/docs/docs/modules/model_io/chat/function_calling.mdx b/docs/docs/modules/model_io/chat/function_calling.mdx index f0cc8b791e..1f5dcb028d 100644 --- a/docs/docs/modules/model_io/chat/function_calling.mdx +++ b/docs/docs/modules/model_io/chat/function_calling.mdx @@ -18,13 +18,13 @@ structured outputs from models more generally. LangChain comes with a number of utilities to make function-calling easy. Namely, it comes with: -- simple syntax for binding functions to models -- converters for formatting various types of objects to the expected - function schemas -- output parsers for extracting the function invocations from API - responses -- chains for getting structured outputs from a model, built on top of - function calling +- simple syntax for binding functions to models +- converters for formatting various types of objects to the expected + function schemas +- output parsers for extracting the function invocations from API + responses +- chains for getting structured outputs from a model, built on top of + function calling We’ll focus here on the first two points. For a detailed guide on output parsing check out the [OpenAI Tools output @@ -38,7 +38,6 @@ Before getting started make sure you have `langchain-core` installed. %pip install -qU langchain-core langchain-openai ``` - ```python import getpass import os @@ -64,38 +63,26 @@ class Multiply(BaseModel): b: int = Field(..., description="Second integer") ``` -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - - - - -Set up dependencies and API keys: +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; -```python -%pip install -qU langchain-openai -``` +import ChatModelTabs from "@theme/ChatModelTabs"; + -```python -os.environ["OPENAI_API_KEY"] = getpass.getpass() -``` - -We can use the `ChatOpenAI.bind_tools()` method to handle converting -`Multiply` to an OpenAI function and binding it to the model (i.e., +We can use the `bind_tools()` method to handle converting +`Multiply` to a "function" and binding it to the model (i.e., passing it in each time the model is invoked). - - ```python -from langchain_openai import ChatOpenAI - -llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0) llm_with_tools = llm.bind_tools([Multiply]) llm_with_tools.invoke("what's 3 * 12") ``` -``` text +```text AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Q8ZQ97Qrj5zalugSkYMGV1Uo', 'function': {'arguments': '{"a":3,"b":12}', 'name': 'Multiply'}, 'type': 'function'}]}) ``` @@ -109,7 +96,7 @@ tool_chain = llm_with_tools | JsonOutputToolsParser() tool_chain.invoke("what's 3 * 12") ``` -``` text +```text [{'type': 'Multiply', 'args': {'a': 3, 'b': 12}}] ``` @@ -122,57 +109,10 @@ tool_chain = llm_with_tools | PydanticToolsParser(tools=[Multiply]) tool_chain.invoke("what's 3 * 12") ``` -``` text +```text [Multiply(a=3, b=12)] ``` -If we wanted to force that a tool is used (and that it is used only -once), we can set the `tool_choice` argument: - -```python -llm_with_multiply = llm.bind_tools([Multiply], tool_choice="Multiply") -llm_with_multiply.invoke( - "make up some numbers if you really want but I'm not forcing you" -) -``` - -``` text -AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_f3DApOzb60iYjTfOhVFhDRMI', 'function': {'arguments': '{"a":5,"b":10}', 'name': 'Multiply'}, 'type': 'function'}]}) -``` - -For more see the [ChatOpenAI API -reference](https://api.python.langchain.com/en/latest/chat_models/langchain_openai.chat_models.base.ChatOpenAI.html#langchain_openai.chat_models.base.ChatOpenAI.bind_tools). - - - - -Install dependencies and set API keys: - -```python -%pip install -qU langchain-fireworks -``` - - -```python -os.environ["FIREWORKS_API_KEY"] = getpass.getpass() -``` - -We can use the `ChatFireworks.bind_tools()` method to handle converting -`Multiply` to a valid function schema and binding it to the model (i.e., -passing it in each time the model is invoked). - -```python -from langchain_fireworks import ChatFireworks - -llm = ChatFireworks(model="accounts/fireworks/models/firefunction-v1", temperature=0) -llm_with_tools = llm.bind_tools([Multiply]) -llm_with_tools.invoke("what's 3 * 12") -``` - -``` text -AIMessage(content='Three multiplied by twelve is 36.') -``` - If our model isn’t using the tool, as is the case here, we can force tool usage by specifying `tool_choice="any"` or by specifying the name of the specific tool we want used: @@ -182,175 +122,12 @@ llm_with_tools = llm.bind_tools([Multiply], tool_choice="Multiply") llm_with_tools.invoke("what's 3 * 12") ``` -``` text +```text AIMessage(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_qIP2bJugb67LGvc6Zhwkvfqc', 'type': 'function', 'function': {'name': 'Multiply', 'arguments': '{"a": 3, "b": 12}'}}]}) ``` -We can add a tool parser to extract the tool calls from the generated -message to JSON: - -```python -from langchain_core.output_parsers.openai_tools import JsonOutputToolsParser - -tool_chain = llm_with_tools | JsonOutputToolsParser() -tool_chain.invoke("what's 3 * 12") -``` - -``` text -[{'type': 'Multiply', 'args': {'a': 3, 'b': 12}}] -``` - -Or back to the original Pydantic class: - -```python -from langchain_core.output_parsers.openai_tools import PydanticToolsParser - -tool_chain = llm_with_tools | PydanticToolsParser(tools=[Multiply]) -tool_chain.invoke("what's 3 * 12") -``` - -``` text -[Multiply(a=3, b=12)] -``` - -For more see the [ChatFireworks](https://api.python.langchain.com/en/latest/chat_models/langchain_fireworks.chat_models.ChatFireworks.html#langchain_fireworks.chat_models.ChatFireworks.bind_tools) reference. - - - - -Install dependencies and set API keys: - -```python -%pip install -qU langchain-mistralai -``` - - -```python -os.environ["MISTRAL_API_KEY"] = getpass.getpass() -``` - -We can use the `ChatMistralAI.bind_tools()` method to handle converting -`Multiply` to a valid function schema and binding it to the model (i.e., -passing it in each time the model is invoked). - -```python -from langchain_mistralai import ChatMistralAI - -llm = ChatMistralAI(model="mistral-large-latest", temperature=0) -llm_with_tools = llm.bind_tools([Multiply]) -llm_with_tools.invoke("what's 3 * 12") -``` - -``` text -AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'null', 'type': , 'function': {'name': 'Multiply', 'arguments': '{"a": 3, "b": 12}'}}]}) -``` - -We can add a tool parser to extract the tool calls from the generated -message to JSON: - -```python -from langchain_core.output_parsers.openai_tools import JsonOutputToolsParser - -tool_chain = llm_with_tools | JsonOutputToolsParser() -tool_chain.invoke("what's 3 * 12") -``` - -``` text -[{'type': 'Multiply', 'args': {'a': 3, 'b': 12}}] -``` - -Or back to the original Pydantic class: - -```python -from langchain_core.output_parsers.openai_tools import PydanticToolsParser - -tool_chain = llm_with_tools | PydanticToolsParser(tools=[Multiply]) -tool_chain.invoke("what's 3 * 12") -``` - -``` text -[Multiply(a=3, b=12)] -``` - -We can force tool usage by specifying `tool_choice="any"`: - -```python -llm_with_tools = llm.bind_tools([Multiply], tool_choice="any") -llm_with_tools.invoke("I don't even want you to use the tool") -``` - -``` text -AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'null', 'type': , 'function': {'name': 'Multiply', 'arguments': '{"a": 5, "b": 7}'}}]}) -``` - -For more see the [ChatMistralAI API reference](https://api.python.langchain.com/en/latest/chat_models/langchain_mistralai.chat_models.ChatMistralAI.html#langchain_mistralai.chat_models.ChatMistralAI). - - - - -Since TogetherAI is a drop-in replacement for OpenAI, we can just use -the OpenAI integration. - -Install dependencies and set API keys: - -```python -%pip install -qU langchain-openai -``` - - -```python -os.environ["TOGETHER_API_KEY"] = getpass.getpass() -``` - -We can use the `ChatOpenAI.bind_tools()` method to handle converting -`Multiply` to a valid function schema and binding it to the model (i.e., -passing it in each time the model is invoked). - -```python -from langchain_openai import ChatOpenAI - -llm = ChatOpenAI( - base_url="https://api.together.xyz/v1", - api_key=os.environ["TOGETHER_API_KEY"], - model="mistralai/Mixtral-8x7B-Instruct-v0.1", -) -llm_with_tools = llm.bind_tools([Multiply]) -llm_with_tools.invoke("what's 3 * 12") -``` - -``` text -AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_4tc61dp0478zafqe33hfriee', 'function': {'arguments': '{"a":3,"b":12}', 'name': 'Multiply'}, 'type': 'function'}]}) -``` - -We can add a tool parser to extract the tool calls from the generated -message to JSON: - -```python -from langchain_core.output_parsers.openai_tools import JsonOutputToolsParser - -tool_chain = llm_with_tools | JsonOutputToolsParser() -tool_chain.invoke("what's 3 * 12") -``` - -``` text -[{'type': 'Multiply', 'args': {'a': 3, 'b': 12}}] -``` - -Or back to the original Pydantic class: - -```python -from langchain_core.output_parsers.openai_tools import PydanticToolsParser - -tool_chain = llm_with_tools | PydanticToolsParser(tools=[Multiply]) -tool_chain.invoke("what's 3 * 12") -``` - -``` text -[Multiply(a=3, b=12)] -``` - If we wanted to force that a tool is used (and that it is used only -once), we can set the `tool_choice` argument: +once), we can set the `tool_choice` argument to the name of the tool: ```python llm_with_multiply = llm.bind_tools([Multiply], tool_choice="Multiply") @@ -359,16 +136,13 @@ llm_with_multiply.invoke( ) ``` -``` text -AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_6k6d0gr3jhqil2kqf7sgeusl', 'function': {'arguments': '{"a":5,"b":7}', 'name': 'Multiply'}, 'type': 'function'}]}) +```text +AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_f3DApOzb60iYjTfOhVFhDRMI', 'function': {'arguments': '{"a":5,"b":10}', 'name': 'Multiply'}, 'type': 'function'}]}) ``` For more see the [ChatOpenAI API reference](https://api.python.langchain.com/en/latest/chat_models/langchain_openai.chat_models.base.ChatOpenAI.html#langchain_openai.chat_models.base.ChatOpenAI.bind_tools). - - - ## Defining functions schemas In case you need to access function schemas directly, LangChain has a built-in converter that can turn @@ -395,7 +169,7 @@ def multiply(a: int, b: int) -> int: print(json.dumps(convert_to_openai_tool(multiply), indent=2)) ``` -``` text +```text { "type": "function", "function": { @@ -438,7 +212,7 @@ class multiply(BaseModel): print(json.dumps(convert_to_openai_tool(multiply), indent=2)) ``` -``` text +```text { "type": "function", "function": { @@ -493,7 +267,7 @@ class Multiply(BaseTool): print(json.dumps(convert_to_openai_tool(Multiply()), indent=2)) ``` -``` text +```text { "type": "function", "function": { @@ -522,14 +296,14 @@ print(json.dumps(convert_to_openai_tool(Multiply()), indent=2)) ## Next steps -- **Output parsing**: See [OpenAI Tools output - parsers](../../../../docs/modules/model_io/output_parsers/types/openai_tools) - and [OpenAI Functions output - parsers](../../../../docs/modules/model_io/output_parsers/types/openai_functions) - to learn about extracting the function calling API responses into - various formats. -- **Structured output chains**: [Some models have constructors](../../../../docs/guides/structured_output) that - handle creating a structured output chain for you. -- **Tool use**: See how to construct chains and agents that actually - call the invoked tools in [these - guides](../../../../docs/use_cases/tool_use/). +- **Output parsing**: See [OpenAI Tools output + parsers](../../../../docs/modules/model_io/output_parsers/types/openai_tools) + and [OpenAI Functions output + parsers](../../../../docs/modules/model_io/output_parsers/types/openai_functions) + to learn about extracting the function calling API responses into + various formats. +- **Structured output chains**: [Some models have constructors](../../../../docs/guides/structured_output) that + handle creating a structured output chain for you. +- **Tool use**: See how to construct chains and agents that actually + call the invoked tools in [these + guides](../../../../docs/use_cases/tool_use/). diff --git a/docs/docs/modules/model_io/chat/quick_start.ipynb b/docs/docs/modules/model_io/chat/quick_start.ipynb index 3c48cf50be..50ed12d7a3 100644 --- a/docs/docs/modules/model_io/chat/quick_start.ipynb +++ b/docs/docs/modules/model_io/chat/quick_start.ipynb @@ -22,32 +22,19 @@ "While chat models use language models under the hood, the interface they use is a bit different.\n", "Rather than using a \"text in, text out\" API, they use an interface where \"chat messages\" are the inputs and outputs.\n", "\n", - "## Setup\n", - "\n", - "For this example we'll need to install the OpenAI partner package:\n", - "\n", - "```bash\n", - "pip install langchain-openai\n", - "```\n", - "\n", - "Accessing the API requires an API key, which you can get by creating an account and heading [here](https://platform.openai.com/account/api-keys). Once we have a key we'll want to set it as an environment variable by running:\n", - "\n", - "```bash\n", - "export OPENAI_API_KEY=\"...\"\n", - "```\n", - "If you'd prefer not to set an environment variable you can pass the key in directly via the `openai_api_key` named parameter when initiating the OpenAI LLM class:\n" + "## Setup\n" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "e230abb2-bc84-438b-b9ff-dd124acb1375", "metadata": {}, - "outputs": [], "source": [ - "from langchain_openai import ChatOpenAI\n", + "```{=mdx}\n", + "import ChatModelTabs from \"@theme/ChatModelTabs\";\n", "\n", - "chat = ChatOpenAI(openai_api_key=\"...\")" + "\n", + "```" ] }, { @@ -55,19 +42,25 @@ "id": "609bbd5c-e5a1-4166-89e1-d6c52054860d", "metadata": {}, "source": [ - "Otherwise you can initialize without any params:" + "If you'd prefer not to set an environment variable you can pass the key in directly via the api key arg named parameter when initiating the chat model class:" ] }, { - "cell_type": "code", - "execution_count": 1, + "cell_type": "markdown", "id": "3d9dbf70-2397-4d6b-87ec-3e6d4699f3df", "metadata": {}, - "outputs": [], "source": [ - "from langchain_openai import ChatOpenAI\n", - "\n", - "chat = ChatOpenAI()" + "```{=mdx}\n", + "\n", + "```" ] }, { @@ -108,6 +101,21 @@ "]" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "570dae71", + "metadata": {}, + "outputs": [], + "source": [ + "# | output: false\n", + "# | echo: false\n", + "\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "chat = ChatOpenAI()" + ] + }, { "cell_type": "code", "execution_count": 11, diff --git a/docs/docs/use_cases/question_answering/chat_history.ipynb b/docs/docs/use_cases/question_answering/chat_history.ipynb index ceac1e88a5..265bc5912d 100644 --- a/docs/docs/use_cases/question_answering/chat_history.ipynb +++ b/docs/docs/use_cases/question_answering/chat_history.ipynb @@ -516,7 +516,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.4" + "version": "3.11.0" } }, "nbformat": 4, diff --git a/docs/docs/use_cases/question_answering/quickstart.mdx b/docs/docs/use_cases/question_answering/quickstart.mdx index 83eb2f5420..d0c1d70f75 100644 --- a/docs/docs/use_cases/question_answering/quickstart.mdx +++ b/docs/docs/use_cases/question_answering/quickstart.mdx @@ -24,7 +24,7 @@ introduction](../../../docs/use_cases/question_answering/), which has two main components: **Indexing**: a pipeline for ingesting data from a source and indexing -it. *This usually happens offline.* +it. _This usually happens offline._ **Retrieval and generation**: the actual RAG chain, which takes the user query at run time and retrieves the relevant data from the index, then @@ -77,7 +77,7 @@ We’ll use the following packages: %pip install --upgrade --quiet langchain langchain-community langchainhub langchain-openai chromadb bs4 ``` -We need to set environment variable `OPENAI_API_KEY`, which can be done +We need to set environment variable `OPENAI_API_KEY` for the embeddings model, which can be done directly or loaded from a `.env` file like so: ```python @@ -125,10 +125,13 @@ from langchain_community.document_loaders import WebBaseLoader from langchain_community.vectorstores import Chroma from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough -from langchain_openai import ChatOpenAI, OpenAIEmbeddings +from langchain_openai import OpenAIEmbeddings from langchain_text_splitters import RecursiveCharacterTextSplitter ``` +import ChatModelTabs from "@theme/ChatModelTabs"; + + ```python # Load, chunk and index the contents of the blog. @@ -149,8 +152,6 @@ vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings # Retrieve and generate using the relevant snippets of the blog. retriever = vectorstore.as_retriever() prompt = hub.pull("rlm/rag-prompt") -llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0) - def format_docs(docs): return "\n\n".join(doc.page_content for doc in docs) @@ -164,12 +165,11 @@ rag_chain = ( ) ``` - ```python rag_chain.invoke("What is Task Decomposition?") ``` -``` text +```text 'Task decomposition is a technique used to break down complex tasks into smaller and simpler steps. It can be done through prompting techniques like Chain of Thought or Tree of Thoughts, or by using task-specific instructions or human inputs. Task decomposition helps agents plan ahead and manage complicated tasks more effectively.' ``` @@ -219,12 +219,11 @@ loader = WebBaseLoader( docs = loader.load() ``` - ```python len(docs[0].page_content) ``` -``` text +```text 42824 ``` @@ -232,11 +231,11 @@ len(docs[0].page_content) print(docs[0].page_content[:500]) ``` -``` text +```text LLM Powered Autonomous Agents - + Date: June 23, 2023 | Estimated Reading Time: 31 min | Author: Lilian Weng @@ -248,13 +247,14 @@ In ### Go deeper `DocumentLoader`: Object that loads data from a source as list of -`Documents`. +`Documents`. + - [Docs](../../../docs/modules/data_connection/document_loaders/): -Detailed documentation on how to use `DocumentLoaders`. + Detailed documentation on how to use `DocumentLoaders`. - [Integrations](../../../docs/integrations/document_loaders/): 160+ -integrations to choose from. + integrations to choose from. - [Interface](https://api.python.langchain.com/en/latest/document_loaders/langchain_core.document_loaders.base.BaseLoader.html): -API reference  for the base interface. + API reference  for the base interface. ## 2. Indexing: Split {#indexing-split} @@ -289,12 +289,11 @@ text_splitter = RecursiveCharacterTextSplitter( all_splits = text_splitter.split_documents(docs) ``` - ```python len(all_splits) ``` -``` text +```text 66 ``` @@ -302,7 +301,7 @@ len(all_splits) len(all_splits[0].page_content) ``` -``` text +```text 969 ``` @@ -310,7 +309,7 @@ len(all_splits[0].page_content) all_splits[10].metadata ``` -``` text +```text {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/', 'start_index': 7056} ``` @@ -318,18 +317,20 @@ all_splits[10].metadata ### Go deeper `TextSplitter`: Object that splits a list of `Document`s into smaller -chunks. Subclass of `DocumentTransformer`s. +chunks. Subclass of `DocumentTransformer`s. + - Explore `Context-aware splitters`, which keep the location (“context”) of each -split in the original `Document`: - [Markdown -files](../../../docs/modules/data_connection/document_transformers/markdown_header_metadata) -- [Code (py or js)](../../../docs/integrations/document_loaders/source_code) -- [Scientific papers](../../../docs/integrations/document_loaders/grobid) + split in the original `Document`: - [Markdown + files](../../../docs/modules/data_connection/document_transformers/markdown_header_metadata) +- [Code (py or js)](../../../docs/integrations/document_loaders/source_code) +- [Scientific papers](../../../docs/integrations/document_loaders/grobid) - [Interface](https://api.python.langchain.com/en/latest/base/langchain_text_splitters.base.TextSplitter.html): API reference for the base interface. `DocumentTransformer`: Object that performs a transformation on a list -of `Document`s. -- [Docs](../../../docs/modules/data_connection/document_transformers/): Detailed documentation on how to use `DocumentTransformers` -- [Integrations](../../../docs/integrations/document_transformers/) +of `Document`s. + +- [Docs](../../../docs/modules/data_connection/document_transformers/): Detailed documentation on how to use `DocumentTransformers` +- [Integrations](../../../docs/integrations/document_transformers/) - [Interface](https://api.python.langchain.com/en/latest/documents/langchain_core.documents.transformers.BaseDocumentTransformer.html): API reference for the base interface. ## 3. Indexing: Store {#indexing-store} @@ -360,15 +361,17 @@ vectorstore = Chroma.from_documents(documents=all_splits, embedding=OpenAIEmbedd ### Go deeper `Embeddings`: Wrapper around a text embedding model, used for converting -text to embeddings. -- [Docs](../../../docs/modules/data_connection/text_embedding): Detailed documentation on how to use embeddings. -- [Integrations](../../../docs/integrations/text_embedding/): 30+ integrations to choose from. +text to embeddings. + +- [Docs](../../../docs/modules/data_connection/text_embedding): Detailed documentation on how to use embeddings. +- [Integrations](../../../docs/integrations/text_embedding/): 30+ integrations to choose from. - [Interface](https://api.python.langchain.com/en/latest/embeddings/langchain_core.embeddings.Embeddings.html): API reference for the base interface. `VectorStore`: Wrapper around a vector database, used for storing and -querying embeddings. -- [Docs](../../../docs/modules/data_connection/vectorstores/): Detailed documentation on how to use vector stores. -- [Integrations](../../../docs/integrations/vectorstores/): 40+ integrations to choose from. +querying embeddings. + +- [Docs](../../../docs/modules/data_connection/vectorstores/): Detailed documentation on how to use vector stores. +- [Integrations](../../../docs/integrations/vectorstores/): 40+ integrations to choose from. - [Interface](https://api.python.langchain.com/en/latest/vectorstores/langchain_core.vectorstores.VectorStore.html): API reference for the base interface. This completes the **Indexing** portion of the pipeline. At this point @@ -399,17 +402,15 @@ facilitate retrieval. Any `VectorStore` can easily be turned into a retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 6}) ``` - ```python retrieved_docs = retriever.invoke("What are the approaches to Task Decomposition?") ``` - ```python len(retrieved_docs) ``` -``` text +```text 6 ``` @@ -417,7 +418,7 @@ len(retrieved_docs) print(retrieved_docs[0].page_content) ``` -``` text +```text Tree of Thoughts (Yao et al. 2023) extends CoT by exploring multiple reasoning possibilities at each step. It first decomposes the problem into multiple thought steps and generates multiple thoughts per step, creating a tree structure. The search process can be BFS (breadth-first search) or DFS (depth-first search) with each state evaluated by a classifier (via a prompt) or majority vote. Task decomposition can be done (1) by LLM with simple prompting like "Steps for XYZ.\n1.", "What are the subgoals for achieving XYZ?", (2) by using task-specific instructions; e.g. "Write a story outline." for writing a novel, or (3) with human inputs. ``` @@ -429,27 +430,27 @@ to do retrieval, too. `Retriever`: An object that returns `Document`s given a text query -- [Docs](../../../docs/modules/data_connection/retrievers/): Further - documentation on the interface and built-in retrieval techniques. - Some of which include: - - `MultiQueryRetriever` [generates variants of the input - question](../../../docs/modules/data_connection/retrievers/MultiQueryRetriever) - to improve retrieval hit rate. - - `MultiVectorRetriever` (diagram below) instead generates - [variants of the - embeddings](../../../docs/modules/data_connection/retrievers/multi_vector), - also in order to improve retrieval hit rate. - - `Max marginal relevance` selects for [relevance and - diversity](https://www.cs.cmu.edu/~jgc/publication/The_Use_MMR_Diversity_Based_LTMIR_1998.pdf) - among the retrieved documents to avoid passing in duplicate - context. - - Documents can be filtered during vector store retrieval using - metadata filters, such as with a [Self Query - Retriever](../../../docs/modules/data_connection/retrievers/self_query). -- [Integrations](../../../docs/integrations/retrievers/): Integrations - with retrieval services. -- [Interface](https://api.python.langchain.com/en/latest/retrievers/langchain_core.retrievers.BaseRetriever.html): - API reference for the base interface. +- [Docs](../../../docs/modules/data_connection/retrievers/): Further + documentation on the interface and built-in retrieval techniques. + Some of which include: + - `MultiQueryRetriever` [generates variants of the input + question](../../../docs/modules/data_connection/retrievers/MultiQueryRetriever) + to improve retrieval hit rate. + - `MultiVectorRetriever` (diagram below) instead generates + [variants of the + embeddings](../../../docs/modules/data_connection/retrievers/multi_vector), + also in order to improve retrieval hit rate. + - `Max marginal relevance` selects for [relevance and + diversity](https://www.cs.cmu.edu/~jgc/publication/The_Use_MMR_Diversity_Based_LTMIR_1998.pdf) + among the retrieved documents to avoid passing in duplicate + context. + - Documents can be filtered during vector store retrieval using + metadata filters, such as with a [Self Query + Retriever](../../../docs/modules/data_connection/retrievers/self_query). +- [Integrations](../../../docs/integrations/retrievers/): Integrations + with retrieval services. +- [Interface](https://api.python.langchain.com/en/latest/retrievers/langchain_core.retrievers.BaseRetriever.html): + API reference for the base interface. ## 5. Retrieval and Generation: Generate {#retrieval-and-generation-generate} @@ -460,34 +461,13 @@ parses the output. We’ll use the gpt-3.5-turbo OpenAI chat model, but any LangChain `LLM` or `ChatModel` could be substituted in. -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - - - - -```python -from langchain_openai import ChatOpenAI - -llm = ChatOpenAI(model_name="gpt-3.5-turbo-0125", temperature=0) -``` - - - - -```python -%pip install -qU langchain-anthropic -``` - +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; -```python -from langchain_anthropic import ChatAnthropic - -llm = ChatAnthropic(model="claude-3-sonnet-20240229", temperature=0.2, max_tokens=1024) -``` - - - + We’ll use a prompt for RAG that is checked into the LangChain prompt hub ([here](https://smith.langchain.com/hub/rlm/rag-prompt)). @@ -498,7 +478,6 @@ from langchain import hub prompt = hub.pull("rlm/rag-prompt") ``` - ```python example_messages = prompt.invoke( {"context": "filler context", "question": "filler question"} @@ -506,7 +485,7 @@ example_messages = prompt.invoke( example_messages ``` -``` text +```text [HumanMessage(content="You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.\nQuestion: filler question \nContext: filler context \nAnswer:")] ``` @@ -514,10 +493,10 @@ example_messages print(example_messages[0].content) ``` -``` text +```text You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise. -Question: filler question -Context: filler context +Question: filler question +Context: filler context Answer: ``` @@ -543,13 +522,12 @@ rag_chain = ( ) ``` - ```python for chunk in rag_chain.stream("What is Task Decomposition?"): print(chunk, end="", flush=True) ``` -``` text +```text Task decomposition is a technique used to break down complex tasks into smaller and simpler steps. It involves transforming big tasks into multiple manageable tasks, allowing for easier interpretation and execution by autonomous agents or models. Task decomposition can be done through various methods, such as using prompting techniques, task-specific instructions, or human inputs. ``` @@ -561,14 +539,16 @@ trace](https://smith.langchain.com/public/1799e8db-8a6d-4eb2-84d5-46e8d7d5a99b/r #### Choosing a model `ChatModel`: An LLM-backed chat model. Takes in a sequence of messages -and returns a message. +and returns a message. + - [Docs](../../../docs/modules/model_io/chat/) -- [Integrations](../../../docs/integrations/chat/): 25+ integrations to choose from. +- [Integrations](../../../docs/integrations/chat/): 25+ integrations to choose from. - [Interface](https://api.python.langchain.com/en/latest/language_models/langchain_core.language_models.chat_models.BaseChatModel.html): API reference for the base interface. -`LLM`: A text-in-text-out LLM. Takes in a string and returns a string. -- [Docs](../../../docs/modules/model_io/llms) -- [Integrations](../../../docs/integrations/llms): 75+ integrations to choose from. +`LLM`: A text-in-text-out LLM. Takes in a string and returns a string. + +- [Docs](../../../docs/modules/model_io/llms) +- [Integrations](../../../docs/integrations/llms): 75+ integrations to choose from. - [Interface](https://api.python.langchain.com/en/latest/language_models/langchain_core.language_models.llms.BaseLLM.html): API reference for the base interface. See a guide on RAG with locally-running models @@ -605,7 +585,7 @@ rag_chain = ( rag_chain.invoke("What is Task Decomposition?") ``` -``` text +```text 'Task decomposition is a technique used to break down complex tasks into smaller and simpler steps. It involves transforming big tasks into multiple manageable tasks, allowing for a more systematic and organized approach to problem-solving. Thanks for asking!' ``` @@ -619,11 +599,11 @@ plenty of features, integrations, and extensions to explore in each of the above sections. Along from the **Go deeper** sources mentioned above, good next steps include: -- [Return - sources](../../../docs/use_cases/question_answering/sources): Learn - how to return source documents -- [Streaming](../../../docs/use_cases/question_answering/streaming): - Learn how to stream outputs and intermediate steps -- [Add chat - history](../../../docs/use_cases/question_answering/chat_history): - Learn how to add chat history to your app +- [Return + sources](../../../docs/use_cases/question_answering/sources): Learn + how to return source documents +- [Streaming](../../../docs/use_cases/question_answering/streaming): + Learn how to stream outputs and intermediate steps +- [Add chat + history](../../../docs/use_cases/question_answering/chat_history): + Learn how to add chat history to your app diff --git a/docs/src/theme/ChatModelTabs.js b/docs/src/theme/ChatModelTabs.js index 5e9fe4a52b..b521bf50d3 100644 --- a/docs/src/theme/ChatModelTabs.js +++ b/docs/src/theme/ChatModelTabs.js @@ -1,4 +1,4 @@ -/* eslint-disable react/jsx-props-no-spreading */ +/* eslint-disable react/jsx-props-no-spreading, react/destructuring-assignment */ import React from "react"; import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; @@ -20,7 +20,24 @@ os.environ["${apiKeyName}"] = getpass.getpass()`; } /** - * @param {{ openaiParams?: string, anthropicParams?: string, fireworksParams?: string, mistralParams?: string, googleParams?: string, hideOpenai?: boolean, hideAnthropic?: boolean, hideFireworks?: boolean, hideMistral?: boolean, hideGoogle?: boolean }} props + * @typedef {Object} ChatModelTabsProps - Component props. + * @property {string} [openaiParams] - Parameters for OpenAI chat model. Defaults to `model="gpt-3.5-turbo-0125"` + * @property {string} [anthropicParams] - Parameters for Anthropic chat model. Defaults to `model="claude-3-sonnet-20240229"` + * @property {string} [fireworksParams] - Parameters for Fireworks chat model. Defaults to `model="accounts/fireworks/models/mixtral-8x7b-instruct"` + * @property {string} [mistralParams] - Parameters for Mistral chat model. Defaults to `model="mistral-large-latest"` + * @property {string} [googleParams] - Parameters for Google chat model. Defaults to `model="gemini-pro"` + * @property {string} [togetherParams] - Parameters for Google chat model. Defaults to `model="gemini-pro"` + * @property {boolean} [hideOpenai] - Whether or not to hide OpenAI chat model. + * @property {boolean} [hideAnthropic] - Whether or not to hide Anthropic chat model. + * @property {boolean} [hideFireworks] - Whether or not to hide Fireworks chat model. + * @property {boolean} [hideMistral] - Whether or not to hide Mistral chat model. + * @property {boolean} [hideGoogle] - Whether or not to hide Google chat model. + * @property {boolean} [hideTogether] - Whether or not to hide Together chat model. + * @property {string} [customVarName] - Custom variable name for the model. Defaults to `model`. + */ + +/** + * @param {ChatModelTabsProps} props - Component props. */ export default function ChatModelTabs(props) { const { @@ -29,24 +46,36 @@ export default function ChatModelTabs(props) { fireworksParams, mistralParams, googleParams, + togetherParams, hideOpenai, hideAnthropic, hideFireworks, hideMistral, hideGoogle, + hideTogether, + customVarName, } = props; - const openAIParamsOrDefault = openaiParams ?? `model="gpt-3.5-turbo-0125"` - const anthropicParamsOrDefault = anthropicParams ?? `model="claude-3-sonnet-20240229"` - const fireworksParamsOrDefault = fireworksParams ?? `model="accounts/fireworks/models/mixtral-8x7b-instruct"` - const mistralParamsOrDefault = mistralParams ?? `model="mistral-large-latest"` - const googleParamsOrDefault = googleParams ?? `model="gemini-pro"` + const openAIParamsOrDefault = openaiParams ?? `model="gpt-3.5-turbo-0125"`; + const anthropicParamsOrDefault = + anthropicParams ?? `model="claude-3-sonnet-20240229"`; + const fireworksParamsOrDefault = + fireworksParams ?? + `model="accounts/fireworks/models/mixtral-8x7b-instruct"`; + const mistralParamsOrDefault = + mistralParams ?? `model="mistral-large-latest"`; + const googleParamsOrDefault = googleParams ?? `model="gemini-pro"`; + const togetherParamsOrDefault = + togetherParams ?? + `\n base_url="https://api.together.xyz/v1",\n api_key=os.environ["TOGETHER_API_KEY"],\n model="mistralai/Mixtral-8x7B-Instruct-v0.1",`; + + const llmVarName = customVarName ?? "model"; const tabItems = [ { value: "OpenAI", label: "OpenAI", - text: `from langchain_openai import ChatOpenAI\n\nmodel = ChatOpenAI(${openAIParamsOrDefault})`, + text: `from langchain_openai import ChatOpenAI\n\n${llmVarName} = ChatOpenAI(${openAIParamsOrDefault})`, apiKeyName: "OPENAI_API_KEY", packageName: "langchain-openai", default: true, @@ -55,7 +84,7 @@ export default function ChatModelTabs(props) { { value: "Anthropic", label: "Anthropic", - text: `from langchain_anthropic import ChatAnthropic\n\nmodel = ChatAnthropic(${anthropicParamsOrDefault})`, + text: `from langchain_anthropic import ChatAnthropic\n\n${llmVarName} = ChatAnthropic(${anthropicParamsOrDefault})`, apiKeyName: "ANTHROPIC_API_KEY", packageName: "langchain-anthropic", default: false, @@ -64,7 +93,7 @@ export default function ChatModelTabs(props) { { value: "FireworksAI", label: "FireworksAI", - text: `from langchain_fireworks import ChatFireworks\n\nmodel = ChatFireworks(${fireworksParamsOrDefault})`, + text: `from langchain_fireworks import ChatFireworks\n\n${llmVarName} = ChatFireworks(${fireworksParamsOrDefault})`, apiKeyName: "FIREWORKS_API_KEY", packageName: "langchain-fireworks", default: false, @@ -73,7 +102,7 @@ export default function ChatModelTabs(props) { { value: "MistralAI", label: "MistralAI", - text: `from langchain_mistralai import ChatMistralAI\n\nmodel = ChatMistralAI(${mistralParamsOrDefault})`, + text: `from langchain_mistralai import ChatMistralAI\n\n${llmVarName} = ChatMistralAI(${mistralParamsOrDefault})`, apiKeyName: "MISTRAL_API_KEY", packageName: "langchain-mistralai", default: false, @@ -82,22 +111,40 @@ export default function ChatModelTabs(props) { { value: "Google", label: "Google", - text: `from langchain_google_genai import ChatGoogleGenerativeAI\n\nmodel = ChatGoogleGenerativeAI(${googleParamsOrDefault})`, + text: `from langchain_google_genai import ChatGoogleGenerativeAI\n\n${llmVarName} = ChatGoogleGenerativeAI(${googleParamsOrDefault})`, apiKeyName: "GOOGLE_API_KEY", packageName: "langchain-google-genai", default: false, shouldHide: hideGoogle, - } - ] + }, + { + value: "TogetherAI", + label: "TogetherAI", + text: `from langchain_openai import ChatOpenAI\n\n${llmVarName} = ChatOpenAI(${togetherParamsOrDefault})`, + apiKeyName: "TOGETHER_API_KEY", + packageName: "langchain-openai", + default: false, + shouldHide: hideTogether, + }, + ]; return ( - {tabItems.filter((tabItem) => !tabItem.shouldHide).map((tabItem) => ( - - - {tabItem.text} - - ))} + {tabItems + .filter((tabItem) => !tabItem.shouldHide) + .map((tabItem) => ( + + + {tabItem.text} + + ))} ); } diff --git a/docs/vercel_build.sh b/docs/vercel_build.sh index 334f435c85..1c32e29398 100755 --- a/docs/vercel_build.sh +++ b/docs/vercel_build.sh @@ -4,9 +4,9 @@ yum -y update yum install gcc bzip2-devel libffi-devel zlib-devel wget tar gzip -y # install quarto -wget -q https://github.com/quarto-dev/quarto-cli/releases/download/v1.3.450/quarto-1.3.450-linux-amd64.tar.gz -tar -xzf quarto-1.3.450-linux-amd64.tar.gz -export PATH=$PATH:$(pwd)/quarto-1.3.450/bin/ +wget -q https://github.com/quarto-dev/quarto-cli/releases/download/v1.4.552/quarto-1.4.552-linux-amd64.tar.gz +tar -xzf quarto-1.4.552-linux-amd64.tar.gz +export PATH=$PATH:$(pwd)/quarto-1.4.552/bin/ # setup python env