semantic-router 0.1.0.dev9__tar.gz → 0.1.0.dev10__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/PKG-INFO +2 -2
  2. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/pyproject.toml +2 -2
  3. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/__init__.py +1 -1
  4. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/index/pinecone.py +190 -58
  5. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/routers/base.py +4 -0
  6. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/schema.py +2 -2
  7. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/LICENSE +0 -0
  8. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/README.md +0 -0
  9. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/encoders/__init__.py +0 -0
  10. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/encoders/aurelio.py +0 -0
  11. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/encoders/base.py +0 -0
  12. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/encoders/bedrock.py +0 -0
  13. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/encoders/bm25.py +0 -0
  14. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/encoders/clip.py +0 -0
  15. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/encoders/cohere.py +0 -0
  16. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/encoders/fastembed.py +0 -0
  17. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/encoders/google.py +0 -0
  18. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/encoders/huggingface.py +0 -0
  19. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/encoders/mistral.py +0 -0
  20. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/encoders/openai.py +0 -0
  21. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/encoders/tfidf.py +0 -0
  22. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/encoders/vit.py +0 -0
  23. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/encoders/zure.py +0 -0
  24. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/index/__init__.py +0 -0
  25. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/index/base.py +0 -0
  26. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/index/hybrid_local.py +0 -0
  27. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/index/local.py +0 -0
  28. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/index/postgres.py +0 -0
  29. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/index/qdrant.py +0 -0
  30. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/linear.py +0 -0
  31. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/llms/__init__.py +0 -0
  32. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/llms/base.py +0 -0
  33. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/llms/cohere.py +0 -0
  34. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/llms/grammars/json.gbnf +0 -0
  35. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/llms/llamacpp.py +0 -0
  36. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/llms/mistral.py +0 -0
  37. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/llms/ollama.py +0 -0
  38. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/llms/openai.py +0 -0
  39. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/llms/openrouter.py +0 -0
  40. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/llms/zure.py +0 -0
  41. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/py.typed +0 -0
  42. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/route.py +0 -0
  43. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/routers/__init__.py +0 -0
  44. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/routers/hybrid.py +0 -0
  45. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/routers/semantic.py +0 -0
  46. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/utils/__init__.py +0 -0
  47. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/utils/defaults.py +0 -0
  48. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/utils/function_call.py +0 -0
  49. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/utils/llm.py +0 -0
  50. {semantic_router-0.1.0.dev9 → semantic_router-0.1.0.dev10}/semantic_router/utils/logger.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: semantic-router
3
- Version: 0.1.0.dev9
3
+ Version: 0.1.0.dev10
4
4
  Summary: Super fast semantic router for AI decision making
5
5
  License: MIT
6
6
  Author: Aurelio AI
@@ -26,7 +26,7 @@ Provides-Extra: postgres
26
26
  Provides-Extra: qdrant
27
27
  Provides-Extra: vision
28
28
  Requires-Dist: aiohttp (>=3.10.11,<4.0.0)
29
- Requires-Dist: aurelio-sdk (>=0.0.16,<0.0.17)
29
+ Requires-Dist: aurelio-sdk (>=0.0.18,<0.0.19)
30
30
  Requires-Dist: boto3 (>=1.34.98,<2.0.0) ; extra == "bedrock"
31
31
  Requires-Dist: botocore (>=1.34.110,<2.0.0) ; extra == "bedrock"
32
32
  Requires-Dist: cohere (>=5.9.4,<6.00) ; extra == "cohere"
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "semantic-router"
3
- version = "0.1.0.dev9"
3
+ version = "0.1.0.dev10"
4
4
  description = "Super fast semantic router for AI decision making"
5
5
  authors = ["Aurelio AI <hello@aurelio.ai>"]
6
6
  readme = "README.md"
@@ -16,7 +16,7 @@ mistralai= {version = ">=0.0.12,<0.1.0", optional = true}
16
16
  numpy = "^1.25.2"
17
17
  colorlog = "^6.8.0"
18
18
  pyyaml = "^6.0.1"
19
- aurelio-sdk = {version = "^0.0.16"}
19
+ aurelio-sdk = {version = "^0.0.18"}
20
20
  pinecone-text = {version = ">=0.7.1", optional = true}
21
21
  torch = {version = ">=2.1.0,<2.6.0", optional = true, python = "<3.13" }
22
22
  transformers = {version = ">=4.36.2", optional = true, python = "<3.13" }
@@ -3,4 +3,4 @@ from semantic_router.routers import HybridRouter, RouterConfig, SemanticRouter
3
3
 
4
4
  __all__ = ["SemanticRouter", "HybridRouter", "Route", "RouterConfig"]
5
5
 
6
- __version__ = "0.1.0.dev9"
6
+ __version__ = "0.1.0.dev10"
@@ -3,6 +3,8 @@ import hashlib
3
3
  import json
4
4
  import os
5
5
  import time
6
+ import requests
7
+ from json.decoder import JSONDecodeError
6
8
  from typing import Any, Dict, List, Optional, Tuple, Union
7
9
 
8
10
  import aiohttp
@@ -18,6 +20,13 @@ def clean_route_name(route_name: str) -> str:
18
20
  return route_name.strip().replace(" ", "-")
19
21
 
20
22
 
23
+ class DeleteRequest(BaseModel):
24
+ ids: list[str] | None = None
25
+ delete_all: bool = False
26
+ namespace: str | None = None
27
+ filter: dict[str, Any] | None = None
28
+
29
+
21
30
  def build_records(
22
31
  embeddings: List[List[float]],
23
32
  routes: List[str],
@@ -114,8 +123,9 @@ class PineconeIndex(BaseIndex):
114
123
  index: Optional[Any] = Field(default=None, exclude=True)
115
124
  ServerlessSpec: Any = Field(default=None, exclude=True)
116
125
  namespace: Optional[str] = ""
117
- base_url: Optional[str] = "https://api.pinecone.io"
126
+ base_url: Optional[str] = None
118
127
  headers: dict[str, str] = {}
128
+ index_host: Optional[str] = "http://localhost:5080"
119
129
 
120
130
  def __init__(
121
131
  self,
@@ -134,12 +144,23 @@ class PineconeIndex(BaseIndex):
134
144
  self.api_key = api_key or os.getenv("PINECONE_API_KEY")
135
145
  if not self.api_key:
136
146
  raise ValueError("Pinecone API key is required.")
147
+
137
148
  self.headers = {
138
149
  "Api-Key": self.api_key,
139
150
  "Content-Type": "application/json",
140
- "X-Pinecone-API-Version": "2024-07",
141
151
  "User-Agent": "source_tag=semanticrouter",
142
152
  }
153
+
154
+ if base_url is not None or os.getenv("PINECONE_API_BASE_URL"):
155
+ logger.info("Using pinecone remote API.")
156
+ if os.getenv("PINECONE_API_BASE_URL"):
157
+ self.base_url = os.getenv("PINECONE_API_BASE_URL")
158
+ else:
159
+ self.base_url = base_url
160
+
161
+ if self.base_url and "api.pinecone.io" in self.base_url:
162
+ self.headers["X-Pinecone-API-Version"] = "2024-07"
163
+
143
164
  self.index_name = index_name
144
165
  self.dimensions = dimensions
145
166
  self.metric = metric
@@ -150,16 +171,11 @@ class PineconeIndex(BaseIndex):
150
171
  raise ValueError("Namespace 'sr_config' is reserved for internal use.")
151
172
  self.namespace = namespace
152
173
  self.type = "pinecone"
153
- self.api_key = api_key or os.getenv("PINECONE_API_KEY")
154
- self.base_url = base_url
155
174
 
156
175
  logger.warning(
157
176
  "Default region changed from us-west-2 to us-east-1 in v0.1.0.dev6"
158
177
  )
159
178
 
160
- if self.api_key is None:
161
- raise ValueError("Pinecone API key is required.")
162
-
163
179
  self.client = self._initialize_client(api_key=self.api_key)
164
180
 
165
181
  # try initializing index
@@ -186,6 +202,20 @@ class PineconeIndex(BaseIndex):
186
202
 
187
203
  return Pinecone(**pinecone_args)
188
204
 
205
+ def _calculate_index_host(self):
206
+ if self.index_host and self.base_url:
207
+ if "api.pinecone.io" in self.base_url:
208
+ if not self.index_host.startswith("http"):
209
+ self.index_host = f"https://{self.index_host}"
210
+ else:
211
+ if "http" not in self.index_host:
212
+ self.index_host = f"http://{self.base_url.split(':')[-2].strip('/')}:{self.index_host.split(':')[-1]}"
213
+ elif not self.index_host.startswith("http://"):
214
+ if "localhost" in self.index_host:
215
+ self.index_host = f"http://{self.base_url.split(':')[-2].strip('/')}:{self.index_host.split(':')[-1]}"
216
+ else:
217
+ self.index_host = f"http://{self.index_host}"
218
+
189
219
  def _init_index(self, force_create: bool = False) -> Union[Any, None]:
190
220
  """Initializing the index can be done after the object has been created
191
221
  to allow for the user to set the dimensions and other parameters.
@@ -199,43 +229,59 @@ class PineconeIndex(BaseIndex):
199
229
  dimensions are not given (which will raise an error).
200
230
  :type force_create: bool, optional
201
231
  """
202
- index_exists = self.index_name in self.client.list_indexes().names()
203
232
  dimensions_given = self.dimensions is not None
204
- if dimensions_given and not index_exists:
205
- # if the index doesn't exist and we have dimension value
206
- # we create the index
207
- self.client.create_index(
208
- name=self.index_name,
209
- dimension=self.dimensions,
210
- metric=self.metric,
211
- spec=self.ServerlessSpec(cloud=self.cloud, region=self.region),
212
- )
213
- # wait for index to be created
214
- while not self.client.describe_index(self.index_name).status["ready"]:
215
- time.sleep(1)
216
- index = self.client.Index(self.index_name)
217
- time.sleep(0.5)
218
- elif index_exists:
219
- # if the index exists we just return it
220
- index = self.client.Index(self.index_name)
221
- # grab the dimensions from the index
222
- self.dimensions = index.describe_index_stats()["dimension"]
223
- elif force_create and not dimensions_given:
224
- raise ValueError(
225
- "Cannot create an index without specifying the dimensions."
226
- )
233
+ if self.index is None:
234
+ index_exists = self.index_name in self.client.list_indexes().names()
235
+ if dimensions_given and not index_exists:
236
+ # if the index doesn't exist and we have dimension value
237
+ # we create the index
238
+ self.client.create_index(
239
+ name=self.index_name,
240
+ dimension=self.dimensions,
241
+ metric=self.metric,
242
+ spec=self.ServerlessSpec(cloud=self.cloud, region=self.region),
243
+ )
244
+ # wait for index to be created
245
+ while not self.client.describe_index(self.index_name).status["ready"]:
246
+ time.sleep(0.2)
247
+ index = self.client.Index(self.index_name)
248
+ self.index = index
249
+ time.sleep(0.2)
250
+ elif index_exists:
251
+ # if the index exists we just return it
252
+ # index = self.client.Index(self.index_name)
253
+
254
+ self.index_host = self.client.describe_index(self.index_name).host
255
+ self._calculate_index_host()
256
+ index = self.client.Index(self.index_name, host=self.index_host)
257
+ self.index = index
258
+
259
+ # grab the dimensions from the index
260
+ self.dimensions = index.describe_index_stats()["dimension"]
261
+ elif force_create and not dimensions_given:
262
+ raise ValueError(
263
+ "Cannot create an index without specifying the dimensions."
264
+ )
265
+ else:
266
+ # if the index doesn't exist and we don't have the dimensions
267
+ # we return None
268
+ logger.warning(
269
+ "Index could not be initialized. Init parameters: "
270
+ f"{self.index_name=}, {self.dimensions=}, {self.metric=}, "
271
+ f"{self.cloud=}, {self.region=}, {self.host=}, {self.namespace=}, "
272
+ f"{force_create=}"
273
+ )
274
+ index = None
227
275
  else:
228
- # if the index doesn't exist and we don't have the dimensions
229
- # we return None
230
- logger.warning(
231
- "Index could not be initialized. Init parameters: "
232
- f"{self.index_name=}, {self.dimensions=}, {self.metric=}, "
233
- f"{self.cloud=}, {self.region=}, {self.host=}, {self.namespace=}, "
234
- f"{force_create=}"
235
- )
236
- index = None
237
- if index is not None:
238
- self.host = self.client.describe_index(self.index_name)["host"]
276
+ index = self.index
277
+ if self.index is not None and self.host == "":
278
+ # if the index exists we just return it
279
+ self.index_host = self.client.describe_index(self.index_name).host
280
+
281
+ if self.index_host and self.base_url:
282
+ self._calculate_index_host()
283
+ index = self.client.Index(self.index_name, host=self.index_host)
284
+ self.host = self.index_host
239
285
  return index
240
286
 
241
287
  async def _init_async_index(self, force_create: bool = False):
@@ -442,12 +488,37 @@ class PineconeIndex(BaseIndex):
442
488
  def delete(self, route_name: str):
443
489
  route_vec_ids = self._get_route_ids(route_name=route_name)
444
490
  if self.index is not None:
445
- self.index.delete(ids=route_vec_ids, namespace=self.namespace)
491
+ logger.info("index is not None, deleting...")
492
+ if self.base_url and "api.pinecone.io" in self.base_url:
493
+ self.index.delete(ids=route_vec_ids, namespace=self.namespace)
494
+ else:
495
+
496
+ response = requests.post(
497
+ f"{self.index_host}/vectors/delete",
498
+ json=DeleteRequest(
499
+ ids=route_vec_ids,
500
+ delete_all=True,
501
+ namespace=self.namespace,
502
+ ).model_dump(exclude_none=True),
503
+ timeout=10,
504
+ )
505
+ if response.status_code == 200:
506
+ logger.info(
507
+ f"Deleted {len(route_vec_ids)} vectors from index {self.index_name}."
508
+ )
509
+ else:
510
+ error_message = response.text
511
+ raise Exception(
512
+ f"Failed to delete vectors: {response.status_code} : {error_message}"
513
+ )
446
514
  else:
447
515
  raise ValueError("Index is None, could not delete.")
448
516
 
449
517
  def delete_all(self):
450
- self.index.delete(delete_all=True, namespace=self.namespace)
518
+ if self.index is not None:
519
+ self.index.delete(delete_all=True, namespace=self.namespace)
520
+ else:
521
+ raise ValueError("Index is None, could not delete.")
451
522
 
452
523
  def describe(self) -> IndexConfig:
453
524
  if self.index is not None:
@@ -501,19 +572,31 @@ class PineconeIndex(BaseIndex):
501
572
  else:
502
573
  filter_query = None
503
574
  if sparse_vector is not None:
575
+ logger.error(f"sparse_vector exists:{sparse_vector}")
504
576
  if isinstance(sparse_vector, dict):
505
577
  sparse_vector = SparseEmbedding.from_dict(sparse_vector)
506
578
  if isinstance(sparse_vector, SparseEmbedding):
507
579
  # unnecessary if-statement but mypy didn't like this otherwise
508
580
  sparse_vector = sparse_vector.to_pinecone()
509
- results = self.index.query(
510
- vector=[query_vector_list],
511
- sparse_vector=sparse_vector,
512
- top_k=top_k,
513
- filter=filter_query,
514
- include_metadata=True,
515
- namespace=self.namespace,
516
- )
581
+ try:
582
+ results = self.index.query(
583
+ vector=[query_vector_list],
584
+ sparse_vector=sparse_vector,
585
+ top_k=top_k,
586
+ filter=filter_query,
587
+ include_metadata=True,
588
+ namespace=self.namespace,
589
+ )
590
+ except Exception:
591
+ logger.error("retrying query with vector as str")
592
+ results = self.index.query(
593
+ vector=query_vector_list,
594
+ sparse_vector=sparse_vector,
595
+ top_k=top_k,
596
+ filter=filter_query,
597
+ include_metadata=True,
598
+ namespace=self.namespace,
599
+ )
517
600
  scores = [result["score"] for result in results["matches"]]
518
601
  route_names = [result["metadata"]["sr_route"] for result in results["matches"]]
519
602
  return np.array(scores), route_names
@@ -710,16 +793,35 @@ class PineconeIndex(BaseIndex):
710
793
  "filter": filter,
711
794
  "top_k": top_k,
712
795
  "include_metadata": include_metadata,
796
+ "topK": top_k,
797
+ "includeMetadata": include_metadata,
713
798
  }
714
799
  if self.host == "":
715
800
  raise ValueError("self.host is not initialized.")
801
+ elif self.base_url and "api.pinecone.io" in self.base_url:
802
+ if not self.host.startswith("http"):
803
+ logger.error(f"host exists:{self.host}")
804
+
805
+ self.host = f"https://{self.host}"
806
+ elif self.host.startswith("localhost") and self.base_url:
807
+ self.host = f"http://{self.base_url.split(':')[-2].strip('/')}:{self.host.split(':')[-1]}"
808
+
716
809
  async with aiohttp.ClientSession() as session:
717
810
  async with session.post(
718
- f"https://{self.host}/query",
811
+ f"{self.host}/query",
719
812
  json=params,
720
813
  headers=self.headers,
721
814
  ) as response:
722
- return await response.json(content_type=None)
815
+ if response.status != 200:
816
+ error_text = await response.text()
817
+ logger.error(f"Error in query response: {error_text}")
818
+ return {} # or handle the error as needed
819
+
820
+ try:
821
+ return await response.json(content_type=None)
822
+ except JSONDecodeError as e:
823
+ logger.error(f"JSON decode error: {e}")
824
+ return {}
723
825
 
724
826
  async def _async_list_indexes(self):
725
827
  async with aiohttp.ClientSession() as session:
@@ -738,9 +840,17 @@ class PineconeIndex(BaseIndex):
738
840
  "vectors": vectors,
739
841
  "namespace": namespace,
740
842
  }
843
+
844
+ if self.base_url and "api.pinecone.io" in self.base_url:
845
+ if not self.host.startswith("http"):
846
+ logger.error(f"host exists:{self.host}")
847
+ self.host = f"https://{self.host}"
848
+
849
+ elif self.host.startswith("localhost") and self.base_url:
850
+ self.host = f"http://{self.base_url.split(':')[-2].strip('/')}:{self.host.split(':')[-1]}"
741
851
  async with aiohttp.ClientSession() as session:
742
852
  async with session.post(
743
- f"https://{self.host}/vectors/upsert",
853
+ f"{self.host}/vectors/upsert",
744
854
  json=params,
745
855
  headers=self.headers,
746
856
  ) as response:
@@ -774,9 +884,16 @@ class PineconeIndex(BaseIndex):
774
884
  "ids": ids,
775
885
  "namespace": namespace,
776
886
  }
887
+ if self.base_url and "api.pinecone.io" in self.base_url:
888
+ if not self.host.startswith("http"):
889
+ logger.error(f"host exists:{self.host}")
890
+ self.host = f"https://{self.host}"
891
+ elif self.host.startswith("localhost") and self.base_url:
892
+ self.host = f"http://{self.base_url.split(':')[-2].strip('/')}:{self.host.split(':')[-1]}"
893
+
777
894
  async with aiohttp.ClientSession() as session:
778
895
  async with session.post(
779
- f"https://{self.host}/vectors/delete",
896
+ f"{self.host}/vectors/delete",
780
897
  json=params,
781
898
  headers=self.headers,
782
899
  ) as response:
@@ -816,7 +933,15 @@ class PineconeIndex(BaseIndex):
816
933
  else:
817
934
  prefix_str = ""
818
935
 
819
- list_url = f"https://{self.host}/vectors/list{prefix_str}"
936
+ if self.base_url and "api.pinecone.io" in self.base_url:
937
+ if not self.host.startswith("http"):
938
+ logger.error(f"host exists:{self.host}")
939
+ self.host = f"https://{self.host}"
940
+
941
+ elif self.host.startswith("localhost") and self.base_url:
942
+ self.host = f"http://{self.base_url.split(':')[-2].strip('/')}:{self.host.split(':')[-1]}"
943
+
944
+ list_url = f"{self.host}/vectors/list{prefix_str}"
820
945
  params: dict = {}
821
946
  if self.namespace:
822
947
  params["namespace"] = self.namespace
@@ -874,7 +999,14 @@ class PineconeIndex(BaseIndex):
874
999
  """
875
1000
  if self.host == "":
876
1001
  raise ValueError("self.host is not initialized.")
877
- url = f"https://{self.host}/vectors/fetch"
1002
+ if self.base_url and "api.pinecone.io" in self.base_url:
1003
+ if not self.host.startswith("http"):
1004
+ logger.error(f"host exists:{self.host}")
1005
+ self.host = f"https://{self.host}"
1006
+ elif self.host.startswith("localhost") and self.base_url:
1007
+ self.host = f"http://{self.base_url.split(':')[-2].strip('/')}:{self.host.split(':')[-1]}"
1008
+
1009
+ url = f"{self.host}/vectors/fetch"
878
1010
 
879
1011
  params = {
880
1012
  "ids": [vector_id],
@@ -397,7 +397,11 @@ class BaseRouter(BaseModel):
397
397
  self.index.dimensions = dims
398
398
  # now init index
399
399
  if isinstance(self.index, PineconeIndex):
400
+ # _init_index will not create index if already exists — it will also check
401
+ # for required attributes like self.index.host and self.index.dimensions and
402
+ # fetch them if not set
400
403
  self.index.index = self.index._init_index(force_create=True)
404
+
401
405
  # run auto sync if active
402
406
  if self.auto_sync:
403
407
  local_utterances = self.to_config().to_utterances()
@@ -5,10 +5,10 @@ from enum import Enum
5
5
  from typing import Any, Dict, List, Optional, Tuple, Union
6
6
 
7
7
  import numpy as np
8
- from aurelio_sdk.schema import BM25Embedding
9
8
  from pydantic import BaseModel, Field
10
9
 
11
10
  from semantic_router.utils.logger import logger
11
+ from aurelio_sdk.schema import SparseEmbedding as BM25SparseEmbedding
12
12
 
13
13
 
14
14
  class EncoderType(Enum):
@@ -450,7 +450,7 @@ class SparseEmbedding(BaseModel):
450
450
  return cls.from_compact_array(np.array([np.arange(len(vector)), vector]).T)
451
451
 
452
452
  @classmethod
453
- def from_aurelio(cls, embedding: BM25Embedding):
453
+ def from_aurelio(cls, embedding: BM25SparseEmbedding):
454
454
  arr = np.array([embedding.indices, embedding.values]).T
455
455
  return cls.from_compact_array(arr)
456
456