From c88ea85709430ab563d50acac33b63f6255ab5f4 Mon Sep 17 00:00:00 2001
From: James Briggs <james.briggs@hotmail.com>
Date: Thu, 14 Nov 2024 22:59:45 +0100
Subject: [PATCH] fix: pinecone init and len

---
 docs/indexes/pinecone-sync-routes.ipynb | 398 +++++++++++++++++-------
 semantic_router/index/pinecone.py       | 259 +--------------
 semantic_router/layer.py                |   5 +-
 tests/unit/test_layer.py                |   4 +-
 4 files changed, 293 insertions(+), 373 deletions(-)

diff --git a/docs/indexes/pinecone-sync-routes.ipynb b/docs/indexes/pinecone-sync-routes.ipynb
index e3853a17..bb69eb7a 100644
--- a/docs/indexes/pinecone-sync-routes.ipynb
+++ b/docs/indexes/pinecone-sync-routes.ipynb
@@ -30,7 +30,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": 18,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -68,7 +68,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": 19,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -93,7 +93,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 26,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -110,7 +110,8 @@
     "    init_async_index=True,  # enables asynchronous methods, it's optional\n",
     "    sync=None,  # defines whether we sync between local and remote route layers\n",
     "    # when sync is None, no sync is performed\n",
-    ")"
+    ")\n",
+    "pc_index.index = pc_index._init_index(force_create=True)"
    ]
   },
   {
@@ -129,72 +130,81 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 27,
    "metadata": {},
    "outputs": [],
    "source": [
     "from semantic_router.layer import RouteLayer\n",
+    "import time\n",
     "\n",
-    "rl = RouteLayer(encoder=encoder, routes=routes, index=pc_index)"
+    "rl = RouteLayer(encoder=encoder, routes=routes, index=pc_index)\n",
+    "# due to pinecone indexing latency we wait 3 seconds\n",
+    "time.sleep(3)"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "We can check our route layer and index information as usual:"
+    "Let's see if our local and remote instances are synchronized..."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": 28,
    "metadata": {},
    "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "hash_id: sr_hash#\n"
+     ]
+    },
     {
      "data": {
       "text/plain": [
-       "['politics', 'chitchat']"
+       "False"
       ]
      },
-     "execution_count": 6,
+     "execution_count": 28,
      "metadata": {},
      "output_type": "execute_result"
     }
    ],
    "source": [
-    "rl.list_route_names()"
+    "rl.is_synced()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "It looks like everything is synced! Let's try deleting our local route layer, initializing it with just the politics route, and checking again."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 29,
    "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "0"
-      ]
-     },
-     "execution_count": 7,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
+   "outputs": [],
    "source": [
-    "len(rl.index)"
+    "del rl\n",
+    "\n",
+    "rl = RouteLayer(encoder=encoder, routes=[politics], index=pc_index)\n",
+    "time.sleep(3)"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Let's see if our local and remote instances are synchronized..."
+    "Let's try `rl.is_synced()` again:"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 8,
+   "execution_count": 30,
    "metadata": {},
    "outputs": [
     {
@@ -207,114 +217,280 @@
     {
      "data": {
       "text/plain": [
-       "ConfigParameter(field='sr_hash', value='f8f04794014c855bd68e283e64c57d8cc7a92f2ecd143386105de98f57c55e04', namespace='', created_at='2024-11-10T21:41:35.991948')"
+       "False"
       ]
      },
-     "execution_count": 8,
+     "execution_count": 30,
      "metadata": {},
      "output_type": "execute_result"
     }
    ],
    "source": [
-    "import time\n",
-    "\n",
-    "# due to pinecone indexing latency we wait 3 seconds\n",
-    "time.sleep(3)\n",
-    "rl.index._read_hash()"
+    "rl.is_synced()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We can use the `get_utterance_diff` method to see exactly _why_ our local and remote are not synced"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 31,
    "metadata": {},
    "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "hash_id: sr_hash#\n"
-     ]
-    },
     {
      "data": {
       "text/plain": [
-       "True"
+       "[\"- politics: don't you just hate the president\",\n",
+       " \"- politics: don't you just love the president\",\n",
+       " \"- politics: isn't politics the best thing ever\",\n",
+       " '- politics: they will save the country!',\n",
+       " \"- politics: they're going to destroy this country!\",\n",
+       " \"- politics: why don't you tell me about your political opinions\",\n",
+       " '+ Route 1: Hello',\n",
+       " '+ Route 1: Hi',\n",
+       " '+ Route 2: Au revoir',\n",
+       " '+ Route 2: Bye',\n",
+       " '+ Route 2: Goodbye',\n",
+       " '+ Route 3: Boo']"
       ]
      },
-     "execution_count": 9,
+     "execution_count": 31,
      "metadata": {},
      "output_type": "execute_result"
     }
    ],
    "source": [
-    "rl.is_synced()"
+    "rl.get_utterance_diff()"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "It looks like everything is synced! Let's try deleting our local route layer, initializing it with just the politics route, and checking again."
+    "## Handling Synchronization"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We may want to handle the resynchronization ourselves and to do that we ideally want a more structured version of the utterance diff returned above. To create that we first need to get a list of utterance objects from our remote and local instances:"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
+   "execution_count": null,
    "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "hash_id: sr_hash#\n"
-     ]
-    },
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "\u001b[33m2024-11-10 22:41:41 WARNING semantic_router.utils.logger Local and remote route layers were not aligned. Remote hash not updated. Use `RouteLayer.get_utterance_diff()` to see details.\u001b[0m\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
-    "del rl\n",
+    "remote_utterances = rl.index.get_utterances()\n",
+    "remote_utterances"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "local_utterances = rl.to_config().to_utterances()\n",
+    "local_utterances"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We can add the `diff_tag` attribute to each of these utterances by loading both lists into a `UtteranceDiff` object:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from semantic_router.schema import UtteranceDiff\n",
     "\n",
-    "rl = RouteLayer(encoder=encoder, routes=[politics], index=pc_index)\n",
-    "time.sleep(3)"
+    "diff = UtteranceDiff.from_utterances(\n",
+    "    local_utterances=local_utterances,\n",
+    "    remote_utterances=remote_utterances\n",
+    ")"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Let's try `rl.is_synced()` again:"
+    "`UtteranceDiff` objects include all diff information inside the `diff` attribute (which is a list of `Utterance` objects):"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 11,
+   "execution_count": null,
    "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "hash_id: sr_hash#\n"
-     ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "False"
-      ]
-     },
-     "execution_count": 11,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
+   "outputs": [],
    "source": [
+    "diff.diff"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Each of our `Utterance` objects now contains a populate `diff_tag` attribute. Where:\n",
+    "\n",
+    "* `diff_tag='+'` means the utterance exists in the remote instance *only*\n",
+    "\n",
+    "* `diff_tag='-'` means the utterance exists in the local instance *only*\n",
+    "\n",
+    "* `diff_tag=' '` means the utterance exists in both remote and local instances\n",
+    "\n",
+    "So, to collect utterances missing from our local instance we can run:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "diff.get_tag(\"+\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "To collect utterances missing from our remote instance we can run:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "diff.get_tag(\"-\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "And, if needed, we can get all utterances that exist in both with:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "diff.get_tag(\" \")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Synchronization"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "There are six synchronization methods that we can use, those are:\n",
+    "\n",
+    "* `error`: Raise an error if local and remote are not synchronized.\n",
+    "\n",
+    "* `remote`: Take remote as the source of truth and update local to align.\n",
+    "\n",
+    "* `local`: Take local as the source of truth and update remote to align.\n",
+    "\n",
+    "* `merge-force-remote`: Merge both local and remote keeping local as the priority. Remote utterances are only merged into local *if* a matching route for the utterance is found in local, all other route-utterances are dropped. Where a route exists in both local and remote, but each contains different `function_schema` or `metadata` information, the local version takes priority and local `function_schemas` and `metadata` is propogated to all remote utterances belonging to the given route.\n",
+    "\n",
+    "* `merge-force-local`: Merge both local and remote keeping remote as the priority. Local utterances are only merged into remote *if* a matching route for the utterance is found in the remote, all other route-utterances are dropped. Where a route exists in both local and remote, but each contains different `function_schema` or `metadata` information, the remote version takes priotity and remote `function_schemas` and `metadata` are propogated to all local routes.\n",
+    "\n",
+    "* `merge`: Merge both local and remote, merging also local and remote utterances when a route with same route name is present both locally and remotely. If a route exists in both local and remote but contains different `function_schemas` or `metadata` information, the remote version takes priority and remote `function_schemas` and `metadata` are propogated to all local routes.\n",
+    "\n",
+    "We can get the synchronization strategy for each of these (with the exception of `error`) using the `diff.get_sync_strategy` method."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "diff.get_sync_strategy(\"local\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "diff.get_sync_strategy(\"remote\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "diff.get_sync_strategy(\"merge-force-remote\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "diff.get_sync_strategy(\"merge-force-local\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "diff.get_sync_strategy(\"merge\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Each of these sync strategies can be fed to our route layer via the `rl._execute_sync_strategy` method:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "strategy = diff.get_sync_strategy(\"local\")\n",
+    "rl._execute_sync_strategy(strategy=strategy)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "time.sleep(3)\n",
     "rl.is_synced()"
    ]
   },
@@ -322,43 +498,14 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "We can use the `get_utterance_diff` method to see exactly _why_ our local and remote are not synced"
+    "We can check our diff method to see what the `local` sync did:"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": null,
    "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "['chitchat: how are things going?', \"chitchat: how's the weather today?\", \"chitchat: let's go to the chippy\", 'chitchat: lovely weather today', 'chitchat: the weather is horrendous', \"politics: don't you just hate the president\", \"politics: don't you just love the president\", \"politics: isn't politics the best thing ever\", 'politics: they will save the country!', \"politics: they're going to destroy this country!\", \"politics: why don't you tell me about your political opinions\"]\n",
-      "[\"politics: don't you just hate the president\", \"politics: don't you just love the president\", \"politics: isn't politics the best thing ever\", 'politics: they will save the country!', \"politics: they're going to destroy this country!\", \"politics: why don't you tell me about your political opinions\"]\n"
-     ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "['+ chitchat: how are things going?',\n",
-       " \"+ chitchat: how's the weather today?\",\n",
-       " \"+ chitchat: let's go to the chippy\",\n",
-       " '+ chitchat: lovely weather today',\n",
-       " '+ chitchat: the weather is horrendous',\n",
-       " \"  politics: don't you just hate the president\",\n",
-       " \"  politics: don't you just love the president\",\n",
-       " \"  politics: isn't politics the best thing ever\",\n",
-       " '  politics: they will save the country!',\n",
-       " \"  politics: they're going to destroy this country!\",\n",
-       " \"  politics: why don't you tell me about your political opinions\"]"
-      ]
-     },
-     "execution_count": 16,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
+   "outputs": [],
    "source": [
     "rl.get_utterance_diff()"
    ]
@@ -367,7 +514,18 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "---"
+    "As expected, it took all local utterances and applied them to the remote instance, removing all utterances that were only present in the remote instance.\n",
+    "\n",
+    "We can simplify this process significantly by running the `rl.sync` method with our chosen `sync_mode`:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "rl.sync(sync_mode=\"local\")"
    ]
   }
  ],
diff --git a/semantic_router/index/pinecone.py b/semantic_router/index/pinecone.py
index d899baf1..47a9307c 100644
--- a/semantic_router/index/pinecone.py
+++ b/semantic_router/index/pinecone.py
@@ -97,11 +97,16 @@ class PineconeIndex(BaseIndex):
         if self.api_key is None:
             raise ValueError("Pinecone API key is required.")
 
+        logger.debug("Init PineconeIndex sync client")
         self.client = self._initialize_client(api_key=self.api_key)
         if init_async_index:
+            logger.debug("Init PineconeIndex async client")
             self.async_client = self._initialize_async_client(api_key=self.api_key)
         else:
             self.async_client = None
+        # try initializing index
+        logger.debug("Init PineconeIndex index")
+        self.index = self._init_index()
 
     def _initialize_client(self, api_key: Optional[str] = None):
         try:
@@ -168,6 +173,7 @@ class PineconeIndex(BaseIndex):
             index = self.client.Index(self.index_name)
             time.sleep(0.5)
         elif index_exists:
+            logger.debug("Index exists, returning index")
             # if the index exists we just return it
             index = self.client.Index(self.index_name)
             # grab the dimensions from the index
@@ -218,255 +224,6 @@ class PineconeIndex(BaseIndex):
             logger.warning("Index could not be initialized.")
         self.host = index_stats["host"] if index_stats else None
 
-    # TODO: deprecate?
-    # def _format_routes_dict_for_sync(
-    #     self,
-    #     local_route_names: List[str],
-    #     local_utterances_list: List[str],
-    #     local_function_schemas_list: List[Dict[str, Any]],
-    #     local_metadata_list: List[Dict[str, Any]],
-    #     remote_routes: List[Tuple],
-    # ) -> Tuple[Dict, Dict]:
-    #     remote_dict: Dict[str, Dict[str, Any]] = {
-    #         route: {
-    #             "utterances": set(),
-    #             "function_schemas": function_schemas,
-    #             "metadata": metadata,
-    #         }
-    #         for route, utterance, function_schemas, metadata in remote_routes
-    #     }
-    #     for route, utterance, function_schemas, metadata in remote_routes:
-    #         remote_dict[route]["utterances"].add(utterance)
-
-    #     local_dict: Dict[str, Dict[str, Any]] = {}
-    #     for route, utterance, function_schemas, metadata in zip(
-    #         local_route_names,
-    #         local_utterances_list,
-    #         local_function_schemas_list,
-    #         local_metadata_list,
-    #     ):
-    #         if route not in local_dict:
-    #             local_dict[route] = {
-    #                 "utterances": set(),
-    #                 "function_schemas": function_schemas,
-    #                 "metadata": metadata,
-    #             }
-    #         local_dict[route]["utterances"].add(utterance)
-    #         local_dict[route]["function_schemas"] = function_schemas
-    #         local_dict[route]["metadata"] = metadata
-
-    #     return local_dict, remote_dict
-
-    # def _sync_index(
-    #     self,
-    #     local_route_names: List[str],
-    #     local_utterances_list: List[str],
-    #     local_function_schemas_list: List[Dict[str, Any]],
-    #     local_metadata_list: List[Dict[str, Any]],
-    #     dimensions: int,
-    # ) -> Tuple[List, List, Dict]:
-    #     if self.index is None:
-    #         self.dimensions = self.dimensions or dimensions
-    #         self.index = self._init_index(force_create=True)
-
-    #     remote_routes = self.get_utterances()
-
-    #     local_dict, remote_dict = self._format_routes_dict_for_sync(
-    #         local_route_names,
-    #         local_utterances_list,
-    #         local_function_schemas_list,
-    #         local_metadata_list,
-    #         remote_routes,
-    #     )
-
-    #     all_routes = set(remote_dict.keys()).union(local_dict.keys())
-
-    #     routes_to_add = []
-    #     routes_to_delete = []
-    #     layer_routes = {}
-
-    #     for route in all_routes:
-    #         local_utterances = local_dict.get(route, {}).get("utterances", set())
-    #         remote_utterances = remote_dict.get(route, {}).get("utterances", set())
-    #         local_function_schemas = (
-    #             local_dict.get(route, {}).get("function_schemas", {}) or {}
-    #         )
-    #         remote_function_schemas = (
-    #             remote_dict.get(route, {}).get("function_schemas", {}) or {}
-    #         )
-    #         local_metadata = local_dict.get(route, {}).get("metadata", {})
-    #         remote_metadata = remote_dict.get(route, {}).get("metadata", {})
-
-    #         utterances_to_include = set()
-
-    #         metadata_changed = local_metadata != remote_metadata
-    #         function_schema_changed = local_function_schemas != remote_function_schemas
-
-    #         if self.sync == "error":
-    #             if (
-    #                 local_utterances != remote_utterances
-    #                 or local_function_schemas != remote_function_schemas
-    #                 or local_metadata != remote_metadata
-    #             ):
-    #                 raise ValueError(
-    #                     f"Synchronization error: Differences found in route '{route}'"
-    #                 )
-
-    #             if local_utterances:
-    #                 layer_routes[route] = {
-    #                     "utterances": list(local_utterances),
-    #                     "function_schemas": (
-    #                         local_function_schemas if local_function_schemas else None
-    #                     ),
-    #                     "metadata": local_metadata,
-    #                 }
-
-    #         elif self.sync == "remote":
-    #             if remote_utterances:
-    #                 layer_routes[route] = {
-    #                     "utterances": list(remote_utterances),
-    #                     "function_schemas": (
-    #                         remote_function_schemas if remote_function_schemas else None
-    #                     ),
-    #                     "metadata": remote_metadata,
-    #                 }
-
-    #         elif self.sync == "local":
-    #             utterances_to_include = local_utterances - remote_utterances
-    #             routes_to_delete.extend(
-    #                 [
-    #                     (route, utterance)
-    #                     for utterance in remote_utterances
-    #                     if utterance not in local_utterances
-    #                 ]
-    #             )
-    #             if local_utterances:
-    #                 layer_routes[route] = {
-    #                     "utterances": list(local_utterances),
-    #                     "function_schemas": (
-    #                         local_function_schemas if local_function_schemas else None
-    #                     ),
-    #                     "metadata": local_metadata,
-    #                 }
-
-    #         elif self.sync == "merge-force-remote":
-    #             if route in local_dict and route not in remote_dict:
-    #                 utterances_to_include = local_utterances
-    #                 if local_utterances:
-    #                     layer_routes[route] = {
-    #                         "utterances": list(local_utterances),
-    #                         "function_schemas": (
-    #                             local_function_schemas
-    #                             if local_function_schemas
-    #                             else None
-    #                         ),
-    #                         "metadata": local_metadata,
-    #                     }
-    #             else:
-    #                 if remote_utterances:
-    #                     layer_routes[route] = {
-    #                         "utterances": list(remote_utterances),
-    #                         "function_schemas": (
-    #                             remote_function_schemas
-    #                             if remote_function_schemas
-    #                             else None
-    #                         ),
-    #                         "metadata": remote_metadata,
-    #                     }
-
-    #         elif self.sync == "merge-force-local":
-    #             if route in local_dict:
-    #                 utterances_to_include = local_utterances - remote_utterances
-    #                 routes_to_delete.extend(
-    #                     [
-    #                         (route, utterance)
-    #                         for utterance in remote_utterances
-    #                         if utterance not in local_utterances
-    #                     ]
-    #                 )
-    #                 if local_utterances:
-    #                     layer_routes[route] = {
-    #                         "utterances": list(local_utterances),
-    #                         "function_schemas": (
-    #                             local_function_schemas
-    #                             if local_function_schemas
-    #                             else None
-    #                         ),
-    #                         "metadata": local_metadata,
-    #                     }
-    #             else:
-    #                 if remote_utterances:
-    #                     layer_routes[route] = {
-    #                         "utterances": list(remote_utterances),
-    #                         "function_schemas": (
-    #                             remote_function_schemas
-    #                             if remote_function_schemas
-    #                             else None
-    #                         ),
-    #                         "metadata": remote_metadata,
-    #                     }
-
-    #         elif self.sync == "merge":
-    #             utterances_to_include = local_utterances - remote_utterances
-    #             if local_utterances or remote_utterances:
-    #                 # Here metadata are merged, with local metadata taking precedence for same keys
-    #                 merged_metadata = {**remote_metadata, **local_metadata}
-    #                 merged_function_schemas = {
-    #                     **remote_function_schemas,
-    #                     **local_function_schemas,
-    #                 }
-    #                 layer_routes[route] = {
-    #                     "utterances": list(remote_utterances.union(local_utterances)),
-    #                     "function_schemas": (
-    #                         merged_function_schemas if merged_function_schemas else None
-    #                     ),
-    #                     "metadata": merged_metadata,
-    #                 }
-
-    #         else:
-    #             raise ValueError("Invalid sync mode specified")
-
-    #         # Add utterances if metadata has changed or if there are new utterances
-    #         if (metadata_changed or function_schema_changed) and self.sync in [
-    #             "local",
-    #             "merge-force-local",
-    #         ]:
-    #             for utterance in local_utterances:
-    #                 routes_to_add.append(
-    #                     (
-    #                         route,
-    #                         utterance,
-    #                         local_function_schemas if local_function_schemas else None,
-    #                         local_metadata,
-    #                     )
-    #                 )
-    #         if (metadata_changed or function_schema_changed) and self.sync == "merge":
-    #             for utterance in local_utterances:
-    #                 routes_to_add.append(
-    #                     (
-    #                         route,
-    #                         utterance,
-    #                         (
-    #                             merged_function_schemas
-    #                             if merged_function_schemas
-    #                             else None
-    #                         ),
-    #                         merged_metadata,
-    #                     )
-    #                 )
-    #         elif utterances_to_include:
-    #             for utterance in utterances_to_include:
-    #                 routes_to_add.append(
-    #                     (
-    #                         route,
-    #                         utterance,
-    #                         local_function_schemas if local_function_schemas else None,
-    #                         local_metadata,
-    #                     )
-    #                 )
-
-    #     return routes_to_add, routes_to_delete, layer_routes
-
     def _batch_upsert(self, batch: List[Dict]):
         """Helper method for upserting a single batch of records."""
         if self.index is not None:
@@ -590,7 +347,7 @@ class PineconeIndex(BaseIndex):
             return {
                 "type": self.type,
                 "dimensions": stats["dimension"],
-                "vectors": stats["total_vector_count"],
+                "vectors": stats["namespaces"][self.namespace]["vector_count"],
             }
         else:
             raise ValueError("Index is None, cannot describe index stats.")
@@ -889,4 +646,4 @@ class PineconeIndex(BaseIndex):
             )
 
     def __len__(self):
-        return self.index.describe_index_stats()["total_vector_count"]
+        return self.index.describe_index_stats()["namespaces"][self.namespace]["vector_count"]
diff --git a/semantic_router/layer.py b/semantic_router/layer.py
index 3013e340..7002dd69 100644
--- a/semantic_router/layer.py
+++ b/semantic_router/layer.py
@@ -320,14 +320,16 @@ class RouteLayer:
                 route.score_threshold = self.score_threshold
         # if routes list has been passed, we initialize index now
         if self.auto_sync:
+            logger.debug(f"Auto sync enabled: {self.auto_sync}")
             # initialize index now, check if we need dimensions
             if self.index.dimensions is None:
                 dims = len(self.encoder(["test"])[0])
                 self.index.dimensions = dims
             # now init index
             if isinstance(self.index, PineconeIndex):
+                logger.debug("Initializing PineconeIndex index")
                 self.index.index = self.index._init_index(force_create=True)
-            if len(self.routes) > 0:
+                logger.debug("Checking for diffs")
                 local_utterances = self.to_config().to_utterances()
                 remote_utterances = self.index.get_utterances()
                 diff = UtteranceDiff.from_utterances(
@@ -335,6 +337,7 @@ class RouteLayer:
                     remote_utterances=remote_utterances,
                 )
                 sync_strategy = diff.get_sync_strategy(self.auto_sync)
+                logger.debug(f"Sync strategy: {sync_strategy}")
                 self._execute_sync_strategy(sync_strategy)
 
     def check_for_matching_routes(self, top_class: str) -> Optional[Route]:
diff --git a/tests/unit/test_layer.py b/tests/unit/test_layer.py
index 8d337f2f..b47fdd0d 100644
--- a/tests/unit/test_layer.py
+++ b/tests/unit/test_layer.py
@@ -193,7 +193,9 @@ class TestRouteLayer:
     def test_initialization(self, openai_encoder, routes, index_cls):
         index = init_index(index_cls)
         route_layer = RouteLayer(
-            encoder=openai_encoder, routes=routes, top_k=10, index=index
+            encoder=openai_encoder, routes=routes, index=index,
+            auto_sync="local" if index_cls is PineconeIndex else None,
+            top_k=10,
         )
         if index_cls is PineconeIndex:
             time.sleep(PINECONE_SLEEP)  # allow for index to be populated
-- 
GitLab