{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How to pass run time values to a tool\n",
    "\n",
    ":::info Prerequisites\n",
    "\n",
    "This guide assumes familiarity with the following concepts:\n",
    "- [Chat models](/docs/concepts/#chat-models)\n",
    "- [LangChain Tools](/docs/concepts/#tools)\n",
    "- [How to create tools](/docs/how_to/custom_tools)\n",
    "- [How to use a model to call tools](https://python.langchain.com/v0.2/docs/how_to/tool_calling)\n",
    ":::\n",
    "\n",
    ":::{.callout-info} Supported models\n",
    "\n",
    "This how-to guide uses models with native tool calling capability.\n",
    "You can find a [list of all models that support tool calling](/docs/integrations/chat/).\n",
    "\n",
    ":::\n",
    "\n",
    ":::{.callout-info} Using with LangGraph\n",
    "\n",
    "If you're using LangGraph, please refer to [this how-to guide](https://langchain-ai.github.io/langgraph/how-tos/pass-run-time-values-to-tools/)\n",
    "which shows how to create an agent that keeps track of a given user's favorite pets.\n",
    ":::\n",
    "\n",
    "You may need to bind values to a tool that are only known at runtime. For example, the tool logic may require using the ID of the user who made the request.\n",
    "\n",
    "Most of the time, such values should not be controlled by the LLM. In fact, allowing the LLM to control the user ID may lead to a security risk.\n",
    "\n",
    "Instead, the LLM should only control the parameters of the tool that are meant to be controlled by the LLM, while other parameters (such as user ID) should be fixed by the application logic.\n",
    "\n",
    "This how-to guide shows a simple design pattern that creates the tool dynamically at run time and binds to them appropriate values."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can bind them to chat models as follows:\n",
    "\n",
    "```{=mdx}\n",
    "import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
    "\n",
    "<ChatModelTabs\n",
    "  customVarName=\"llm\"\n",
    "  fireworksParams={`model=\"accounts/fireworks/models/firefunction-v1\", temperature=0`}\n",
    "/>\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.2.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.0\u001b[0m\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpython -m pip install --upgrade pip\u001b[0m\n",
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "# | output: false\n",
    "# | echo: false\n",
    "\n",
    "%pip install -qU langchain langchain_openai\n",
    "\n",
    "import os\n",
    "from getpass import getpass\n",
    "\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "if \"OPENAI_API_KEY\" not in os.environ:\n",
    "    os.environ[\"OPENAI_API_KEY\"] = getpass()\n",
    "\n",
    "llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Passing request time information\n",
    "\n",
    "The idea is to create the tool dynamically at request time, and bind to it the appropriate information. For example,\n",
    "this information may be the user ID as resolved from the request itself."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "\n",
    "from langchain_core.output_parsers import JsonOutputParser\n",
    "from langchain_core.tools import BaseTool, tool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "user_to_pets = {}\n",
    "\n",
    "\n",
    "def generate_tools_for_user(user_id: str) -> List[BaseTool]:\n",
    "    \"\"\"Generate a set of tools that have a user id associated with them.\"\"\"\n",
    "\n",
    "    @tool\n",
    "    def update_favorite_pets(pets: List[str]) -> None:\n",
    "        \"\"\"Add the list of favorite pets.\"\"\"\n",
    "        user_to_pets[user_id] = pets\n",
    "\n",
    "    @tool\n",
    "    def delete_favorite_pets() -> None:\n",
    "        \"\"\"Delete the list of favorite pets.\"\"\"\n",
    "        if user_id in user_to_pets:\n",
    "            del user_to_pets[user_id]\n",
    "\n",
    "    @tool\n",
    "    def list_favorite_pets() -> None:\n",
    "        \"\"\"List favorite pets if any.\"\"\"\n",
    "        return user_to_pets.get(user_id, [])\n",
    "\n",
    "    return [update_favorite_pets, delete_favorite_pets, list_favorite_pets]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Verify that the tools work correctly"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'eugene': ['cat', 'dog']}\n",
      "['cat', 'dog']\n"
     ]
    }
   ],
   "source": [
    "update_pets, delete_pets, list_pets = generate_tools_for_user(\"eugene\")\n",
    "update_pets.invoke({\"pets\": [\"cat\", \"dog\"]})\n",
    "print(user_to_pets)\n",
    "print(list_pets.invoke({}))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "\n",
    "\n",
    "def handle_run_time_request(user_id: str, query: str):\n",
    "    \"\"\"Handle run time request.\"\"\"\n",
    "    tools = generate_tools_for_user(user_id)\n",
    "    llm_with_tools = llm.bind_tools(tools)\n",
    "    prompt = ChatPromptTemplate.from_messages(\n",
    "        [(\"system\", \"You are a helpful assistant.\")],\n",
    "    )\n",
    "    chain = prompt | llm_with_tools\n",
    "    return llm_with_tools.invoke(query)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This code will allow the LLM to invoke the tools, but the LLM is **unaware** of the fact that a **user ID** even exists!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'name': 'update_favorite_pets',\n",
       "  'args': {'pets': ['cats', 'parrots']},\n",
       "  'id': 'call_jJvjPXsNbFO5MMgW0q84iqCN'}]"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ai_message = handle_run_time_request(\n",
    "    \"eugene\", \"my favorite animals are cats and parrots.\"\n",
    ")\n",
    "ai_message.tool_calls"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{.callout-important}\n",
    "\n",
    "Chat models only output requests to invoke tools, they don't actually invoke the underlying tools.\n",
    "\n",
    "To see how to invoke the tools, please refer to [how to use a model to call tools](https://python.langchain.com/v0.2/docs/how_to/tool_calling).\n",
    ":::"
   ]
  }
 ],
 "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.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}