From 8fd88ad0aaca2f4bf079b4eadc1f98cfcb2469b6 Mon Sep 17 00:00:00 2001 From: Vits Date: Thu, 5 Sep 2024 12:11:33 +0200 Subject: [PATCH 1/9] Adjusted all tests + minor fixes --- semantic_router/index/pinecone.py | 26 ++--- semantic_router/layer.py | 14 +-- tests/unit/test_layer.py | 185 ++++++++++++++++++++++++------ 3 files changed, 170 insertions(+), 55 deletions(-) diff --git a/semantic_router/index/pinecone.py b/semantic_router/index/pinecone.py index 174fb49a..3de221ba 100644 --- a/semantic_router/index/pinecone.py +++ b/semantic_router/index/pinecone.py @@ -270,10 +270,10 @@ def _sync_index( 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", {}) @@ -295,7 +295,7 @@ def _sync_index( if local_utterances: layer_routes[route] = { "utterances": list(local_utterances), - "function_schemas": local_function_schemas, + "function_schemas": local_function_schemas if local_function_schemas else None, "metadata": local_metadata, } @@ -303,7 +303,7 @@ def _sync_index( if remote_utterances: layer_routes[route] = { "utterances": list(remote_utterances), - "function_schemas": remote_function_schemas, + "function_schemas": remote_function_schemas if remote_function_schemas else None, "metadata": remote_metadata, } @@ -319,7 +319,7 @@ def _sync_index( if local_utterances: layer_routes[route] = { "utterances": list(local_utterances), - "function_schemas": local_function_schemas, + "function_schemas": local_function_schemas if local_function_schemas else None, "metadata": local_metadata, } @@ -329,14 +329,14 @@ def _sync_index( if local_utterances: layer_routes[route] = { "utterances": list(local_utterances), - "function_schemas": local_function_schemas, + "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, + "function_schemas": remote_function_schemas if remote_function_schemas else None, "metadata": remote_metadata, } @@ -353,14 +353,14 @@ def _sync_index( if local_utterances: layer_routes[route] = { "utterances": list(local_utterances), - "function_schemas": local_function_schemas, + "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, + "function_schemas": remote_function_schemas if remote_function_schemas else None, "metadata": remote_metadata, } @@ -375,7 +375,7 @@ def _sync_index( } layer_routes[route] = { "utterances": list(remote_utterances.union(local_utterances)), - "function_schemas": merged_function_schemas, + "function_schemas": merged_function_schemas if merged_function_schemas else None, "metadata": merged_metadata, } @@ -389,17 +389,17 @@ def _sync_index( ]: for utterance in local_utterances: routes_to_add.append( - (route, utterance, local_function_schemas, local_metadata) + (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, merged_metadata) + (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, local_metadata) + (route, utterance, local_function_schemas if local_function_schemas else None, local_metadata) ) return routes_to_add, routes_to_delete, layer_routes diff --git a/semantic_router/layer.py b/semantic_router/layer.py index 0bf7d99f..f7d1395a 100644 --- a/semantic_router/layer.py +++ b/semantic_router/layer.py @@ -250,7 +250,6 @@ def __call__( if text is None: raise ValueError("Either text or vector must be provided") vector = self._encode(text=text) - route, top_class_scores = self._retrieve_top_route(vector, route_filter) passed = self._check_threshold(top_class_scores, route) if passed and route is not None and not simulate_static: @@ -448,7 +447,10 @@ def delete(self, route_name: str): if route_name not in [route.name for route in self.routes]: err_msg = f"Route `{route_name}` not found in RouteLayer" logger.warning(err_msg) - self.index.delete(route_name=route_name) + try: + self.index.delete(route_name=route_name) + except Exception as e: + logger.error(f"Failed to delete route from the index: {e}") else: self.routes = [route for route in self.routes if route.name != route_name] self.index.delete(route_name=route_name) @@ -503,13 +505,9 @@ def _add_and_sync_routes(self, routes: List[Route]): local_utterances, local_function_schemas, local_metadata, - dimensions=len(self.encoder(["dummy"])[0]), + dimensions=self.index.dimensions or len(self.encoder(["dummy"])[0]), ) - logger.info(f"Routes to add: {routes_to_add}") - logger.info(f"Routes to delete: {routes_to_delete}") - logger.info(f"Layer routes: {layer_routes_dict}") - data_to_delete = {} # type: ignore for route, utterance in routes_to_delete: data_to_delete.setdefault(route, []).append(utterance) @@ -548,7 +546,7 @@ def _add_and_sync_routes(self, routes: List[Route]): Route( name=route, utterances=data.get("utterances", []), - function_schemas=[data.get("function_schemas", None)], + function_schemas=[data.get("function_schemas", None)] if data.get("function_schemas") else None, metadata=data.get("metadata", {}), ) for route, data in layer_routes_dict.items() diff --git a/tests/unit/test_layer.py b/tests/unit/test_layer.py index 05979cd2..eaec568e 100644 --- a/tests/unit/test_layer.py +++ b/tests/unit/test_layer.py @@ -23,6 +23,7 @@ def mock_encoder_call(utterances): "Goodbye": [0.7, 0.8, 0.9], "Bye": [1.0, 1.1, 1.2], "Au revoir": [1.3, 1.4, 1.5], + "Asparagus": [-2.0, 1.0, 0.0], } return [mock_responses.get(u, [0.0, 0.0, 0.0]) for u in utterances] @@ -93,7 +94,7 @@ def openai_encoder(mocker): @pytest.fixture def routes(): return [ - Route(name="Route 1", utterances=["Hello", "Hi"]), + Route(name="Route 1", utterances=["Hello", "Hi"], metadata={"type": "default"}), Route(name="Route 2", utterances=["Goodbye", "Bye", "Au revoir"]), ] @@ -102,7 +103,7 @@ def routes(): def routes_2(): return [ Route(name="Route 1", utterances=["Hello"]), - Route(name="Route 2", utterances=["Hello"]), + Route(name="Route 2", utterances=["Hi"]), ] @@ -113,6 +114,13 @@ def routes_3(): Route(name="Route 2", utterances=["Asparagus"]), ] +@pytest.fixture +def routes_4(): + return [ + Route(name="Route 1", utterances=["Goodbye"], metadata={"type": "default"}), + Route(name="Route 2", utterances=["Asparagus"]), + ] + @pytest.fixture def dynamic_routes(): @@ -146,6 +154,9 @@ def get_test_indexes(): if importlib.util.find_spec("qdrant_client") is not None: indexes.append(QdrantIndex) + if importlib.util.find_spec("pinecone") is not None: + indexes.append(PineconeIndex) + return indexes @@ -155,6 +166,9 @@ def test_initialization(self, openai_encoder, routes, index_cls): route_layer = RouteLayer( encoder=openai_encoder, routes=routes, top_k=10, index=index_cls() ) + if index_cls is PineconeIndex: + time.sleep(15) # allow for index to be populated + assert openai_encoder.score_threshold == 0.3 assert route_layer.score_threshold == 0.3 assert route_layer.top_k == 10 @@ -192,25 +206,28 @@ def test_initialization_dynamic_route( assert openai_encoder.score_threshold == 0.3 assert route_layer_openai.score_threshold == 0.3 - def test_add_route(self, openai_encoder, index_cls): + def test_add_route(self, routes, openai_encoder, index_cls): route_layer = RouteLayer(encoder=openai_encoder, index=index_cls()) - route1 = Route(name="Route 1", utterances=["Yes", "No"]) - route2 = Route(name="Route 2", utterances=["Maybe", "Sure"]) # Initially, the routes list should be empty assert route_layer.routes == [] + if index_cls is PineconeIndex: + time.sleep(15) # allow for index to be populated # Add route1 and check - route_layer.add(route=route1) - assert route_layer.routes == [route1] + route_layer.add(route=routes[0]) + if index_cls is PineconeIndex: + time.sleep(15) # allow for index to be populated + assert route_layer.routes == [routes[0]] assert route_layer.index is not None - # Use the describe method to get the number of vectors - assert route_layer.index.describe()["vectors"] == 2 + assert len(route_layer.index.get_routes()) == 2 # Add route2 and check - route_layer.add(route=route2) - assert route_layer.routes == [route1, route2] - assert route_layer.index.describe()["vectors"] == 4 + route_layer.add(route=routes[1]) + if index_cls is PineconeIndex: + time.sleep(15) # allow for index to be populated + assert route_layer.routes == [routes[0], routes[1]] + assert len(route_layer.index.get_routes()) == 5 def test_list_route_names(self, openai_encoder, routes, index_cls): route_layer = RouteLayer( @@ -228,6 +245,8 @@ def test_delete_route(self, openai_encoder, routes, index_cls): # Delete a route by name route_to_delete = routes[0].name route_layer.delete(route_to_delete) + if index_cls is PineconeIndex: + time.sleep(15) # allow for index to be populated # Ensure the route is no longer in the route layer assert ( route_to_delete not in route_layer.list_route_names() @@ -242,6 +261,8 @@ def test_remove_route_not_found(self, openai_encoder, routes, index_cls): route_layer = RouteLayer( encoder=openai_encoder, routes=routes, index=index_cls() ) + if index_cls is PineconeIndex: + time.sleep(15) # Attempt to remove a route that does not exist non_existent_route = "non-existent-route" route_layer.delete(non_existent_route) @@ -249,21 +270,44 @@ def test_remove_route_not_found(self, openai_encoder, routes, index_cls): def test_add_multiple_routes(self, openai_encoder, routes, index_cls): route_layer = RouteLayer(encoder=openai_encoder, index=index_cls()) + if index_cls is PineconeIndex: + time.sleep(15) route_layer._add_routes(routes=routes) + if index_cls is PineconeIndex: + time.sleep(15) # allow for index to be populated assert route_layer.index is not None - assert route_layer.index.describe()["vectors"] == 5 + assert len(route_layer.index.get_routes()) == 5 def test_query_and_classification(self, openai_encoder, routes, index_cls): - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() - ) + if index_cls is PineconeIndex: + pineconeindex = PineconeIndex(dimensions=3) + route_layer = RouteLayer( + encoder=openai_encoder, routes=routes, index=pineconeindex + ) + time.sleep(15) + else: + route_layer = RouteLayer( + encoder=openai_encoder, routes=routes, index=index_cls() + ) + if index_cls is PineconeIndex: + time.sleep(15) # allow for index to be populated query_result = route_layer(text="Hello").name assert query_result in ["Route 1", "Route 2"] def test_query_filter(self, openai_encoder, routes, index_cls): - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() - ) + if index_cls is PineconeIndex: + pineconeindex = PineconeIndex(dimensions=3) + route_layer = RouteLayer( + encoder=openai_encoder, routes=routes, index=pineconeindex + ) + time.sleep(15) + else: + route_layer = RouteLayer( + encoder=openai_encoder, routes=routes, index=index_cls() + ) + + if index_cls is PineconeIndex: + time.sleep(15) # allow for index to be populated query_result = route_layer(text="Hello", route_filter=["Route 1"]).name try: @@ -277,13 +321,12 @@ def test_query_filter(self, openai_encoder, routes, index_cls): os.environ.get("PINECONE_API_KEY") is None, reason="Pinecone API key required" ) def test_query_filter_pinecone(self, openai_encoder, routes, index_cls): - if type(index_cls) == PineconeIndex: - pinecone_api_key = os.environ["PINECONE_API_KEY"] - pineconeindex = PineconeIndex(api_key=pinecone_api_key) + if index_cls is PineconeIndex: + pineconeindex = PineconeIndex(dimensions=3) route_layer = RouteLayer( encoder=openai_encoder, routes=routes, index=pineconeindex ) - time.sleep(10) # allow for index to be populated + time.sleep(15) # allow for index to be populated query_result = route_layer(text="Hello", route_filter=["Route 1"]).name try: @@ -297,13 +340,12 @@ def test_query_filter_pinecone(self, openai_encoder, routes, index_cls): os.environ.get("PINECONE_API_KEY") is None, reason="Pinecone API key required" ) def test_namespace_pinecone_index(self, openai_encoder, routes, index_cls): - if type(index_cls) == PineconeIndex: - pinecone_api_key = os.environ["PINECONE_API_KEY"] - pineconeindex = PineconeIndex(api_key=pinecone_api_key, namespace="test") + if index_cls is PineconeIndex: + pineconeindex = PineconeIndex(namespace="test") route_layer = RouteLayer( encoder=openai_encoder, routes=routes, index=pineconeindex ) - time.sleep(10) # allow for index to be populated + time.sleep(15) # allow for index to be populated query_result = route_layer(text="Hello", route_filter=["Route 1"]).name try: @@ -312,9 +354,72 @@ def test_namespace_pinecone_index(self, openai_encoder, routes, index_cls): assert True assert query_result in ["Route 1"] + route_layer.index.index.delete(namespace="test", delete_all=True) + + @pytest.mark.skipif( + os.environ.get("PINECONE_API_KEY") is None, reason="Pinecone API key required" + ) + def test_sync_pinecone(self, openai_encoder, routes, routes_2, routes_4, index_cls): + if index_cls is PineconeIndex: + # TEST LOCAL + pinecone_index = PineconeIndex(sync="local") + route_layer = RouteLayer(encoder=openai_encoder, routes=routes_2, index=pinecone_index) + time.sleep(15) # allow for index to be populated + assert route_layer.index.get_routes() == [ + ('Route 1', 'Hello', None, {}), + ('Route 2', 'Hi', None, {}), + ], "The routes in the index should match the local routes" + + # TEST REMOTE + pinecone_index = PineconeIndex(sync="remote") + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=pinecone_index) + + time.sleep(15) # allow for index to be populated + assert route_layer.index.get_routes() == [ + ('Route 1', 'Hello', None, {}), + ('Route 2', 'Hi', None, {}), + ], "The routes in the index should match the local routes" + + # TEST MERGE FORCE REMOTE + pinecone_index = PineconeIndex(sync="merge-force-remote") + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=pinecone_index) + + time.sleep(15) # allow for index to be populated + assert route_layer.index.get_routes() == [ + ('Route 1', 'Hello', None, {}), + ('Route 2', 'Hi', None, {}), + ], "The routes in the index should match the local routes" + + # TEST MERGE FORCE LOCAL + pinecone_index = PineconeIndex(sync="merge-force-local") + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=pinecone_index) + + time.sleep(15) # allow for index to be populated + assert route_layer.index.get_routes() == [ + ('Route 1', 'Hello', None, {'type': 'default'}), + ('Route 1', 'Hi', None, {'type': 'default'}), + ('Route 2', 'Bye', None, {}), + ('Route 2', 'Au revoir', None, {}), + ('Route 2', 'Goodbye', None, {}) + ], "The routes in the index should match the local routes" + + # TEST MERGE + pinecone_index = PineconeIndex(sync="merge") + route_layer = RouteLayer(encoder=openai_encoder, routes=routes_4, index=pinecone_index) + + time.sleep(15) # allow for index to be populated + assert route_layer.index.get_routes() == [ + ('Route 1', 'Hello', None, {'type': 'default'}), + ('Route 1', 'Hi', None, {'type': 'default'}), + ('Route 1', 'Goodbye', None, {'type': 'default'}), + ('Route 2', 'Bye', None, {}), + ('Route 2', 'Asparagus', None, {}), + ('Route 2', 'Au revoir', None, {}), + ('Route 2', 'Goodbye', None, {}), + ], "The routes in the index should match the local routes" def test_query_with_no_index(self, openai_encoder, index_cls): - route_layer = RouteLayer(encoder=openai_encoder, index=index_cls()) + route_layer = RouteLayer(encoder=openai_encoder) with pytest.raises(ValueError): assert route_layer(text="Anything").name is None @@ -322,6 +427,8 @@ def test_query_with_vector(self, openai_encoder, routes, index_cls): route_layer = RouteLayer( encoder=openai_encoder, routes=routes, index=index_cls() ) + if index_cls is PineconeIndex: + time.sleep(15) # allow for index to be populated vector = [0.1, 0.2, 0.3] query_result = route_layer(vector=vector).name assert query_result in ["Route 1", "Route 2"] @@ -372,12 +479,12 @@ def test_query_no_text_dynamic_route( def test_pass_threshold(self, openai_encoder, index_cls): route_layer = RouteLayer(encoder=openai_encoder, index=index_cls()) - assert not route_layer._pass_threshold([], 0.5) - assert route_layer._pass_threshold([0.6, 0.7], 0.5) + assert not route_layer._pass_threshold([], 0.3) + assert route_layer._pass_threshold([0.6, 0.7], 0.3) - def test_failover_score_threshold(self, base_encoder, index_cls): - route_layer = RouteLayer(encoder=base_encoder, index=index_cls()) - assert route_layer.score_threshold == 0.5 + def test_failover_score_threshold(self, openai_encoder, index_cls): + route_layer = RouteLayer(encoder=openai_encoder, index=index_cls()) + assert route_layer.score_threshold == 0.3 def test_json(self, openai_encoder, routes, index_cls): temp = tempfile.NamedTemporaryFile(suffix=".yaml", delete=False) @@ -391,6 +498,8 @@ def test_json(self, openai_encoder, routes, index_cls): route_layer.to_json(temp_path) assert os.path.exists(temp_path) route_layer_from_file = RouteLayer.from_json(temp_path) + if index_cls is PineconeIndex: + time.sleep(15) # allow for index to be populated assert ( route_layer_from_file.index is not None and route_layer_from_file._get_route_names() is not None @@ -410,6 +519,8 @@ def test_yaml(self, openai_encoder, routes, index_cls): route_layer.to_yaml(temp_path) assert os.path.exists(temp_path) route_layer_from_file = RouteLayer.from_yaml(temp_path) + if index_cls is PineconeIndex: + time.sleep(15) # allow for index to be populated assert ( route_layer_from_file.index is not None and route_layer_from_file._get_route_names() is not None @@ -530,9 +641,11 @@ def test_config(self, openai_encoder, routes, index_cls): ) # confirm route creation functions as expected layer_config = route_layer.to_config() - assert layer_config.routes == routes + assert layer_config.routes == route_layer.routes # now load from config and confirm it's the same route_layer_from_config = RouteLayer.from_config(layer_config, index_cls()) + if index_cls is PineconeIndex: + time.sleep(15) # allow for index to be populated assert ( route_layer_from_config._get_route_names() == route_layer._get_route_names() ) @@ -647,6 +760,8 @@ def test_retrieve_one_match(self, openai_encoder, routes_3, index_cls): encoder=openai_encoder, routes=routes_3, index=index_cls() ) text = "Hello" + if index_cls is PineconeIndex: + time.sleep(15) results = route_layer.retrieve_multiple_routes(text=text) assert len(results) == 1, f"Expected one result, and got {len(results)}" matched_routes = [result.name for result in results] @@ -659,6 +774,8 @@ def test_retrieve_with_text_for_multiple_matches( encoder=openai_encoder, routes=routes_2, index=index_cls() ) text = "Hello" + if index_cls is PineconeIndex: + time.sleep(15) results = route_layer.retrieve_multiple_routes(text=text) assert len(results) == 2, "Expected two results" matched_routes = [result.name for result in results] From 3e5a9bc278519180e88f016c18c2d95b2398746a Mon Sep 17 00:00:00 2001 From: Vits Date: Thu, 5 Sep 2024 12:12:30 +0200 Subject: [PATCH 2/9] Formatting and linting --- semantic_router/index/pinecone.py | 77 ++++++++++++++++++++++++------- semantic_router/layer.py | 6 ++- tests/unit/test_layer.py | 59 +++++++++++++---------- 3 files changed, 100 insertions(+), 42 deletions(-) diff --git a/semantic_router/index/pinecone.py b/semantic_router/index/pinecone.py index 3de221ba..9e5d0f2e 100644 --- a/semantic_router/index/pinecone.py +++ b/semantic_router/index/pinecone.py @@ -268,12 +268,12 @@ def _sync_index( 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_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", {}) @@ -295,7 +295,9 @@ def _sync_index( if local_utterances: layer_routes[route] = { "utterances": list(local_utterances), - "function_schemas": local_function_schemas if local_function_schemas else None, + "function_schemas": ( + local_function_schemas if local_function_schemas else None + ), "metadata": local_metadata, } @@ -303,7 +305,9 @@ def _sync_index( if remote_utterances: layer_routes[route] = { "utterances": list(remote_utterances), - "function_schemas": remote_function_schemas if remote_function_schemas else None, + "function_schemas": ( + remote_function_schemas if remote_function_schemas else None + ), "metadata": remote_metadata, } @@ -319,7 +323,9 @@ def _sync_index( if local_utterances: layer_routes[route] = { "utterances": list(local_utterances), - "function_schemas": local_function_schemas if local_function_schemas else None, + "function_schemas": ( + local_function_schemas if local_function_schemas else None + ), "metadata": local_metadata, } @@ -329,14 +335,22 @@ def _sync_index( if local_utterances: layer_routes[route] = { "utterances": list(local_utterances), - "function_schemas": local_function_schemas if local_function_schemas else None, + "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, + "function_schemas": ( + remote_function_schemas + if remote_function_schemas + else None + ), "metadata": remote_metadata, } @@ -353,14 +367,22 @@ def _sync_index( if local_utterances: layer_routes[route] = { "utterances": list(local_utterances), - "function_schemas": local_function_schemas if local_function_schemas else None, + "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, + "function_schemas": ( + remote_function_schemas + if remote_function_schemas + else None + ), "metadata": remote_metadata, } @@ -375,7 +397,9 @@ def _sync_index( } layer_routes[route] = { "utterances": list(remote_utterances.union(local_utterances)), - "function_schemas": merged_function_schemas if merged_function_schemas else None, + "function_schemas": ( + merged_function_schemas if merged_function_schemas else None + ), "metadata": merged_metadata, } @@ -389,17 +413,36 @@ def _sync_index( ]: for utterance in local_utterances: routes_to_add.append( - (route, utterance, local_function_schemas if local_function_schemas else None, local_metadata) + ( + 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) + ( + 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) + ( + route, + utterance, + local_function_schemas if local_function_schemas else None, + local_metadata, + ) ) return routes_to_add, routes_to_delete, layer_routes diff --git a/semantic_router/layer.py b/semantic_router/layer.py index f7d1395a..45a80bde 100644 --- a/semantic_router/layer.py +++ b/semantic_router/layer.py @@ -546,7 +546,11 @@ def _add_and_sync_routes(self, routes: List[Route]): Route( name=route, utterances=data.get("utterances", []), - function_schemas=[data.get("function_schemas", None)] if data.get("function_schemas") else None, + function_schemas=( + [data.get("function_schemas", None)] + if data.get("function_schemas") + else None + ), metadata=data.get("metadata", {}), ) for route, data in layer_routes_dict.items() diff --git a/tests/unit/test_layer.py b/tests/unit/test_layer.py index eaec568e..ff87e6f4 100644 --- a/tests/unit/test_layer.py +++ b/tests/unit/test_layer.py @@ -114,6 +114,7 @@ def routes_3(): Route(name="Route 2", utterances=["Asparagus"]), ] + @pytest.fixture def routes_4(): return [ @@ -168,7 +169,7 @@ def test_initialization(self, openai_encoder, routes, index_cls): ) if index_cls is PineconeIndex: time.sleep(15) # allow for index to be populated - + assert openai_encoder.score_threshold == 0.3 assert route_layer.score_threshold == 0.3 assert route_layer.top_k == 10 @@ -363,59 +364,69 @@ def test_sync_pinecone(self, openai_encoder, routes, routes_2, routes_4, index_c if index_cls is PineconeIndex: # TEST LOCAL pinecone_index = PineconeIndex(sync="local") - route_layer = RouteLayer(encoder=openai_encoder, routes=routes_2, index=pinecone_index) + route_layer = RouteLayer( + encoder=openai_encoder, routes=routes_2, index=pinecone_index + ) time.sleep(15) # allow for index to be populated assert route_layer.index.get_routes() == [ - ('Route 1', 'Hello', None, {}), - ('Route 2', 'Hi', None, {}), + ("Route 1", "Hello", None, {}), + ("Route 2", "Hi", None, {}), ], "The routes in the index should match the local routes" # TEST REMOTE pinecone_index = PineconeIndex(sync="remote") - route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=pinecone_index) + route_layer = RouteLayer( + encoder=openai_encoder, routes=routes, index=pinecone_index + ) time.sleep(15) # allow for index to be populated assert route_layer.index.get_routes() == [ - ('Route 1', 'Hello', None, {}), - ('Route 2', 'Hi', None, {}), + ("Route 1", "Hello", None, {}), + ("Route 2", "Hi", None, {}), ], "The routes in the index should match the local routes" # TEST MERGE FORCE REMOTE pinecone_index = PineconeIndex(sync="merge-force-remote") - route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=pinecone_index) + route_layer = RouteLayer( + encoder=openai_encoder, routes=routes, index=pinecone_index + ) time.sleep(15) # allow for index to be populated assert route_layer.index.get_routes() == [ - ('Route 1', 'Hello', None, {}), - ('Route 2', 'Hi', None, {}), + ("Route 1", "Hello", None, {}), + ("Route 2", "Hi", None, {}), ], "The routes in the index should match the local routes" # TEST MERGE FORCE LOCAL pinecone_index = PineconeIndex(sync="merge-force-local") - route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=pinecone_index) + route_layer = RouteLayer( + encoder=openai_encoder, routes=routes, index=pinecone_index + ) time.sleep(15) # allow for index to be populated assert route_layer.index.get_routes() == [ - ('Route 1', 'Hello', None, {'type': 'default'}), - ('Route 1', 'Hi', None, {'type': 'default'}), - ('Route 2', 'Bye', None, {}), - ('Route 2', 'Au revoir', None, {}), - ('Route 2', 'Goodbye', None, {}) + ("Route 1", "Hello", None, {"type": "default"}), + ("Route 1", "Hi", None, {"type": "default"}), + ("Route 2", "Bye", None, {}), + ("Route 2", "Au revoir", None, {}), + ("Route 2", "Goodbye", None, {}), ], "The routes in the index should match the local routes" # TEST MERGE pinecone_index = PineconeIndex(sync="merge") - route_layer = RouteLayer(encoder=openai_encoder, routes=routes_4, index=pinecone_index) + route_layer = RouteLayer( + encoder=openai_encoder, routes=routes_4, index=pinecone_index + ) time.sleep(15) # allow for index to be populated assert route_layer.index.get_routes() == [ - ('Route 1', 'Hello', None, {'type': 'default'}), - ('Route 1', 'Hi', None, {'type': 'default'}), - ('Route 1', 'Goodbye', None, {'type': 'default'}), - ('Route 2', 'Bye', None, {}), - ('Route 2', 'Asparagus', None, {}), - ('Route 2', 'Au revoir', None, {}), - ('Route 2', 'Goodbye', None, {}), + ("Route 1", "Hello", None, {"type": "default"}), + ("Route 1", "Hi", None, {"type": "default"}), + ("Route 1", "Goodbye", None, {"type": "default"}), + ("Route 2", "Bye", None, {}), + ("Route 2", "Asparagus", None, {}), + ("Route 2", "Au revoir", None, {}), + ("Route 2", "Goodbye", None, {}), ], "The routes in the index should match the local routes" def test_query_with_no_index(self, openai_encoder, index_cls): From 1c73d261d06cf270b4134b4ca8b9371c5abb8f84 Mon Sep 17 00:00:00 2001 From: James Briggs Date: Thu, 5 Sep 2024 12:51:15 +0200 Subject: [PATCH 3/9] fix: add pinecone sleep param --- tests/unit/test_layer.py | 52 +++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/tests/unit/test_layer.py b/tests/unit/test_layer.py index ff87e6f4..92d40843 100644 --- a/tests/unit/test_layer.py +++ b/tests/unit/test_layer.py @@ -15,6 +15,8 @@ from semantic_router.route import Route +PINECONE_SLEEP = 20 + def mock_encoder_call(utterances): # Define a mapping of utterances to return values mock_responses = { @@ -168,7 +170,7 @@ def test_initialization(self, openai_encoder, routes, index_cls): encoder=openai_encoder, routes=routes, top_k=10, index=index_cls() ) if index_cls is PineconeIndex: - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated assert openai_encoder.score_threshold == 0.3 assert route_layer.score_threshold == 0.3 @@ -213,12 +215,12 @@ def test_add_route(self, routes, openai_encoder, index_cls): # Initially, the routes list should be empty assert route_layer.routes == [] if index_cls is PineconeIndex: - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated # Add route1 and check route_layer.add(route=routes[0]) if index_cls is PineconeIndex: - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated assert route_layer.routes == [routes[0]] assert route_layer.index is not None assert len(route_layer.index.get_routes()) == 2 @@ -226,7 +228,7 @@ def test_add_route(self, routes, openai_encoder, index_cls): # Add route2 and check route_layer.add(route=routes[1]) if index_cls is PineconeIndex: - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated assert route_layer.routes == [routes[0], routes[1]] assert len(route_layer.index.get_routes()) == 5 @@ -247,7 +249,7 @@ def test_delete_route(self, openai_encoder, routes, index_cls): route_to_delete = routes[0].name route_layer.delete(route_to_delete) if index_cls is PineconeIndex: - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated # Ensure the route is no longer in the route layer assert ( route_to_delete not in route_layer.list_route_names() @@ -263,7 +265,7 @@ def test_remove_route_not_found(self, openai_encoder, routes, index_cls): encoder=openai_encoder, routes=routes, index=index_cls() ) if index_cls is PineconeIndex: - time.sleep(15) + time.sleep(PINECONE_SLEEP) # Attempt to remove a route that does not exist non_existent_route = "non-existent-route" route_layer.delete(non_existent_route) @@ -272,10 +274,10 @@ def test_remove_route_not_found(self, openai_encoder, routes, index_cls): def test_add_multiple_routes(self, openai_encoder, routes, index_cls): route_layer = RouteLayer(encoder=openai_encoder, index=index_cls()) if index_cls is PineconeIndex: - time.sleep(15) + time.sleep(PINECONE_SLEEP) route_layer._add_routes(routes=routes) if index_cls is PineconeIndex: - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated assert route_layer.index is not None assert len(route_layer.index.get_routes()) == 5 @@ -285,13 +287,13 @@ def test_query_and_classification(self, openai_encoder, routes, index_cls): route_layer = RouteLayer( encoder=openai_encoder, routes=routes, index=pineconeindex ) - time.sleep(15) + time.sleep(PINECONE_SLEEP) else: route_layer = RouteLayer( encoder=openai_encoder, routes=routes, index=index_cls() ) if index_cls is PineconeIndex: - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated query_result = route_layer(text="Hello").name assert query_result in ["Route 1", "Route 2"] @@ -301,14 +303,14 @@ def test_query_filter(self, openai_encoder, routes, index_cls): route_layer = RouteLayer( encoder=openai_encoder, routes=routes, index=pineconeindex ) - time.sleep(15) + time.sleep(PINECONE_SLEEP) else: route_layer = RouteLayer( encoder=openai_encoder, routes=routes, index=index_cls() ) if index_cls is PineconeIndex: - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated query_result = route_layer(text="Hello", route_filter=["Route 1"]).name try: @@ -327,7 +329,7 @@ def test_query_filter_pinecone(self, openai_encoder, routes, index_cls): route_layer = RouteLayer( encoder=openai_encoder, routes=routes, index=pineconeindex ) - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated query_result = route_layer(text="Hello", route_filter=["Route 1"]).name try: @@ -346,7 +348,7 @@ def test_namespace_pinecone_index(self, openai_encoder, routes, index_cls): route_layer = RouteLayer( encoder=openai_encoder, routes=routes, index=pineconeindex ) - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated query_result = route_layer(text="Hello", route_filter=["Route 1"]).name try: @@ -367,7 +369,7 @@ def test_sync_pinecone(self, openai_encoder, routes, routes_2, routes_4, index_c route_layer = RouteLayer( encoder=openai_encoder, routes=routes_2, index=pinecone_index ) - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated assert route_layer.index.get_routes() == [ ("Route 1", "Hello", None, {}), ("Route 2", "Hi", None, {}), @@ -379,7 +381,7 @@ def test_sync_pinecone(self, openai_encoder, routes, routes_2, routes_4, index_c encoder=openai_encoder, routes=routes, index=pinecone_index ) - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated assert route_layer.index.get_routes() == [ ("Route 1", "Hello", None, {}), ("Route 2", "Hi", None, {}), @@ -391,7 +393,7 @@ def test_sync_pinecone(self, openai_encoder, routes, routes_2, routes_4, index_c encoder=openai_encoder, routes=routes, index=pinecone_index ) - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated assert route_layer.index.get_routes() == [ ("Route 1", "Hello", None, {}), ("Route 2", "Hi", None, {}), @@ -403,7 +405,7 @@ def test_sync_pinecone(self, openai_encoder, routes, routes_2, routes_4, index_c encoder=openai_encoder, routes=routes, index=pinecone_index ) - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated assert route_layer.index.get_routes() == [ ("Route 1", "Hello", None, {"type": "default"}), ("Route 1", "Hi", None, {"type": "default"}), @@ -418,7 +420,7 @@ def test_sync_pinecone(self, openai_encoder, routes, routes_2, routes_4, index_c encoder=openai_encoder, routes=routes_4, index=pinecone_index ) - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated assert route_layer.index.get_routes() == [ ("Route 1", "Hello", None, {"type": "default"}), ("Route 1", "Hi", None, {"type": "default"}), @@ -439,7 +441,7 @@ def test_query_with_vector(self, openai_encoder, routes, index_cls): encoder=openai_encoder, routes=routes, index=index_cls() ) if index_cls is PineconeIndex: - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated vector = [0.1, 0.2, 0.3] query_result = route_layer(vector=vector).name assert query_result in ["Route 1", "Route 2"] @@ -510,7 +512,7 @@ def test_json(self, openai_encoder, routes, index_cls): assert os.path.exists(temp_path) route_layer_from_file = RouteLayer.from_json(temp_path) if index_cls is PineconeIndex: - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated assert ( route_layer_from_file.index is not None and route_layer_from_file._get_route_names() is not None @@ -531,7 +533,7 @@ def test_yaml(self, openai_encoder, routes, index_cls): assert os.path.exists(temp_path) route_layer_from_file = RouteLayer.from_yaml(temp_path) if index_cls is PineconeIndex: - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated assert ( route_layer_from_file.index is not None and route_layer_from_file._get_route_names() is not None @@ -656,7 +658,7 @@ def test_config(self, openai_encoder, routes, index_cls): # now load from config and confirm it's the same route_layer_from_config = RouteLayer.from_config(layer_config, index_cls()) if index_cls is PineconeIndex: - time.sleep(15) # allow for index to be populated + time.sleep(PINECONE_SLEEP) # allow for index to be populated assert ( route_layer_from_config._get_route_names() == route_layer._get_route_names() ) @@ -772,7 +774,7 @@ def test_retrieve_one_match(self, openai_encoder, routes_3, index_cls): ) text = "Hello" if index_cls is PineconeIndex: - time.sleep(15) + time.sleep(PINECONE_SLEEP) results = route_layer.retrieve_multiple_routes(text=text) assert len(results) == 1, f"Expected one result, and got {len(results)}" matched_routes = [result.name for result in results] @@ -786,7 +788,7 @@ def test_retrieve_with_text_for_multiple_matches( ) text = "Hello" if index_cls is PineconeIndex: - time.sleep(15) + time.sleep(PINECONE_SLEEP) results = route_layer.retrieve_multiple_routes(text=text) assert len(results) == 2, "Expected two results" matched_routes = [result.name for result in results] From 40a1b64b73b77bd2cf15b736e1d6e5aff8943fdd Mon Sep 17 00:00:00 2001 From: James Briggs Date: Thu, 5 Sep 2024 13:37:09 +0200 Subject: [PATCH 4/9] fix: adjust tests to use same index per version and run --- tests/unit/test_layer.py | 169 ++++++++++++++++++++++++--------------- 1 file changed, 105 insertions(+), 64 deletions(-) diff --git a/tests/unit/test_layer.py b/tests/unit/test_layer.py index 92d40843..9fdc4459 100644 --- a/tests/unit/test_layer.py +++ b/tests/unit/test_layer.py @@ -2,10 +2,10 @@ import os import tempfile from unittest.mock import mock_open, patch - +from datetime import datetime import pytest import time - +from typing import Optional from semantic_router.encoders import BaseEncoder, CohereEncoder, OpenAIEncoder from semantic_router.index.local import LocalIndex from semantic_router.index.pinecone import PineconeIndex @@ -13,9 +13,10 @@ from semantic_router.layer import LayerConfig, RouteLayer from semantic_router.llms.base import BaseLLM from semantic_router.route import Route +from platform import python_version -PINECONE_SLEEP = 20 +PINECONE_SLEEP = 5 def mock_encoder_call(utterances): # Define a mapping of utterances to return values @@ -29,6 +30,27 @@ def mock_encoder_call(utterances): } return [mock_responses.get(u, [0.0, 0.0, 0.0]) for u in utterances] +TEST_ID = f"{python_version().replace('.', '')}-{datetime.now().strftime('%Y%m%d%H%M%S')}" + +def init_index( + index_cls, + dimensions: Optional[int] = None, + namespace: Optional[str] = "", + sync: str = "local" +): + """We use this function to initialize indexes with different names to avoid + issues during testing. + """ + if index_cls is PineconeIndex: + index = index_cls( + index_name=TEST_ID, + dimensions=dimensions, + namespace=namespace, + sync=sync + ) + else: + index = index_cls() + return index def layer_json(): return """{ @@ -166,8 +188,9 @@ def get_test_indexes(): @pytest.mark.parametrize("index_cls", get_test_indexes()) 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_cls() + encoder=openai_encoder, routes=routes, top_k=10, index=index ) if index_cls is PineconeIndex: time.sleep(PINECONE_SLEEP) # allow for index to be populated @@ -185,10 +208,11 @@ def test_initialization(self, openai_encoder, routes, index_cls): def test_initialization_different_encoders( self, cohere_encoder, openai_encoder, index_cls ): - route_layer_cohere = RouteLayer(encoder=cohere_encoder, index=index_cls()) + index = init_index(index_cls) + route_layer_cohere = RouteLayer(encoder=cohere_encoder, index=index) assert cohere_encoder.score_threshold == 0.3 assert route_layer_cohere.score_threshold == 0.3 - route_layer_openai = RouteLayer(encoder=openai_encoder, index=index_cls()) + route_layer_openai = RouteLayer(encoder=openai_encoder, index=index) assert route_layer_openai.score_threshold == 0.3 def test_initialization_no_encoder(self, openai_encoder, index_cls): @@ -199,18 +223,20 @@ def test_initialization_no_encoder(self, openai_encoder, index_cls): def test_initialization_dynamic_route( self, cohere_encoder, openai_encoder, dynamic_routes, index_cls ): + index = init_index(index_cls) route_layer_cohere = RouteLayer( - encoder=cohere_encoder, routes=dynamic_routes, index=index_cls() + encoder=cohere_encoder, routes=dynamic_routes, index=index ) assert route_layer_cohere.score_threshold == 0.3 route_layer_openai = RouteLayer( - encoder=openai_encoder, routes=dynamic_routes, index=index_cls() + encoder=openai_encoder, routes=dynamic_routes, index=index ) assert openai_encoder.score_threshold == 0.3 assert route_layer_openai.score_threshold == 0.3 def test_add_route(self, routes, openai_encoder, index_cls): - route_layer = RouteLayer(encoder=openai_encoder, index=index_cls()) + index = init_index(index_cls) + route_layer = RouteLayer(encoder=openai_encoder, index=index) # Initially, the routes list should be empty assert route_layer.routes == [] @@ -233,8 +259,9 @@ def test_add_route(self, routes, openai_encoder, index_cls): assert len(route_layer.index.get_routes()) == 5 def test_list_route_names(self, openai_encoder, routes, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) route_names = route_layer.list_route_names() assert set(route_names) == { @@ -242,8 +269,9 @@ def test_list_route_names(self, openai_encoder, routes, index_cls): }, "The list of route names should match the names of the routes added." def test_delete_route(self, openai_encoder, routes, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) # Delete a route by name route_to_delete = routes[0].name @@ -261,8 +289,9 @@ def test_delete_route(self, openai_encoder, routes, index_cls): ), "The route's utterances should be deleted from the index." def test_remove_route_not_found(self, openai_encoder, routes, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) if index_cls is PineconeIndex: time.sleep(PINECONE_SLEEP) @@ -272,7 +301,8 @@ def test_remove_route_not_found(self, openai_encoder, routes, index_cls): # we should see warning in logs only (ie no errors) def test_add_multiple_routes(self, openai_encoder, routes, index_cls): - route_layer = RouteLayer(encoder=openai_encoder, index=index_cls()) + index = init_index(index_cls) + route_layer = RouteLayer(encoder=openai_encoder, index=index) if index_cls is PineconeIndex: time.sleep(PINECONE_SLEEP) route_layer._add_routes(routes=routes) @@ -282,32 +312,20 @@ def test_add_multiple_routes(self, openai_encoder, routes, index_cls): assert len(route_layer.index.get_routes()) == 5 def test_query_and_classification(self, openai_encoder, routes, index_cls): - if index_cls is PineconeIndex: - pineconeindex = PineconeIndex(dimensions=3) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=pineconeindex - ) - time.sleep(PINECONE_SLEEP) - else: - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() - ) + index = init_index(index_cls, dimensions=3) + route_layer = RouteLayer( + encoder=openai_encoder, routes=routes, index=index + ) if index_cls is PineconeIndex: time.sleep(PINECONE_SLEEP) # allow for index to be populated query_result = route_layer(text="Hello").name assert query_result in ["Route 1", "Route 2"] def test_query_filter(self, openai_encoder, routes, index_cls): - if index_cls is PineconeIndex: - pineconeindex = PineconeIndex(dimensions=3) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=pineconeindex - ) - time.sleep(PINECONE_SLEEP) - else: - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() - ) + index = init_index(index_cls, dimensions=3) + route_layer = RouteLayer( + encoder=openai_encoder, routes=routes, index=index + ) if index_cls is PineconeIndex: time.sleep(PINECONE_SLEEP) # allow for index to be populated @@ -325,7 +343,7 @@ def test_query_filter(self, openai_encoder, routes, index_cls): ) def test_query_filter_pinecone(self, openai_encoder, routes, index_cls): if index_cls is PineconeIndex: - pineconeindex = PineconeIndex(dimensions=3) + pineconeindex = init_index(index_cls, dimensions=3) route_layer = RouteLayer( encoder=openai_encoder, routes=routes, index=pineconeindex ) @@ -344,7 +362,7 @@ def test_query_filter_pinecone(self, openai_encoder, routes, index_cls): ) def test_namespace_pinecone_index(self, openai_encoder, routes, index_cls): if index_cls is PineconeIndex: - pineconeindex = PineconeIndex(namespace="test") + pineconeindex = init_index(index_cls, namespace="test") route_layer = RouteLayer( encoder=openai_encoder, routes=routes, index=pineconeindex ) @@ -365,7 +383,7 @@ def test_namespace_pinecone_index(self, openai_encoder, routes, index_cls): def test_sync_pinecone(self, openai_encoder, routes, routes_2, routes_4, index_cls): if index_cls is PineconeIndex: # TEST LOCAL - pinecone_index = PineconeIndex(sync="local") + pinecone_index = init_index(index_cls, sync="local") route_layer = RouteLayer( encoder=openai_encoder, routes=routes_2, index=pinecone_index ) @@ -376,7 +394,7 @@ def test_sync_pinecone(self, openai_encoder, routes, routes_2, routes_4, index_c ], "The routes in the index should match the local routes" # TEST REMOTE - pinecone_index = PineconeIndex(sync="remote") + pinecone_index = init_index(index_cls, sync="remote") route_layer = RouteLayer( encoder=openai_encoder, routes=routes, index=pinecone_index ) @@ -388,7 +406,7 @@ def test_sync_pinecone(self, openai_encoder, routes, routes_2, routes_4, index_c ], "The routes in the index should match the local routes" # TEST MERGE FORCE REMOTE - pinecone_index = PineconeIndex(sync="merge-force-remote") + pinecone_index = init_index(index_cls, sync="merge-force-remote") route_layer = RouteLayer( encoder=openai_encoder, routes=routes, index=pinecone_index ) @@ -400,7 +418,7 @@ def test_sync_pinecone(self, openai_encoder, routes, routes_2, routes_4, index_c ], "The routes in the index should match the local routes" # TEST MERGE FORCE LOCAL - pinecone_index = PineconeIndex(sync="merge-force-local") + pinecone_index = init_index(index_cls, sync="merge-force-local") route_layer = RouteLayer( encoder=openai_encoder, routes=routes, index=pinecone_index ) @@ -415,7 +433,7 @@ def test_sync_pinecone(self, openai_encoder, routes, routes_2, routes_4, index_c ], "The routes in the index should match the local routes" # TEST MERGE - pinecone_index = PineconeIndex(sync="merge") + pinecone_index = init_index(index_cls, sync="merge") route_layer = RouteLayer( encoder=openai_encoder, routes=routes_4, index=pinecone_index ) @@ -437,8 +455,9 @@ def test_query_with_no_index(self, openai_encoder, index_cls): assert route_layer(text="Anything").name is None def test_query_with_vector(self, openai_encoder, routes, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) if index_cls is PineconeIndex: time.sleep(PINECONE_SLEEP) # allow for index to be populated @@ -447,15 +466,17 @@ def test_query_with_vector(self, openai_encoder, routes, index_cls): assert query_result in ["Route 1", "Route 2"] def test_query_with_no_text_or_vector(self, openai_encoder, routes, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) with pytest.raises(ValueError): route_layer() def test_semantic_classify(self, openai_encoder, routes, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) classification, score = route_layer._semantic_classify( [ @@ -467,8 +488,9 @@ def test_semantic_classify(self, openai_encoder, routes, index_cls): assert score == [0.9] def test_semantic_classify_multiple_routes(self, openai_encoder, routes, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) classification, score = route_layer._semantic_classify( [ @@ -483,20 +505,23 @@ def test_semantic_classify_multiple_routes(self, openai_encoder, routes, index_c def test_query_no_text_dynamic_route( self, openai_encoder, dynamic_routes, index_cls ): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=dynamic_routes, index=index_cls() + encoder=openai_encoder, routes=dynamic_routes, index=index ) vector = [0.1, 0.2, 0.3] with pytest.raises(ValueError): route_layer(vector=vector) def test_pass_threshold(self, openai_encoder, index_cls): - route_layer = RouteLayer(encoder=openai_encoder, index=index_cls()) + index = init_index(index_cls) + route_layer = RouteLayer(encoder=openai_encoder, index=index) assert not route_layer._pass_threshold([], 0.3) assert route_layer._pass_threshold([0.6, 0.7], 0.3) def test_failover_score_threshold(self, openai_encoder, index_cls): - route_layer = RouteLayer(encoder=openai_encoder, index=index_cls()) + index = init_index(index_cls) + route_layer = RouteLayer(encoder=openai_encoder, index=index) assert route_layer.score_threshold == 0.3 def test_json(self, openai_encoder, routes, index_cls): @@ -505,8 +530,9 @@ def test_json(self, openai_encoder, routes, index_cls): temp_path = temp.name # Save the temporary file's path temp.close() # Close the file to ensure it can be opened again on Windows os.environ["OPENAI_API_KEY"] = "test_api_key" + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) route_layer.to_json(temp_path) assert os.path.exists(temp_path) @@ -526,8 +552,9 @@ def test_yaml(self, openai_encoder, routes, index_cls): temp_path = temp.name # Save the temporary file's path temp.close() # Close the file to ensure it can be opened again on Windows os.environ["OPENAI_API_KEY"] = "test_api_key" + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) route_layer.to_yaml(temp_path) assert os.path.exists(temp_path) @@ -649,14 +676,15 @@ def test_from_file_with_llm(self, tmp_path, index_cls): def test_config(self, openai_encoder, routes, index_cls): os.environ["OPENAI_API_KEY"] = "test_api_key" + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) # confirm route creation functions as expected layer_config = route_layer.to_config() assert layer_config.routes == route_layer.routes # now load from config and confirm it's the same - route_layer_from_config = RouteLayer.from_config(layer_config, index_cls()) + route_layer_from_config = RouteLayer.from_config(layer_config, index) if index_cls is PineconeIndex: time.sleep(PINECONE_SLEEP) # allow for index to be populated assert ( @@ -665,16 +693,18 @@ def test_config(self, openai_encoder, routes, index_cls): assert route_layer_from_config.score_threshold == route_layer.score_threshold def test_get_thresholds(self, openai_encoder, routes, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) assert route_layer.get_thresholds() == {"Route 1": 0.3, "Route 2": 0.3} def test_with_multiple_routes_passing_threshold( self, openai_encoder, routes, index_cls ): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) route_layer.score_threshold = 0.5 # Set the score_threshold if needed # Assuming route_layer is already set up with routes "Route 1" and "Route 2" @@ -692,8 +722,9 @@ def test_with_multiple_routes_passing_threshold( ), "Should classify and return routes above their thresholds" def test_with_no_routes_passing_threshold(self, openai_encoder, routes, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) route_layer.score_threshold = 0.5 # Override _pass_threshold to always return False for this test @@ -709,8 +740,9 @@ def test_with_no_routes_passing_threshold(self, openai_encoder, routes, index_cl ), "Should return an empty list when no routes pass their thresholds" def test_with_no_query_results(self, openai_encoder, routes, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) route_layer.score_threshold = 0.5 query_results = [] @@ -721,8 +753,9 @@ def test_with_no_query_results(self, openai_encoder, routes, index_cls): ), "Should return an empty list when there are no query results" def test_with_unrecognized_route(self, openai_encoder, routes, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) route_layer.score_threshold = 0.5 # Test with a route name that does not exist in the route_layer's routes @@ -732,8 +765,9 @@ def test_with_unrecognized_route(self, openai_encoder, routes, index_cls): assert results == expected, "Should ignore and not return unrecognized routes" def test_retrieve_with_text(self, openai_encoder, routes, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) text = "Hello" results = route_layer.retrieve_multiple_routes(text=text) @@ -743,8 +777,9 @@ def test_retrieve_with_text(self, openai_encoder, routes, index_cls): ), "Expected the result to be either 'Route 1' or 'Route 2'" def test_retrieve_with_vector(self, openai_encoder, routes, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) vector = [0.1, 0.2, 0.3] results = route_layer.retrieve_multiple_routes(vector=vector) @@ -754,23 +789,26 @@ def test_retrieve_with_vector(self, openai_encoder, routes, index_cls): ), "Expected the result to be either 'Route 1' or 'Route 2'" def test_retrieve_without_text_or_vector(self, openai_encoder, routes, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) with pytest.raises(ValueError, match="Either text or vector must be provided"): route_layer.retrieve_multiple_routes() def test_retrieve_no_matches(self, openai_encoder, routes, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) text = "Asparagus" results = route_layer.retrieve_multiple_routes(text=text) assert len(results) == 0, f"Expected no results, but got {len(results)}" def test_retrieve_one_match(self, openai_encoder, routes_3, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes_3, index=index_cls() + encoder=openai_encoder, routes=routes_3, index=index ) text = "Hello" if index_cls is PineconeIndex: @@ -783,8 +821,9 @@ def test_retrieve_one_match(self, openai_encoder, routes_3, index_cls): def test_retrieve_with_text_for_multiple_matches( self, openai_encoder, routes_2, index_cls ): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes_2, index=index_cls() + encoder=openai_encoder, routes=routes_2, index=index ) text = "Hello" if index_cls is PineconeIndex: @@ -798,8 +837,9 @@ def test_retrieve_with_text_for_multiple_matches( def test_set_aggregation_method_with_unsupported_value( self, openai_encoder, routes, index_cls ): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) unsupported_aggregation = "unsupported_aggregation_method" with pytest.raises( @@ -809,8 +849,9 @@ def test_set_aggregation_method_with_unsupported_value( route_layer._set_aggregation_method(unsupported_aggregation) def test_refresh_routes_not_implemented(self, openai_encoder, routes, index_cls): + index = init_index(index_cls) route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index_cls() + encoder=openai_encoder, routes=routes, index=index ) with pytest.raises( NotImplementedError, match="This method has not yet been implemented." From 6b440eca8aeb0f670683b99729d7780e8a8148aa Mon Sep 17 00:00:00 2001 From: James Briggs Date: Thu, 5 Sep 2024 13:48:21 +0200 Subject: [PATCH 5/9] fix: increase pinecone sleep --- tests/unit/test_layer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_layer.py b/tests/unit/test_layer.py index 9fdc4459..fc0a4c45 100644 --- a/tests/unit/test_layer.py +++ b/tests/unit/test_layer.py @@ -16,7 +16,7 @@ from platform import python_version -PINECONE_SLEEP = 5 +PINECONE_SLEEP = 10 def mock_encoder_call(utterances): # Define a mapping of utterances to return values From dcf8d52ceb5c791e6c46458effde39631b37ab79 Mon Sep 17 00:00:00 2001 From: James Briggs Date: Thu, 5 Sep 2024 14:01:39 +0200 Subject: [PATCH 6/9] fix: increase pinecone sleep --- tests/unit/test_layer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_layer.py b/tests/unit/test_layer.py index fc0a4c45..920796bd 100644 --- a/tests/unit/test_layer.py +++ b/tests/unit/test_layer.py @@ -16,7 +16,7 @@ from platform import python_version -PINECONE_SLEEP = 10 +PINECONE_SLEEP = 20 def mock_encoder_call(utterances): # Define a mapping of utterances to return values From c6262b7a1a87de854553edee6fb951ee5ce24e2f Mon Sep 17 00:00:00 2001 From: James Briggs Date: Thu, 19 Sep 2024 10:24:13 +0200 Subject: [PATCH 7/9] chore: lint --- tests/unit/test_layer.py | 115 ++++++++++++--------------------------- 1 file changed, 34 insertions(+), 81 deletions(-) diff --git a/tests/unit/test_layer.py b/tests/unit/test_layer.py index 920796bd..72778326 100644 --- a/tests/unit/test_layer.py +++ b/tests/unit/test_layer.py @@ -18,6 +18,7 @@ PINECONE_SLEEP = 20 + def mock_encoder_call(utterances): # Define a mapping of utterances to return values mock_responses = { @@ -30,28 +31,30 @@ def mock_encoder_call(utterances): } return [mock_responses.get(u, [0.0, 0.0, 0.0]) for u in utterances] -TEST_ID = f"{python_version().replace('.', '')}-{datetime.now().strftime('%Y%m%d%H%M%S')}" + +TEST_ID = ( + f"{python_version().replace('.', '')}-{datetime.now().strftime('%Y%m%d%H%M%S')}" +) + def init_index( index_cls, dimensions: Optional[int] = None, namespace: Optional[str] = "", - sync: str = "local" + sync: str = "local", ): """We use this function to initialize indexes with different names to avoid issues during testing. """ if index_cls is PineconeIndex: index = index_cls( - index_name=TEST_ID, - dimensions=dimensions, - namespace=namespace, - sync=sync + index_name=TEST_ID, dimensions=dimensions, namespace=namespace, sync=sync ) else: index = index_cls() return index + def layer_json(): return """{ "encoder_type": "cohere", @@ -260,9 +263,7 @@ def test_add_route(self, routes, openai_encoder, index_cls): def test_list_route_names(self, openai_encoder, routes, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) route_names = route_layer.list_route_names() assert set(route_names) == { route.name for route in routes @@ -270,9 +271,7 @@ def test_list_route_names(self, openai_encoder, routes, index_cls): def test_delete_route(self, openai_encoder, routes, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) # Delete a route by name route_to_delete = routes[0].name route_layer.delete(route_to_delete) @@ -290,9 +289,7 @@ def test_delete_route(self, openai_encoder, routes, index_cls): def test_remove_route_not_found(self, openai_encoder, routes, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) if index_cls is PineconeIndex: time.sleep(PINECONE_SLEEP) # Attempt to remove a route that does not exist @@ -313,9 +310,7 @@ def test_add_multiple_routes(self, openai_encoder, routes, index_cls): def test_query_and_classification(self, openai_encoder, routes, index_cls): index = init_index(index_cls, dimensions=3) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) if index_cls is PineconeIndex: time.sleep(PINECONE_SLEEP) # allow for index to be populated query_result = route_layer(text="Hello").name @@ -323,9 +318,7 @@ def test_query_and_classification(self, openai_encoder, routes, index_cls): def test_query_filter(self, openai_encoder, routes, index_cls): index = init_index(index_cls, dimensions=3) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) if index_cls is PineconeIndex: time.sleep(PINECONE_SLEEP) # allow for index to be populated @@ -456,9 +449,7 @@ def test_query_with_no_index(self, openai_encoder, index_cls): def test_query_with_vector(self, openai_encoder, routes, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) if index_cls is PineconeIndex: time.sleep(PINECONE_SLEEP) # allow for index to be populated vector = [0.1, 0.2, 0.3] @@ -467,17 +458,13 @@ def test_query_with_vector(self, openai_encoder, routes, index_cls): def test_query_with_no_text_or_vector(self, openai_encoder, routes, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) with pytest.raises(ValueError): route_layer() def test_semantic_classify(self, openai_encoder, routes, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) classification, score = route_layer._semantic_classify( [ {"route": "Route 1", "score": 0.9}, @@ -489,9 +476,7 @@ def test_semantic_classify(self, openai_encoder, routes, index_cls): def test_semantic_classify_multiple_routes(self, openai_encoder, routes, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) classification, score = route_layer._semantic_classify( [ {"route": "Route 1", "score": 0.9}, @@ -531,9 +516,7 @@ def test_json(self, openai_encoder, routes, index_cls): temp.close() # Close the file to ensure it can be opened again on Windows os.environ["OPENAI_API_KEY"] = "test_api_key" index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) route_layer.to_json(temp_path) assert os.path.exists(temp_path) route_layer_from_file = RouteLayer.from_json(temp_path) @@ -553,9 +536,7 @@ def test_yaml(self, openai_encoder, routes, index_cls): temp.close() # Close the file to ensure it can be opened again on Windows os.environ["OPENAI_API_KEY"] = "test_api_key" index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) route_layer.to_yaml(temp_path) assert os.path.exists(temp_path) route_layer_from_file = RouteLayer.from_yaml(temp_path) @@ -677,9 +658,7 @@ def test_from_file_with_llm(self, tmp_path, index_cls): def test_config(self, openai_encoder, routes, index_cls): os.environ["OPENAI_API_KEY"] = "test_api_key" index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) # confirm route creation functions as expected layer_config = route_layer.to_config() assert layer_config.routes == route_layer.routes @@ -694,18 +673,14 @@ def test_config(self, openai_encoder, routes, index_cls): def test_get_thresholds(self, openai_encoder, routes, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) assert route_layer.get_thresholds() == {"Route 1": 0.3, "Route 2": 0.3} def test_with_multiple_routes_passing_threshold( self, openai_encoder, routes, index_cls ): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) route_layer.score_threshold = 0.5 # Set the score_threshold if needed # Assuming route_layer is already set up with routes "Route 1" and "Route 2" query_results = [ @@ -723,9 +698,7 @@ def test_with_multiple_routes_passing_threshold( def test_with_no_routes_passing_threshold(self, openai_encoder, routes, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) route_layer.score_threshold = 0.5 # Override _pass_threshold to always return False for this test route_layer._pass_threshold = lambda scores, threshold: False @@ -741,9 +714,7 @@ def test_with_no_routes_passing_threshold(self, openai_encoder, routes, index_cl def test_with_no_query_results(self, openai_encoder, routes, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) route_layer.score_threshold = 0.5 query_results = [] expected = [] @@ -754,9 +725,7 @@ def test_with_no_query_results(self, openai_encoder, routes, index_cls): def test_with_unrecognized_route(self, openai_encoder, routes, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) route_layer.score_threshold = 0.5 # Test with a route name that does not exist in the route_layer's routes query_results = [{"route": "UnrecognizedRoute", "score": 0.9}] @@ -766,9 +735,7 @@ def test_with_unrecognized_route(self, openai_encoder, routes, index_cls): def test_retrieve_with_text(self, openai_encoder, routes, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) text = "Hello" results = route_layer.retrieve_multiple_routes(text=text) assert len(results) >= 1, "Expected at least one result" @@ -778,9 +745,7 @@ def test_retrieve_with_text(self, openai_encoder, routes, index_cls): def test_retrieve_with_vector(self, openai_encoder, routes, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) vector = [0.1, 0.2, 0.3] results = route_layer.retrieve_multiple_routes(vector=vector) assert len(results) >= 1, "Expected at least one result" @@ -790,26 +755,20 @@ def test_retrieve_with_vector(self, openai_encoder, routes, index_cls): def test_retrieve_without_text_or_vector(self, openai_encoder, routes, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) with pytest.raises(ValueError, match="Either text or vector must be provided"): route_layer.retrieve_multiple_routes() def test_retrieve_no_matches(self, openai_encoder, routes, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) text = "Asparagus" results = route_layer.retrieve_multiple_routes(text=text) assert len(results) == 0, f"Expected no results, but got {len(results)}" def test_retrieve_one_match(self, openai_encoder, routes_3, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes_3, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes_3, index=index) text = "Hello" if index_cls is PineconeIndex: time.sleep(PINECONE_SLEEP) @@ -822,9 +781,7 @@ def test_retrieve_with_text_for_multiple_matches( self, openai_encoder, routes_2, index_cls ): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes_2, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes_2, index=index) text = "Hello" if index_cls is PineconeIndex: time.sleep(PINECONE_SLEEP) @@ -838,9 +795,7 @@ def test_set_aggregation_method_with_unsupported_value( self, openai_encoder, routes, index_cls ): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) unsupported_aggregation = "unsupported_aggregation_method" with pytest.raises( ValueError, @@ -850,9 +805,7 @@ def test_set_aggregation_method_with_unsupported_value( def test_refresh_routes_not_implemented(self, openai_encoder, routes, index_cls): index = init_index(index_cls) - route_layer = RouteLayer( - encoder=openai_encoder, routes=routes, index=index - ) + route_layer = RouteLayer(encoder=openai_encoder, routes=routes, index=index) with pytest.raises( NotImplementedError, match="This method has not yet been implemented." ): From d9d25b524ffb266de4abc1811b97f3d5f073a7db Mon Sep 17 00:00:00 2001 From: Vits Date: Thu, 19 Sep 2024 21:19:27 +0200 Subject: [PATCH 8/9] Test --- tests/unit/test_layer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_layer.py b/tests/unit/test_layer.py index 72778326..6f29e036 100644 --- a/tests/unit/test_layer.py +++ b/tests/unit/test_layer.py @@ -16,7 +16,7 @@ from platform import python_version -PINECONE_SLEEP = 20 +PINECONE_SLEEP = 15 def mock_encoder_call(utterances): @@ -50,6 +50,7 @@ def init_index( index = index_cls( index_name=TEST_ID, dimensions=dimensions, namespace=namespace, sync=sync ) + time.sleep(PINECONE_SLEEP) else: index = index_cls() return index From 0196644b268e683b697edf9e2cdfbf3aa64fa5c5 Mon Sep 17 00:00:00 2001 From: Vits Date: Thu, 19 Sep 2024 22:26:29 +0200 Subject: [PATCH 9/9] Fixed broken tests --- Makefile | 2 +- tests/unit/test_layer.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 3d76b7b8..8283b96d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ lint lint_diff: poetry run mypy $(PYTHON_FILES) test: - poetry run pytest -vv -n 20 --cov=semantic_router --cov-report=term-missing --cov-report=xml + poetry run pytest -vv --cov=semantic_router --cov-report=term-missing --cov-report=xml test_functional: poetry run pytest -vv -n 20 tests/functional diff --git a/tests/unit/test_layer.py b/tests/unit/test_layer.py index 6f29e036..12773995 100644 --- a/tests/unit/test_layer.py +++ b/tests/unit/test_layer.py @@ -16,7 +16,7 @@ from platform import python_version -PINECONE_SLEEP = 15 +PINECONE_SLEEP = 12 def mock_encoder_call(utterances): @@ -50,7 +50,6 @@ def init_index( index = index_cls( index_name=TEST_ID, dimensions=dimensions, namespace=namespace, sync=sync ) - time.sleep(PINECONE_SLEEP) else: index = index_cls() return index