"query_transforming_retriever_chain = RunnableBranch(\n",
    "    (\n",
    "        lambda x: len(x.get(\"messages\", [])) == 1,\n",
    "        # If only one message, then we just pass that message's content to retriever\n",
    "        (lambda x: x[\"messages\"][-1].content) | retriever,\n",
    "    ),\n",
    "    # If messages, then we pass inputs to LLM chain to transform the query, then pass to retriever\n",
    "    query_transform_prompt | chat | StrOutputParser() | retriever,\n",
    ").with_config(run_name=\"chat_retriever_chain\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then, we can use this query transformation chain to make our retrieval chain better able to handle such followup questions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "SYSTEM_TEMPLATE = \"\"\"\n",
    "Answer the user's questions based on the below context. \n",
    "If the context doesn't contain any relevant information to the question, don't make something up and just say \"I don't know\":\n",
    "\n",
    "<context>\n",
    "{context}\n",
    "</context>\n",
    "\"\"\"\n",
    "\n",
    "question_answering_prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\n",
    "            \"system\",\n",
    "            SYSTEM_TEMPLATE,\n",
    "        ),\n",
    "        MessagesPlaceholder(variable_name=\"messages\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "document_chain = create_stuff_documents_chain(chat, question_answering_prompt)\n",
    "\n",
    "conversational_retrieval_chain = RunnablePassthrough.assign(\n",
    "    context=query_transforming_retriever_chain,\n",
    ").assign(\n",
    "    answer=document_chain,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Awesome! Let's invoke this new chain with the same inputs as earlier:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='Can LangSmith help test my LLM applications?')],\n",
       " 'context': [Document(page_content='Skip to main content🦜🛠 LangSmith DocsPython DocsJS/TS DocsSearchGo to AppLangSmithOverviewTracingTesting & EvaluationOrganizationsHubLangSmith CookbookOverviewOn this pageLangSmith Overview and User GuideBuilding reliable LLM applications can be challenging. LangChain simplifies the initial setup, but there is still work needed to bring the performance of prompts, chains and agents up the level where they are reliable enough to be used in production.Over the past two months, we at LangChain', metadata={'description': 'Building reliable LLM applications can be challenging. LangChain simplifies the initial setup, but there is still work needed to bring the performance of prompts, chains and agents up the level where they are reliable enough to be used in production.', 'language': 'en', 'source': 'https://docs.smith.langchain.com/overview', 'title': 'LangSmith Overview and User Guide | 🦜🛠 LangSmith'}),\n",
       "  Document(page_content='LangSmith Overview and User Guide | 🦜🛠 LangSmith', metadata={'description': 'Building reliable LLM applications can be challenging. LangChain simplifies the initial setup, but there is still work needed to bring the performance of prompts, chains and agents up the level where they are reliable enough to be used in production.', 'language': 'en', 'source': 'https://docs.smith.langchain.com/overview', 'title': 'LangSmith Overview and User Guide | 🦜🛠 LangSmith'}),\n",
       "  Document(page_content='You can also quickly edit examples and add them to datasets to expand the surface area of your evaluation sets or to fine-tune a model for improved quality or reduced costs.Monitoring\\u200bAfter all this, your app might finally ready to go in production. LangSmith can also be used to monitor your application in much the same way that you used for debugging. You can log all traces, visualize latency and token usage statistics, and troubleshoot specific issues as they arise. Each run can also be', metadata={'description': 'Building reliable LLM applications can be challenging. LangChain simplifies the initial setup, but there is still work needed to bring the performance of prompts, chains and agents up the level where they are reliable enough to be used in production.', 'language': 'en', 'source': 'https://docs.smith.langchain.com/overview', 'title': 'LangSmith Overview and User Guide | 🦜🛠 LangSmith'}),\n",
       "  Document(page_content=\"does that affect the output?\\u200bSo you notice a bad output, and you go into LangSmith to see what's going on. You find the faulty LLM call and are now looking at the exact input. You want to try changing a word or a phrase to see what happens -- what do you do?We constantly ran into this issue. Initially, we copied the prompt to a playground of sorts. But this got annoying, so we built a playground of our own! When examining an LLM call, you can click the Open in Playground button to access this\", metadata={'description': 'Building reliable LLM applications can be challenging. LangChain simplifies the initial setup, but there is still work needed to bring the performance of prompts, chains and agents up the level where they are reliable enough to be used in production.', 'language': 'en', 'source': 'https://docs.smith.langchain.com/overview', 'title': 'LangSmith Overview and User Guide | 🦜🛠 LangSmith'})],\n",
       " 'answer': 'Yes, LangSmith can help test and evaluate LLM (Language Model) applications. It simplifies the initial setup, and you can use it to monitor your application, log all traces, visualize latency and token usage statistics, and troubleshoot specific issues as they arise.'}"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "conversational_retrieval_chain.invoke(\n",
    "    {\n",
    "        \"messages\": [\n",
    "            HumanMessage(content=\"Can LangSmith help test my LLM applications?\"),\n",
    "        ]\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='Can LangSmith help test my LLM applications?'),\n",
       "  AIMessage(content='Yes, LangSmith can help test and evaluate your LLM applications. It allows you to quickly edit examples and add them to datasets to expand the surface area of your evaluation sets or to fine-tune a model for improved quality or reduced costs. Additionally, LangSmith can be used to monitor your application, log all traces, visualize latency and token usage statistics, and troubleshoot specific issues as they arise.'),\n",
       "  HumanMessage(content='Tell me more!')],\n",
       " 'context': [Document(page_content='LangSmith Overview and User Guide | 🦜🛠 LangSmith', metadata={'description': 'Building reliable LLM applications can be challenging. LangChain simplifies the initial setup, but there is still work needed to bring the performance of prompts, chains and agents up the level where they are reliable enough to be used in production.', 'language': 'en', 'source': 'https://docs.smith.langchain.com/overview', 'title': 'LangSmith Overview and User Guide | 🦜🛠 LangSmith'}),\n",


{
 "cells": [
  {
   "cell_type": "raw",
   "id": "8165bd4c",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "---\n",
    "keywords: [memory]\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f47033eb",
   "metadata": {},
   "source": [
    "# How to add message history\n",
    "\n",
    ":::info Prerequisites\n",
    "\n",
    "This guide assumes familiarity with the following concepts:\n",
    "- [Chaining runnables](/docs/how_to/sequence/)\n",
    "- [Prompt templates](/docs/concepts/#prompt-templates)\n",
    "- [Chat Messages](/docs/concepts/#message-types)\n",
    "- [LangGraph persistence](https://langchain-ai.github.io/langgraph/how-tos/persistence/)\n",
    "\n",
    ":::\n",
    "\n",
    ":::note\n",
    "\n",
    "This guide previously covered the [RunnableWithMessageHistory](https://python.langchain.com/api_reference/core/runnables/langchain_core.runnables.history.RunnableWithMessageHistory.html) abstraction. You can access this version of the guide in the [v0.2 docs](https://python.langchain.com/v0.2/docs/how_to/message_history/).\n",
    "\n",
    "As of the v0.3 release of LangChain, we recommend that LangChain users take advantage of [LangGraph persistence](https://langchain-ai.github.io/langgraph/concepts/persistence/) to incorporate `memory` into new LangChain applications.\n",
    "\n",
    "If your code is already relying on `RunnableWithMessageHistory` or `BaseChatMessageHistory`, you do **not** need to make any changes. We do not plan on deprecating this functionality in the near future as it works for simple chat applications and any code that uses `RunnableWithMessageHistory` will continue to work as expected.\n",
    "\n",
    "Please see [How to migrate to LangGraph Memory](/docs/versions/migrating_memory/) for more details.\n",
    ":::\n",
    "\n",
    "Passing conversation state into and out a chain is vital when building a chatbot. LangGraph implements a built-in persistence layer, allowing chain states to be automatically persisted in memory, or external backends such as SQLite, Postgres or Redis. Details can be found in the LangGraph [persistence documentation](https://langchain-ai.github.io/langgraph/how-tos/persistence/).\n",
    "\n",
    "In this guide we demonstrate how to add persistence to arbitrary LangChain runnables by wrapping them in a minimal LangGraph application. This lets us persist the message history and other elements of the chain's state, simplifying the development of multi-turn applications. It also supports multiple threads, enabling a single application to interact separately with multiple users.\n",
    "\n",
    "## Setup\n",
    "\n",
    "Let's initialize a chat model:\n",
    "\n",
    "import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
    "\n",
    "<ChatModelTabs\n",
    "  customVarName=\"llm\"\n",
    "/>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "ca50d084-ae4b-4aea-9eb7-2ebc699df9bc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | output: false\n",
    "# | echo: false\n",
    "\n",
    "# %pip install -qU langchain langchain_anthropic\n",
    "\n",
    "# import os\n",
    "# from getpass import getpass\n",
    "\n",
    "# os.environ[\"ANTHROPIC_API_KEY\"] = getpass()\n",
    "from langchain_anthropic import ChatAnthropic\n",
    "\n",
    "llm = ChatAnthropic(model=\"claude-3-haiku-20240307\", temperature=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f6121bc-2080-4ccc-acf0-f77de4bc951d",
   "metadata": {},
   "source": [
    "## Example: message inputs\n",
    "\n",
    "Adding memory to a [chat model](/docs/concepts/#chat-models) provides a simple example. Chat models accept a list of messages as input and output a message. LangGraph includes a built-in `MessagesState` that we can use for this purpose.\n",
    "\n",
    "Below, we:\n",
    "1. Define the graph state to be a list of messages;\n",
    "2. Add a single node to the graph that calls a chat model;\n",
    "3. Compile the graph with an in-memory checkpointer to store messages between runs.\n",
    "\n",
    ":::info\n",
    "\n",
    "The output of a LangGraph application is its [state](https://langchain-ai.github.io/langgraph/concepts/low_level/). This can be any Python type, but in this context it will typically be a `TypedDict` that matches the schema of your runnable.\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "f691a73a-a866-4354-9fff-8315605e2b8f",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "from langgraph.graph import START, MessagesState, StateGraph\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(state_schema=MessagesState)\n",
    "\n",
    "\n",
    "# Define the function that calls the model\n",
    "def call_model(state: MessagesState):\n",
    "    response = llm.invoke(state[\"messages\"])\n",
    "    # Update message history with response:\n",
    "    return {\"messages\": response}\n",
    "\n",
    "\n",
    "# Define the (single) node in the graph\n",
    "workflow.add_edge(START, \"model\")\n",
    "workflow.add_node(\"model\", call_model)\n",
    "\n",
    "# Add memory\n",
    "memory = MemorySaver()\n",
    "app = workflow.compile(checkpointer=memory)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0b396a8-f81e-4139-b4b2-75adf61d8179",
   "metadata": {},
   "source": [
    "When we run the application, we pass in a configuration `dict` that specifies a `thread_id`. This ID is used to distinguish conversational threads (e.g., between different users)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "e4309511-2140-4d91-8f5f-ea3661e6d179",
   "metadata": {},
   "outputs": [],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"abc123\"}}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "108c45a2-4971-4120-ba64-9a4305a414bb",
   "metadata": {},
   "source": [
    "We can then invoke the application:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "72a5ff6c-501f-4151-8dd9-f600f70554be",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "It's nice to meet you, Bob! I'm Claude, an AI assistant created by Anthropic. How can I help you today?\n"
     ]
    }
   ],
   "source": [
    "query = \"Hi! I'm Bob.\"\n",
    "\n",
    "input_messages = [HumanMessage(query)]\n",
    "output = app.invoke({\"messages\": input_messages}, config)\n",
    "output[\"messages\"][-1].pretty_print()  # output contains all messages in state"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "5931fb35-0fac-40e7-8ac6-b14cb4e926cd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [


"message_history = messages[\"messages\"]\n",
    "\n",
    "new_query = \"Pardon?\"\n",
    "\n",
    "messages = langgraph_agent_executor.invoke(\n",
    "    {\"messages\": message_history + [(\"human\", new_query)]}\n",
    ")\n",
    "{\n",
    "    \"input\": new_query,\n",
    "    \"output\": messages[\"messages\"][-1].content,\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4466a4d-e55e-4ece-bee8-2269a0b5677b",
   "metadata": {},
   "source": [
    "## Prompt Templates\n",
    "\n",
    "With legacy LangChain agents you have to pass in a prompt template. You can use this to control the agent.\n",
    "\n",
    "With LangGraph [react agent executor](https://langchain-ai.github.io/langgraph/reference/prebuilt/#create_react_agent), by default there is no prompt. You can achieve similar control over the agent in a few ways:\n",
    "\n",
    "1. Pass in a system message as input\n",
    "2. Initialize the agent with a system message\n",
    "3. Initialize the agent with a function to transform messages before passing to the model.\n",
    "\n",
    "Let's take a look at all of these below. We will pass in custom instructions to get the agent to respond in Spanish.\n",
    "\n",
    "First up, using `AgentExecutor`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "a9a11ccd-75e2-4c11-844d-a34870b0ff91",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'input': 'what is the value of magic_function(3)?',\n",
       " 'output': 'El valor de `magic_function(3)` es 5.'}"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", \"You are a helpful assistant. Respond only in Spanish.\"),\n",
    "        (\"human\", \"{input}\"),\n",
    "        # Placeholders fill up a **list** of messages\n",
    "        (\"placeholder\", \"{agent_scratchpad}\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "\n",
    "agent = create_tool_calling_agent(model, tools, prompt)\n",
    "agent_executor = AgentExecutor(agent=agent, tools=tools)\n",
    "\n",
    "agent_executor.invoke({\"input\": query})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd5f5500-5ae4-4000-a9fd-8c5a2cc6404d",
   "metadata": {},
   "source": [
    "Now, let's pass a custom system message to [react agent executor](https://langchain-ai.github.io/langgraph/reference/prebuilt/#create_react_agent).\n",
    "\n",
    "LangGraph's prebuilt `create_react_agent` does not take a prompt template directly as a parameter, but instead takes a [`state_modifier`](https://langchain-ai.github.io/langgraph/reference/prebuilt/#create_react_agent) parameter. This modifies the graph state before the llm is called, and can be one of four values:\n",
    "\n",
    "- A `SystemMessage`, which is added to the beginning of the list of messages.\n",
    "- A `string`, which is converted to a `SystemMessage` and added to the beginning of the list of messages.\n",
    "- A `Callable`, which should take in full graph state. The output is then passed to the language model.\n",
    "- Or a [`Runnable`](/docs/concepts/#langchain-expression-language-lcel), which should take in full graph state. The output is then passed to the language model.\n",
    "\n",
    "Here's how it looks in action:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "a9486805-676a-4d19-a5c4-08b41b172989",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import SystemMessage\n",
    "from langgraph.prebuilt import create_react_agent\n",
    "\n",
    "system_message = \"You are a helpful assistant. Respond only in Spanish.\"\n",
    "# This could also be a SystemMessage object\n",
    "# system_message = SystemMessage(content=\"You are a helpful assistant. Respond only in Spanish.\")\n",
    "\n",
    "langgraph_agent_executor = create_react_agent(\n",
    "    model, tools, state_modifier=system_message\n",
    ")\n",
    "\n",
    "\n",
    "messages = langgraph_agent_executor.invoke({\"messages\": [(\"user\", query)]})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc6059fd-0df7-4b6f-a84c-b5874e983638",
   "metadata": {},
   "source": [
    "We can also pass in an arbitrary function. This function should take in a list of messages and output a list of messages.\n",
    "We can do all types of arbitrary formatting of messages here. In this cases, let's just add a SystemMessage to the start of the list of messages."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "d369ab45-0c82-45f4-9d3e-8efb8dd47e2c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': 'what is the value of magic_function(3)?', 'output': 'The value of magic_function(3) is 5. ¡Pandamonium!'}\n"
     ]
    }
   ],
   "source": [
    "from langgraph.prebuilt import create_react_agent\n",
    "from langgraph.prebuilt.chat_agent_executor import AgentState\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", \"You are a helpful assistant. Respond only in Spanish.\"),\n",
    "        (\"placeholder\", \"{messages}\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "\n",
    "def _modify_state_messages(state: AgentState):\n",
    "    return prompt.invoke({\"messages\": state[\"messages\"]}).to_messages() + [\n",
    "        (\"user\", \"Also say 'Pandamonium!' after the answer.\")\n",
    "    ]\n",
    "\n",
    "\n",
    "langgraph_agent_executor = create_react_agent(\n",
    "    model, tools, state_modifier=_modify_state_messages\n",
    ")\n",
    "\n",
    "\n",
    "messages = langgraph_agent_executor.invoke({\"messages\": [(\"human\", query)]})\n",
    "print(\n",
    "    {\n",
    "        \"input\": query,\n",
    "        \"output\": messages[\"messages\"][-1].content,\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68df3a09",
   "metadata": {},
   "source": [
    "## Memory"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "96e7ffc8",
   "metadata": {},
   "source": [
    "### In LangChain\n",
    "\n",
    "With LangChain's [AgentExecutor](https://python.langchain.com/api_reference/langchain/agents/langchain.agents.agent.AgentExecutor.html#langchain.agents.agent.AgentExecutor.iter), you could add chat [Memory](https://python.langchain.com/api_reference/langchain/agents/langchain.agents.agent.AgentExecutor.html#langchain.agents.agent.AgentExecutor.memory) so it can engage in a multi-turn conversation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "b97beba5-8f74-430c-9399-91b77c8fa15c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hi Polly! The output of applying the magic function to the input 3 is 5.\n",
      "---\n",


"        (\"system\", \"You are a helpful assistant.\"),\n",
    "        (\"placeholder\", \"{messages}\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "\n",
    "def _modify_state_messages(state: AgentState):\n",
    "    return prompt.invoke({\"messages\": state[\"messages\"]}).to_messages()\n",
    "\n",
    "\n",
    "langgraph_agent_executor = create_react_agent(\n",
    "    model, tools, state_modifier=_modify_state_messages\n",
    ")\n",
    "\n",
    "for step in langgraph_agent_executor.stream(\n",
    "    {\"messages\": [(\"human\", query)]}, stream_mode=\"updates\"\n",
    "):\n",
    "    print(step)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6898ccbc-42b1-4373-954a-2c7b3849fbb0",
   "metadata": {},
   "source": [
    "## `return_intermediate_steps`\n",
    "\n",
    "### In LangChain\n",
    "\n",
    "Setting this parameter on AgentExecutor allows users to access intermediate_steps, which pairs agent actions (e.g., tool invocations) with their outcomes.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "a2f720f3-c121-4be2-b498-92c16bb44b0a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(ToolAgentAction(tool='magic_function', tool_input={'input': 3}, log=\"\\nInvoking: `magic_function` with `{'input': 3}`\\n\\n\\n\", message_log=[AIMessageChunk(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_wjaAyTjI2LSYOq7C8QZYSxEs', 'function': {'arguments': '{\"input\":3}', 'name': 'magic_function'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_c9aa9c0491'}, id='run-99e06b70-1ef6-4761-834b-87b6c5252e20', tool_calls=[{'name': 'magic_function', 'args': {'input': 3}, 'id': 'call_wjaAyTjI2LSYOq7C8QZYSxEs', 'type': 'tool_call'}], tool_call_chunks=[{'name': 'magic_function', 'args': '{\"input\":3}', 'id': 'call_wjaAyTjI2LSYOq7C8QZYSxEs', 'index': 0, 'type': 'tool_call_chunk'}])], tool_call_id='call_wjaAyTjI2LSYOq7C8QZYSxEs'), 5)]\n"
     ]
    }
   ],
   "source": [
    "agent_executor = AgentExecutor(agent=agent, tools=tools, return_intermediate_steps=True)\n",
    "result = agent_executor.invoke({\"input\": query})\n",
    "print(result[\"intermediate_steps\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "594f7567-302f-4fa8-85bb-025ac8322162",
   "metadata": {},
   "source": [
    "### In LangGraph\n",
    "\n",
    "By default the [react agent executor](https://langchain-ai.github.io/langgraph/reference/prebuilt/#create_react_agent) in LangGraph appends all messages to the central state. Therefore, it is easy to see any intermediate steps by just looking at the full state."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "ef23117a-5ccb-42ce-80c3-ea49a9d3a942",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='what is the value of magic_function(3)?', id='2d369331-8052-4167-bd85-9f6d8ad021ae'),\n",
       "  AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_oXiSQSe6WeWj7XIKXxZrO2IC', 'function': {'arguments': '{\"input\":3}', 'name': 'magic_function'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 14, 'prompt_tokens': 55, 'total_tokens': 69}, 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-297e7fc9-726f-46a0-8c67-dc28ed1724d0-0', tool_calls=[{'name': 'magic_function', 'args': {'input': 3}, 'id': 'call_oXiSQSe6WeWj7XIKXxZrO2IC', 'type': 'tool_call'}], usage_metadata={'input_tokens': 55, 'output_tokens': 14, 'total_tokens': 69}),\n",
       "  ToolMessage(content='5', name='magic_function', id='46370faf-9598-423c-b94b-aca8cb4f035d', tool_call_id='call_oXiSQSe6WeWj7XIKXxZrO2IC'),\n",
       "  AIMessage(content='The value of `magic_function(3)` is 5.', response_metadata={'token_usage': {'completion_tokens': 14, 'prompt_tokens': 78, 'total_tokens': 92}, 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27', 'finish_reason': 'stop', 'logprobs': None}, id='run-f48efaff-0c2c-4632-bbf9-7ee626f73d02-0', usage_metadata={'input_tokens': 78, 'output_tokens': 14, 'total_tokens': 92})]}"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langgraph.prebuilt import create_react_agent\n",
    "\n",
    "langgraph_agent_executor = create_react_agent(model, tools=tools)\n",
    "\n",
    "messages = langgraph_agent_executor.invoke({\"messages\": [(\"human\", query)]})\n",
    "\n",
    "messages"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "45b528e5-57e1-450e-8d91-513eab53b543",
   "metadata": {},
   "source": [
    "## `max_iterations`\n",
    "\n",
    "### In LangChain\n",
    "\n",
    "`AgentExecutor` implements a `max_iterations` parameter, allowing users to abort a run that exceeds a specified number of iterations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "16f189a7-fc78-4cb5-aa16-a94ca06401a6",
   "metadata": {},
   "outputs": [],
   "source": [
    "@tool\n",
    "def magic_function(input: str) -> str:\n",
    "    \"\"\"Applies a magic function to an input.\"\"\"\n",
    "    return \"Sorry, there was an error. Please try again.\"\n",
    "\n",
    "\n",
    "tools = [magic_function]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "c96aefd7-6f6e-4670-aca6-1ac3d4e7871f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "Invoking: `magic_function` with `{'input': '3'}`\n",
      "\n",
      "\n",


"  'content': 'San Francisco Weather Forecast for Apr 2024 - Risk of Rain Graph. Rain Risk Graph: Monthly Overview. Bar heights indicate rain risk percentages. Yellow bars mark low-risk days, while black and grey bars signal higher risks. Grey-yellow bars act as buffers, advising to keep at least one day clear from the riskier grey and black days, guiding ...'}]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "search.invoke(\"what is the weather in SF\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e8097977",
   "metadata": {},
   "source": [
    "### Retriever\n",
    "\n",
    "We will also create a retriever over some data of our own. For a deeper explanation of each step here, see [this tutorial](/docs/tutorials/rag)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "9c9ce713",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_community.document_loaders import WebBaseLoader\n",
    "from langchain_community.vectorstores import FAISS\n",
    "from langchain_openai import OpenAIEmbeddings\n",
    "from langchain_text_splitters import RecursiveCharacterTextSplitter\n",
    "\n",
    "loader = WebBaseLoader(\"https://docs.smith.langchain.com/overview\")\n",
    "docs = loader.load()\n",
    "documents = RecursiveCharacterTextSplitter(\n",
    "    chunk_size=1000, chunk_overlap=200\n",
    ").split_documents(docs)\n",
    "vector = FAISS.from_documents(documents, OpenAIEmbeddings())\n",
    "retriever = vector.as_retriever()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "dae53ec6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Document(page_content='# The data to predict and grade over    evaluators=[exact_match], # The evaluators to score the results    experiment_prefix=\"sample-experiment\", # The name of the experiment    metadata={      \"version\": \"1.0.0\",      \"revision_id\": \"beta\"    },)import { Client, Run, Example } from \\'langsmith\\';import { runOnDataset } from \\'langchain/smith\\';import { EvaluationResult } from \\'langsmith/evaluation\\';const client = new Client();// Define dataset: these are your test casesconst datasetName = \"Sample Dataset\";const dataset = await client.createDataset(datasetName, {    description: \"A sample dataset in LangSmith.\"});await client.createExamples({    inputs: [        { postfix: \"to LangSmith\" },        { postfix: \"to Evaluations in LangSmith\" },    ],    outputs: [        { output: \"Welcome to LangSmith\" },        { output: \"Welcome to Evaluations in LangSmith\" },    ],    datasetId: dataset.id,});// Define your evaluatorconst exactMatch = async ({ run, example }: { run: Run; example?:', metadata={'source': 'https://docs.smith.langchain.com/overview', 'title': 'Getting started with LangSmith | \\uf8ffü¶úÔ∏è\\uf8ffüõ†Ô∏è LangSmith', 'description': 'Introduction', 'language': 'en'})"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "retriever.invoke(\"how to upload a dataset\")[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "04aeca39",
   "metadata": {},
   "source": [
    "Now that we have populated our index that we will do doing retrieval over, we can easily turn it into a tool (the format needed for an agent to properly use it)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "117594b5",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.tools.retriever import create_retriever_tool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "7280b031",
   "metadata": {},
   "outputs": [],
   "source": [
    "retriever_tool = create_retriever_tool(\n",
    "    retriever,\n",
    "    \"langsmith_search\",\n",
    "    \"Search for information about LangSmith. For any questions about LangSmith, you must use this tool!\",\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3b47c1d",
   "metadata": {},
   "source": [
    "### Tools\n",
    "\n",
    "Now that we have created both, we can create a list of tools that we will use downstream."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "b8e8e710",
   "metadata": {},
   "outputs": [],
   "source": [
    "tools = [search, retriever_tool]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e00068b0",
   "metadata": {},
   "source": [
    "## Using Language Models\n",
    "\n",
    "Next, let's learn how to use a language model by to call tools. LangChain supports many different language models that you can use interchangably - select the one you want to use below!\n",
    "\n",
    "import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
    "\n",
    "<ChatModelTabs openaiParams={`model=\"gpt-4\"`} />\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "69185491",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | output: false\n",
    "# | echo: false\n",
    "\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "model = ChatOpenAI(model=\"gpt-4\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "642ed8bf",
   "metadata": {},
   "source": [
    "You can call the language model by passing in a list of messages. By default, the response is a `content` string."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "c96c960b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Hello! How can I assist you today?'"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "response = model.invoke([HumanMessage(content=\"hi!\")])\n",
    "response.content"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47bf8210",
   "metadata": {},
   "source": [
    "We can now see what it is like to enable this model to do tool calling. In order to enable that we use `.bind_tools` to give the language model knowledge of these tools"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "ba692a74",
   "metadata": {},
   "outputs": [],
   "source": [
    "model_with_tools = model.bind_tools(tools)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fd920b69",
   "metadata": {},
   "source": [
    "We can now call the model. Let's first call it with a normal message, and see how it responds. We can look at both the `content` field as well as the `tool_calls` field."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "b6a7e925",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ContentString: Hello! How can I assist you today?\n",
      "ToolCalls: []\n"
     ]
    }
   ],
   "source": [
    "response = model_with_tools.invoke([HumanMessage(content=\"Hi!\")])\n",
    "\n",
    "print(f\"ContentString: {response.content}\")\n",
    "print(f\"ToolCalls: {response.tool_calls}\")"
   ]
  },
  {


{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ea37db49-d389-4291-be73-885d06c1fb7e",
   "metadata": {},
   "source": [
    "# How to use prompting alone (no tool calling) to do extraction\n",
    "\n",
    "Tool calling features are not required for generating structured output from LLMs. LLMs that are able to follow prompt instructions well can be tasked with outputting information in a given format.\n",
    "\n",
    "This approach relies on designing good prompts and then parsing the output of the LLMs to make them extract information well.\n",
    "\n",
    "To extract data without tool-calling features: \n",
    "\n",
    "1. Instruct the LLM to generate text following an expected format (e.g., JSON with a certain schema);\n",
    "2. Use [output parsers](/docs/concepts#output-parsers) to structure the model response into a desired Python object.\n",
    "\n",
    "First we select a LLM:\n",
    "\n",
    "import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
    "\n",
    "<ChatModelTabs customVarName=\"model\" />\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "25487939-8713-4ec7-b774-e4a761ac8298",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-10T20:35:44.442501Z",
     "iopub.status.busy": "2024-09-10T20:35:44.442044Z",
     "iopub.status.idle": "2024-09-10T20:35:44.872217Z",
     "shell.execute_reply": "2024-09-10T20:35:44.871897Z"
    }
   },
   "outputs": [],
   "source": [
    "# | output: false\n",
    "# | echo: false\n",
    "\n",
    "from langchain_anthropic.chat_models import ChatAnthropic\n",
    "\n",
    "model = ChatAnthropic(model_name=\"claude-3-sonnet-20240229\", temperature=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3e412374-3beb-4bbf-966b-400c1f66a258",
   "metadata": {},
   "source": [
    ":::tip\n",
    "This tutorial is meant to be simple, but generally should really include reference examples to squeeze out performance!\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "abc1a945-0f80-4953-add4-cd572b6f2a51",
   "metadata": {},
   "source": [
    "## Using PydanticOutputParser\n",
    "\n",
    "The following example uses the built-in `PydanticOutputParser` to parse the output of a chat model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "497eb023-c043-443d-ac62-2d4ea85fe1b0",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-10T20:35:44.873979Z",
     "iopub.status.busy": "2024-09-10T20:35:44.873840Z",
     "iopub.status.idle": "2024-09-10T20:35:44.878966Z",
     "shell.execute_reply": "2024-09-10T20:35:44.878718Z"
    }
   },
   "outputs": [],
   "source": [
    "from typing import List, Optional\n",
    "\n",
    "from langchain_core.output_parsers import PydanticOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from pydantic import BaseModel, Field, validator\n",
    "\n",
    "\n",
    "class Person(BaseModel):\n",
    "    \"\"\"Information about a person.\"\"\"\n",
    "\n",
    "    name: str = Field(..., description=\"The name of the person\")\n",
    "    height_in_meters: float = Field(\n",
    "        ..., description=\"The height of the person expressed in meters.\"\n",
    "    )\n",
    "\n",
    "\n",
    "class People(BaseModel):\n",
    "    \"\"\"Identifying information about all people in a text.\"\"\"\n",
    "\n",
    "    people: List[Person]\n",
    "\n",
    "\n",
    "# Set up a parser\n",
    "parser = PydanticOutputParser(pydantic_object=People)\n",
    "\n",
    "# Prompt\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\n",
    "            \"system\",\n",
    "            \"Answer the user query. Wrap the output in `json` tags\\n{format_instructions}\",\n",
    "        ),\n",
    "        (\"human\", \"{query}\"),\n",
    "    ]\n",
    ").partial(format_instructions=parser.get_format_instructions())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c31aa2c8-05a9-4a12-80c5-ea1250dea0ae",
   "metadata": {},
   "source": [
    "Let's take a look at what information is sent to the model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "20b99ffb-a114-49a9-a7be-154c525f8ada",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-10T20:35:44.880355Z",
     "iopub.status.busy": "2024-09-10T20:35:44.880277Z",
     "iopub.status.idle": "2024-09-10T20:35:44.881834Z",
     "shell.execute_reply": "2024-09-10T20:35:44.881601Z"
    }
   },
   "outputs": [],
   "source": [
    "query = \"Anna is 23 years old and she is 6 feet tall\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "4f3a66ce-de19-4571-9e54-67504ae3fba7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-10T20:35:44.883138Z",
     "iopub.status.busy": "2024-09-10T20:35:44.883049Z",
     "iopub.status.idle": "2024-09-10T20:35:44.885139Z",
     "shell.execute_reply": "2024-09-10T20:35:44.884801Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "System: Answer the user query. Wrap the output in `json` tags\n",
      "The output should be formatted as a JSON instance that conforms to the JSON schema below.\n",
      "\n",
      "As an example, for the schema {\"properties\": {\"foo\": {\"title\": \"Foo\", \"description\": \"a list of strings\", \"type\": \"array\", \"items\": {\"type\": \"string\"}}}, \"required\": [\"foo\"]}\n",
      "the object {\"foo\": [\"bar\", \"baz\"]} is a well-formatted instance of the schema. The object {\"properties\": {\"foo\": [\"bar\", \"baz\"]}} is not well-formatted.\n",
      "\n",
      "Here is the output schema:\n",
      "```\n",


"{\"$defs\": {\"Person\": {\"description\": \"Information about a person.\", \"properties\": {\"name\": {\"description\": \"The name of the person\", \"title\": \"Name\", \"type\": \"string\"}, \"height_in_meters\": {\"description\": \"The height of the person expressed in meters.\", \"title\": \"Height In Meters\", \"type\": \"number\"}}, \"required\": [\"name\", \"height_in_meters\"], \"title\": \"Person\", \"type\": \"object\"}}, \"description\": \"Identifying information about all people in a text.\", \"properties\": {\"people\": {\"items\": {\"$ref\": \"#/$defs/Person\"}, \"title\": \"People\", \"type\": \"array\"}}, \"required\": [\"people\"]}\n",
      "```\n",
      "Human: Anna is 23 years old and she is 6 feet tall\n"
     ]
    }
   ],
   "source": [
    "print(prompt.format_prompt(query=query).to_string())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f1048e0-1bfd-49f9-b697-74389a5ce69c",
   "metadata": {},
   "source": [
    "Having defined our prompt, we simply chain together the prompt, model and output parser:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "7e0041eb-37dc-4384-9fe3-6dd8c356371e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-10T20:35:44.886765Z",
     "iopub.status.busy": "2024-09-10T20:35:44.886675Z",
     "iopub.status.idle": "2024-09-10T20:35:46.835960Z",
     "shell.execute_reply": "2024-09-10T20:35:46.835282Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "People(people=[Person(name='Anna', height_in_meters=1.83)])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain = prompt | model | parser\n",
    "chain.invoke({\"query\": query})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dd492fe4-110a-4b83-a191-79fffbc1055a",
   "metadata": {},
   "source": [
    "Check out the associated [Langsmith trace](https://smith.langchain.com/public/92ed52a3-92b9-45af-a663-0a9c00e5e396/r).\n",
    "\n",
    "Note that the schema shows up in two places: \n",
    "\n",
    "1. In the prompt, via `parser.get_format_instructions()`;\n",
    "2. In the chain, to receive the formatted output and structure it into a Python object (in this case, the Pydantic object `People`)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "815b3b87-3bc6-4b56-835e-c6b6703cef5d",
   "metadata": {},
   "source": [
    "## Custom Parsing\n",
    "\n",
    "If desired, it's easy to create a custom prompt and parser with `LangChain` and `LCEL`.\n",
    "\n",
    "To create a custom parser, define a function to parse the output from the model (typically an [AIMessage](https://python.langchain.com/api_reference/core/messages/langchain_core.messages.ai.AIMessage.html)) into an object of your choice.\n",
    "\n",
    "See below for a simple implementation of a JSON parser."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "b1f11912-c1bb-4a2a-a482-79bf3996961f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-10T20:35:46.839577Z",
     "iopub.status.busy": "2024-09-10T20:35:46.839233Z",
     "iopub.status.idle": "2024-09-10T20:35:46.849663Z",
     "shell.execute_reply": "2024-09-10T20:35:46.849177Z"
    }
   },
   "outputs": [],
   "source": [
    "import json\n",
    "import re\n",
    "from typing import List, Optional\n",
    "\n",
    "from langchain_anthropic.chat_models import ChatAnthropic\n",
    "from langchain_core.messages import AIMessage\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from pydantic import BaseModel, Field, validator\n",
    "\n",
    "\n",
    "class Person(BaseModel):\n",
    "    \"\"\"Information about a person.\"\"\"\n",
    "\n",
    "    name: str = Field(..., description=\"The name of the person\")\n",
    "    height_in_meters: float = Field(\n",
    "        ..., description=\"The height of the person expressed in meters.\"\n",
    "    )\n",
    "\n",
    "\n",
    "class People(BaseModel):\n",
    "    \"\"\"Identifying information about all people in a text.\"\"\"\n",
    "\n",
    "    people: List[Person]\n",
    "\n",
    "\n",
    "# Prompt\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\n",
    "            \"system\",\n",
    "            \"Answer the user query. Output your answer as JSON that  \"\n",
    "            \"matches the given schema: ```json\\n{schema}\\n```. \"\n",
    "            \"Make sure to wrap the answer in ```json and ``` tags\",\n",
    "        ),\n",
    "        (\"human\", \"{query}\"),\n",
    "    ]\n",
    ").partial(schema=People.schema())\n",
    "\n",
    "\n",
    "# Custom parser\n",
    "def extract_json(message: AIMessage) -> List[dict]:\n",
    "    \"\"\"Extracts JSON content from a string where JSON is embedded between ```json and ``` tags.\n",
    "\n",
    "    Parameters:\n",
    "        text (str): The text containing the JSON content.\n",
    "\n",
    "    Returns:\n",
    "        list: A list of extracted JSON strings.\n",
    "    \"\"\"\n",
    "    text = message.content\n",
    "    # Define the regular expression pattern to match JSON blocks\n",
    "    pattern = r\"```json(.*?)```\"\n",
    "\n",
    "    # Find all non-overlapping matches of the pattern in the string\n",
    "    matches = re.findall(pattern, text, re.DOTALL)\n",
    "\n",
    "    # Return the list of matched JSON strings, stripping any leading or trailing whitespace\n",
    "    try:\n",
    "        return [json.loads(match.strip()) for match in matches]\n",
    "    except Exception:\n",
    "        raise ValueError(f\"Failed to parse: {message}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "9260d5e8-3b6c-4639-9f3b-fb2f90239e4b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-09-10T20:35:46.851870Z",
     "iopub.status.busy": "2024-09-10T20:35:46.851698Z",
     "iopub.status.idle": "2024-09-10T20:35:46.854786Z",
     "shell.execute_reply": "2024-09-10T20:35:46.854424Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "System: Answer the user query. Output your answer as JSON that  matches the given schema: ```json\n",


---
keywords: [charactertextsplitter]
---

This is the simplest method. This splits based on a given character sequence, which defaults to `"\n\n"`. Chunk length is measured by number of characters.

1. How the text is split: by single character separator.

2. How the chunk size is measured: by number of characters.

To obtain the string content directly, use `.split_text`.

To create LangChain [Document](https://python.langchain.com/api_reference/core/documents/langchain_core.documents.base.Document.html) objects (e.g., for use in downstream tasks), use `.create_documents`.

%pip install -qU langchain-text-splitters

stdout

page_content='Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans.  \n\nLast year COVID-19 kept us apart. This year we are finally together again. \n\nTonight, we meet as Democrats Republicans and Independents. But most importantly as Americans. \n\nWith a duty to one another to the American people to the Constitution. \n\nAnd with an unwavering resolve that freedom will always triumph over tyranny. \n\nSix days ago, Russia’s Vladimir Putin sought to shake the foundations of the free world thinking he could make it bend to his menacing ways. But he badly miscalculated. \n\nHe thought he could roll into Ukraine and the world would roll over. Instead he met a wall of strength he never imagined. \n\nHe met the Ukrainian people. \n\nFrom President Zelenskyy to every Ukrainian, their fearlessness, their courage, their determination, inspires the world.'

from langchain_text_splitters import CharacterTextSplitter

with open("state_of_the_union.txt") as f:

Use `.create_documents` to propagate metadata associated with each document to the output chunks:

page_content='Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans.  \n\nLast year COVID-19 kept us apart. This year we are finally together again. \n\nTonight, we meet as Democrats Republicans and Independents. But most importantly as Americans. \n\nWith a duty to one another to the American people to the Constitution. \n\nAnd with an unwavering resolve that freedom will always triumph over tyranny. \n\nSix days ago, Russia’s Vladimir Putin sought to shake the foundations of the free world thinking he could make it bend to his menacing ways. But he badly miscalculated. \n\nHe thought he could roll into Ukraine and the world would roll over. Instead he met a wall of strength he never imagined. \n\nHe met the Ukrainian people. \n\nFrom President Zelenskyy to every Ukrainian, their fearlessness, their courage, their determination, inspires the world.' metadata={'document': 1}

metadatas = [{"document": 1}, {"document": 2}]

Use `.split_text` to obtain the string content directly:

'Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans.  \n\nLast year COVID-19 kept us apart. This year we are finally together again. \n\nTonight, we meet as Democrats Republicans and Independents. But most importantly as Americans. \n\nWith a duty to one another to the American people to the Constitution. \n\nAnd with an unwavering resolve that freedom will always triumph over tyranny. \n\nSix days ago, Russia’s Vladimir Putin sought to shake the foundations of the free world thinking he could make it bend to his menacing ways. But he badly miscalculated. \n\nHe thought he could roll into Ukraine and the world would roll over. Instead he met a wall of strength he never imagined. \n\nHe met the Ukrainian people. \n\nFrom President Zelenskyy to every Ukrainian, their fearlessness, their courage, their determination, inspires the world.'


{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0fee7096",
   "metadata": {},
   "source": [
    "# How to use the output-fixing parser\n",
    "\n",
    "This output parser wraps another output parser, and in the event that the first one fails it calls out to another LLM to fix any errors.\n",
    "\n",
    "But we can do other things besides throw errors. Specifically, we can pass the misformatted output, along with the formatted instructions, to the model and ask it to fix it.\n",
    "\n",
    "For this example, we'll use the above Pydantic output parser. Here's what happens if we pass it a result that does not comply with the schema:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "9bad594d",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "\n",
    "from langchain_core.output_parsers import PydanticOutputParser\n",
    "from langchain_openai import ChatOpenAI\n",
    "from pydantic import BaseModel, Field"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "15283e0b",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Actor(BaseModel):\n",
    "    name: str = Field(description=\"name of an actor\")\n",
    "    film_names: List[str] = Field(description=\"list of names of films they starred in\")\n",
    "\n",
    "\n",
    "actor_query = \"Generate the filmography for a random actor.\"\n",
    "\n",
    "parser = PydanticOutputParser(pydantic_object=Actor)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "072d2d4c",
   "metadata": {},
   "outputs": [],
   "source": [
    "misformatted = \"{'name': 'Tom Hanks', 'film_names': ['Forrest Gump']}\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "4cbb35b3",
   "metadata": {},
   "outputs": [
    {
     "ename": "OutputParserException",
     "evalue": "Failed to parse Actor from completion {'name': 'Tom Hanks', 'film_names': ['Forrest Gump']}. Got: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mJSONDecodeError\u001b[0m                           Traceback (most recent call last)",
      "File \u001b[0;32m~/workplace/langchain/libs/langchain/langchain/output_parsers/pydantic.py:29\u001b[0m, in \u001b[0;36mPydanticOutputParser.parse\u001b[0;34m(self, text)\u001b[0m\n\u001b[1;32m     28\u001b[0m     json_str \u001b[38;5;241m=\u001b[39m match\u001b[38;5;241m.\u001b[39mgroup()\n\u001b[0;32m---> 29\u001b[0m json_object \u001b[38;5;241m=\u001b[39m \u001b[43mjson\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mloads\u001b[49m\u001b[43m(\u001b[49m\u001b[43mjson_str\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstrict\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m)\u001b[49m\n\u001b[1;32m     30\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpydantic_object\u001b[38;5;241m.\u001b[39mparse_obj(json_object)\n",
      "File \u001b[0;32m~/.pyenv/versions/3.10.1/lib/python3.10/json/__init__.py:359\u001b[0m, in \u001b[0;36mloads\u001b[0;34m(s, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)\u001b[0m\n\u001b[1;32m    358\u001b[0m     kw[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mparse_constant\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m parse_constant\n\u001b[0;32m--> 359\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkw\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdecode\u001b[49m\u001b[43m(\u001b[49m\u001b[43ms\u001b[49m\u001b[43m)\u001b[49m\n",


# How to propagate callbacks  constructor

This guide assumes familiarity with the following concepts:

- [Custom callback handlers](/docs/how_to/custom_callbacks)

Most LangChain modules allow you to pass `callbacks` directly into the constructor (i.e., initializer). In this case, the callbacks will only be called for that instance (and any nested runs).

Constructor callbacks are scoped only to the object they are defined on. They are **not** inherited by children of the object. This can lead to confusing behavior,

and it's generally better to pass callbacks as a run time argument.

%pip install -qU langchain langchain_anthropic

Chat model started

Chat model ended, response: generations=[[ChatGeneration(text='1 + 2 = 3', message=AIMessage(content='1 + 2 = 3', response_metadata={'id': 'msg_01CdKsRmeS9WRb8BWnHDEHm7', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 16, 'output_tokens': 13}}, id='run-2d7fdf2a-7405-4e17-97c0-67e6b2a65305-0'))]] llm_output={'id': 'msg_01CdKsRmeS9WRb8BWnHDEHm7', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 16, 'output_tokens': 13}} run=None

AIMessage(content='1 + 2 = 3', response_metadata={'id': 'msg_01CdKsRmeS9WRb8BWnHDEHm7', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 16, 'output_tokens': 13}}, id='run-2d7fdf2a-7405-4e17-97c0-67e6b2a65305-0')

from langchain_anthropic import ChatAnthropic

from langchain_core.callbacks import BaseCallbackHandler

from langchain_core.messages import BaseMessage

from langchain_core.outputs import LLMResult

from langchain_core.prompts import ChatPromptTemplate

self, serialized: Dict[str, Any], messages: List[List[BaseMessage]], **kwargs

def on_llm_end(self, response: LLMResult, **kwargs) -> None:

print(f"Chat model ended, response: {response}")

self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs

def on_chain_end(self, outputs: Dict[str, Any], **kwargs) -> None:

print(f"Chain ended, outputs: {outputs}")

llm = ChatAnthropic(model="claude-3-sonnet-20240229", callbacks=callbacks)

prompt = ChatPromptTemplate.from_template("What is 1 + {number}?")

You can see that we only see events from the chat model run - no chain events from the prompt or broader chain.

You've now learned how to pass callbacks into a constructor.

Next, check out the other how-to guides in this section, such as how to [pass callbacks at runtime](/docs/how_to/callbacks_runtime).


{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "4d6c0c86",
   "metadata": {},
   "source": [
    "# How to retry when a parsing error occurs\n",
    "\n",
    "While in some cases it is possible to fix any parsing mistakes by only looking at the output, in other cases it isn't. An example of this is when the output is not just in the incorrect format, but is partially complete. Consider the below example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f28526bd",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.output_parsers import OutputFixingParser\n",
    "from langchain_core.output_parsers import PydanticOutputParser\n",
    "from langchain_core.prompts import PromptTemplate\n",
    "from langchain_openai import ChatOpenAI, OpenAI\n",
    "from pydantic import BaseModel, Field"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "67c5e1ac",
   "metadata": {},
   "outputs": [],
   "source": [
    "template = \"\"\"Based on the user question, provide an Action and Action Input for what step should be taken.\n",
    "{format_instructions}\n",
    "Question: {query}\n",
    "Response:\"\"\"\n",
    "\n",
    "\n",
    "class Action(BaseModel):\n",
    "    action: str = Field(description=\"action to take\")\n",
    "    action_input: str = Field(description=\"input to the action\")\n",
    "\n",
    "\n",
    "parser = PydanticOutputParser(pydantic_object=Action)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "007aa87f",
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = PromptTemplate(\n",
    "    template=\"Answer the user query.\\n{format_instructions}\\n{query}\\n\",\n",
    "    input_variables=[\"query\"],\n",
    "    partial_variables={\"format_instructions\": parser.get_format_instructions()},\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "10d207ff",
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt_value = prompt.format_prompt(query=\"who is leo di caprios gf?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "68622837",
   "metadata": {},
   "outputs": [],
   "source": [
    "bad_response = '{\"action\": \"search\"}'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25631465",
   "metadata": {},
   "source": [
    "If we try to parse this response as is, we will get an error:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "894967c1",
   "metadata": {},
   "outputs": [
    {
     "ename": "OutputParserException",
     "evalue": "Failed to parse Action from completion {\"action\": \"search\"}. Got: 1 validation error for Action\naction_input\n  field required (type=value_error.missing)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValidationError\u001b[0m                           Traceback (most recent call last)",
      "File \u001b[0;32m~/workplace/langchain/libs/langchain/langchain/output_parsers/pydantic.py:30\u001b[0m, in \u001b[0;36mPydanticOutputParser.parse\u001b[0;34m(self, text)\u001b[0m\n\u001b[1;32m     29\u001b[0m     json_object \u001b[38;5;241m=\u001b[39m json\u001b[38;5;241m.\u001b[39mloads(json_str, strict\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mFalse\u001b[39;00m)\n\u001b[0;32m---> 30\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpydantic_object\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse_obj\u001b[49m\u001b[43m(\u001b[49m\u001b[43mjson_object\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     32\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m (json\u001b[38;5;241m.\u001b[39mJSONDecodeError, ValidationError) \u001b[38;5;28;01mas\u001b[39;00m e:\n",
      "File \u001b[0;32m~/.pyenv/versions/3.10.1/envs/langchain/lib/python3.10/site-packages/pydantic/main.py:526\u001b[0m, in \u001b[0;36mpydantic.main.BaseModel.parse_obj\u001b[0;34m()\u001b[0m\n",
      "File \u001b[0;32m~/.pyenv/versions/3.10.1/envs/langchain/lib/python3.10/site-packages/pydantic/main.py:341\u001b[0m, in \u001b[0;36mpydantic.main.BaseModel.__init__\u001b[0;34m()\u001b[0m\n",
      "\u001b[0;31mValidationError\u001b[0m: 1 validation error for Action\naction_input\n  field required (type=value_error.missing)",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[0;31mOutputParserException\u001b[0m                     Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[6], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mparser\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse\u001b[49m\u001b[43m(\u001b[49m\u001b[43mbad_response\u001b[49m\u001b[43m)\u001b[49m\n",


"File \u001b[0;32m~/workplace/langchain/libs/langchain/langchain/output_parsers/pydantic.py:35\u001b[0m, in \u001b[0;36mPydanticOutputParser.parse\u001b[0;34m(self, text)\u001b[0m\n\u001b[1;32m     33\u001b[0m name \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpydantic_object\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m\n\u001b[1;32m     34\u001b[0m msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mFailed to parse \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m from completion \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mtext\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m. Got: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00me\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m---> 35\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m OutputParserException(msg, llm_output\u001b[38;5;241m=\u001b[39mtext)\n",
      "\u001b[0;31mOutputParserException\u001b[0m: Failed to parse Action from completion {\"action\": \"search\"}. Got: 1 validation error for Action\naction_input\n  field required (type=value_error.missing)"
     ]
    }
   ],
   "source": [
    "parser.parse(bad_response)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f6b64696",
   "metadata": {},
   "source": [
    "If we try to use the `OutputFixingParser` to fix this error, it will be confused - namely, it doesn't know what to actually put for action input."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "78b2b40d",
   "metadata": {},
   "outputs": [],
   "source": [
    "fix_parser = OutputFixingParser.from_llm(parser=parser, llm=ChatOpenAI())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "4fe1301d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Action(action='search', action_input='input')"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fix_parser.parse(bad_response)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9bd9ea7d",
   "metadata": {},
   "source": [
    "Instead, we can use the RetryOutputParser, which passes in the prompt (as well as the original output) to try again to get a better response."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "7e8a8a28",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.output_parsers import RetryOutputParser"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "5c86e141",
   "metadata": {},
   "outputs": [],
   "source": [
    "retry_parser = RetryOutputParser.from_llm(parser=parser, llm=OpenAI(temperature=0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "9c04f731",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Action(action='search', action_input='leo di caprio girlfriend')"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "retry_parser.parse_with_prompt(bad_response, prompt_value)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16827256-5801-4388-b6fa-608991e29961",
   "metadata": {},
   "source": [
    "We can also add the RetryOutputParser easily with a custom chain which transform the raw LLM/ChatModel output into a more workable format."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "7eaff2fb-56d3-481c-99a1-a968a49d0654",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Action(action='search', action_input='leo di caprio girlfriend')\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.runnables import RunnableLambda, RunnableParallel\n",
    "\n",
    "completion_chain = prompt | OpenAI(temperature=0)\n",
    "\n",
    "main_chain = RunnableParallel(\n",
    "    completion=completion_chain, prompt_value=prompt\n",
    ") | RunnableLambda(lambda x: retry_parser.parse_with_prompt(**x))\n",
    "\n",
    "\n",
    "main_chain.invoke({\"query\": \"who is leo di caprios gf?\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e3a2513a",
   "metadata": {},
   "source": [
    "Find out api documentation for [RetryOutputParser](https://python.langchain.com/api_reference/langchain/output_parsers/langchain.output_parsers.retry.RetryOutputParser.html#langchain.output_parsers.retry.RetryOutputParser)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a2f94fd8",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}


{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How to better prompt when doing SQL question-answering\n",
    "\n",
    "In this guide we'll go over prompting strategies to improve SQL query generation using [create_sql_query_chain](https://python.langchain.com/api_reference/langchain/chains/langchain.chains.sql_database.query.create_sql_query_chain.html). We'll largely focus on methods for getting relevant database-specific information in your prompt.\n",
    "\n",
    "We will cover: \n",
    "\n",
    "- How the dialect of the LangChain [SQLDatabase](https://python.langchain.com/api_reference/community/utilities/langchain_community.utilities.sql_database.SQLDatabase.html) impacts the prompt of the chain;\n",
    "- How to format schema information into the prompt using `SQLDatabase.get_context`;\n",
    "- How to build and select few-shot examples to assist the model.\n",
    "\n",
    "## Setup\n",
    "\n",
    "First, get required packages and set environment variables:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install --upgrade --quiet  langchain langchain-community langchain-experimental langchain-openai"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Uncomment the below to use LangSmith. Not required.\n",
    "# import os\n",
    "# os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass()\n",
    "# os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The below example will use a SQLite connection with Chinook database. Follow [these installation steps](https://database.guide/2-sample-databases-sqlite/) to create `Chinook.db` in the same directory as this notebook:\n",
    "\n",
    "* Save [this file](https://raw.githubusercontent.com/lerocha/chinook-database/master/ChinookDatabase/DataSources/Chinook_Sqlite.sql) as `Chinook_Sqlite.sql`\n",
    "* Run `sqlite3 Chinook.db`\n",
    "* Run `.read Chinook_Sqlite.sql`\n",
    "* Test `SELECT * FROM Artist LIMIT 10;`\n",
    "\n",
    "Now, `Chinhook.db` is in our directory and we can interface with it using the SQLAlchemy-driven `SQLDatabase` class:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sqlite\n",
      "['Album', 'Artist', 'Customer', 'Employee', 'Genre', 'Invoice', 'InvoiceLine', 'MediaType', 'Playlist', 'PlaylistTrack', 'Track']\n",
      "[(1, 'AC/DC'), (2, 'Accept'), (3, 'Aerosmith'), (4, 'Alanis Morissette'), (5, 'Alice In Chains'), (6, 'Antônio Carlos Jobim'), (7, 'Apocalyptica'), (8, 'Audioslave'), (9, 'BackBeat'), (10, 'Billy Cobham')]\n"
     ]
    }
   ],
   "source": [
    "from langchain_community.utilities import SQLDatabase\n",
    "\n",
    "db = SQLDatabase.from_uri(\"sqlite:///Chinook.db\", sample_rows_in_table_info=3)\n",
    "print(db.dialect)\n",
    "print(db.get_usable_table_names())\n",
    "print(db.run(\"SELECT * FROM Artist LIMIT 10;\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dialect-specific prompting\n",
    "\n",
    "One of the simplest things we can do is make our prompt specific to the SQL dialect we're using. When using the built-in [create_sql_query_chain](https://python.langchain.com/api_reference/langchain/chains/langchain.chains.sql_database.query.create_sql_query_chain.html) and [SQLDatabase](https://python.langchain.com/api_reference/community/utilities/langchain_community.utilities.sql_database.SQLDatabase.html), this is handled for you for any of the following dialects:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['crate',\n",
       " 'duckdb',\n",
       " 'googlesql',\n",
       " 'mssql',\n",
       " 'mysql',\n",
       " 'mariadb',\n",
       " 'oracle',\n",
       " 'postgresql',\n",
       " 'sqlite',\n",
       " 'clickhouse',\n",
       " 'prestodb']"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.chains.sql_database.prompt import SQL_PROMPTS\n",
    "\n",
    "list(SQL_PROMPTS)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For example, using our current DB we can see that we'll get a SQLite-specific prompt.\n",
    "\n",
    "import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
    "\n",
    "<ChatModelTabs customVarName=\"llm\" />\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# | output: false\n",
    "# | echo: false\n",
    "\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "llm = ChatOpenAI()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "You are a SQLite expert. Given an input question, first create a syntactically correct SQLite query to run, then look at the results of the query and return the answer to the input question.\n",
      "Unless the user specifies in the question a specific number of examples to obtain, query for at most 5 results using the LIMIT clause as per SQLite. You can order the results to return the most informative data in the database.\n",
      "Never query for all columns from a table. You must query only the columns that are needed to answer the question. Wrap each column name in double quotes (\") to denote them as delimited identifiers.\n",
      "Pay attention to use only the column names you can see in the tables below. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table.\n",
      "Pay attention to use date('now') function to get the current date, if the question involves \"today\".\n",
      "\n",
      "Use the following format:\n",
      "\n",
      "Question: Question here\n",
      "SQLQuery: SQL Query to run\n",
      "SQLResult: Result of the SQLQuery\n",
      "Answer: Final answer here\n",
      "\n",
      "Only use the following tables:\n",
      "\u001b[33;1m\u001b[1;3m{table_info}\u001b[0m\n",
      "\n",
      "Question: \u001b[33;1m\u001b[1;3m{input}\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "from langchain.chains import create_sql_query_chain\n",
    "\n",
    "chain = create_sql_query_chain(llm, db)\n",
    "chain.get_prompts()[0].pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Table definitions and example rows\n",
    "\n",
    "In most SQL chains, we'll need to feed the model at least part of the database schema. Without this it won't be able to write valid queries. Our database comes with some convenience methods to give us the relevant context. Specifically, we can get the table names, their schemas, and a sample of rows from each table.\n",
    "\n",
    "Here we will use `SQLDatabase.get_context`, which provides available tables and their schemas:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [


{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How to debug your LLM apps\n",
    "\n",
    "Like building any type of software, at some point you'll need to debug when building with LLMs. A model call will fail, or model output will be misformatted, or there will be some nested model calls and it won't be clear where along the way an incorrect output was created.\n",
    "\n",
    "There are three main methods for debugging:\n",
    "\n",
    "- Verbose Mode: This adds print statements for \"important\" events in your chain.\n",
    "- Debug Mode: This add logging statements for ALL events in your chain.\n",
    "- LangSmith Tracing: This logs events to [LangSmith](https://docs.smith.langchain.com/) to allow for visualization there.\n",
    "\n",
    "|                        | Verbose Mode | Debug Mode | LangSmith Tracing |\n",
    "|------------------------|--------------|------------|-------------------|\n",
    "| Free                   | ✅            | ✅          | ✅                 |\n",
    "| UI                     | ❌            | ❌          | ✅                 |\n",
    "| Persisted              | ❌            | ❌          | ✅                 |\n",
    "| See all events         | ❌            | ✅          | ✅                 |\n",
    "| See \"important\" events | ✅            | ❌          | ✅                 |\n",
    "| Runs Locally           | ✅            | ✅          | ❌                 |\n",
    "\n",
    "\n",
    "## Tracing\n",
    "\n",
    "Many of the applications you build with LangChain will contain multiple steps with multiple invocations of LLM calls.\n",
    "As these applications get more and more complex, it becomes crucial to be able to inspect what exactly is going on inside your chain or agent.\n",
    "The best way to do this is with [LangSmith](https://smith.langchain.com).\n",
    "\n",
    "After you sign up at the link above, make sure to set your environment variables to start logging traces:\n",
    "\n",
    "```shell\n",
    "export LANGCHAIN_TRACING_V2=\"true\"\n",
    "export LANGCHAIN_API_KEY=\"...\"\n",
    "```\n",
    "\n",
    "Or, if in a notebook, you can set them with:\n",
    "\n",
    "```python\n",
    "import getpass\n",
    "import os\n",
    "\n",
    "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
    "os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass()\n",
    "```\n",
    "\n",
    "Let's suppose we have an agent, and want to visualize the actions it takes and tool outputs it receives. Without any debugging, here's what we see:\n",
    "\n",
    "import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
    "\n",
    "<ChatModelTabs\n",
    "  customVarName=\"llm\"\n",
    "/>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# | output: false\n",
    "# | echo: false\n",
    "\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "llm = ChatOpenAI(model=\"gpt-4-turbo\", temperature=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'input': 'Who directed the 2023 film Oppenheimer and what is their age in days?',\n",
       " 'output': 'The 2023 film \"Oppenheimer\" was directed by Christopher Nolan.\\n\\nTo calculate Christopher Nolan\\'s age in days, we first need his birthdate, which is July 30, 1970. Let\\'s calculate his age in days from his birthdate to today\\'s date, December 7, 2023.\\n\\n1. Calculate the total number of days from July 30, 1970, to December 7, 2023.\\n2. Nolan was born on July 30, 1970. From July 30, 1970, to July 30, 2023, is 53 years.\\n3. From July 30, 2023, to December 7, 2023, is 130 days.\\n\\nNow, calculate the total days:\\n- 53 years = 53 x 365 = 19,345 days\\n- Adding leap years from 1970 to 2023: There are 13 leap years (1972, 1976, 1980, 1984, 1988, 1992, 1996, 2000, 2004, 2008, 2012, 2016, 2020). So, add 13 days.\\n- Total days from years and leap years = 19,345 + 13 = 19,358 days\\n- Add the days from July 30, 2023, to December 7, 2023 = 130 days\\n\\nTotal age in days = 19,358 + 130 = 19,488 days\\n\\nChristopher Nolan is 19,488 days old as of December 7, 2023.'}"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.agents import AgentExecutor, create_tool_calling_agent\n",
    "from langchain_community.tools.tavily_search import TavilySearchResults\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "\n",
    "tools = [TavilySearchResults(max_results=1)]\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\n",
    "            \"system\",\n",
    "            \"You are a helpful assistant.\",\n",
    "        ),\n",
    "        (\"placeholder\", \"{chat_history}\"),\n",
    "        (\"human\", \"{input}\"),\n",
    "        (\"placeholder\", \"{agent_scratchpad}\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "# Construct the Tools agent\n",
    "agent = create_tool_calling_agent(llm, tools, prompt)\n",
    "\n",
    "# Create an agent executor by passing in the agent and tools\n",
    "agent_executor = AgentExecutor(agent=agent, tools=tools)\n",
    "agent_executor.invoke(\n",
    "    {\"input\": \"Who directed the 2023 film Oppenheimer and what is their age in days?\"}\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We don't get much output, but since we set up LangSmith we can easily see what happened under the hood:\n",
    "\n",
    "https://smith.langchain.com/public/a89ff88f-9ddc-4757-a395-3a1b365655bf/r"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `set_debug` and `set_verbose`\n",
    "\n",
    "If you're prototyping in Jupyter Notebooks or running Python scripts, it can be helpful to print out the intermediate steps of a chain run.\n",
    "\n",
    "There are a number of ways to enable printing at varying degrees of verbosity.\n",
    "\n",
    "Note: These still work even with LangSmith enabled, so you can have both turned on and running at the same time\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### `set_verbose(True)`\n",
    "\n",
    "Setting the `verbose` flag will print out inputs and outputs in a slightly more readable format and will skip logging certain raw outputs (like the token usage stats for an LLM call) so that you can focus on application logic."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "Invoking: `tavily_search_results_json` with `{'query': 'director of the 2023 film Oppenheimer'}`\n",


# How to use LangChain with different Pydantic versions

As of the `0.3` release, LangChain uses Pydantic 2 internally.

Users should install Pydantic 2 and are advised to **avoid** using the `pydantic.v1` namespace of Pydantic 2 with
LangChain APIs.

If you're working with prior versions of LangChain, please see the following guide
on [Pydantic compatibility](https://python.langchain.com/v0.2/docs/how_to/pydantic_compatibility).


This guide assumes familiarity with the following concepts:

- [Custom callback handlers](/docs/how_to/custom_callbacks)

- [Attach runtime arguments to a Runnable](/docs/how_to/binding)

If you are composing a chain of runnables and want to reuse callbacks across multiple executions, you can attach callbacks with the [`.with_config()`](https://python.langchain.com/api_reference/core/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.with_config) method. This saves you the need to pass callbacks in each time you invoke the chain.

`with_config()` binds a configuration which will be interpreted as **runtime** configuration. So these callbacks will propagate to all child components.

%pip install -qU langchain langchain_anthropic

Chain RunnableSequence started

Chain ChatPromptTemplate started

Chain ended, outputs: messages=[HumanMessage(content='What is 1 + 2?')]

Chat model started

Chat model ended, response: generations=[[ChatGeneration(text='1 + 2 = 3', message=AIMessage(content='1 + 2 = 3', response_metadata={'id': 'msg_01NTYMsH9YxkoWsiPYs4Lemn', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 16, 'output_tokens': 13}}, id='run-d6bcfd72-9c94-466d-bac0-f39e456ad6e3-0'))]] llm_output={'id': 'msg_01NTYMsH9YxkoWsiPYs4Lemn', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 16, 'output_tokens': 13}} run=None

Chain ended, outputs: content='1 + 2 = 3' response_metadata={'id': 'msg_01NTYMsH9YxkoWsiPYs4Lemn', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 16, 'output_tokens': 13}} id='run-d6bcfd72-9c94-466d-bac0-f39e456ad6e3-0'

AIMessage(content='1 + 2 = 3', response_metadata={'id': 'msg_01NTYMsH9YxkoWsiPYs4Lemn', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 16, 'output_tokens': 13}}, id='run-d6bcfd72-9c94-466d-bac0-f39e456ad6e3-0')

from langchain_anthropic import ChatAnthropic

from langchain_core.callbacks import BaseCallbackHandler

from langchain_core.messages import BaseMessage

from langchain_core.outputs import LLMResult

from langchain_core.prompts import ChatPromptTemplate

self, serialized: Dict[str, Any], messages: List[List[BaseMessage]], **kwargs

def on_llm_end(self, response: LLMResult, **kwargs) -> None:

print(f"Chat model ended, response: {response}")

self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs

def on_chain_end(self, outputs: Dict[str, Any], **kwargs) -> None:

print(f"Chain ended, outputs: {outputs}")

prompt = ChatPromptTemplate.from_template("What is 1 + {number}?")

The bound callbacks will run for all nested module runs.

You've now learned how to attach callbacks to a chain.

Next, check out the other how-to guides in this section, such as how to [pass callbacks in at runtime](/docs/how_to/callbacks_runtime).


# Text embedding models

:::info
Head to [Integrations](/docs/integrations/text_embedding/) for documentation on built-in integrations with text embedding model providers.
:::

The Embeddings class is a class designed for interfacing with text embedding models. There are lots of embedding model providers (OpenAI, Cohere, Hugging Face, etc) - this class is designed to provide a standard interface for all of them.

Embeddings create a vector representation of a piece of text. This is useful because it means we can think about text in the vector space, and do things like semantic search where we look for pieces of text that are most similar in the vector space.

The base Embeddings class in LangChain provides two methods: one for embedding documents and one for embedding a query. The former, `.embed_documents`, takes as input multiple texts, while the latter, `.embed_query`, takes a single text. The reason for having these as two separate methods is that some embedding providers have different embedding methods for documents (to be searched over) vs queries (the search query itself).
`.embed_query` will return a list of floats, whereas `.embed_documents` returns a list of lists of floats.

## Get started

### Setup

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

<Tabs>
  <TabItem value="openai" label="OpenAI" default>
To start we'll need to install the OpenAI partner package:

```bash
pip install langchain-openai
```

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:

```bash
export OPENAI_API_KEY="..."
```

If you'd prefer not to set an environment variable you can pass the key in directly via the `api_key` named parameter when initiating the OpenAI LLM class:

```python
from langchain_openai import OpenAIEmbeddings

embeddings_model = OpenAIEmbeddings(api_key="...")
```

Otherwise you can initialize without any params:
```python
from langchain_openai import OpenAIEmbeddings

embeddings_model = OpenAIEmbeddings()
```

  </TabItem>
  <TabItem value="cohere" label="Cohere">

To start we'll need to install the Cohere SDK package:

```bash
pip install langchain-cohere
```

Accessing the API requires an API key, which you can get by creating an account and heading [here](https://dashboard.cohere.com/api-keys). Once we have a key we'll want to set it as an environment variable by running:

```shell
export COHERE_API_KEY="..."
```

If you'd prefer not to set an environment variable you can pass the key in directly via the `cohere_api_key` named parameter when initiating the Cohere LLM class:

```python
from langchain_cohere import CohereEmbeddings

embeddings_model = CohereEmbeddings(cohere_api_key="...", model='embed-english-v3.0')
```

Otherwise you can initialize simply as shown below:
```python
from langchain_cohere import CohereEmbeddings

embeddings_model = CohereEmbeddings(model='embed-english-v3.0')
```
Do note that it is mandatory to pass the model parameter while initializing the CohereEmbeddings class.

  </TabItem>
  <TabItem value="huggingface" label="Hugging Face">

To start we'll need to install the Hugging Face partner package:

```bash
pip install langchain-huggingface
```

You can then load any [Sentence Transformers model](https://huggingface.co/models?library=sentence-transformers) from the Hugging Face Hub.

```python
from langchain_huggingface import HuggingFaceEmbeddings

embeddings_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")
```
  </TabItem>
</Tabs>

### `embed_documents`
#### Embed list of texts

Use `.embed_documents` to embed a list of strings, recovering a list of embeddings:

```python
embeddings = embeddings_model.embed_documents(
    [
        "Hi there!",
        "Oh, hello!",
        "What's your name?",
        "My friends call me World",
        "Hello World!"
    ]
)
len(embeddings), len(embeddings[0])
```

```output
(5, 1536)
```

### `embed_query`
#### Embed single query
Use `.embed_query` to embed a single piece of text (e.g., for the purpose of comparing to other embedded pieces of texts).

```python
embedded_query = embeddings_model.embed_query("What was the name mentioned in the conversation?")
embedded_query[:5]
```

```output
[0.0053587136790156364,
 -0.0004999046213924885,
 0.038883671164512634,
 -0.003001077566295862,
 -0.00900818221271038]
```


{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "---\n",
    "sidebar_position: 1\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How to add memory to chatbots\n",
    "\n",
    "A key feature of chatbots is their ability to use content of previous conversation turns as context. This state management can take several forms, including:\n",
    "\n",
    "- Simply stuffing previous messages into a chat model prompt.\n",
    "- The above, but trimming old messages to reduce the amount of distracting information the model has to deal with.\n",
    "- More complex modifications like synthesizing summaries for long running conversations.\n",
    "\n",
    "We'll go into more detail on a few techniques below!\n",
    "\n",
    ":::note\n",
    "\n",
    "This how-to guide previously built a chatbot using [RunnableWithMessageHistory](https://python.langchain.com/api_reference/core/runnables/langchain_core.runnables.history.RunnableWithMessageHistory.html). You can access this version of the guide in the [v0.2 docs](https://python.langchain.com/v0.2/docs/how_to/chatbots_memory/).\n",
    "\n",
    "As of the v0.3 release of LangChain, we recommend that LangChain users take advantage of [LangGraph persistence](https://langchain-ai.github.io/langgraph/concepts/persistence/) to incorporate `memory` into new LangChain applications.\n",
    "\n",
    "If your code is already relying on `RunnableWithMessageHistory` or `BaseChatMessageHistory`, you do **not** need to make any changes. We do not plan on deprecating this functionality in the near future as it works for simple chat applications and any code that uses `RunnableWithMessageHistory` will continue to work as expected.\n",
    "\n",
    "Please see [How to migrate to LangGraph Memory](/docs/versions/migrating_memory/) for more details.\n",
    ":::\n",
    "\n",
    "## Setup\n",
    "\n",
    "You'll need to install a few packages, and have your OpenAI API key set as an environment variable named `OPENAI_API_KEY`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "OpenAI API Key: ········\n"
     ]
    }
   ],
   "source": [
    "%pip install --upgrade --quiet langchain langchain-openai langgraph\n",
    "\n",
    "import getpass\n",
    "import os\n",
    "\n",
    "if not os.environ.get(\"OPENAI_API_KEY\"):\n",
    "    os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's also set up a chat model that we'll use for the below examples."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "model = ChatOpenAI(model=\"gpt-4o-mini\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Message passing\n",
    "\n",
    "The simplest form of memory is simply passing chat history messages into a chain. Here's an example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "I said, \"I love programming\" in French: \"J'adore la programmation.\"\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import AIMessage, HumanMessage, SystemMessage\n",
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        SystemMessage(\n",
    "            content=\"You are a helpful assistant. Answer all questions to the best of your ability.\"\n",
    "        ),\n",
    "        MessagesPlaceholder(variable_name=\"messages\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "chain = prompt | model\n",
    "\n",
    "ai_msg = chain.invoke(\n",
    "    {\n",
    "        \"messages\": [\n",
    "            HumanMessage(\n",
    "                content=\"Translate from English to French: I love programming.\"\n",
    "            ),\n",
    "            AIMessage(content=\"J'adore la programmation.\"),\n",
    "            HumanMessage(content=\"What did you just say?\"),\n",
    "        ],\n",
    "    }\n",
    ")\n",
    "print(ai_msg.content)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see that by passing the previous conversation into a chain, it can use it as context to answer questions. This is the basic concept underpinning chatbot memory - the rest of the guide will demonstrate convenient techniques for passing or reformatting messages."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Automatic history management\n",
    "\n",
    "The previous examples pass messages to the chain (and model) explicitly. This is a completely acceptable approach, but it does require external management of new messages. LangChain also provides a way to build applications that have memory using LangGraph's [persistence](https://langchain-ai.github.io/langgraph/concepts/persistence/). You can [enable persistence](https://langchain-ai.github.io/langgraph/how-tos/persistence/) in LangGraph applications by providing a `checkpointer` when compiling the graph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "from langgraph.graph import START, MessagesState, StateGraph\n",
    "\n",
    "workflow = StateGraph(state_schema=MessagesState)\n",
    "\n",
    "\n",
    "# Define the function that calls the model\n",
    "def call_model(state: MessagesState):\n",
    "    system_prompt = (\n",
    "        \"You are a helpful assistant. \"\n",
    "        \"Answer all questions to the best of your ability.\"\n",
    "    )\n",
    "    messages = [SystemMessage(content=system_prompt)] + state[\"messages\"]\n",
    "    response = model.invoke(messages)\n",
    "    return {\"messages\": response}\n",
    "\n",
    "\n",
    "# Define the node and edge\n",
    "workflow.add_node(\"model\", call_model)\n",
    "workflow.add_edge(START, \"model\")\n",
    "\n",
    "# Add simple in-memory checkpointer\n",
    "# highlight-start\n",
    "memory = MemorySaver()\n",
    "app = workflow.compile(checkpointer=memory)\n",
    "# highlight-end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " We'll pass the latest input to the conversation here and let the LangGraph keep track of the conversation history using the checkpointer:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='Translate to French: I love programming.', additional_kwargs={}, response_metadata={}, id='be5e7099-3149-4293-af49-6b36c8ccd71b'),\n",
       "  AIMessage(content=\"J'aime programmer.\", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 4, 'prompt_tokens': 35, 'total_tokens': 39, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_e9627b5346', 'finish_reason': 'stop', 'logprobs': None}, id='run-8a753d7a-b97b-4d01-a661-626be6f41b38-0', usage_metadata={'input_tokens': 35, 'output_tokens': 4, 'total_tokens': 39})]}"
      ]
     },