From abc8783477266b188719708df9b061e2807745e6 Mon Sep 17 00:00:00 2001
From: Simonas <20096648+simjak@users.noreply.github.com>
Date: Wed, 10 Jan 2024 10:54:32 +0200
Subject: [PATCH] feat: compatible with python 3.9

---
 .gitignore                         |  3 +++
 .pre-commit-config.yaml            | 14 ++++++--------
 Makefile                           |  4 ++--
 poetry.lock                        |  8 ++++----
 pyproject.toml                     |  4 ++--
 replace.py                         | 23 +++++++++++++++++++++++
 semantic_router/encoders/bm25.py   |  4 ++--
 semantic_router/encoders/cohere.py |  7 ++++---
 semantic_router/encoders/openai.py |  7 ++++---
 semantic_router/hybrid_layer.py    |  3 ++-
 semantic_router/layer.py           | 13 +++++++------
 semantic_router/llms/base.py       |  4 +++-
 semantic_router/llms/cohere.py     |  7 ++++---
 semantic_router/llms/openai.py     | 11 ++++++-----
 semantic_router/llms/openrouter.py | 13 +++++++------
 semantic_router/route.py           |  5 +++--
 semantic_router/schema.py          |  9 +++++----
 semantic_router/utils/llm.py       |  5 +++--
 test_output.txt                    |  0
 19 files changed, 90 insertions(+), 54 deletions(-)
 create mode 100644 replace.py
 delete mode 100644 test_output.txt

diff --git a/.gitignore b/.gitignore
index cb4c0022..41aac4aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,9 @@ venv/
 .idea
 **/__pycache__
 **/*.py[cod]
+node_modules
+package-lock.json
+package.json
 
 # local env files
 .env*.local
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 64e5c9e1..daf970d5 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,5 +1,5 @@
 default_language_version:
-  python: python3.12
+  python: python3.9
 repos:
   - repo: meta
     hooks:
@@ -17,14 +17,12 @@ repos:
       - id: blacken-docs
         additional_dependencies: [black==22.10.0]
 
-  - repo: https://github.com/astral-sh/ruff-pre-commit
-    rev: v0.0.290
+  - repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
+    rev: v9.11.0
     hooks:
-      - id: ruff
-        types_or: [ python, pyi, jupyter ]
-        args: [ --fix ]
-      - id: ruff-format
-        types_or: [ python, pyi, jupyter ]
+      - id: commitlint
+        stages: [commit-msg]
+        additional_dependencies: ['@commitlint/config-conventional']
 
   - repo: https://github.com/codespell-project/codespell
     rev: v2.2.4
diff --git a/Makefile b/Makefile
index aeb3d3b1..adf4eb0c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 format:
-	poetry run black .
+	poetry run black --target-version py39 .
 	poetry run ruff --select I --fix .
 
 PYTHON_FILES=.
@@ -7,7 +7,7 @@ lint: PYTHON_FILES=.
 lint_diff: PYTHON_FILES=$(shell git diff --name-only --diff-filter=d main | grep -E '\.py$$')
 
 lint lint_diff:
-	poetry run black $(PYTHON_FILES) --check
+	poetry run black --target-version py39 $(PYTHON_FILES) --check
 	poetry run ruff .
 	poetry run mypy $(PYTHON_FILES)
 
diff --git a/poetry.lock b/poetry.lock
index 3b8775c6..3131c0d8 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -440,13 +440,13 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""}
 
 [[package]]
 name = "cohere"
-version = "4.40"
+version = "4.41"
 description = "Python SDK for the Cohere API"
 optional = false
 python-versions = ">=3.8,<4.0"
 files = [
-    {file = "cohere-4.40-py3-none-any.whl", hash = "sha256:75dac8369d97fadc05901352d9db64a0ca6cd40c08423f3c4691f57eb7b131e7"},
-    {file = "cohere-4.40.tar.gz", hash = "sha256:d9e5c1fa7f80a193c03330a634954b927bf188ead7dcfdb51865480f73aebda8"},
+    {file = "cohere-4.41-py3-none-any.whl", hash = "sha256:39470cc412fa96a1c612f522d48d7d86b34b3163a04030cff83ec48ebbaff32f"},
+    {file = "cohere-4.41.tar.gz", hash = "sha256:8509ca196dc038eca81e474d3cd5896da2ea168a4d3c578b4cb6969994be34ef"},
 ]
 
 [package.dependencies]
@@ -2696,4 +2696,4 @@ hybrid = ["pinecone-text"]
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.9"
-content-hash = "bd435dd14b07738872f14483a7a781ea4a16394c4739917e487acde26235cfae"
+content-hash = "3e3b13e2493e7bef6ef1d9487d4618f834f3387a55379edf63d00f76fe4def0a"
diff --git a/pyproject.toml b/pyproject.toml
index 90b97835..bbe14c56 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -18,10 +18,10 @@ python = "^3.9"
 pydantic = "^1.8.2"
 openai = "^1.3.9"
 cohere = "^4.32"
-numpy = "^1.24.3"
-pinecone-text = {version = "^0.7.0", optional = true}
+numpy = "^1.25.2"
 colorlog = "^6.8.0"
 pyyaml = "^6.0.1"
+pinecone-text = {version = "^0.7.1", optional = true, python = "<3.12"}
 fastembed = {version = "^0.1.3", optional = true, python = "<3.12"}
 
 [tool.poetry.extras]
diff --git a/replace.py b/replace.py
new file mode 100644
index 00000000..d1fbe597
--- /dev/null
+++ b/replace.py
@@ -0,0 +1,23 @@
+import re
+import os
+
+
+def replace_type_hints(file_path):
+    with open(file_path, "rb") as file:
+        file_data = file.read()
+
+    # Decode the file data with error handling
+    file_data = file_data.decode("utf-8", errors="ignore")
+
+    # Regular expression pattern to find '| None' and replace with 'Optional'
+    file_data = re.sub(r"(\w+)\s*\|\s*None", r"Optional[\1]", file_data)
+
+    with open(file_path, "w") as file:
+        file.write(file_data)
+
+
+# Walk through the repository and update all .py files
+for root, dirs, files in os.walk("/Users/jakit/customers/aurelio/semantic-router"):
+    for file in files:
+        if file.endswith(".py"):
+            replace_type_hints(os.path.join(root, file))
diff --git a/semantic_router/encoders/bm25.py b/semantic_router/encoders/bm25.py
index e96e4d7d..11a964b6 100644
--- a/semantic_router/encoders/bm25.py
+++ b/semantic_router/encoders/bm25.py
@@ -1,11 +1,11 @@
-from typing import Any
+from typing import Any, Optional
 
 from semantic_router.encoders import BaseEncoder
 from semantic_router.utils.logger import logger
 
 
 class BM25Encoder(BaseEncoder):
-    model: Any | None = None
+    model: Optional[Any] = None
     idx_mapping: dict[int, int] | None = None
     type: str = "sparse"
 
diff --git a/semantic_router/encoders/cohere.py b/semantic_router/encoders/cohere.py
index 2f80aaaf..ec8ee0f8 100644
--- a/semantic_router/encoders/cohere.py
+++ b/semantic_router/encoders/cohere.py
@@ -1,4 +1,5 @@
 import os
+from typing import Optional
 
 import cohere
 
@@ -6,13 +7,13 @@ from semantic_router.encoders import BaseEncoder
 
 
 class CohereEncoder(BaseEncoder):
-    client: cohere.Client | None = None
+    client: Optional[cohere.Client] = None
     type: str = "cohere"
 
     def __init__(
         self,
-        name: str | None = None,
-        cohere_api_key: str | None = None,
+        name: Optional[str] = None,
+        cohere_api_key: Optional[str] = None,
         score_threshold: float = 0.3,
     ):
         if name is None:
diff --git a/semantic_router/encoders/openai.py b/semantic_router/encoders/openai.py
index 4ec87638..4504fefe 100644
--- a/semantic_router/encoders/openai.py
+++ b/semantic_router/encoders/openai.py
@@ -7,16 +7,17 @@ from openai.types import CreateEmbeddingResponse
 
 from semantic_router.encoders import BaseEncoder
 from semantic_router.utils.logger import logger
+from typing import Optional
 
 
 class OpenAIEncoder(BaseEncoder):
-    client: openai.Client | None
+    client: Optional[openai.Client]
     type: str = "openai"
 
     def __init__(
         self,
-        name: str | None = None,
-        openai_api_key: str | None = None,
+        name: Optional[str] = None,
+        openai_api_key: Optional[str] = None,
         score_threshold: float = 0.82,
     ):
         if name is None:
diff --git a/semantic_router/hybrid_layer.py b/semantic_router/hybrid_layer.py
index cd9f7ccb..06862a63 100644
--- a/semantic_router/hybrid_layer.py
+++ b/semantic_router/hybrid_layer.py
@@ -7,6 +7,7 @@ from semantic_router.encoders import (
 )
 from semantic_router.route import Route
 from semantic_router.utils.logger import logger
+from typing import Optional
 
 
 class HybridRouteLayer:
@@ -29,7 +30,7 @@ class HybridRouteLayer:
             #     self._add_route(route=route)
             self._add_routes(routes)
 
-    def __call__(self, text: str) -> str | None:
+    def __call__(self, text: str) -> Optional[str]:
         results = self._query(text)
         top_class, top_class_scores = self._semantic_classify(results)
         passed = self._pass_threshold(top_class_scores, self.score_threshold)
diff --git a/semantic_router/layer.py b/semantic_router/layer.py
index 08261756..e6a214f9 100644
--- a/semantic_router/layer.py
+++ b/semantic_router/layer.py
@@ -1,5 +1,6 @@
 import json
 import os
+from typing import Optional
 
 import numpy as np
 import yaml
@@ -52,7 +53,7 @@ class LayerConfig:
         self,
         routes: list[Route] = [],
         encoder_type: str = "openai",
-        encoder_name: str | None = None,
+        encoder_name: Optional[str] = None,
     ):
         self.encoder_type = encoder_type
         if encoder_name is None:
@@ -131,7 +132,7 @@ class LayerConfig:
         self.routes.append(route)
         logger.info(f"Added route `{route.name}`")
 
-    def get(self, name: str) -> Route | None:
+    def get(self, name: str) -> Optional[Route]:
         for route in self.routes:
             if route.name == name:
                 return route
@@ -147,15 +148,15 @@ class LayerConfig:
 
 
 class RouteLayer:
-    index: np.ndarray | None = None
-    categories: np.ndarray | None = None
+    index: Optional[np.ndarray] = None
+    categories: Optional[np.ndarray] = None
     score_threshold: float
     encoder: BaseEncoder
 
     def __init__(
         self,
-        encoder: BaseEncoder | None = None,
-        llm: BaseLLM | None = None,
+        encoder: Optional[BaseEncoder] = None,
+        llm: Optional[BaseLLM] = None,
         routes: list[Route] | None = None,
     ):
         logger.info("Initializing RouteLayer")
diff --git a/semantic_router/llms/base.py b/semantic_router/llms/base.py
index 51db1fd0..bf5f29b6 100644
--- a/semantic_router/llms/base.py
+++ b/semantic_router/llms/base.py
@@ -1,3 +1,5 @@
+from typing import Optional
+
 from pydantic import BaseModel
 
 from semantic_router.schema import Message
@@ -9,5 +11,5 @@ class BaseLLM(BaseModel):
     class Config:
         arbitrary_types_allowed = True
 
-    def __call__(self, messages: list[Message]) -> str | None:
+    def __call__(self, messages: list[Message]) -> Optional[str]:
         raise NotImplementedError("Subclasses must implement this method")
diff --git a/semantic_router/llms/cohere.py b/semantic_router/llms/cohere.py
index 77581700..0ec21f35 100644
--- a/semantic_router/llms/cohere.py
+++ b/semantic_router/llms/cohere.py
@@ -1,4 +1,5 @@
 import os
+from typing import Optional
 
 import cohere
 
@@ -7,12 +8,12 @@ from semantic_router.schema import Message
 
 
 class CohereLLM(BaseLLM):
-    client: cohere.Client | None = None
+    client: Optional[cohere.Client] = None
 
     def __init__(
         self,
-        name: str | None = None,
-        cohere_api_key: str | None = None,
+        name: Optional[str] = None,
+        cohere_api_key: Optional[str] = None,
     ):
         if name is None:
             name = os.getenv("COHERE_CHAT_MODEL_NAME", "command")
diff --git a/semantic_router/llms/openai.py b/semantic_router/llms/openai.py
index 43ddd642..d3b215bf 100644
--- a/semantic_router/llms/openai.py
+++ b/semantic_router/llms/openai.py
@@ -5,17 +5,18 @@ import openai
 from semantic_router.llms import BaseLLM
 from semantic_router.schema import Message
 from semantic_router.utils.logger import logger
+from typing import Optional
 
 
 class OpenAILLM(BaseLLM):
-    client: openai.OpenAI | None
-    temperature: float | None
-    max_tokens: int | None
+    client: Optional[openai.OpenAI]
+    temperature: Optional[float]
+    max_tokens: Optional[int]
 
     def __init__(
         self,
-        name: str | None = None,
-        openai_api_key: str | None = None,
+        name: Optional[str] = None,
+        openai_api_key: Optional[str] = None,
         temperature: float = 0.01,
         max_tokens: int = 200,
     ):
diff --git a/semantic_router/llms/openrouter.py b/semantic_router/llms/openrouter.py
index 587eeb12..6130e0a7 100644
--- a/semantic_router/llms/openrouter.py
+++ b/semantic_router/llms/openrouter.py
@@ -5,18 +5,19 @@ import openai
 from semantic_router.llms import BaseLLM
 from semantic_router.schema import Message
 from semantic_router.utils.logger import logger
+from typing import Optional
 
 
 class OpenRouterLLM(BaseLLM):
-    client: openai.OpenAI | None
-    base_url: str | None
-    temperature: float | None
-    max_tokens: int | None
+    client: Optional[openai.OpenAI]
+    base_url: Optional[str]
+    temperature: Optional[float]
+    max_tokens: Optional[int]
 
     def __init__(
         self,
-        name: str | None = None,
-        openrouter_api_key: str | None = None,
+        name: Optional[str] = None,
+        openrouter_api_key: Optional[str] = None,
         base_url: str = "https://openrouter.ai/api/v1",
         temperature: float = 0.01,
         max_tokens: int = 200,
diff --git a/semantic_router/route.py b/semantic_router/route.py
index 0d8269f0..c2b9b3dc 100644
--- a/semantic_router/route.py
+++ b/semantic_router/route.py
@@ -8,6 +8,7 @@ from semantic_router.llms import BaseLLM
 from semantic_router.schema import Message, RouteChoice
 from semantic_router.utils import function_call
 from semantic_router.utils.logger import logger
+from typing import Optional
 
 
 def is_valid(route_config: str) -> bool:
@@ -41,9 +42,9 @@ def is_valid(route_config: str) -> bool:
 class Route(BaseModel):
     name: str
     utterances: list[str]
-    description: str | None = None
+    description: Optional[str] = None
     function_schema: dict[str, Any] | None = None
-    llm: BaseLLM | None = None
+    llm: Optional[BaseLLM] = None
 
     def __call__(self, query: str) -> RouteChoice:
         if self.function_schema:
diff --git a/semantic_router/schema.py b/semantic_router/schema.py
index 5e94c23b..9505df24 100644
--- a/semantic_router/schema.py
+++ b/semantic_router/schema.py
@@ -10,6 +10,7 @@ from semantic_router.encoders import (
     OpenAIEncoder,
 )
 from semantic_router.utils.splitters import semantic_splitter
+from typing import Optional
 
 
 class EncoderType(Enum):
@@ -20,17 +21,17 @@ class EncoderType(Enum):
 
 
 class RouteChoice(BaseModel):
-    name: str | None = None
-    function_call: dict | None = None
+    name: Optional[str] = None
+    function_call: Optional[dict] = None
 
 
 @dataclass
 class Encoder:
     type: EncoderType
-    name: str | None
+    name: Optional[str]
     model: BaseEncoder
 
-    def __init__(self, type: str, name: str | None):
+    def __init__(self, type: str, name: Optional[str]):
         self.type = EncoderType(type)
         self.name = name
         if self.type == EncoderType.HUGGINGFACE:
diff --git a/semantic_router/utils/llm.py b/semantic_router/utils/llm.py
index e92c1bcf..f0db13c8 100644
--- a/semantic_router/utils/llm.py
+++ b/semantic_router/utils/llm.py
@@ -3,9 +3,10 @@ import os
 import openai
 
 from semantic_router.utils.logger import logger
+from typing import Optional
 
 
-def llm(prompt: str) -> str | None:
+def llm(prompt: str) -> Optional[str]:
     try:
         client = openai.OpenAI(
             base_url="https://openrouter.ai/api/v1",
@@ -35,7 +36,7 @@ def llm(prompt: str) -> str | None:
 
 
 # TODO integrate async LLM function
-# async def allm(prompt: str) -> str | None:
+# async def allm(prompt: str) -> Optional[str]:
 #     try:
 #         client = openai.AsyncOpenAI(
 #             base_url="https://openrouter.ai/api/v1",
diff --git a/test_output.txt b/test_output.txt
deleted file mode 100644
index e69de29b..00000000
-- 
GitLab