NavyDevilDoc commited on
Commit
b6c13b7
·
verified ·
1 Parent(s): 80bc233

Update src/rag_engine.py

Browse files
Files changed (1) hide show
  1. src/rag_engine.py +33 -17
src/rag_engine.py CHANGED
@@ -5,6 +5,7 @@ from typing import List, Literal, Tuple
5
 
6
  # --- LANGCHAIN & DB IMPORTS ---
7
  from langchain_huggingface import HuggingFaceEmbeddings
 
8
  from langchain_core.documents import Document
9
  from langchain_text_splitters import MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter
10
  from sentence_transformers import CrossEncoder
@@ -30,14 +31,29 @@ logger = logging.getLogger(__name__)
30
  _embedding_func = None
31
  _rerank_model = None
32
 
33
- def get_embedding_func():
34
- """Lazy loads the embedding model to save startup resources."""
35
- global _embedding_func
36
- if _embedding_func is None:
37
- logger.info(f"⏳ Loading Embedding Model: {EMBED_MODEL_NAME}...")
38
- _embedding_func = HuggingFaceEmbeddings(model_name=EMBED_MODEL_NAME)
39
- logger.info(" Embedding Model Loaded.")
40
- return _embedding_func
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
  def get_rerank_model():
43
  """Lazy loads the Cross-Encoder model."""
@@ -150,7 +166,7 @@ def save_uploaded_file(uploaded_file, username: str = "default") -> str:
150
  logger.error(f"Error saving file: {e}")
151
  return None
152
 
153
- def process_and_add_text(text: str, source_name: str, username: str, index_name: str) -> Tuple[bool, str]:
154
  """Ingests raw text (Flattener) -> Saves Backup to Disk -> Uploads to Pinecone."""
155
  if not PINECONE_KEY or not index_name: return False, "Pinecone Configuration Missing."
156
 
@@ -165,7 +181,7 @@ def process_and_add_text(text: str, source_name: str, username: str, index_name:
165
 
166
  # 2. UPLOAD TO PINECONE
167
  pm = PineconeManager(PINECONE_KEY)
168
- emb_fn = get_embedding_func()
169
 
170
  # Create Document
171
  doc = Document(
@@ -182,8 +198,7 @@ def process_and_add_text(text: str, source_name: str, username: str, index_name:
182
  logger.error(f"Error indexing text: {e}")
183
  return False, str(e)
184
 
185
- def ingest_file(file_path: str, username: str, index_name: str, strategy: str = "paragraph") -> Tuple[bool, str]:
186
- """Chunks File -> Scans Acronyms -> Uploads to Pinecone."""
187
  if not PINECONE_KEY or not index_name: return False, "Pinecone Configuration Missing."
188
 
189
  try:
@@ -198,7 +213,7 @@ def ingest_file(file_path: str, username: str, index_name: str, strategy: str =
198
 
199
  # 3. Pinecone Safety Check (Dynamic)
200
  pm = PineconeManager(PINECONE_KEY)
201
- emb_fn = get_embedding_func()
202
 
203
  # DYNAMIC CHECK: Generate a test embedding to see true dimension
204
  # This allows you to swap models in CONFIGURATION later without breaking code
@@ -209,9 +224,10 @@ def ingest_file(file_path: str, username: str, index_name: str, strategy: str =
209
  return False, f"Dimension Mismatch! Index '{index_name}' expects {model_dim}d vectors (based on current model), but found incompatible dimensions."
210
 
211
  # 4. Upload
212
- emb_fn = get_embedding_func()
213
  vstore = pm.get_vectorstore(index_name, emb_fn, namespace=username)
214
- vstore.add_documents(docs)
 
215
 
216
  return True, f"Successfully indexed {len(docs)} chunks."
217
 
@@ -219,7 +235,7 @@ def ingest_file(file_path: str, username: str, index_name: str, strategy: str =
219
  logger.error(f"Ingestion failed: {e}")
220
  return False, str(e)
221
 
222
- def search_knowledge_base(query: str, username: str, index_name: str, k: int = 10, final_k: int = 4) -> List[Document]:
223
  """Retrieves from Pinecone -> Reranks."""
224
  if not PINECONE_KEY or not index_name: return []
225
 
@@ -230,7 +246,7 @@ def search_knowledge_base(query: str, username: str, index_name: str, k: int = 1
230
 
231
  # 2. Vector Search
232
  pm = PineconeManager(PINECONE_KEY)
233
- emb_fn = get_embedding_func()
234
  vstore = pm.get_vectorstore(index_name, emb_fn, namespace=username)
235
 
236
  results = vstore.similarity_search(expanded_query, k=k)
 
5
 
6
  # --- LANGCHAIN & DB IMPORTS ---
7
  from langchain_huggingface import HuggingFaceEmbeddings
8
+ from langchain_openai import OpenAIEmbeddings
9
  from langchain_core.documents import Document
10
  from langchain_text_splitters import MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter
11
  from sentence_transformers import CrossEncoder
 
31
  _embedding_func = None
32
  _rerank_model = None
33
 
34
+ def get_embedding_func(model_name: str = "sentence-transformers/all-MiniLM-L6-v2"):
35
+ """
36
+ Dynamically loads the correct embedding model based on the selection.
37
+ """
38
+ try:
39
+ # 1. OpenAI Models
40
+ if "openai" in model_name.lower():
41
+ if not os.getenv("OPENAI_API_KEY"):
42
+ raise ValueError("OpenAI API Key not found.")
43
+
44
+ # Map friendly names to actual API model names if needed
45
+ # But usually we just pass the exact string like "text-embedding-3-small"
46
+ return OpenAIEmbeddings(model=model_name)
47
+
48
+ # 2. Hugging Face Models (Local / CPU-friendly)
49
+ else:
50
+ # Default to all-MiniLM if something weird is passed, or use the specific HF model
51
+ return HuggingFaceEmbeddings(model_name=model_name)
52
+
53
+ except Exception as e:
54
+ logger.error(f"Failed to load embedding model '{model_name}': {e}")
55
+ # Fallback to the safe default if everything explodes
56
+ return HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
57
 
58
  def get_rerank_model():
59
  """Lazy loads the Cross-Encoder model."""
 
166
  logger.error(f"Error saving file: {e}")
167
  return None
168
 
169
+ def process_and_add_text(text: str, source_name: str, username: str, embed_model_name: str, index_name: str) -> Tuple[bool, str]:
170
  """Ingests raw text (Flattener) -> Saves Backup to Disk -> Uploads to Pinecone."""
171
  if not PINECONE_KEY or not index_name: return False, "Pinecone Configuration Missing."
172
 
 
181
 
182
  # 2. UPLOAD TO PINECONE
183
  pm = PineconeManager(PINECONE_KEY)
184
+ emb_fn = get_embedding_func(embed_model_name)
185
 
186
  # Create Document
187
  doc = Document(
 
198
  logger.error(f"Error indexing text: {e}")
199
  return False, str(e)
200
 
201
+ def ingest_file(file_path: str, username: str, index_name: str, embed_model_name: str, strategy: str = "paragraph") -> Tuple[bool, str]: """Chunks File -> Scans Acronyms -> Uploads to Pinecone."""
 
202
  if not PINECONE_KEY or not index_name: return False, "Pinecone Configuration Missing."
203
 
204
  try:
 
213
 
214
  # 3. Pinecone Safety Check (Dynamic)
215
  pm = PineconeManager(PINECONE_KEY)
216
+ emb_fn = get_embedding_func(embed_model_name)
217
 
218
  # DYNAMIC CHECK: Generate a test embedding to see true dimension
219
  # This allows you to swap models in CONFIGURATION later without breaking code
 
224
  return False, f"Dimension Mismatch! Index '{index_name}' expects {model_dim}d vectors (based on current model), but found incompatible dimensions."
225
 
226
  # 4. Upload
227
+ emb_fn = get_embedding_func(embed_model_name)
228
  vstore = pm.get_vectorstore(index_name, emb_fn, namespace=username)
229
+ custom_ids = [f"{doc.metadata.get('source', 'doc')}_{i}" for i, doc in enumerate(docs)]
230
+ vstore.add_documents(docs, ids=custom_ids)
231
 
232
  return True, f"Successfully indexed {len(docs)} chunks."
233
 
 
235
  logger.error(f"Ingestion failed: {e}")
236
  return False, str(e)
237
 
238
+ def search_knowledge_base(query: str, username: str, index_name: str, embed_model_name: str, k: int = 10, final_k: int = 4) -> List[Document]:
239
  """Retrieves from Pinecone -> Reranks."""
240
  if not PINECONE_KEY or not index_name: return []
241
 
 
246
 
247
  # 2. Vector Search
248
  pm = PineconeManager(PINECONE_KEY)
249
+ emb_fn = get_embedding_func(embed_model_name)
250
  vstore = pm.get_vectorstore(index_name, emb_fn, namespace=username)
251
 
252
  results = vstore.similarity_search(expanded_query, k=k)