semantic-router 0.1.0.dev2__tar.gz → 0.1.0.dev3__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 (49) hide show
  1. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/LICENSE +1 -1
  2. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/PKG-INFO +12 -10
  3. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/pyproject.toml +10 -10
  4. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/__init__.py +1 -1
  5. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/encoders/aurelio.py +1 -1
  6. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/encoders/base.py +2 -2
  7. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/encoders/clip.py +3 -2
  8. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/encoders/cohere.py +1 -1
  9. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/encoders/fastembed.py +1 -1
  10. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/encoders/huggingface.py +5 -3
  11. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/encoders/mistral.py +1 -1
  12. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/encoders/openai.py +9 -9
  13. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/encoders/vit.py +4 -3
  14. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/index/base.py +1 -1
  15. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/index/pinecone.py +1 -1
  16. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/index/postgres.py +3 -2
  17. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/index/qdrant.py +2 -2
  18. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/llms/base.py +3 -1
  19. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/llms/cohere.py +1 -1
  20. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/llms/llamacpp.py +1 -3
  21. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/llms/mistral.py +1 -3
  22. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/llms/ollama.py +5 -10
  23. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/llms/openai.py +9 -10
  24. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/llms/openrouter.py +8 -8
  25. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/llms/zure.py +5 -6
  26. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/route.py +2 -7
  27. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/routers/base.py +1 -1
  28. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/routers/hybrid.py +1 -1
  29. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/schema.py +14 -2
  30. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/utils/function_call.py +1 -1
  31. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/README.md +0 -0
  32. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/encoders/__init__.py +0 -0
  33. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/encoders/bedrock.py +0 -0
  34. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/encoders/bm25.py +0 -0
  35. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/encoders/google.py +0 -0
  36. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/encoders/tfidf.py +0 -0
  37. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/encoders/zure.py +0 -0
  38. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/index/__init__.py +0 -0
  39. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/index/hybrid_local.py +0 -0
  40. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/index/local.py +0 -0
  41. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/linear.py +0 -0
  42. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/llms/__init__.py +0 -0
  43. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/llms/grammars/json.gbnf +0 -0
  44. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/routers/__init__.py +0 -0
  45. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/routers/semantic.py +0 -0
  46. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/utils/__init__.py +0 -0
  47. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/utils/defaults.py +0 -0
  48. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/utils/llm.py +0 -0
  49. {semantic_router-0.1.0.dev2 → semantic_router-0.1.0.dev3}/semantic_router/utils/logger.py +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 Aurelio AI
3
+ Copyright (c) 2024 Aurelio AI
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,16 +1,18 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: semantic-router
3
- Version: 0.1.0.dev2
3
+ Version: 0.1.0.dev3
4
4
  Summary: Super fast semantic router for AI decision making
5
5
  License: MIT
6
6
  Author: Aurelio AI
7
7
  Author-email: hello@aurelio.ai
8
- Requires-Python: >=3.9,<3.13
8
+ Requires-Python: >=3.9,<3.14
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.9
12
12
  Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
14
16
  Provides-Extra: bedrock
15
17
  Provides-Extra: cohere
16
18
  Provides-Extra: docs
@@ -30,17 +32,17 @@ Requires-Dist: botocore (>=1.34.110,<2.0.0) ; extra == "bedrock"
30
32
  Requires-Dist: cohere (>=5.9.4,<6.00) ; extra == "cohere"
31
33
  Requires-Dist: colorama (>=0.4.6,<0.5.0)
32
34
  Requires-Dist: colorlog (>=6.8.0,<7.0.0)
33
- Requires-Dist: fastembed (>=0.3.0,<0.4.0) ; extra == "fastembed"
35
+ Requires-Dist: fastembed (>=0.3.0,<0.4.0) ; (python_version < "3.13") and (extra == "fastembed")
34
36
  Requires-Dist: google-cloud-aiplatform (>=1.45.0,<2.0.0) ; extra == "google"
35
- Requires-Dist: llama-cpp-python (>=0.2.28,<0.2.86) ; extra == "local"
37
+ Requires-Dist: llama-cpp-python (>=0.2.28,<0.2.86) ; (python_version < "3.13") and (extra == "local")
36
38
  Requires-Dist: mistralai (>=0.0.12,<0.1.0) ; extra == "mistralai"
37
39
  Requires-Dist: numpy (>=1.25.2,<2.0.0)
38
40
  Requires-Dist: openai (>=1.10.0,<2.0.0)
39
- Requires-Dist: pillow (>=10.2.0,<11.0.0) ; extra == "vision"
41
+ Requires-Dist: pillow (>=10.2.0,<11.0.0) ; (python_version < "3.13") and (extra == "vision")
40
42
  Requires-Dist: pinecone (>=5.0.0) ; extra == "pinecone"
41
43
  Requires-Dist: pinecone-text (>=0.7.1,<0.10.0) ; extra == "hybrid"
42
44
  Requires-Dist: psycopg2-binary (>=2.9.9,<3.0.0)
43
- Requires-Dist: pydantic (>=2.5.3,<3.0.0)
45
+ Requires-Dist: pydantic (>=2.10.2,<3.0.0)
44
46
  Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
45
47
  Requires-Dist: qdrant-client (>=1.11.1,<2.0.0) ; extra == "qdrant"
46
48
  Requires-Dist: regex (>=2023.12.25)
@@ -48,11 +50,11 @@ Requires-Dist: requests-mock (>=1.12.1,<2.0.0)
48
50
  Requires-Dist: sphinx (>=7.0.0,<8.0.0) ; extra == "docs"
49
51
  Requires-Dist: sphinxawesome-theme (>=5.2.0,<6.0.0) ; extra == "docs"
50
52
  Requires-Dist: tiktoken (>=0.6.0,<1.0.0)
51
- Requires-Dist: tokenizers (>=0.19) ; extra == "local"
52
- Requires-Dist: torch (>=2.1.0,<2.6.0) ; extra == "local" or extra == "vision"
53
- Requires-Dist: torchvision (>=0.17.0,<0.18.0) ; extra == "vision"
53
+ Requires-Dist: tokenizers (>=0.19) ; (python_version < "3.13") and (extra == "local")
54
+ Requires-Dist: torch (>=2.1.0,<2.6.0) ; (python_version < "3.13") and (extra == "local" or extra == "vision")
55
+ Requires-Dist: torchvision (>=0.17.0,<0.18.0) ; (python_version < "3.13") and (extra == "vision")
54
56
  Requires-Dist: tornado (>=6.4.2,<7.0.0)
55
- Requires-Dist: transformers (>=4.36.2) ; extra == "local" or extra == "vision"
57
+ Requires-Dist: transformers (>=4.36.2) ; (python_version < "3.13") and (extra == "local" or extra == "vision")
56
58
  Description-Content-Type: text/markdown
57
59
 
58
60
  [![Semantic Router](https://i.ibb.co.com/g423grt/semantic-router-banner.png)](https://aurelio.ai)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "semantic-router"
3
- version = "0.1.0.dev2"
3
+ version = "0.1.0.dev3"
4
4
  description = "Super fast semantic router for AI decision making"
5
5
  authors = ["Aurelio AI <hello@aurelio.ai>"]
6
6
  readme = "README.md"
@@ -8,8 +8,8 @@ packages = [{include = "semantic_router"}]
8
8
  license = "MIT"
9
9
 
10
10
  [tool.poetry.dependencies]
11
- python = ">=3.9,<3.13"
12
- pydantic = "^2.5.3"
11
+ python = ">=3.9,<3.14"
12
+ pydantic = "^2.10.2"
13
13
  openai = ">=1.10.0,<2.0.0"
14
14
  cohere = {version = ">=5.9.4,<6.00", optional = true}
15
15
  mistralai= {version = ">=0.0.12,<0.1.0", optional = true}
@@ -18,15 +18,15 @@ colorlog = "^6.8.0"
18
18
  pyyaml = "^6.0.1"
19
19
  aurelio-sdk = {version = "^0.0.16"}
20
20
  pinecone-text = {version = ">=0.7.1,<0.10.0", optional = true}
21
- torch = {version = ">=2.1.0,<2.6.0", optional = true}
22
- transformers = {version = ">=4.36.2", optional = true}
23
- tokenizers = {version = ">=0.19", optional = true}
24
- llama-cpp-python = {version = ">=0.2.28,<0.2.86", optional = true}
21
+ torch = {version = ">=2.1.0,<2.6.0", optional = true, python = "<3.13" }
22
+ transformers = {version = ">=4.36.2", optional = true, python = "<3.13" }
23
+ tokenizers = {version = ">=0.19", optional = true, python = "<3.13" }
24
+ llama-cpp-python = {version = ">=0.2.28,<0.2.86", optional = true, python = "<3.13" }
25
25
  colorama = "^0.4.6"
26
26
  pinecone = {version=">=5.0.0", optional = true}
27
27
  regex = ">=2023.12.25"
28
- torchvision = { version = ">=0.17.0,<0.18.0", optional = true}
29
- pillow = { version = ">=10.2.0,<11.0.0", optional = true}
28
+ torchvision = { version = ">=0.17.0,<0.18.0", optional = true, python = "<3.13" }
29
+ pillow = { version = ">=10.2.0,<11.0.0", optional = true, python = "<3.13" }
30
30
  tiktoken = ">=0.6.0,<1.0.0"
31
31
  qdrant-client = {version = "^1.11.1", optional = true}
32
32
  google-cloud-aiplatform = {version = "^1.45.0", optional = true}
@@ -34,7 +34,7 @@ requests-mock = "^1.12.1"
34
34
  boto3 = { version = "^1.34.98", optional = true }
35
35
  botocore = {version = "^1.34.110", optional = true}
36
36
  aiohttp = "^3.10.11"
37
- fastembed = {version = "^0.3.0", optional = true}
37
+ fastembed = {version = "^0.3.0", optional = true, python = "<3.13" }
38
38
  psycopg2-binary = {version = "^2.9.9", optional = true}
39
39
  sphinx = {version = "^7.0.0", optional = true}
40
40
  sphinxawesome-theme = {version = "^5.2.0", optional = true}
@@ -3,4 +3,4 @@ from semantic_router.route import Route
3
3
 
4
4
  __all__ = ["SemanticRouter", "HybridRouter", "Route", "RouterConfig"]
5
5
 
6
- __version__ = "0.1.0.dev2"
6
+ __version__ = "0.1.0.dev3"
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  from typing import Any, List, Optional
3
- from pydantic.v1 import Field
3
+ from pydantic import Field
4
4
 
5
5
  from aurelio_sdk import AurelioClient, AsyncAurelioClient, EmbeddingResponse
6
6
 
@@ -1,6 +1,6 @@
1
1
  from typing import Any, Coroutine, List, Optional
2
2
 
3
- from pydantic.v1 import BaseModel, Field, validator
3
+ from pydantic import BaseModel, Field, field_validator
4
4
  import numpy as np
5
5
 
6
6
  from semantic_router.schema import SparseEmbedding
@@ -14,7 +14,7 @@ class DenseEncoder(BaseModel):
14
14
  class Config:
15
15
  arbitrary_types_allowed = True
16
16
 
17
- @validator("score_threshold", pre=True, always=True)
17
+ @field_validator("score_threshold")
18
18
  def set_score_threshold(cls, v):
19
19
  return float(v) if v is not None else None
20
20
 
@@ -1,7 +1,7 @@
1
1
  from typing import Any, List, Optional
2
2
 
3
3
  import numpy as np
4
- from pydantic.v1 import PrivateAttr
4
+ from pydantic import PrivateAttr
5
5
  from typing import Dict
6
6
  from semantic_router.encoders import DenseEncoder
7
7
 
@@ -9,7 +9,6 @@ from semantic_router.encoders import DenseEncoder
9
9
  class CLIPEncoder(DenseEncoder):
10
10
  name: str = "openai/clip-vit-base-patch16"
11
11
  type: str = "huggingface"
12
- score_threshold: float = 0.2
13
12
  tokenizer_kwargs: Dict = {}
14
13
  processor_kwargs: Dict = {}
15
14
  model_kwargs: Dict = {}
@@ -21,6 +20,8 @@ class CLIPEncoder(DenseEncoder):
21
20
  _Image: Any = PrivateAttr()
22
21
 
23
22
  def __init__(self, **data):
23
+ if data.get("score_threshold") is None:
24
+ data["score_threshold"] = 0.2
24
25
  super().__init__(**data)
25
26
  self._tokenizer, self._processor, self._model = self._initialize_hf_model()
26
27
 
@@ -1,7 +1,7 @@
1
1
  import os
2
2
  from typing import Any, List, Optional
3
3
 
4
- from pydantic.v1 import PrivateAttr
4
+ from pydantic import PrivateAttr
5
5
 
6
6
  from semantic_router.encoders import DenseEncoder
7
7
  from semantic_router.utils.defaults import EncoderDefault
@@ -1,7 +1,7 @@
1
1
  from typing import Any, List, Optional
2
2
 
3
3
  import numpy as np
4
- from pydantic.v1 import PrivateAttr
4
+ from pydantic import PrivateAttr
5
5
 
6
6
  from semantic_router.encoders import DenseEncoder
7
7
 
@@ -25,7 +25,7 @@ import time
25
25
  import os
26
26
  from typing import Any, List, Optional, Dict
27
27
 
28
- from pydantic.v1 import PrivateAttr
28
+ from pydantic import PrivateAttr
29
29
 
30
30
  from semantic_router.encoders import DenseEncoder
31
31
  from semantic_router.utils.logger import logger
@@ -34,7 +34,6 @@ from semantic_router.utils.logger import logger
34
34
  class HuggingFaceEncoder(DenseEncoder):
35
35
  name: str = "sentence-transformers/all-MiniLM-L6-v2"
36
36
  type: str = "huggingface"
37
- score_threshold: float = 0.5
38
37
  tokenizer_kwargs: Dict = {}
39
38
  model_kwargs: Dict = {}
40
39
  device: Optional[str] = None
@@ -43,6 +42,8 @@ class HuggingFaceEncoder(DenseEncoder):
43
42
  _torch: Any = PrivateAttr()
44
43
 
45
44
  def __init__(self, **data):
45
+ if data.get("score_threshold") is None:
46
+ data["score_threshold"] = 0.5
46
47
  super().__init__(**data)
47
48
  self._tokenizer, self._model = self._initialize_hf_model()
48
49
 
@@ -153,7 +154,6 @@ class HFEndpointEncoder(DenseEncoder):
153
154
  name: str = "hugging_face_custom_endpoint"
154
155
  huggingface_url: Optional[str] = None
155
156
  huggingface_api_key: Optional[str] = None
156
- score_threshold: float = 0.8
157
157
 
158
158
  def __init__(
159
159
  self,
@@ -180,6 +180,8 @@ class HFEndpointEncoder(DenseEncoder):
180
180
  """
181
181
  huggingface_url = huggingface_url or os.getenv("HF_API_URL")
182
182
  huggingface_api_key = huggingface_api_key or os.getenv("HF_API_KEY")
183
+ if score_threshold is None:
184
+ score_threshold = 0.8
183
185
 
184
186
  super().__init__(name=name, score_threshold=score_threshold) # type: ignore
185
187
 
@@ -4,7 +4,7 @@ import os
4
4
  from time import sleep
5
5
  from typing import Any, List, Optional
6
6
 
7
- from pydantic.v1 import PrivateAttr
7
+ from pydantic import PrivateAttr
8
8
 
9
9
  from semantic_router.encoders import DenseEncoder
10
10
  from semantic_router.utils.defaults import EncoderDefault
@@ -2,7 +2,7 @@ from asyncio import sleep as asleep
2
2
  import os
3
3
  from time import sleep
4
4
  from typing import Any, List, Optional, Union
5
- from pydantic.v1 import PrivateAttr
5
+ from pydantic import PrivateAttr
6
6
 
7
7
  import openai
8
8
  from openai import OpenAIError
@@ -36,8 +36,8 @@ model_configs = {
36
36
 
37
37
 
38
38
  class OpenAIEncoder(DenseEncoder):
39
- client: Optional[openai.Client]
40
- async_client: Optional[openai.AsyncClient]
39
+ _client: Optional[openai.Client] = PrivateAttr(default=None)
40
+ _async_client: Optional[openai.AsyncClient] = PrivateAttr(default=None)
41
41
  dimensions: Union[int, NotGiven] = NotGiven()
42
42
  token_limit: int = 8192 # default value, should be replaced by config
43
43
  _token_encoder: Any = PrivateAttr()
@@ -77,10 +77,10 @@ class OpenAIEncoder(DenseEncoder):
77
77
  if max_retries is not None:
78
78
  self.max_retries = max_retries
79
79
  try:
80
- self.client = openai.Client(
80
+ self._client = openai.Client(
81
81
  base_url=base_url, api_key=api_key, organization=openai_org_id
82
82
  )
83
- self.async_client = openai.AsyncClient(
83
+ self._async_client = openai.AsyncClient(
84
84
  base_url=base_url, api_key=api_key, organization=openai_org_id
85
85
  )
86
86
  except Exception as e:
@@ -103,7 +103,7 @@ class OpenAIEncoder(DenseEncoder):
103
103
  False and a document exceeds the token limit, an error will be
104
104
  raised.
105
105
  :return: List of embeddings for each document."""
106
- if self.client is None:
106
+ if self._client is None:
107
107
  raise ValueError("OpenAI client is not initialized.")
108
108
  embeds = None
109
109
 
@@ -114,7 +114,7 @@ class OpenAIEncoder(DenseEncoder):
114
114
  # Exponential backoff
115
115
  for j in range(self.max_retries + 1):
116
116
  try:
117
- embeds = self.client.embeddings.create(
117
+ embeds = self._client.embeddings.create(
118
118
  input=docs,
119
119
  model=self.name,
120
120
  dimensions=self.dimensions,
@@ -160,7 +160,7 @@ class OpenAIEncoder(DenseEncoder):
160
160
  return text
161
161
 
162
162
  async def acall(self, docs: List[str], truncate: bool = True) -> List[List[float]]:
163
- if self.async_client is None:
163
+ if self._async_client is None:
164
164
  raise ValueError("OpenAI async client is not initialized.")
165
165
  embeds = None
166
166
 
@@ -171,7 +171,7 @@ class OpenAIEncoder(DenseEncoder):
171
171
  # Exponential backoff
172
172
  for j in range(self.max_retries + 1):
173
173
  try:
174
- embeds = await self.async_client.embeddings.create(
174
+ embeds = await self._async_client.embeddings.create(
175
175
  input=docs,
176
176
  model=self.name,
177
177
  dimensions=self.dimensions,
@@ -1,6 +1,6 @@
1
- from typing import Any, List, Optional, Dict
1
+ from typing import Any, Dict, List, Optional
2
2
 
3
- from pydantic.v1 import PrivateAttr
3
+ from pydantic import PrivateAttr
4
4
 
5
5
  from semantic_router.encoders import DenseEncoder
6
6
 
@@ -8,7 +8,6 @@ from semantic_router.encoders import DenseEncoder
8
8
  class VitEncoder(DenseEncoder):
9
9
  name: str = "google/vit-base-patch16-224"
10
10
  type: str = "huggingface"
11
- score_threshold: float = 0.5
12
11
  processor_kwargs: Dict = {}
13
12
  model_kwargs: Dict = {}
14
13
  device: Optional[str] = None
@@ -19,6 +18,8 @@ class VitEncoder(DenseEncoder):
19
18
  _Image: Any = PrivateAttr()
20
19
 
21
20
  def __init__(self, **data):
21
+ if data.get("score_threshold") is None:
22
+ data["score_threshold"] = 0.5
22
23
  super().__init__(**data)
23
24
  self._processor, self._model = self._initialize_hf_model()
24
25
 
@@ -2,7 +2,7 @@ from typing import Any, List, Optional, Tuple, Union, Dict
2
2
  import json
3
3
 
4
4
  import numpy as np
5
- from pydantic.v1 import BaseModel
5
+ from pydantic import BaseModel
6
6
 
7
7
  from semantic_router.schema import ConfigParameter, SparseEmbedding, Utterance
8
8
  from semantic_router.route import Route
@@ -8,7 +8,7 @@ import json
8
8
  from typing import Any, Dict, List, Optional, Union, Tuple
9
9
 
10
10
  import numpy as np
11
- from pydantic.v1 import BaseModel, Field
11
+ from pydantic import BaseModel, Field
12
12
 
13
13
  from semantic_router.index.base import BaseIndex
14
14
  from semantic_router.schema import ConfigParameter, SparseEmbedding
@@ -103,7 +103,6 @@ class PostgresIndex(BaseIndex):
103
103
  connection_string: Optional[str] = None
104
104
  index_prefix: str = "semantic_router_"
105
105
  index_name: str = "index"
106
- dimensions: int = 1536
107
106
  metric: Metric = Metric.COSINE
108
107
  namespace: Optional[str] = ""
109
108
  conn: Optional["psycopg2.extensions.connection"] = None
@@ -115,9 +114,9 @@ class PostgresIndex(BaseIndex):
115
114
  connection_string: Optional[str] = None,
116
115
  index_prefix: str = "semantic_router_",
117
116
  index_name: str = "index",
118
- dimensions: int = 1536,
119
117
  metric: Metric = Metric.COSINE,
120
118
  namespace: Optional[str] = "",
119
+ dimensions: int | None = None,
121
120
  ):
122
121
  """
123
122
  Initializes the Postgres index with the specified parameters.
@@ -135,6 +134,8 @@ class PostgresIndex(BaseIndex):
135
134
  :param namespace: An optional namespace for the index.
136
135
  :type namespace: Optional[str]
137
136
  """
137
+ if dimensions is None:
138
+ dimensions = 1536
138
139
  super().__init__()
139
140
  # try and import psycopg2
140
141
  try:
@@ -1,7 +1,7 @@
1
1
  from typing import Any, Dict, List, Optional, Tuple, Union
2
2
 
3
3
  import numpy as np
4
- from pydantic.v1 import Field
4
+ from pydantic import Field
5
5
 
6
6
  from semantic_router.index.base import BaseIndex
7
7
  from semantic_router.schema import ConfigParameter, Metric, SparseEmbedding, Utterance
@@ -40,7 +40,7 @@ class QdrantIndex(BaseIndex):
40
40
  default=6334,
41
41
  description="Port of the gRPC interface.",
42
42
  )
43
- prefer_grpc: bool = Field(
43
+ prefer_grpc: Optional[bool] = Field(
44
44
  default=None,
45
45
  description="Whether to use gPRC interface whenever possible in methods",
46
46
  )
@@ -1,7 +1,7 @@
1
1
  import json
2
2
  from typing import Any, List, Optional, Dict
3
3
 
4
- from pydantic.v1 import BaseModel
4
+ from pydantic import BaseModel
5
5
 
6
6
  from semantic_router.schema import Message
7
7
  from semantic_router.utils.logger import logger
@@ -9,6 +9,8 @@ from semantic_router.utils.logger import logger
9
9
 
10
10
  class BaseLLM(BaseModel):
11
11
  name: str
12
+ temperature: Optional[float] = 0.0
13
+ max_tokens: Optional[int] = None
12
14
 
13
15
  class Config:
14
16
  arbitrary_types_allowed = True
@@ -1,7 +1,7 @@
1
1
  import os
2
2
  from typing import Any, List, Optional
3
3
 
4
- from pydantic.v1 import PrivateAttr
4
+ from pydantic import PrivateAttr
5
5
 
6
6
  from semantic_router.llms import BaseLLM
7
7
  from semantic_router.schema import Message
@@ -2,7 +2,7 @@ from contextlib import contextmanager
2
2
  from pathlib import Path
3
3
  from typing import Any, Optional, List, Dict
4
4
 
5
- from pydantic.v1 import PrivateAttr
5
+ from pydantic import PrivateAttr
6
6
 
7
7
  from semantic_router.llms.base import BaseLLM
8
8
  from semantic_router.schema import Message
@@ -11,8 +11,6 @@ from semantic_router.utils.logger import logger
11
11
 
12
12
  class LlamaCppLLM(BaseLLM):
13
13
  llm: Any
14
- temperature: float
15
- max_tokens: Optional[int] = 200
16
14
  grammar: Optional[Any] = None
17
15
  _llama_cpp: Any = PrivateAttr()
18
16
 
@@ -1,7 +1,7 @@
1
1
  import os
2
2
  from typing import Any, List, Optional
3
3
 
4
- from pydantic.v1 import PrivateAttr
4
+ from pydantic import PrivateAttr
5
5
 
6
6
  from semantic_router.llms import BaseLLM
7
7
  from semantic_router.schema import Message
@@ -11,8 +11,6 @@ from semantic_router.utils.logger import logger
11
11
 
12
12
  class MistralAILLM(BaseLLM):
13
13
  _client: Any = PrivateAttr()
14
- temperature: Optional[float]
15
- max_tokens: Optional[int]
16
14
  _mistralai: Any = PrivateAttr()
17
15
 
18
16
  def __init__(
@@ -8,22 +8,17 @@ from semantic_router.utils.logger import logger
8
8
 
9
9
 
10
10
  class OllamaLLM(BaseLLM):
11
- temperature: Optional[float]
12
- llm_name: Optional[str]
13
- max_tokens: Optional[int]
14
- stream: Optional[bool]
11
+ stream: bool = False
15
12
 
16
13
  def __init__(
17
14
  self,
18
- name: str = "ollama",
15
+ name: str = "openhermes",
19
16
  temperature: float = 0.2,
20
- llm_name: str = "openhermes",
21
17
  max_tokens: Optional[int] = 200,
22
18
  stream: bool = False,
23
19
  ):
24
20
  super().__init__(name=name)
25
21
  self.temperature = temperature
26
- self.llm_name = llm_name
27
22
  self.max_tokens = max_tokens
28
23
  self.stream = stream
29
24
 
@@ -31,19 +26,19 @@ class OllamaLLM(BaseLLM):
31
26
  self,
32
27
  messages: List[Message],
33
28
  temperature: Optional[float] = None,
34
- llm_name: Optional[str] = None,
29
+ name: Optional[str] = None,
35
30
  max_tokens: Optional[int] = None,
36
31
  stream: Optional[bool] = None,
37
32
  ) -> str:
38
33
  # Use instance defaults if not overridden
39
34
  temperature = temperature if temperature is not None else self.temperature
40
- llm_name = llm_name if llm_name is not None else self.llm_name
35
+ name = name if name is not None else self.name
41
36
  max_tokens = max_tokens if max_tokens is not None else self.max_tokens
42
37
  stream = stream if stream is not None else self.stream
43
38
 
44
39
  try:
45
40
  payload = {
46
- "model": llm_name,
41
+ "model": name,
47
42
  "messages": [m.to_openai() for m in messages],
48
43
  "options": {"temperature": temperature, "num_predict": max_tokens},
49
44
  "format": "json",
@@ -1,5 +1,6 @@
1
1
  import os
2
2
  from typing import List, Optional, Any, Callable, Dict, Union
3
+ from pydantic import PrivateAttr
3
4
 
4
5
  import openai
5
6
  from openai._types import NotGiven, NOT_GIVEN
@@ -21,10 +22,8 @@ from openai.types.chat.chat_completion_message_tool_call import (
21
22
 
22
23
 
23
24
  class OpenAILLM(BaseLLM):
24
- client: Optional[openai.OpenAI]
25
- async_client: Optional[openai.AsyncOpenAI]
26
- temperature: Optional[float]
27
- max_tokens: Optional[int]
25
+ _client: Optional[openai.OpenAI] = PrivateAttr(default=None)
26
+ _async_client: Optional[openai.AsyncOpenAI] = PrivateAttr(default=None)
28
27
 
29
28
  def __init__(
30
29
  self,
@@ -40,8 +39,8 @@ class OpenAILLM(BaseLLM):
40
39
  if api_key is None:
41
40
  raise ValueError("OpenAI API key cannot be 'None'.")
42
41
  try:
43
- self.async_client = openai.AsyncOpenAI(api_key=api_key)
44
- self.client = openai.OpenAI(api_key=api_key)
42
+ self._async_client = openai.AsyncOpenAI(api_key=api_key)
43
+ self._client = openai.OpenAI(api_key=api_key)
45
44
  except Exception as e:
46
45
  raise ValueError(
47
46
  f"OpenAI API client failed to initialize. Error: {e}"
@@ -88,14 +87,14 @@ class OpenAILLM(BaseLLM):
88
87
  messages: List[Message],
89
88
  function_schemas: Optional[List[Dict[str, Any]]] = None,
90
89
  ) -> str:
91
- if self.client is None:
90
+ if self._client is None:
92
91
  raise ValueError("OpenAI client is not initialized.")
93
92
  try:
94
93
  tools: Union[List[Dict[str, Any]], NotGiven] = (
95
94
  function_schemas if function_schemas else NOT_GIVEN
96
95
  )
97
96
 
98
- completion = self.client.chat.completions.create(
97
+ completion = self._client.chat.completions.create(
99
98
  model=self.name,
100
99
  messages=[m.to_openai() for m in messages],
101
100
  temperature=self.temperature,
@@ -132,14 +131,14 @@ class OpenAILLM(BaseLLM):
132
131
  messages: List[Message],
133
132
  function_schemas: Optional[List[Dict[str, Any]]] = None,
134
133
  ) -> str:
135
- if self.async_client is None:
134
+ if self._async_client is None:
136
135
  raise ValueError("OpenAI async_client is not initialized.")
137
136
  try:
138
137
  tools: Union[List[Dict[str, Any]], NotGiven] = (
139
138
  function_schemas if function_schemas is not None else NOT_GIVEN
140
139
  )
141
140
 
142
- completion = await self.async_client.chat.completions.create(
141
+ completion = await self._async_client.chat.completions.create(
143
142
  model=self.name,
144
143
  messages=[m.to_openai() for m in messages],
145
144
  temperature=self.temperature,
@@ -1,6 +1,8 @@
1
1
  import os
2
2
  from typing import List, Optional
3
3
 
4
+ from pydantic import PrivateAttr
5
+
4
6
  import openai
5
7
 
6
8
  from semantic_router.llms import BaseLLM
@@ -9,10 +11,8 @@ from semantic_router.utils.logger import logger
9
11
 
10
12
 
11
13
  class OpenRouterLLM(BaseLLM):
12
- client: Optional[openai.OpenAI]
13
- base_url: Optional[str]
14
- temperature: Optional[float]
15
- max_tokens: Optional[int]
14
+ _client: Optional[openai.OpenAI] = PrivateAttr(default=None)
15
+ _base_url: str = PrivateAttr(default="https://openrouter.ai/api/v1")
16
16
 
17
17
  def __init__(
18
18
  self,
@@ -27,12 +27,12 @@ class OpenRouterLLM(BaseLLM):
27
27
  "OPENROUTER_CHAT_MODEL_NAME", "mistralai/mistral-7b-instruct"
28
28
  )
29
29
  super().__init__(name=name)
30
- self.base_url = base_url
30
+ self._base_url = base_url
31
31
  api_key = openrouter_api_key or os.getenv("OPENROUTER_API_KEY")
32
32
  if api_key is None:
33
33
  raise ValueError("OpenRouter API key cannot be 'None'.")
34
34
  try:
35
- self.client = openai.OpenAI(api_key=api_key, base_url=self.base_url)
35
+ self._client = openai.OpenAI(api_key=api_key, base_url=self._base_url)
36
36
  except Exception as e:
37
37
  raise ValueError(
38
38
  f"OpenRouter API client failed to initialize. Error: {e}"
@@ -41,10 +41,10 @@ class OpenRouterLLM(BaseLLM):
41
41
  self.max_tokens = max_tokens
42
42
 
43
43
  def __call__(self, messages: List[Message]) -> str:
44
- if self.client is None:
44
+ if self._client is None:
45
45
  raise ValueError("OpenRouter client is not initialized.")
46
46
  try:
47
- completion = self.client.chat.completions.create(
47
+ completion = self._client.chat.completions.create(
48
48
  model=self.name,
49
49
  messages=[m.to_openai() for m in messages],
50
50
  temperature=self.temperature,
@@ -1,5 +1,6 @@
1
1
  import os
2
2
  from typing import List, Optional
3
+ from pydantic import PrivateAttr
3
4
 
4
5
  import openai
5
6
 
@@ -10,9 +11,7 @@ from semantic_router.utils.logger import logger
10
11
 
11
12
 
12
13
  class AzureOpenAILLM(BaseLLM):
13
- client: Optional[openai.AzureOpenAI]
14
- temperature: Optional[float]
15
- max_tokens: Optional[int]
14
+ _client: Optional[openai.AzureOpenAI] = PrivateAttr(default=None)
16
15
 
17
16
  def __init__(
18
17
  self,
@@ -33,7 +32,7 @@ class AzureOpenAILLM(BaseLLM):
33
32
  if azure_endpoint is None:
34
33
  raise ValueError("Azure endpoint API key cannot be 'None'.")
35
34
  try:
36
- self.client = openai.AzureOpenAI(
35
+ self._client = openai.AzureOpenAI(
37
36
  api_key=api_key, azure_endpoint=azure_endpoint, api_version=api_version
38
37
  )
39
38
  except Exception as e:
@@ -42,10 +41,10 @@ class AzureOpenAILLM(BaseLLM):
42
41
  self.max_tokens = max_tokens
43
42
 
44
43
  def __call__(self, messages: List[Message]) -> str:
45
- if self.client is None:
44
+ if self._client is None:
46
45
  raise ValueError("AzureOpenAI client is not initialized.")
47
46
  try:
48
- completion = self.client.chat.completions.create(
47
+ completion = self._client.chat.completions.create(
49
48
  model=self.name,
50
49
  messages=[m.to_openai() for m in messages],
51
50
  temperature=self.temperature,
@@ -2,18 +2,13 @@ import json
2
2
  import re
3
3
  from typing import Any, Callable, Dict, List, Optional, Union
4
4
 
5
- from pydantic.v1 import BaseModel
5
+ from pydantic import BaseModel
6
6
 
7
7
  from semantic_router.llms import BaseLLM
8
8
  from semantic_router.schema import Message, RouteChoice
9
9
  from semantic_router.utils import function_call
10
10
  from semantic_router.utils.logger import logger
11
11
 
12
- try:
13
- from PIL.Image import Image
14
- except ImportError:
15
- pass
16
-
17
12
 
18
13
  def is_valid(route_config: str) -> bool:
19
14
  try:
@@ -45,7 +40,7 @@ def is_valid(route_config: str) -> bool:
45
40
 
46
41
  class Route(BaseModel):
47
42
  name: str
48
- utterances: Union[List[str], List[Union[Any, "Image"]]]
43
+ utterances: Union[List[str], List[Any]]
49
44
  description: Optional[str] = None
50
45
  function_schemas: Optional[List[Dict[str, Any]]] = None
51
46
  llm: Optional[BaseLLM] = None
@@ -4,7 +4,7 @@ import os
4
4
  import random
5
5
  import hashlib
6
6
  from typing import Any, Callable, Dict, List, Optional, Tuple, Union
7
- from pydantic.v1 import BaseModel, Field
7
+ from pydantic import BaseModel, Field
8
8
 
9
9
  import numpy as np
10
10
  import yaml # type: ignore
@@ -1,6 +1,6 @@
1
1
  from typing import Dict, List, Optional
2
2
  import asyncio
3
- from pydantic.v1 import Field
3
+ from pydantic import Field
4
4
 
5
5
  import numpy as np
6
6
 
@@ -1,9 +1,10 @@
1
1
  from datetime import datetime
2
2
  from difflib import Differ
3
3
  from enum import Enum
4
+ import json
4
5
  import numpy as np
5
6
  from typing import List, Optional, Union, Any, Dict, Tuple
6
- from pydantic.v1 import BaseModel, Field
7
+ from pydantic import BaseModel, Field
7
8
  from semantic_router.utils.logger import logger
8
9
  from aurelio_sdk.schema import BM25Embedding
9
10
 
@@ -126,7 +127,18 @@ class Utterance(BaseModel):
126
127
 
127
128
  def to_str(self, include_metadata: bool = False):
128
129
  if include_metadata:
129
- return f"{self.route}: {self.utterance} | {self.function_schemas} | {self.metadata}"
130
+ # we sort the dicts to ensure consistent order as we need this to compare
131
+ # stringified function schemas accurately
132
+ if self.function_schemas is not None:
133
+ function_schemas_sorted: List[str] | None = [
134
+ json.dumps(schema, sort_keys=True)
135
+ for schema in self.function_schemas
136
+ ]
137
+ else:
138
+ function_schemas_sorted = None
139
+ # we must do the same for metadata
140
+ metadata_sorted = json.dumps(self.metadata, sort_keys=True)
141
+ return f"{self.route}: {self.utterance} | {function_schemas_sorted} | {metadata_sorted}"
130
142
  return f"{self.route}: {self.utterance}"
131
143
 
132
144
  def to_diff_str(self, include_metadata: bool = False):
@@ -1,7 +1,7 @@
1
1
  import inspect
2
2
  from typing import Any, Callable, Dict, List, Optional, Union
3
3
 
4
- from pydantic.v1 import BaseModel
4
+ from pydantic import BaseModel
5
5
 
6
6
  from semantic_router.llms import BaseLLM
7
7
  from semantic_router.schema import Message, RouteChoice