diff --git a/semantic_router/index/base.py b/semantic_router/index/base.py index c3c9ed0fb740e7195e723965a23dddedeb2948bc..77053ef08100a5d7dd5f06048ce9360b0987f091 100644 --- a/semantic_router/index/base.py +++ b/semantic_router/index/base.py @@ -13,8 +13,8 @@ class BaseIndex(BaseModel): # You can define common attributes here if there are any. # For example, a placeholder for the index attribute: index: Optional[Any] = None - routes: Optional[List[str]] = None - utterances: Optional[List[str]] = None + routes: Optional[np.ndarray] = None + utterances: Optional[np.ndarray] = None dimensions: Union[int, None] = None type: str = "base" diff --git a/semantic_router/index/local.py b/semantic_router/index/local.py index 08cd088591850f2b5da2aefa5a430fbf7870cbb2..058ee2bc08e562a56327c61850471b14409dfa6a 100644 --- a/semantic_router/index/local.py +++ b/semantic_router/index/local.py @@ -8,12 +8,10 @@ class LocalIndex(BaseIndex): def __init__( self, index: Optional[np.ndarray] = None, - routes: Optional[List[str]] = None, - utterances: Optional[List[str]] = None, + routes: Optional[np.ndarray] = None, + utterances: Optional[np.ndarray] = None, ): - super().__init__( - index=index, routes=routes, utterances=utterances - ) + super().__init__(index=index, routes=routes, utterances=utterances) self.type = "local" class Config: # Stop pydantic from complaining about Optional[np.ndarray] type hints. @@ -41,9 +39,9 @@ class LocalIndex(BaseIndex): Returns: List[Tuple]: A list of (route_name, utterance) objects. """ - if self.route_names is None or self.utterances is None: + if self.routes is None or self.utterances is None: raise ValueError("No routes have been added to the index.") - return list(zip(self.route_names, self.utterances)) + return list(zip(self.routes, self.utterances)) def describe(self) -> dict: return { @@ -64,7 +62,7 @@ class LocalIndex(BaseIndex): # get routes from index values route_names = self.routes[idx].copy() return scores, route_names - + def delete(self, route_name: str): """ Delete all records of a specific route from the index. @@ -95,7 +93,7 @@ class LocalIndex(BaseIndex): raise ValueError("Routes are not populated.") idx = [i for i, route in enumerate(self.routes) if route == route_name] return idx - + def __len__(self): if self.index is not None: return self.index.shape[0] diff --git a/semantic_router/index/pinecone.py b/semantic_router/index/pinecone.py index 4594962f5a3d612dadcf7161c8bb6001d5d1edce..a6492226a46ebd2550f9c467fa64aa44af36d0a3 100644 --- a/semantic_router/index/pinecone.py +++ b/semantic_router/index/pinecone.py @@ -3,7 +3,7 @@ import requests import time import hashlib import os -from typing import Any, List, Tuple, Optional, Union +from typing import Any, Dict, List, Tuple, Optional, Union from semantic_router.index.base import BaseIndex from semantic_router.utils.logger import logger import numpy as np @@ -128,11 +128,13 @@ class PineconeIndex(BaseIndex): clean_route = clean_route_name(route_name) ids, _ = self._get_all(prefix=f"{clean_route}#") return ids - + def _get_all(self, prefix: Optional[str] = None, include_metadata: bool = False): """ Retrieves all vector IDs from the Pinecone index using pagination. """ + if self.index is None: + raise ValueError("Index is None, could not retrieve vector IDs.") all_vector_ids = [] next_page_token = None @@ -143,8 +145,8 @@ class PineconeIndex(BaseIndex): # Construct the request URL for listing vectors. Adjust parameters as needed. list_url = f"https://{self.host}/vectors/list{prefix_str}" - params = {} - headers = {"Api-Key": os.getenv("PINECONE_API_KEY")} + params: Dict = {} + headers = {"Api-Key": os.environ["PINECONE_API_KEY"]} metadata = [] while True: @@ -164,9 +166,7 @@ class PineconeIndex(BaseIndex): if include_metadata: res_meta = self.index.fetch(ids=vector_ids) # extract metadata only - metadata.extend( - [x["metadata"] for x in res_meta["vectors"].values()] - ) + metadata.extend([x["metadata"] for x in res_meta["vectors"].values()]) # Check if there's a next page token; if not, break the loop next_page_token = response_data.get("pagination", {}).get("next") @@ -174,7 +174,7 @@ class PineconeIndex(BaseIndex): break return all_vector_ids, metadata - + def get_routes(self) -> List[Tuple]: """ Gets a list of route and utterance objects currently stored in the index. diff --git a/semantic_router/layer.py b/semantic_router/layer.py index bec876748caa563f2941bb7d800c240466d9930b..5d56aefb96f86881838b960c9c80f02b28f40f29 100644 --- a/semantic_router/layer.py +++ b/semantic_router/layer.py @@ -303,8 +303,7 @@ class RouteLayer: self.index.delete(route_name=route_name) def _refresh_routes(self): - """Pulls out the latest routes from the index. - """ + """Pulls out the latest routes from the index.""" raise NotImplementedError("This method has not yet been implemented.") route_mapping = {route.name: route for route in self.routes} index_routes = self.index.get_routes() @@ -320,7 +319,6 @@ class RouteLayer: route = route_mapping[route_name] self.routes.append(route) - def _add_routes(self, routes: List[Route]): # create embeddings for all routes all_utterances = [ @@ -458,7 +456,7 @@ class RouteLayer: correct += 1 accuracy = correct / len(Xq) return accuracy - + def _get_route_names(self) -> List[str]: return [route.name for route in self.routes] diff --git a/tests/unit/test_layer.py b/tests/unit/test_layer.py index e5ba76a2f2fbd997cd1db9f1d593aa6e9a834cbe..03c0c1eaba540ec6f0f9ae0dc93ab38e41a3265d 100644 --- a/tests/unit/test_layer.py +++ b/tests/unit/test_layer.py @@ -302,7 +302,9 @@ class TestRouteLayer: # now load from config and confirm it's the same route_layer_from_config = RouteLayer.from_config(layer_config) assert (route_layer_from_config.index.index == route_layer.index.index).all() - assert route_layer_from_config._get_route_names() == route_layer._get_route_names() + assert ( + route_layer_from_config._get_route_names() == route_layer._get_route_names() + ) assert route_layer_from_config.score_threshold == route_layer.score_threshold def test_get_thresholds(self, openai_encoder, routes):