{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/aurelio-labs/semantic-router/blob/main/docs/02-dynamic-routes.ipynb) [![Open nbviewer](https://raw.githubusercontent.com/pinecone-io/examples/master/assets/nbviewer-shield.svg)](https://nbviewer.org/github/aurelio-labs/semantic-router/blob/main/docs/02-dynamic-routes.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Dynamic Routes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In semantic-router there are two types of routes that can be chosen. Both routes belong to the `Route` object, the only difference between them is that _static_ routes return a `Route.name` when chosen, whereas _dynamic_ routes use an LLM call to produce parameter input values.\n",
    "\n",
    "For example, a _static_ route will tell us if a query is talking about mathematics by returning the route name (which could be `\"math\"` for example). A _dynamic_ route can generate additional values, so it may decide a query is talking about maths, but it can also generate Python code that we can later execute to answer the user's query, this output may look like `\"math\", \"import math; output = math.sqrt(64)`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Installing the Library"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install -qU semantic-router==0.0.14"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "_**⚠️ If using Google Colab, install the prerequisites and then restart the notebook before continuing**_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Initializing Routes and RouteLayer"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dynamic routes are treated in the same way as static routes, let's begin by initializing a `RouteLayer` consisting of static routes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/jamesbriggs/opt/anaconda3/envs/decision-layer/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n",
      "None of PyTorch, TensorFlow >= 2.0, or Flax have been found. Models won't be available and only tokenizers, configuration and file/data utilities can be used.\n"
     ]
    }
   ],
   "source": [
    "from semantic_router import Route\n",
    "\n",
    "politics = Route(\n",
    "    name=\"politics\",\n",
    "    utterances=[\n",
    "        \"isn't politics the best thing ever\",\n",
    "        \"why don't you tell me about your political opinions\",\n",
    "        \"don't you just love the president\" \"don't you just hate the president\",\n",
    "        \"they're going to destroy this country!\",\n",
    "        \"they will save the country!\",\n",
    "    ],\n",
    ")\n",
    "chitchat = Route(\n",
    "    name=\"chitchat\",\n",
    "    utterances=[\n",
    "        \"how's the weather today?\",\n",
    "        \"how are things going?\",\n",
    "        \"lovely weather today\",\n",
    "        \"the weather is horrendous\",\n",
    "        \"let's go to the chippy\",\n",
    "    ],\n",
    ")\n",
    "\n",
    "routes = [politics, chitchat]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\u001b[32m2023-12-28 19:19:39 INFO semantic_router.utils.logger Initializing RouteLayer\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "from getpass import getpass\n",
    "from semantic_router import RouteLayer\n",
    "\n",
    "# dashboard.cohere.ai\n",
    "os.environ[\"COHERE_API_KEY\"] = os.getenv(\"COHERE_API_KEY\") or getpass(\n",
    "    \"Enter Cohere API Key: \"\n",
    ")\n",
    "\n",
    "layer = RouteLayer(routes=routes)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We run the solely static routes layer:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "RouteChoice(name='chitchat', function_call=None)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "layer(\"how's the weather today?\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating a Dynamic Route"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As with static routes, we must create a dynamic route before adding it to our route layer. To make a route dynamic, we need to provide a `function_schema`. The function schema provides instructions on what a function is, so that an LLM can decide how to use it correctly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "from datetime import datetime\n",
    "from zoneinfo import ZoneInfo\n",
    "\n",
    "\n",
    "def get_time(timezone: str) -> str:\n",
    "    \"\"\"Finds the current time in a specific timezone.\n",
    "\n",
    "    :param timezone: The timezone to find the current time in, should\n",
    "        be a valid timezone from the IANA Time Zone Database like\n",
    "        \"America/New_York\" or \"Europe/London\".\n",
    "    :type timezone: str\n",
    "    :return: The current time in the specified timezone.\"\"\"\n",
    "    now = datetime.now(ZoneInfo(timezone))\n",
    "    return now.strftime(\"%H:%M\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'13:19'"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "get_time(\"America/New_York\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To get the function schema we can use the `get_schema` function from the `function_call` module."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'name': 'get_time',\n",
       " 'description': 'Finds the current time in a specific timezone.\\n\\n:param timezone: The timezone to find the current time in, should\\n    be a valid timezone from the IANA Time Zone Database like\\n    \"America/New_York\" or \"Europe/London\".\\n:type timezone: str\\n:return: The current time in the specified timezone.',\n",
       " 'signature': '(timezone: str) -> str',\n",
       " 'output': \"<class 'str'>\"}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from semantic_router.utils.function_call import get_schema\n",
    "\n",
    "schema = get_schema(get_time)\n",
    "schema"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We use this to define our dynamic route:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "time_route = Route(\n",
    "    name=\"get_time\",\n",
    "    utterances=[\n",
    "        \"what is the time in new york city?\",\n",
    "        \"what is the time in london?\",\n",
    "        \"I live in Rome, what time is it?\",\n",
    "    ],\n",
    "    function_schema=schema,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Add the new route to our `layer`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding route `get_time`\n",
      "Adding route to categories\n",
      "Adding route to index\n"
     ]
    }
   ],
   "source": [
    "layer.add(time_route)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can ask our layer a time related question to trigger our new dynamic route."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\u001b[32m2023-12-28 19:21:58 INFO semantic_router.utils.logger Extracting function input...\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "RouteChoice(name='get_time', function_call={'timezone': 'America/New_York'})"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# https://openrouter.ai/keys\n",
    "os.environ[\"OPENROUTER_API_KEY\"] = os.getenv(\"OPENROUTER_API_KEY\") or getpass(\n",
    "    \"Enter OpenRouter API Key: \"\n",
    ")\n",
    "\n",
    "layer(\"what is the time in new york city?\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "decision-layer",
   "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": 2
}