From 1dda7f1e80ebf8738400221d5ae359ea2c75725a Mon Sep 17 00:00:00 2001
From: Siraj R Aizlewood <siraj@aurelio.ai>
Date: Thu, 2 May 2024 01:35:25 +0400
Subject: [PATCH] Testing and bug fixing - Now multiple functions can be called
 at once.

See Notebook 02.
---
 docs/02-dynamic-routes.ipynb   | 323 ++++++++++++++++++---------------
 semantic_router/llms/base.py   |  28 +--
 semantic_router/llms/openai.py |   8 +-
 3 files changed, 198 insertions(+), 161 deletions(-)

diff --git a/docs/02-dynamic-routes.ipynb b/docs/02-dynamic-routes.ipynb
index 59f1366e..5470ddfb 100644
--- a/docs/02-dynamic-routes.ipynb
+++ b/docs/02-dynamic-routes.ipynb
@@ -185,27 +185,27 @@
           "name": "stderr",
           "output_type": "stream",
           "text": [
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger local\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 1 length: 34\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 1 trunc length: 34\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 2 length: 51\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 2 trunc length: 51\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 3 length: 66\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 3 trunc length: 66\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 4 length: 38\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 4 trunc length: 38\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 5 length: 27\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 5 trunc length: 27\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 6 length: 24\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 6 trunc length: 24\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 7 length: 21\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 7 trunc length: 21\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 8 length: 20\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 8 trunc length: 20\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 9 length: 25\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 9 trunc length: 25\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 10 length: 22\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:13 INFO semantic_router.utils.logger Document 10 trunc length: 22\u001b[0m\n"
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger local\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 1 length: 34\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 1 trunc length: 34\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 2 length: 51\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 2 trunc length: 51\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 3 length: 66\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 3 trunc length: 66\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 4 length: 38\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 4 trunc length: 38\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 5 length: 27\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 5 trunc length: 27\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 6 length: 24\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 6 trunc length: 24\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 7 length: 21\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 7 trunc length: 21\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 8 length: 20\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 8 trunc length: 20\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 9 length: 25\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 9 trunc length: 25\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 10 length: 22\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:39 INFO semantic_router.utils.logger Document 10 trunc length: 22\u001b[0m\n"
           ]
         }
       ],
@@ -254,8 +254,8 @@
           "name": "stderr",
           "output_type": "stream",
           "text": [
-            "\u001b[32m2024-05-01 23:07:14 INFO semantic_router.utils.logger Document 1 length: 24\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:14 INFO semantic_router.utils.logger Document 1 trunc length: 24\u001b[0m\n"
+            "\u001b[32m2024-05-02 00:45:40 INFO semantic_router.utils.logger Document 1 length: 24\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:40 INFO semantic_router.utils.logger Document 1 trunc length: 24\u001b[0m\n"
           ]
         },
         {
@@ -332,7 +332,7 @@
         {
           "data": {
             "text/plain": [
-              "'15:07'"
+              "'16:45'"
             ]
           },
           "execution_count": 6,
@@ -475,13 +475,13 @@
           "name": "stderr",
           "output_type": "stream",
           "text": [
-            "\u001b[32m2024-05-01 23:07:14 INFO semantic_router.utils.logger Adding `get_time` route\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:14 INFO semantic_router.utils.logger Document 1 length: 34\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:14 INFO semantic_router.utils.logger Document 1 trunc length: 34\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:14 INFO semantic_router.utils.logger Document 2 length: 27\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:14 INFO semantic_router.utils.logger Document 2 trunc length: 27\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:14 INFO semantic_router.utils.logger Document 3 length: 32\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:14 INFO semantic_router.utils.logger Document 3 trunc length: 32\u001b[0m\n"
+            "\u001b[32m2024-05-02 00:45:41 INFO semantic_router.utils.logger Adding `get_time` route\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:41 INFO semantic_router.utils.logger Document 1 length: 34\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:41 INFO semantic_router.utils.logger Document 1 trunc length: 34\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:41 INFO semantic_router.utils.logger Document 2 length: 27\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:41 INFO semantic_router.utils.logger Document 2 trunc length: 27\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:41 INFO semantic_router.utils.logger Document 3 length: 32\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:41 INFO semantic_router.utils.logger Document 3 trunc length: 32\u001b[0m\n"
           ]
         }
       ],
@@ -523,15 +523,15 @@
           "name": "stderr",
           "output_type": "stream",
           "text": [
-            "\u001b[32m2024-05-01 23:07:15 INFO semantic_router.utils.logger Document 1 length: 34\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:15 INFO semantic_router.utils.logger Document 1 trunc length: 34\u001b[0m\n",
-            "\u001b[33m2024-05-01 23:07:15 WARNING semantic_router.utils.logger No LLM provided for dynamic route, will use OpenAI LLM default. Ensure API key is set in OPENAI_API_KEY environment variable.\u001b[0m\n"
+            "\u001b[32m2024-05-02 00:45:41 INFO semantic_router.utils.logger Document 1 length: 34\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:41 INFO semantic_router.utils.logger Document 1 trunc length: 34\u001b[0m\n",
+            "\u001b[33m2024-05-02 00:45:41 WARNING semantic_router.utils.logger No LLM provided for dynamic route, will use OpenAI LLM default. Ensure API key is set in OPENAI_API_KEY environment variable.\u001b[0m\n"
           ]
         },
         {
           "data": {
             "text/plain": [
-              "RouteChoice(name='get_time', function_call=[{'function_name': 'get_time', 'arguments': '{\"timezone\":\"America/New_York\"}'}], similarity_score=None)"
+              "RouteChoice(name='get_time', function_call=[{'function_name': 'get_time', 'arguments': {'timezone': 'America/New_York'}}], similarity_score=None)"
             ]
           },
           "execution_count": 13,
@@ -553,7 +553,7 @@
           "name": "stdout",
           "output_type": "stream",
           "text": [
-            "[{'function_name': 'get_time', 'arguments': '{\"timezone\":\"America/New_York\"}'}]\n"
+            "[{'function_name': 'get_time', 'arguments': {'timezone': 'America/New_York'}}]\n"
           ]
         }
       ],
@@ -570,7 +570,7 @@
           "name": "stdout",
           "output_type": "stream",
           "text": [
-            "15:07\n"
+            "16:45\n"
           ]
         }
       ],
@@ -579,7 +579,7 @@
         "\n",
         "for call in response.function_call:\n",
         "    if call['function_name'] == 'get_time':\n",
-        "        args = json.loads(call['arguments'])\n",
+        "        args = call['arguments']\n",
         "        result = get_time(**args)\n",
         "print(result)"
       ]
@@ -646,28 +646,12 @@
         "    now = datetime.now(ZoneInfo(timezone))\n",
         "    return now.strftime(\"%H:%M\")\n",
         "\n",
-        "# # Function with two arguments\n",
-        "# def get_time_difference(timezone1: str, timezone2: str) -> str:\n",
-        "#     \"\"\"Calculates the time difference between two timezones.\n",
-        "#     :param timezone1: The first timezone.\n",
-        "#     :param timezone2: The second timezone.\n",
-        "#     :type timezone1: str\n",
-        "#     :type timezone2: str\n",
-        "#     :return: The time difference in hours between the two timezones.\"\"\"\n",
-        "#     now = datetime.now()\n",
-        "#     tz1 = now.astimezone(ZoneInfo(timezone1))\n",
-        "#     tz2 = now.astimezone(ZoneInfo(timezone2))\n",
-        "#     difference = tz2 - tz1\n",
-        "#     total_seconds = difference.total_seconds()\n",
-        "#     hours_difference = total_seconds / 3600\n",
-        "#     return f\"The time difference between {timezone1} and {timezone2} is {hours_difference} hours.\"\n",
-        "\n",
         "\n",
         "\n",
         "def get_time_difference(timezone1: str, timezone2: str) -> str:\n",
         "    \"\"\"Calculates the time difference between two timezones.\n",
-        "    :param timezone1: The first timezone.\n",
-        "    :param timezone2: The second timezone.\n",
+        "    :param timezone1: The first timezone, should be a valid timezone from the IANA Time Zone Database like \"America/New_York\" or \"Europe/London\".\n",
+        "    :param timezone2: The second timezone, should be a valid timezone from the IANA Time Zone Database like \"America/New_York\" or \"Europe/London\".\n",
         "    :type timezone1: str\n",
         "    :type timezone2: str\n",
         "    :return: The time difference in hours between the two timezones.\"\"\"\n",
@@ -703,22 +687,16 @@
         "        convert_time(\"12:30\", \"America/New_York\", \"Asia/Tokyo\") -> \"03:30\"\n",
         "    \"\"\"\n",
         "    try:\n",
-        "        print(f\"Attempting to parse the time '{time}' and apply timezone '{from_timezone}'\")\n",
         "        # Use today's date to avoid historical timezone issues\n",
         "        today = datetime.now().date()\n",
         "        datetime_string = f\"{today} {time}\"\n",
         "        time_obj = datetime.strptime(datetime_string, \"%Y-%m-%d %H:%M\").replace(tzinfo=ZoneInfo(from_timezone))\n",
-        "        print(f\"Time parsed successfully: {time_obj}\")\n",
         "        \n",
-        "        print(f\"Converting time from '{from_timezone}' to '{to_timezone}'\")\n",
         "        converted_time = time_obj.astimezone(ZoneInfo(to_timezone))\n",
-        "        print(f\"Time conversion successful: {converted_time}\")\n",
         "        \n",
         "        formatted_time = converted_time.strftime(\"%H:%M\")\n",
-        "        print(f\"Formatted converted time: {formatted_time}\")\n",
         "        return formatted_time\n",
         "    except Exception as e:\n",
-        "        print(f\"Error encountered: {e}\")\n",
         "        raise ValueError(f\"Error converting time: {e}\")\n",
         "\n",
         "\n"
@@ -750,11 +728,12 @@
               "    'required': ['timezone']}}},\n",
               " {'type': 'function',\n",
               "  'function': {'name': 'get_time_difference',\n",
-              "   'description': 'Calculates the time difference between two timezones.\\n:param timezone1: The first timezone.\\n:param timezone2: The second timezone.\\n:type timezone1: str\\n:type timezone2: str\\n:return: The time difference in hours between the two timezones.',\n",
+              "   'description': 'Calculates the time difference between two timezones.\\n:param timezone1: The first timezone, should be a valid timezone from the IANA Time Zone Database like \"America/New_York\" or \"Europe/London\".\\n:param timezone2: The second timezone, should be a valid timezone from the IANA Time Zone Database like \"America/New_York\" or \"Europe/London\".\\n:type timezone1: str\\n:type timezone2: str\\n:return: The time difference in hours between the two timezones.',\n",
               "   'parameters': {'type': 'object',\n",
               "    'properties': {'timezone1': {'type': 'string',\n",
-              "      'description': 'The first timezone.'},\n",
-              "     'timezone2': {'type': 'string', 'description': 'The second timezone.'}},\n",
+              "      'description': 'The first timezone, should be a valid timezone from the IANA Time Zone Database like \"America/New_York\" or \"Europe/London\".'},\n",
+              "     'timezone2': {'type': 'string',\n",
+              "      'description': 'The second timezone, should be a valid timezone from the IANA Time Zone Database like \"America/New_York\" or \"Europe/London\".'}},\n",
               "    'required': ['timezone1', 'timezone2']}}},\n",
               " {'type': 'function',\n",
               "  'function': {'name': 'convert_time',\n",
@@ -811,6 +790,9 @@
         "        \"adjust 20:00 from Rome time to London time\",\n",
         "        \"convert 12:00 from Madrid time to Chicago time\",\n",
         "        \"change 18:00 from Beijing time to Los Angeles time\"\n",
+        "\n",
+        "        # All three functions\n",
+        "        \"What is the time in Seattle? What is the time difference between Mumbai and Tokyo? What is 5:53 Toronto time in Sydney time?\"\n",
         "    ],\n",
         "    function_schemas=schemas\n",
         ")\n"
@@ -834,57 +816,57 @@
           "name": "stderr",
           "output_type": "stream",
           "text": [
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger local\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 1 length: 34\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 1 trunc length: 34\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 2 length: 51\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 2 trunc length: 51\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 3 length: 66\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 3 trunc length: 66\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 4 length: 38\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 4 trunc length: 38\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 5 length: 27\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 5 trunc length: 27\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 6 length: 24\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 6 trunc length: 24\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 7 length: 21\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 7 trunc length: 21\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 8 length: 20\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 8 trunc length: 20\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 9 length: 25\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 9 trunc length: 25\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 10 length: 22\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 10 trunc length: 22\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 11 length: 29\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 11 trunc length: 29\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 12 length: 23\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 12 trunc length: 23\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 13 length: 36\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 13 trunc length: 36\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 14 length: 42\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 14 trunc length: 42\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 15 length: 41\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 15 trunc length: 41\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 16 length: 42\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 16 trunc length: 42\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 17 length: 40\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 17 trunc length: 40\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 18 length: 53\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 18 trunc length: 53\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 19 length: 59\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 19 trunc length: 59\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 20 length: 55\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 20 trunc length: 55\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 21 length: 47\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 21 trunc length: 47\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 22 length: 43\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 22 trunc length: 43\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 23 length: 42\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 23 trunc length: 42\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 24 length: 46\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 24 trunc length: 46\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 25 length: 50\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:16 INFO semantic_router.utils.logger Document 25 trunc length: 50\u001b[0m\n"
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger local\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 1 length: 34\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 1 trunc length: 34\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 2 length: 51\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 2 trunc length: 51\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 3 length: 66\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 3 trunc length: 66\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 4 length: 38\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 4 trunc length: 38\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 5 length: 27\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 5 trunc length: 27\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 6 length: 24\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 6 trunc length: 24\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 7 length: 21\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 7 trunc length: 21\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 8 length: 20\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 8 trunc length: 20\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 9 length: 25\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 9 trunc length: 25\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 10 length: 22\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 10 trunc length: 22\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 11 length: 29\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 11 trunc length: 29\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 12 length: 23\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 12 trunc length: 23\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 13 length: 36\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 13 trunc length: 36\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 14 length: 42\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 14 trunc length: 42\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 15 length: 41\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 15 trunc length: 41\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 16 length: 42\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 16 trunc length: 42\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 17 length: 40\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 17 trunc length: 40\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 18 length: 53\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 18 trunc length: 53\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 19 length: 59\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 19 trunc length: 59\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 20 length: 55\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 20 trunc length: 55\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 21 length: 47\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 21 trunc length: 47\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 22 length: 43\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 22 trunc length: 43\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 23 length: 42\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 23 trunc length: 42\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 24 length: 46\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 24 trunc length: 46\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 25 length: 174\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:42 INFO semantic_router.utils.logger Document 25 trunc length: 174\u001b[0m\n"
           ]
         }
       ],
@@ -908,7 +890,7 @@
         "def parse_response(response: str):\n",
         "\n",
         "    for call in response.function_call:\n",
-        "        args = json.loads(call['arguments'])\n",
+        "        args = call['arguments']\n",
         "        if call['function_name'] == 'get_time':\n",
         "            result = get_time(**args)\n",
         "            print(result)\n",
@@ -936,8 +918,8 @@
           "name": "stderr",
           "output_type": "stream",
           "text": [
-            "\u001b[32m2024-05-01 23:07:17 INFO semantic_router.utils.logger Document 1 length: 31\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:17 INFO semantic_router.utils.logger Document 1 trunc length: 31\u001b[0m\n"
+            "\u001b[32m2024-05-02 00:45:47 INFO semantic_router.utils.logger Document 1 length: 31\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:47 INFO semantic_router.utils.logger Document 1 trunc length: 31\u001b[0m\n"
           ]
         },
         {
@@ -972,8 +954,8 @@
           "name": "stderr",
           "output_type": "stream",
           "text": [
-            "\u001b[32m2024-05-01 23:07:17 INFO semantic_router.utils.logger Document 1 length: 29\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:17 INFO semantic_router.utils.logger Document 1 trunc length: 29\u001b[0m\n"
+            "\u001b[32m2024-05-02 00:45:47 INFO semantic_router.utils.logger Document 1 length: 29\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:47 INFO semantic_router.utils.logger Document 1 trunc length: 29\u001b[0m\n"
           ]
         },
         {
@@ -1008,15 +990,15 @@
           "name": "stderr",
           "output_type": "stream",
           "text": [
-            "\u001b[32m2024-05-01 23:07:18 INFO semantic_router.utils.logger Document 1 length: 29\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:18 INFO semantic_router.utils.logger Document 1 trunc length: 29\u001b[0m\n",
-            "\u001b[33m2024-05-01 23:07:18 WARNING semantic_router.utils.logger No LLM provided for dynamic route, will use OpenAI LLM default. Ensure API key is set in OPENAI_API_KEY environment variable.\u001b[0m\n"
+            "\u001b[32m2024-05-02 00:45:48 INFO semantic_router.utils.logger Document 1 length: 29\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:48 INFO semantic_router.utils.logger Document 1 trunc length: 29\u001b[0m\n",
+            "\u001b[33m2024-05-02 00:45:48 WARNING semantic_router.utils.logger No LLM provided for dynamic route, will use OpenAI LLM default. Ensure API key is set in OPENAI_API_KEY environment variable.\u001b[0m\n"
           ]
         },
         {
           "data": {
             "text/plain": [
-              "RouteChoice(name='timezone_management', function_call=[{'function_name': 'get_time', 'arguments': '{\"timezone\":\"America/New_York\"}'}], similarity_score=None)"
+              "RouteChoice(name='timezone_management', function_call=[{'function_name': 'get_time', 'arguments': {'timezone': 'America/New_York'}}], similarity_score=None)"
             ]
           },
           "execution_count": 25,
@@ -1038,7 +1020,7 @@
           "name": "stdout",
           "output_type": "stream",
           "text": [
-            "15:07\n"
+            "16:45\n"
           ]
         }
       ],
@@ -1062,14 +1044,14 @@
           "name": "stderr",
           "output_type": "stream",
           "text": [
-            "\u001b[32m2024-05-01 23:07:19 INFO semantic_router.utils.logger Document 1 length: 61\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:19 INFO semantic_router.utils.logger Document 1 trunc length: 61\u001b[0m\n"
+            "\u001b[32m2024-05-02 00:45:49 INFO semantic_router.utils.logger Document 1 length: 61\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:49 INFO semantic_router.utils.logger Document 1 trunc length: 61\u001b[0m\n"
           ]
         },
         {
           "data": {
             "text/plain": [
-              "RouteChoice(name='timezone_management', function_call=[{'function_name': 'get_time_difference', 'arguments': '{\"timezone1\":\"America/Los_Angeles\",\"timezone2\":\"Europe/Istanbul\"}'}], similarity_score=None)"
+              "RouteChoice(name='timezone_management', function_call=[{'function_name': 'get_time_difference', 'arguments': {'timezone1': 'America/Los_Angeles', 'timezone2': 'Europe/Istanbul'}}], similarity_score=None)"
             ]
           },
           "execution_count": 27,
@@ -1115,14 +1097,14 @@
           "name": "stderr",
           "output_type": "stream",
           "text": [
-            "\u001b[32m2024-05-01 23:07:21 INFO semantic_router.utils.logger Document 1 length: 72\u001b[0m\n",
-            "\u001b[32m2024-05-01 23:07:21 INFO semantic_router.utils.logger Document 1 trunc length: 72\u001b[0m\n"
+            "\u001b[32m2024-05-02 00:45:51 INFO semantic_router.utils.logger Document 1 length: 61\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:51 INFO semantic_router.utils.logger Document 1 trunc length: 61\u001b[0m\n"
           ]
         },
         {
           "data": {
             "text/plain": [
-              "RouteChoice(name='timezone_management', function_call=[{'function_name': 'convert_time', 'arguments': '{\"time\":\"23:02\",\"from_timezone\":\"Asia/Dubai\",\"to_timezone\":\"Asia/Tokyo\"}'}], similarity_score=None)"
+              "RouteChoice(name='timezone_management', function_call=[{'function_name': 'convert_time', 'arguments': {'time': '23:02', 'from_timezone': 'Asia/Dubai', 'to_timezone': 'Asia/Tokyo'}}], similarity_score=None)"
             ]
           },
           "execution_count": 29,
@@ -1131,7 +1113,7 @@
         }
       ],
       "source": [
-        "response = rl2(\"What is 23:02 Dubai time in Tokyo time. Please and thank you.\")\n",
+        "response = rl2(\"What is 23:02 Dubai time in Tokyo time? Please and thank you.\")\n",
         "response"
       ]
     },
@@ -1144,12 +1126,7 @@
           "name": "stdout",
           "output_type": "stream",
           "text": [
-            "Attempting to parse the time '23:02' and apply timezone 'Asia/Dubai'\n",
-            "Time parsed successfully: 1900-01-01 23:02:00+03:41:12\n",
-            "Converting time from 'Asia/Dubai' to 'Asia/Tokyo'\n",
-            "Time conversion successful: 1900-01-02 04:20:48+09:00\n",
-            "Formatted converted time: 04:20\n",
-            "04:20\n"
+            "04:02\n"
           ]
         }
       ],
@@ -1157,12 +1134,74 @@
         "parse_response(response)"
       ]
     },
+    {
+      "cell_type": "markdown",
+      "metadata": {},
+      "source": [
+        "### The Cool Bit - Testing `multi_function_route` - Multiple Functions at Once"
+      ]
+    },
     {
       "cell_type": "code",
-      "execution_count": null,
+      "execution_count": 31,
       "metadata": {},
-      "outputs": [],
-      "source": []
+      "outputs": [
+        {
+          "name": "stderr",
+          "output_type": "stream",
+          "text": [
+            "\u001b[32m2024-05-02 00:45:52 INFO semantic_router.utils.logger Document 1 length: 142\u001b[0m\n",
+            "\u001b[32m2024-05-02 00:45:52 INFO semantic_router.utils.logger Document 1 trunc length: 142\u001b[0m\n"
+          ]
+        }
+      ],
+      "source": [
+        "response = rl2(\"\"\"\n",
+        "    What is the time in Prague?\n",
+        "    What is the time difference between Frankfurt and Beijing?\n",
+        "    What is 5:53 Lisbon time in Bangkok time?\n",
+        "\"\"\"               \n",
+        ")"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": 32,
+      "metadata": {},
+      "outputs": [
+        {
+          "data": {
+            "text/plain": [
+              "RouteChoice(name='timezone_management', function_call=[{'function_name': 'get_time', 'arguments': {'timezone': 'Europe/Prague'}}, {'function_name': 'get_time_difference', 'arguments': {'timezone1': 'Europe/Berlin', 'timezone2': 'Asia/Shanghai'}}, {'function_name': 'convert_time', 'arguments': {'time': '05:53', 'from_timezone': 'Europe/Lisbon', 'to_timezone': 'Asia/Bangkok'}}], similarity_score=None)"
+            ]
+          },
+          "execution_count": 32,
+          "metadata": {},
+          "output_type": "execute_result"
+        }
+      ],
+      "source": [
+        "response"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": 33,
+      "metadata": {},
+      "outputs": [
+        {
+          "name": "stdout",
+          "output_type": "stream",
+          "text": [
+            "22:45\n",
+            "The time difference between Europe/Berlin and Asia/Shanghai is 6.0 hours.\n",
+            "11:53\n"
+          ]
+        }
+      ],
+      "source": [
+        "parse_response(response)"
+      ]
     }
   ],
   "metadata": {
diff --git a/semantic_router/llms/base.py b/semantic_router/llms/base.py
index 3170ccc2..d3f207a1 100644
--- a/semantic_router/llms/base.py
+++ b/semantic_router/llms/base.py
@@ -108,9 +108,9 @@ Return only JSON, stating the function name and the argument names with their co
 
 ### FORMATTING_INSTRUCTIONS Start ###
     Return a response in valid JSON format. Do not return any other explanation or text, just the JSON.
-    The JSON output should include a key 'function_name' with the value being the name of the function.
+    The JSON output should always be an array of JSON objects. If only one function is relevant, return an array with a single JSON object.
+    Each JSON object should include a key 'function_name' with the value being the name of the function.
     Under the key 'arguments', include a nested JSON object where the keys are the names of the arguments and the values are the values those arguments should take.
-    If multiple function schemas are relevant, return a list of JSON objects.
 ### FORMATTING_INSTRUCTIONS End ###
 
 ### EXAMPLE Start ###
@@ -139,26 +139,26 @@ Return only JSON, stating the function name and the argument names with their co
     === EXAMPLE_INPUT_SCHEMA End ===
     === EXAMPLE_OUTPUT Start ===
         [
-            {
+            {{
                 "function_name": "get_temperature",
-                "arguments": {
+                "arguments": {{
                     "location": "Hawaii",
                     "degree": "Celsius"
-                }
-            },
-            {
+                }}
+            }},
+            {{
                 "function_name": "get_temperature",
-                "arguments": {
+                "arguments": {{
                     "location": "New York",
                     "degree": "Celsius"
-                }
-            },
-            {
+                }}
+            }},
+            {{
                 "function_name": "get_humidity",
-                "arguments": {
+                "arguments": {{
                     "location": "Hawaii"
-                }
-            }
+                }}
+            }}
         ]
     === EXAMPLE_OUTPUT End ===
 ### EXAMPLE End ###
diff --git a/semantic_router/llms/openai.py b/semantic_router/llms/openai.py
index c3cb347b..b8306740 100644
--- a/semantic_router/llms/openai.py
+++ b/semantic_router/llms/openai.py
@@ -49,9 +49,9 @@ class OpenAILLM(BaseLLM):
                 )
             tool_calls_info.append({
                 "function_name": tool_call.function.name,
-                "arguments": tool_call.function.arguments
+                "arguments": json.loads(tool_call.function.arguments)
             })
-        return json.dumps(tool_calls_info)
+        return tool_calls_info
     
     def __call__(
         self,
@@ -104,9 +104,7 @@ class OpenAILLM(BaseLLM):
         system_prompt = "You are an intelligent AI. Given a command or request from the user, call the function to complete the request."
         messages.append(Message(role="system", content=system_prompt))
         messages.append(Message(role="user", content=query))
-        function_inputs_str = self(messages=messages, function_schemas=function_schemas)
-        function_inputs = json.loads(function_inputs_str)
-        return function_inputs
+        return self(messages=messages, function_schemas=function_schemas)
 
 def get_schemas_openai(items: List[Callable]) -> List[Dict[str, Any]]:
     schemas = []
-- 
GitLab