{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "39eaf61b",
   "metadata": {},
   "source": [
    "# Configure chain internals at runtime\n",
    "\n",
    "Oftentimes you may want to experiment with, or even expose to the end user, multiple different ways of doing things.\n",
    "In order to make this experience as easy as possible, we have defined two methods.\n",
    "\n",
    "First, a `configurable_fields` method. \n",
    "This lets you configure particular fields of a runnable.\n",
    "\n",
    "Second, a `configurable_alternatives` method.\n",
    "With this method, you can list out alternatives for any particular runnable that can be set during runtime."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2347a11",
   "metadata": {},
   "source": [
    "## Configuration Fields"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a06f6e2d",
   "metadata": {},
   "source": [
    "### With LLMs\n",
    "With LLMs we can configure things like temperature"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "7ba735f4",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chat_models import ChatOpenAI\n",
    "from langchain.prompts import PromptTemplate\n",
    "from langchain.schema.runnable import ConfigurableField\n",
    "\n",
    "model = ChatOpenAI(temperature=0).configurable_fields(\n",
    "    temperature=ConfigurableField(\n",
    "        id=\"llm_temperature\",\n",
    "        name=\"LLM Temperature\",\n",
    "        description=\"The temperature of the LLM\",\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "63a71165",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='7')"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.invoke(\"pick a random number\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "4f83245c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='34')"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.with_config(configurable={\"llm_temperature\": 0.9}).invoke(\"pick a random number\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9da1fcd2",
   "metadata": {},
   "source": [
    "We can also do this when its used as part of a chain"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "e75ae678",
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = PromptTemplate.from_template(\"Pick a random number above {x}\")\n",
    "chain = prompt | model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "44886071",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='57')"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"x\": 0})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "c09fac15",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='6')"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.with_config(configurable={\"llm_temperature\": 0.9}).invoke({\"x\": 0})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb9637d0",
   "metadata": {},
   "source": [
    "### With HubRunnables\n",
    "\n",
    "This is useful to allow for switching of prompts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "7d5836b2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.runnables.hub import HubRunnable"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "9a9ea077",
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = HubRunnable(\"rlm/rag-prompt\").configurable_fields(\n",
    "    owner_repo_commit=ConfigurableField(\n",
    "        id=\"hub_commit\",\n",
    "        name=\"Hub Commit\",\n",
    "        description=\"The Hub commit to pull from\",\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "c4a62cee",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ChatPromptValue(messages=[HumanMessage(content=\"You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.\\nQuestion: foo \\nContext: bar \\nAnswer:\")])"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prompt.invoke({\"question\": \"foo\", \"context\": \"bar\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "f33f3cf2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ChatPromptValue(messages=[HumanMessage(content=\"[INST]<<SYS>> You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.<</SYS>> \\nQuestion: foo \\nContext: bar \\nAnswer: [/INST]\")])"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prompt.with_config(configurable={\"hub_commit\": \"rlm/rag-prompt-llama\"}).invoke(\n",
    "    {\"question\": \"foo\", \"context\": \"bar\"}\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "79d51519",
   "metadata": {},
   "source": [
    "## Configurable Alternatives\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac733d35",
   "metadata": {},
   "source": [
    "### With LLMs\n",
    "\n",
    "Let's take a look at doing this with LLMs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "430ab8cc",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chat_models import ChatAnthropic, ChatOpenAI\n",
    "from langchain.prompts import PromptTemplate\n",
    "from langchain.schema.runnable import ConfigurableField"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "71248a9f",
   "metadata": {},
   "outputs": [],
   "source": [
    "llm = ChatAnthropic(temperature=0).configurable_alternatives(\n",
    "    # This gives this field an id\n",
    "    # When configuring the end runnable, we can then use this id to configure this field\n",
    "    ConfigurableField(id=\"llm\"),\n",
    "    # This sets a default_key.\n",
    "    # If we specify this key, the default LLM (ChatAnthropic initialized above) will be used\n",
    "    default_key=\"anthropic\",\n",
    "    # This adds a new option, with name `openai` that is equal to `ChatOpenAI()`\n",
    "    openai=ChatOpenAI(),\n",
    "    # This adds a new option, with name `gpt4` that is equal to `ChatOpenAI(model=\"gpt-4\")`\n",
    "    gpt4=ChatOpenAI(model=\"gpt-4\"),\n",
    "    # You can add more configuration options here\n",
    ")\n",
    "prompt = PromptTemplate.from_template(\"Tell me a joke about {topic}\")\n",
    "chain = prompt | llm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "e598b1f1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content=\" Here's a silly joke about bears:\\n\\nWhat do you call a bear with no teeth?\\nA gummy bear!\")"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# By default it will call Anthropic\n",
    "chain.invoke({\"topic\": \"bears\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "48b45337",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content=\"Sure, here's a bear joke for you:\\n\\nWhy don't bears wear shoes?\\n\\nBecause they already have bear feet!\")"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# We can use `.with_config(configurable={\"llm\": \"openai\"})` to specify an llm to use\n",
    "chain.with_config(configurable={\"llm\": \"openai\"}).invoke({\"topic\": \"bears\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "42647fb7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content=\" Here's a silly joke about bears:\\n\\nWhat do you call a bear with no teeth?\\nA gummy bear!\")"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# If we use the `default_key` then it uses the default\n",
    "chain.with_config(configurable={\"llm\": \"anthropic\"}).invoke({\"topic\": \"bears\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a9134559",
   "metadata": {},
   "source": [
    "### With Prompts\n",
    "\n",
    "We can do a similar thing, but alternate between prompts\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "9f6a7c6c",
   "metadata": {},
   "outputs": [],
   "source": [
    "llm = ChatAnthropic(temperature=0)\n",
    "prompt = PromptTemplate.from_template(\n",
    "    \"Tell me a joke about {topic}\"\n",
    ").configurable_alternatives(\n",
    "    # This gives this field an id\n",
    "    # When configuring the end runnable, we can then use this id to configure this field\n",
    "    ConfigurableField(id=\"prompt\"),\n",
    "    # This sets a default_key.\n",
    "    # If we specify this key, the default LLM (ChatAnthropic initialized above) will be used\n",
    "    default_key=\"joke\",\n",
    "    # This adds a new option, with name `poem`\n",
    "    poem=PromptTemplate.from_template(\"Write a short poem about {topic}\"),\n",
    "    # You can add more configuration options here\n",
    ")\n",
    "chain = prompt | llm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "97eda915",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content=\" Here's a silly joke about bears:\\n\\nWhat do you call a bear with no teeth?\\nA gummy bear!\")"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# By default it will write a joke\n",
    "chain.invoke({\"topic\": \"bears\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "927297a1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content=' Here is a short poem about bears:\\n\\nThe bears awaken from their sleep\\nAnd lumber out into the deep\\nForests filled with trees so tall\\nForaging for food before nightfall \\nTheir furry coats and claws so sharp\\nSniffing for berries and fish to nab\\nLumbering about without a care\\nThe mighty grizzly and black bear\\nProud creatures, wild and free\\nRuling their domain majestically\\nWandering the woods they call their own\\nBefore returning to their dens alone')"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# We can configure it write a poem\n",
    "chain.with_config(configurable={\"prompt\": \"poem\"}).invoke({\"topic\": \"bears\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c77124e",
   "metadata": {},
   "source": [
    "### With Prompts and LLMs\n",
    "\n",
    "We can also have multiple things configurable!\n",
    "Here's an example doing that with both prompts and LLMs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "97538c23",
   "metadata": {},
   "outputs": [],
   "source": [
    "llm = ChatAnthropic(temperature=0).configurable_alternatives(\n",
    "    # This gives this field an id\n",
    "    # When configuring the end runnable, we can then use this id to configure this field\n",
    "    ConfigurableField(id=\"llm\"),\n",
    "    # This sets a default_key.\n",
    "    # If we specify this key, the default LLM (ChatAnthropic initialized above) will be used\n",
    "    default_key=\"anthropic\",\n",
    "    # This adds a new option, with name `openai` that is equal to `ChatOpenAI()`\n",
    "    openai=ChatOpenAI(),\n",
    "    # This adds a new option, with name `gpt4` that is equal to `ChatOpenAI(model=\"gpt-4\")`\n",
    "    gpt4=ChatOpenAI(model=\"gpt-4\"),\n",
    "    # You can add more configuration options here\n",
    ")\n",
    "prompt = PromptTemplate.from_template(\n",
    "    \"Tell me a joke about {topic}\"\n",
    ").configurable_alternatives(\n",
    "    # This gives this field an id\n",
    "    # When configuring the end runnable, we can then use this id to configure this field\n",
    "    ConfigurableField(id=\"prompt\"),\n",
    "    # This sets a default_key.\n",
    "    # If we specify this key, the default LLM (ChatAnthropic initialized above) will be used\n",
    "    default_key=\"joke\",\n",
    "    # This adds a new option, with name `poem`\n",
    "    poem=PromptTemplate.from_template(\"Write a short poem about {topic}\"),\n",
    "    # You can add more configuration options here\n",
    ")\n",
    "chain = prompt | llm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "1dcc7ccc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content=\"In the forest, where tall trees sway,\\nA creature roams, both fierce and gray.\\nWith mighty paws and piercing eyes,\\nThe bear, a symbol of strength, defies.\\n\\nThrough snow-kissed mountains, it does roam,\\nA guardian of its woodland home.\\nWith fur so thick, a shield of might,\\nIt braves the coldest winter night.\\n\\nA gentle giant, yet wild and free,\\nThe bear commands respect, you see.\\nWith every step, it leaves a trace,\\nOf untamed power and ancient grace.\\n\\nFrom honeyed feast to salmon's leap,\\nIt takes its place, in nature's keep.\\nA symbol of untamed delight,\\nThe bear, a wonder, day and night.\\n\\nSo let us honor this noble beast,\\nIn forests where its soul finds peace.\\nFor in its presence, we come to know,\\nThe untamed spirit that in us also flows.\")"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# We can configure it write a poem with OpenAI\n",
    "chain.with_config(configurable={\"prompt\": \"poem\", \"llm\": \"openai\"}).invoke(\n",
    "    {\"topic\": \"bears\"}\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "e4ee9fbc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content=\"Sure, here's a bear joke for you:\\n\\nWhy don't bears wear shoes?\\n\\nBecause they have bear feet!\")"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# We can always just configure only one if we want\n",
    "chain.with_config(configurable={\"llm\": \"openai\"}).invoke({\"topic\": \"bears\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02fc4841",
   "metadata": {},
   "source": [
    "### Saving configurations\n",
    "\n",
    "We can also easily save configured chains as their own objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "5cf53202",
   "metadata": {},
   "outputs": [],
   "source": [
    "openai_poem = chain.with_config(configurable={\"llm\": \"openai\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "9486d701",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content=\"Why don't bears wear shoes?\\n\\nBecause they have bear feet!\")"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "openai_poem.invoke({\"topic\": \"bears\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a43e3b70",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
