robertolofaro commited on
Commit
3545fe7
·
verified ·
1 Parent(s): 196a72d

Upload 5 files

Browse files
Files changed (5) hide show
  1. .gitattributes +1 -0
  2. app.py +83 -29
  3. faiss_hnsw/index.faiss +3 -0
  4. faiss_hnsw/index.pkl +3 -0
  5. requirements.txt +1 -0
.gitattributes CHANGED
@@ -35,3 +35,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  faiss_hnsw/vector_search.index filter=lfs diff=lfs merge=lfs -text
37
  qdrant_db/collection/articles/storage.sqlite filter=lfs diff=lfs merge=lfs -text
 
 
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  faiss_hnsw/vector_search.index filter=lfs diff=lfs merge=lfs -text
37
  qdrant_db/collection/articles/storage.sqlite filter=lfs diff=lfs merge=lfs -text
38
+ faiss_hnsw/index.faiss filter=lfs diff=lfs merge=lfs -text
app.py CHANGED
@@ -7,6 +7,8 @@ import gradio as gr
7
  from llama_cpp import Llama
8
  from huggingface_hub import hf_hub_download
9
  from langchain_huggingface import HuggingFaceEmbeddings
 
 
10
 
11
  # ====================== LOGGING ======================
12
  logging.basicConfig(level=logging.INFO, format="%(levelname)s | %(message)s")
@@ -128,51 +130,78 @@ def _load_llm() -> Llama:
128
  llm = _load_llm()
129
 
130
  # ====================== RAG CACHE ======================
131
- vectorstores: dict = {}
132
 
 
 
133
 
134
  def get_vectorstore(backend_name: str):
135
  if backend_name in vectorstores:
136
  return vectorstores[backend_name]
 
137
  try:
138
  embeddings = HuggingFaceEmbeddings(
139
  model_name="BAAI/bge-small-en-v1.5",
140
  encode_kwargs={"normalize_embeddings": True},
141
  )
 
142
  if backend_name == "FAISS":
 
143
  from langchain_community.vectorstores import FAISS
144
- vs = FAISS.load_local(FAISS_PATH, embeddings, allow_dangerous_deserialization=True)
 
 
 
 
 
 
145
  elif backend_name == "Qdrant":
146
- from langchain_community.vectorstores import Qdrant
147
- vs = Qdrant(path=QDRANT_PATH, collection_name=QDRANT_COLLECTION, embeddings=embeddings)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  else:
 
149
  from langchain_community.vectorstores import FAISS
150
- vs = FAISS.load_local(FAISS_PATH, embeddings, allow_dangerous_deserialization=True)
 
 
 
 
 
151
  vectorstores[backend_name] = vs
152
  logger.info("Vector store '%s' loaded successfully", backend_name)
153
  return vs
 
154
  except Exception as exc:
155
  logger.error("Failed to load vector store '%s': %s", backend_name, exc)
 
 
156
  return None
157
 
158
-
159
  def _rag_search(vs, query: str, k: int, article_filter: str, category_filter: str):
160
  """
161
  Similarity search with optional metadata filtering.
162
-
163
- FAISS does not support dict-based server-side metadata filtering reliably
164
- across langchain versions: it either ignores the filter silently or raises.
165
- We therefore fetch a generous candidate pool and post-filter in Python.
166
-
167
- Qdrant supports native dict filtering, so we pass it directly.
168
  """
169
  want_title = None if article_filter in (None, "", "All articles in category") else article_filter
170
  want_category = None if category_filter in (None, "", "All categories") else category_filter
171
 
172
- backend_type = type(vs).__name__ # "FAISS" or "Qdrant"
173
 
174
- if backend_type == "FAISS":
175
- # Fetch a large pool, then filter in Python.
176
  pool_size = min(k * 10, 80)
177
  pool = vs.similarity_search(query, k=pool_size)
178
 
@@ -188,34 +217,59 @@ def _rag_search(vs, query: str, k: int, article_filter: str, category_filter: st
188
  break
189
 
190
  if not filtered and (want_title or want_category):
191
- # Nothing matched the filter — warn and fall back to unfiltered results.
192
  logger.warning(
193
- "FAISS post-filter (title=%r, cat=%r) matched 0 docs — "
194
- "returning unfiltered top-%d",
195
- want_title, want_category, k,
196
  )
197
  return pool[:k]
198
 
199
  logger.info(
200
  "FAISS post-filter (title=%r, cat=%r) → %d/%d docs kept",
201
- want_title, want_category, len(filtered), len(pool),
202
  )
203
  return filtered
204
 
205
  else:
206
- # Qdrant: use its native metadata filter dict.
207
- filter_dict = None
 
 
 
208
  if want_title:
209
- filter_dict = {"article_title": want_title}
 
 
 
 
 
210
  elif want_category:
211
- filter_dict = {"article_category": want_category}
 
 
 
 
 
212
 
213
- docs = vs.similarity_search(query, k=k, filter=filter_dict)
214
- logger.info(
215
- "Qdrant search (filter=%r) → %d docs", filter_dict, len(docs)
216
- )
217
- return docs
 
 
 
 
 
 
 
 
 
218
 
 
 
 
 
 
219
 
220
  # ====================== SYSTEM PROMPT ======================
221
  SYSTEM_PROMPT = """You are the reference expert for the articles contained in the training \
 
7
  from llama_cpp import Llama
8
  from huggingface_hub import hf_hub_download
9
  from langchain_huggingface import HuggingFaceEmbeddings
10
+ # Qdrant filter models
11
+ from qdrant_client.http.models import Filter, FieldCondition, MatchValue
12
 
13
  # ====================== LOGGING ======================
14
  logging.basicConfig(level=logging.INFO, format="%(levelname)s | %(message)s")
 
130
  llm = _load_llm()
131
 
132
  # ====================== RAG CACHE ======================
 
133
 
134
+ # ====================== VECTOR STORES ======================
135
+ vectorstores: dict = {}
136
 
137
  def get_vectorstore(backend_name: str):
138
  if backend_name in vectorstores:
139
  return vectorstores[backend_name]
140
+
141
  try:
142
  embeddings = HuggingFaceEmbeddings(
143
  model_name="BAAI/bge-small-en-v1.5",
144
  encode_kwargs={"normalize_embeddings": True},
145
  )
146
+
147
  if backend_name == "FAISS":
148
+ # Modern recommended import (still under langchain-community)
149
  from langchain_community.vectorstores import FAISS
150
+ vs = FAISS.load_local(
151
+ FAISS_PATH,
152
+ embeddings,
153
+ allow_dangerous_deserialization=True
154
+ )
155
+ logger.info("FAISS index loaded from %s", FAISS_PATH)
156
+
157
  elif backend_name == "Qdrant":
158
+ # Modern Qdrant integration
159
+ from langchain_qdrant import QdrantVectorStore
160
+ from qdrant_client import QdrantClient
161
+
162
+ client = QdrantClient(
163
+ path=QDRANT_PATH, # path to your qdrant_db folder
164
+ timeout=60,
165
+ )
166
+
167
+ vs = QdrantVectorStore(
168
+ client=client,
169
+ collection_name=QDRANT_COLLECTION,
170
+ embedding=embeddings,
171
+ )
172
+ logger.info("Qdrant collection '%s' loaded from %s",
173
+ QDRANT_COLLECTION, QDRANT_PATH)
174
+
175
  else:
176
+ # fallback to FAISS
177
  from langchain_community.vectorstores import FAISS
178
+ vs = FAISS.load_local(
179
+ FAISS_PATH,
180
+ embeddings,
181
+ allow_dangerous_deserialization=True
182
+ )
183
+
184
  vectorstores[backend_name] = vs
185
  logger.info("Vector store '%s' loaded successfully", backend_name)
186
  return vs
187
+
188
  except Exception as exc:
189
  logger.error("Failed to load vector store '%s': %s", backend_name, exc)
190
+ import traceback
191
+ logger.error(traceback.format_exc())
192
  return None
193
 
 
194
  def _rag_search(vs, query: str, k: int, article_filter: str, category_filter: str):
195
  """
196
  Similarity search with optional metadata filtering.
 
 
 
 
 
 
197
  """
198
  want_title = None if article_filter in (None, "", "All articles in category") else article_filter
199
  want_category = None if category_filter in (None, "", "All categories") else category_filter
200
 
201
+ backend_type = type(vs).__name__
202
 
203
+ if "FAISS" in backend_type:
204
+ # FAISS: post-filtering (unchanged)
205
  pool_size = min(k * 10, 80)
206
  pool = vs.similarity_search(query, k=pool_size)
207
 
 
217
  break
218
 
219
  if not filtered and (want_title or want_category):
 
220
  logger.warning(
221
+ "FAISS post-filter (title=%r, cat=%r) matched 0 docs — returning unfiltered top-%d",
222
+ want_title, want_category, k
 
223
  )
224
  return pool[:k]
225
 
226
  logger.info(
227
  "FAISS post-filter (title=%r, cat=%r) → %d/%d docs kept",
228
+ want_title, want_category, len(filtered), len(pool)
229
  )
230
  return filtered
231
 
232
  else:
233
+ # === QDRANT - FIXED METADATA FILTER ===
234
+ from qdrant_client.http.models import Filter, FieldCondition, MatchValue
235
+
236
+ conditions = []
237
+
238
  if want_title:
239
+ conditions.append(
240
+ FieldCondition(
241
+ key="metadata.article_title", # ← Fixed: metadata. prefix
242
+ match=MatchValue(value=want_title)
243
+ )
244
+ )
245
  elif want_category:
246
+ conditions.append(
247
+ FieldCondition(
248
+ key="metadata.article_category", # ← Fixed: metadata. prefix
249
+ match=MatchValue(value=want_category)
250
+ )
251
+ )
252
 
253
+ filter_dict = Filter(must=conditions) if conditions else None
254
+
255
+ try:
256
+ docs = vs.similarity_search(
257
+ query,
258
+ k=k,
259
+ filter=filter_dict
260
+ )
261
+ logger.info(
262
+ "Qdrant search (filter=%s) → %d docs",
263
+ "title" if want_title else "category" if want_category else "none",
264
+ len(docs)
265
+ )
266
+ return docs
267
 
268
+ except Exception as e:
269
+ logger.error("Qdrant search failed with filter: %s", e)
270
+ # Fallback: search without filter
271
+ logger.warning("Falling back to unfiltered Qdrant search")
272
+ return vs.similarity_search(query, k=k)
273
 
274
  # ====================== SYSTEM PROMPT ======================
275
  SYSTEM_PROMPT = """You are the reference expert for the articles contained in the training \
faiss_hnsw/index.faiss ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ea56fb5d03cb5c7d4b8b243f81b383fc613927a7dc98956db3e506f1f623ff28
3
+ size 553005
faiss_hnsw/index.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1d5924462b6f8b8b7ce92dac44cacb29a8f61f6de0bc1e02f42831585db5e06e
3
+ size 72893
requirements.txt CHANGED
@@ -3,6 +3,7 @@ huggingface_hub
3
  llama-cpp-python @ https://huggingface.co/robertolofaro/libraries_prebuilt/resolve/main/llama_cpp_python-0.3.23-py3-none-linux_x86_64.whl
4
  langchain_huggingface
5
  langchain-community
 
6
  chromadb
7
  faiss-cpu
8
  qdrant-client
 
3
  llama-cpp-python @ https://huggingface.co/robertolofaro/libraries_prebuilt/resolve/main/llama_cpp_python-0.3.23-py3-none-linux_x86_64.whl
4
  langchain_huggingface
5
  langchain-community
6
+ langchain-qdrant
7
  chromadb
8
  faiss-cpu
9
  qdrant-client