Skip to content

Commit bae0ff8

Browse files
committed
refactor: type hints
1 parent 1989605 commit bae0ff8

File tree

5 files changed

+77
-70
lines changed

5 files changed

+77
-70
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ repos:
2929
hooks:
3030
- id: pylint
3131
name: pylint
32-
entry: ./run_pylint.sh
32+
entry: ./scripts/run_pylint.sh
3333
language: script
3434
types: [python]
3535
- repo: https://github.com/PyCQA/bandit

models/conf.py

Lines changed: 60 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# -*- coding: utf-8 -*-
2-
# pylint: disable=no-member
3-
# pylint: disable=E0213,C0103
2+
# pylint: disable=E0213,C0103,C0301
43
"""
54
Configuration for Lambda functions.
65
@@ -34,6 +33,8 @@ def load_version() -> Dict[str, str]:
3433
"""Stringify the __version__ module."""
3534
version_file_path = os.path.join(HERE, "__version__.py")
3635
spec = importlib.util.spec_from_file_location("__version__", version_file_path)
36+
if spec is None or spec.loader is None:
37+
raise ModelConfigurationError(f"Could not load version file: {version_file_path}")
3738
version_module = importlib.util.module_from_spec(spec)
3839
spec.loader.exec_module(version_module)
3940
return version_module.__dict__
@@ -74,22 +75,22 @@ class SettingsDefaults:
7475

7576
LANGCHAIN_MEMORY_KEY = "chat_history"
7677

77-
PINECONE_API_KEY: SecretStr = SecretStr(None)
78-
PINECONE_ENVIRONMENT = "gcp-starter"
79-
PINECONE_INDEX_NAME = "openai-embeddings"
80-
PINECONE_VECTORSTORE_TEXT_KEY = "lc_id"
81-
PINECONE_METRIC = "dotproduct"
82-
PINECONE_DIMENSIONS = 1536
83-
84-
OPENAI_API_ORGANIZATION: str = None
85-
OPENAI_API_KEY: SecretStr = SecretStr(None)
86-
OPENAI_ENDPOINT_IMAGE_N = 4
87-
OPENAI_ENDPOINT_IMAGE_SIZE = "1024x768"
88-
OPENAI_CHAT_CACHE = True
89-
OPENAI_CHAT_MODEL_NAME = "gpt-4"
90-
OPENAI_PROMPT_MODEL_NAME = "gpt-4"
91-
OPENAI_CHAT_TEMPERATURE = 0.0
92-
OPENAI_CHAT_MAX_RETRIES = 3
78+
PINECONE_API_KEY: Optional[SecretStr] = SecretStr(os.environ.get("PINECONE_API_KEY")) if os.environ.get("PINECONE_API_KEY") else None # type: ignore[assignment]
79+
PINECONE_ENVIRONMENT = os.environ.get("PINECONE_ENVIRONMENT", "gcp-starter")
80+
PINECONE_INDEX_NAME = os.environ.get("PINECONE_INDEX_NAME", "openai-embeddings")
81+
PINECONE_VECTORSTORE_TEXT_KEY = os.environ.get("PINECONE_VECTORSTORE_TEXT_KEY", "lc_id")
82+
PINECONE_METRIC = os.environ.get("PINECONE_METRIC", "dotproduct")
83+
PINECONE_DIMENSIONS = int(os.environ.get("PINECONE_DIMENSIONS", 1536))
84+
85+
OPENAI_API_ORGANIZATION: Optional[str] = os.environ.get("OPENAI_API_ORGANIZATION", None)
86+
OPENAI_API_KEY: Optional[SecretStr] = SecretStr(os.environ.get("OPENAI_API_KEY")) if os.environ.get("OPENAI_API_KEY") else None # type: ignore[assignment]
87+
OPENAI_ENDPOINT_IMAGE_N = int(os.environ.get("OPENAI_ENDPOINT_IMAGE_N", 4))
88+
OPENAI_ENDPOINT_IMAGE_SIZE = os.environ.get("OPENAI_ENDPOINT_IMAGE_SIZE", "1024x768")
89+
OPENAI_CHAT_CACHE = os.environ.get("OPENAI_CHAT_CACHE", "true").lower() in ["true", "1", "t", "y", "yes"]
90+
OPENAI_CHAT_MODEL_NAME = os.environ.get("OPENAI_CHAT_MODEL_NAME", "gpt-4o-mini")
91+
OPENAI_PROMPT_MODEL_NAME = os.environ.get("OPENAI_PROMPT_MODEL_NAME", "gpt-4o-mini")
92+
OPENAI_CHAT_TEMPERATURE = float(os.environ.get("OPENAI_CHAT_TEMPERATURE", 0.0))
93+
OPENAI_CHAT_MAX_RETRIES = int(os.environ.get("OPENAI_CHAT_MAX_RETRIES", 3))
9394

9495
@classmethod
9596
def to_dict(cls):
@@ -123,7 +124,7 @@ def empty_str_to_int_default(v: str, default: int) -> int:
123124
class Settings(BaseSettings):
124125
"""Settings for Lambda functions"""
125126

126-
_dump: dict = None
127+
_dump: Optional[dict] = None
127128
_pinecone_api_key_source: str = "unset"
128129
_openai_api_key_source: str = "unset"
129130
_initialized: bool = False
@@ -142,59 +143,38 @@ def __init__(self, **data: Any):
142143

143144
debug_mode: Optional[bool] = Field(
144145
SettingsDefaults.DEBUG_MODE,
145-
env="DEBUG_MODE",
146-
pre=True,
147-
getter=lambda v: empty_str_to_bool_default(v, SettingsDefaults.DEBUG_MODE),
148146
)
149147
dump_defaults: Optional[bool] = Field(
150148
SettingsDefaults.DUMP_DEFAULTS,
151-
env="DUMP_DEFAULTS",
152-
pre=True,
153-
getter=lambda v: empty_str_to_bool_default(v, SettingsDefaults.DUMP_DEFAULTS),
154149
)
155150

156-
langchain_memory_key: Optional[str] = Field(SettingsDefaults.LANGCHAIN_MEMORY_KEY, env="LANGCHAIN_MEMORY_KEY")
151+
langchain_memory_key: Optional[str] = Field(SettingsDefaults.LANGCHAIN_MEMORY_KEY)
157152

158-
openai_api_organization: Optional[str] = Field(
159-
SettingsDefaults.OPENAI_API_ORGANIZATION, env="OPENAI_API_ORGANIZATION"
160-
)
161-
openai_api_key: Optional[SecretStr] = Field(SettingsDefaults.OPENAI_API_KEY, env="OPENAI_API_KEY")
162-
openai_endpoint_image_n: Optional[int] = Field(
163-
SettingsDefaults.OPENAI_ENDPOINT_IMAGE_N, env="OPENAI_ENDPOINT_IMAGE_N"
164-
)
165-
openai_endpoint_image_size: Optional[str] = Field(
166-
SettingsDefaults.OPENAI_ENDPOINT_IMAGE_SIZE, env="OPENAI_ENDPOINT_IMAGE_SIZE"
167-
)
153+
openai_api_organization: Optional[str] = Field(SettingsDefaults.OPENAI_API_ORGANIZATION)
154+
openai_api_key: Optional[SecretStr] = Field(SettingsDefaults.OPENAI_API_KEY)
155+
openai_endpoint_image_n: Optional[int] = Field(SettingsDefaults.OPENAI_ENDPOINT_IMAGE_N)
156+
openai_endpoint_image_size: Optional[str] = Field(SettingsDefaults.OPENAI_ENDPOINT_IMAGE_SIZE)
168157
openai_chat_cache: Optional[bool] = Field(
169158
SettingsDefaults.OPENAI_CHAT_CACHE,
170-
env="OPENAI_CHAT_CACHE",
171-
pre=True,
172-
getter=lambda v: empty_str_to_bool_default(v, SettingsDefaults.OPENAI_CHAT_CACHE),
173-
)
174-
openai_chat_model_name: Optional[str] = Field(SettingsDefaults.OPENAI_CHAT_MODEL_NAME, env="OPENAI_CHAT_MODEL_NAME")
175-
openai_prompt_model_name: Optional[str] = Field(
176-
SettingsDefaults.OPENAI_PROMPT_MODEL_NAME, env="OPENAI_PROMPT_MODEL_NAME"
177159
)
160+
openai_chat_model_name: str = Field(SettingsDefaults.OPENAI_CHAT_MODEL_NAME)
161+
openai_prompt_model_name: str = Field(SettingsDefaults.OPENAI_PROMPT_MODEL_NAME)
178162
openai_chat_temperature: Optional[float] = Field(
179163
SettingsDefaults.OPENAI_CHAT_TEMPERATURE,
180-
env="OPENAI_CHAT_TEMPERATURE",
181164
ge=0.0,
182165
le=1.0,
183166
)
184167
openai_chat_max_retries: Optional[int] = Field(
185168
SettingsDefaults.OPENAI_CHAT_MAX_RETRIES,
186-
env="OPENAI_CHAT_MAX_RETRIES",
187169
ge=0,
188170
)
189171

190-
pinecone_api_key: Optional[SecretStr] = Field(SettingsDefaults.PINECONE_API_KEY, env="PINECONE_API_KEY")
191-
pinecone_environment: Optional[str] = Field(SettingsDefaults.PINECONE_ENVIRONMENT, env="PINECONE_ENVIRONMENT")
192-
pinecone_index_name: Optional[str] = Field(SettingsDefaults.PINECONE_INDEX_NAME, env="PINECONE_INDEX_NAME")
193-
pinecone_vectorstore_text_key: Optional[str] = Field(
194-
SettingsDefaults.PINECONE_VECTORSTORE_TEXT_KEY, env="PINECONE_VECTORSTORE_TEXT_KEY"
195-
)
196-
pinecone_metric: Optional[str] = Field(SettingsDefaults.PINECONE_METRIC, env="PINECONE_METRIC")
197-
pinecone_dimensions: Optional[int] = Field(SettingsDefaults.PINECONE_DIMENSIONS, env="PINECONE_DIMENSIONS", gt=0)
172+
pinecone_api_key: Optional[SecretStr] = Field(SettingsDefaults.PINECONE_API_KEY)
173+
pinecone_environment: Optional[str] = Field(SettingsDefaults.PINECONE_ENVIRONMENT)
174+
pinecone_index_name: Optional[str] = Field(SettingsDefaults.PINECONE_INDEX_NAME)
175+
pinecone_vectorstore_text_key: Optional[str] = Field(SettingsDefaults.PINECONE_VECTORSTORE_TEXT_KEY)
176+
pinecone_metric: Optional[str] = Field(SettingsDefaults.PINECONE_METRIC)
177+
pinecone_dimensions: Optional[int] = Field(SettingsDefaults.PINECONE_DIMENSIONS, gt=0)
198178

199179
@property
200180
def pinecone_api_key_source(self) -> str:
@@ -331,18 +311,33 @@ def check_langchain_memory_key(cls, v) -> str:
331311
return v
332312

333313
@field_validator("openai_api_organization")
334-
def check_openai_api_organization(cls, v) -> str:
314+
def check_openai_api_organization(cls, v) -> Optional[str]:
335315
"""Check openai_api_organization"""
336-
if v in [None, ""]:
337-
return SettingsDefaults.OPENAI_API_ORGANIZATION
338-
return v
316+
if isinstance(v, str) and len(v.strip()) > 0:
317+
return v.strip()
318+
if (
319+
isinstance(SettingsDefaults.OPENAI_API_ORGANIZATION, str)
320+
and len(SettingsDefaults.OPENAI_API_ORGANIZATION.strip()) > 0
321+
):
322+
return SettingsDefaults.OPENAI_API_ORGANIZATION.strip()
323+
return None
339324

340325
@field_validator("openai_api_key")
341326
def check_openai_api_key(cls, v) -> SecretStr:
342327
"""Check openai_api_key"""
343-
if v in [None, ""]:
328+
if v is None:
329+
raise ModelValueError(
330+
"OpenAI API key is required. Please set the OPENAI_API_KEY environment variable or pass it as an argument."
331+
)
332+
if isinstance(v, SecretStr):
333+
return v
334+
if isinstance(v, str):
335+
return SecretStr(v)
336+
if isinstance(SettingsDefaults.OPENAI_API_KEY, SecretStr):
344337
return SettingsDefaults.OPENAI_API_KEY
345-
return v
338+
raise ModelValueError(
339+
"OpenAI API key must be a string or SecretStr. Please set the OPENAI_API_KEY environment variable or pass it as an argument."
340+
)
346341

347342
@field_validator("openai_endpoint_image_n")
348343
def check_openai_endpoint_image_n(cls, v) -> int:
@@ -400,9 +395,13 @@ def check_openai_chat_max_retries(cls, v) -> int:
400395
@field_validator("pinecone_api_key")
401396
def check_pinecone_api_key(cls, v) -> SecretStr:
402397
"""Check pinecone_api_key"""
403-
if v in [None, ""]:
398+
if isinstance(v, SecretStr):
399+
return v
400+
if isinstance(SettingsDefaults.PINECONE_API_KEY, SecretStr):
404401
return SettingsDefaults.PINECONE_API_KEY
405-
return v
402+
raise ModelValueError(
403+
"Pinecone API key must be a string or SecretStr. Please set the PINECONE_API_KEY environment variable or pass it as an argument."
404+
)
406405

407406
@field_validator("pinecone_environment")
408407
def check_pinecone_environment(cls, v) -> str:

models/hybrid_search_retreiver.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
"""
1818

1919
# general purpose imports
20+
import json
2021
import logging
2122
import textwrap
22-
from typing import Union
23+
from typing import Optional, Union
2324

2425
# embedding
2526
from langchain.globals import set_llm_cache
@@ -51,10 +52,10 @@
5152
class HybridSearchRetriever:
5253
"""Hybrid Search Retriever"""
5354

54-
_chat: ChatOpenAI = None
55-
_b25_encoder: BM25Encoder = None
56-
_pinecone: PineconeIndex = None
57-
_retriever: PineconeHybridSearchRetriever = None
55+
_chat: Optional[ChatOpenAI] = None
56+
_b25_encoder: Optional[BM25Encoder] = None
57+
_pinecone: Optional[PineconeIndex] = None
58+
_retriever: Optional[PineconeHybridSearchRetriever] = None
5859

5960
def __init__(self):
6061
"""Constructor"""
@@ -73,7 +74,7 @@ def chat(self) -> ChatOpenAI:
7374
"""ChatOpenAI lazy read-only property."""
7475
if self._chat is None:
7576
self._chat = ChatOpenAI(
76-
api_key=settings.openai_api_key.get_secret_value(), # pylint: disable=no-member
77+
api_key=settings.openai_api_key,
7778
organization=settings.openai_api_organization,
7879
cache=settings.openai_chat_cache,
7980
max_retries=settings.openai_chat_max_retries,
@@ -150,6 +151,11 @@ def rag(self, human_message: Union[str, HumanMessage]):
150151
# ---------------------------------------------------------------------
151152
# 1.) Retrieve relevant documents from Pinecone vector database
152153
# ---------------------------------------------------------------------
154+
if not isinstance(human_message.content, str):
155+
if isinstance(human_message.content, (dict, list)):
156+
human_message.content = json.dumps(human_message.content)
157+
else:
158+
human_message.content = str(human_message.content)
153159
documents = self.pinecone.vector_store.similarity_search(query=human_message.content)
154160

155161
# Extract the text from the documents
@@ -181,7 +187,7 @@ def rag(self, human_message: Union[str, HumanMessage]):
181187
"\n <============================= END ================================>\n\n",
182188
star_line,
183189
len(documents),
184-
len(system_message.content.split()),
190+
len(system_message.content.split()) if isinstance(system_message.content, str) else 0,
185191
system_message.content,
186192
)
187193

requirements/base.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
python-decouple==3.8
2+
isort==6.0.1
33
langchainhub==0.1.21
44
langchain-openai==0.3.18
55
langchain-experimental==0.3.4
@@ -11,6 +11,8 @@ pinecone-client==5.0.1
1111
pinecone-text==0.10.0
1212
pydantic==2.10.4
1313
pydantic-settings==2.9.1
14+
pylint==3.3.7
15+
python-decouple==3.8
1416
python-dotenv==1.1.0
1517
pypdf==5.6.0
1618
tiktoken==0.9.0
File renamed without changes.

0 commit comments

Comments
 (0)