singhankur01 commited on
Commit
b54e384
·
verified ·
1 Parent(s): 37bcdcb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +85 -67
app.py CHANGED
@@ -7,6 +7,9 @@ import asyncio
7
  from contextlib import asynccontextmanager
8
  from dotenv import load_dotenv
9
  from operator import itemgetter
 
 
 
10
 
11
  from fastapi import FastAPI, Depends, HTTPException, Header
12
  from fastapi.responses import JSONResponse
@@ -49,7 +52,7 @@ load_dotenv()
49
 
50
  vector_cache = {}
51
  ml_models = {}
52
-
53
  PRELOAD_URLS = [
54
  "https://hackrx.blob.core.windows.net/assets/Arogya%20Sanjeevani%20Policy%20-%20CIN%20-%20U10200WB1906GOI001713%201.pdf?sv=2023-01-03&st=2025-07-21T08%3A29%3A02Z&se=2025-09-22T08%3A29%3A00Z&sr=b&sp=r&sig=nzrz1K9Iurt%2BBXom%2FB%2BMPTFMFP3PRnIvEsipAX10Ig4%3D",
55
  "https://hackrx.blob.core.windows.net/assets/Super_Splendor_(Feb_2023).pdf?sv=2023-01-03&st=2025-07-21T08%3A10%3A00Z&se=2025-09-22T08%3A10%3A00Z&sr=b&sp=r&sig=vhHrl63YtrEOCsAy%2BpVKr20b3ZUo5HMz1lF9%2BJh6LQ0%3D",
@@ -173,6 +176,17 @@ Step 3 – **Final Output**:
173
  # We pass the lifespan function to the FastAPI constructor
174
  app = FastAPI(title="HackRX RAG Server", lifespan=lifespan)
175
 
 
 
 
 
 
 
 
 
 
 
 
176
  # --- 3. API Key Verification ---
177
  TEAM_API_KEY = os.getenv("TEAM_API_KEY")
178
  TEAM_API_KEY2 = os.getenv("TEAM_API_KEY2")
@@ -181,7 +195,7 @@ def verify_api_key(authorization: str = Header(...)):
181
  if not authorization.startswith("Bearer "):
182
  raise HTTPException(status_code=401, detail="Invalid Authorization header format")
183
  token = authorization.split("Bearer ")[1]
184
- if token != TEAM_API_KEY and token != TEAM_API_KEY2:
185
  raise HTTPException(status_code=403, detail="Invalid or missing API key")
186
 
187
 
@@ -214,72 +228,76 @@ def parse_llm_response(content: str) -> str:
214
  # --- 5. Main API Endpoint ---
215
  @app.post("/api/v1/hackrx/run", response_model=RunResponse, dependencies=[Depends(verify_api_key)])
216
  async def run_hackrx(req: RunRequest):
217
- doc_url = str(req.documents)
218
- start_time = time.time()
219
- # if(doc_url not in vector_cache):
220
- chunks = load_and_chunk(str(req.documents))
221
- if not chunks:
222
- return JSONResponse({"error": "No documents could be processed."}, status_code=400)
223
- end_time = time.time() - start_time
224
- print(f"chunking done: {end_time}")
225
- # if not chunks:
226
- # return JSONResponse({"error": "No documents could be processed."}, status_code=400)
227
-
228
-
229
- start_time2 = time.time()
230
- # Reuse vectorstore if already cached
231
- if doc_url in vector_cache:
232
- print(f"♻ Using cached vectorstore for: {doc_url}")
233
- vectorstore = vector_cache[doc_url]
234
- else:
235
- print(f"📄 Processing new document: {doc_url}")
236
- # Build vectorstore & save to cache
237
- vectorstore = await FAISS.afrom_documents(documents=chunks, embedding=ml_models["embedder"])
238
- vector_cache[doc_url] = vectorstore # store in memory cache
239
- print(f" Vectorstore cached for: {doc_url}")
240
- end_time2 = time.time() - start_time2
241
- print(f"vector done: {end_time2}")
242
-
243
- # start_time2 = time.time()
244
- # vectorstore = await FAISS.afrom_documents(
245
- # documents=chunks,
246
- # embedding=ml_models["embedder"]
247
- # )
248
- # end_time2 = time.time() - start_time2
249
- # print(f"vector done: {end_time2}")
250
- # dense_retriever = vectorstore.as_retriever(search_type="mmr",search_kwargs={"k": 8})
251
- dense_retriever = vectorstore.as_retriever(search_type="mmr",search_kwargs={"k": 14 ,"lambda_mult": 0.7} ) # prev 16
252
- # dense_retriever = vectorstore.as_retriever(search_type="similarity" ,search_kwargs={"k": 11} )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
 
254
-
255
- # Create retrievers using the pre-loaded models from our ml_models dictionary
256
- keyword_retriever = BM25Retriever.from_documents(chunks)
257
- keyword_retriever.k = 9 #prev 11
258
- # dense_retriever = Chroma.from_documents(documents=chunks, embedding=ml_models["embedder"]).as_retriever()
259
- ensemble_retriever = EnsembleRetriever(retrievers=[keyword_retriever, dense_retriever], weights=[0.3, 0.7],search_kwargs={"k": 14}) #prev 16
260
- ### to make it faster we are now using our built reranker thats why commenting the code below
261
- # compression_retriever = ContextualCompressionRetriever(
262
- # base_retriever=ensemble_retriever, base_compressor=ml_models["reranker_compressor"]
263
- # )
264
-
265
- # Define the RAG chain using pre-loaded components
266
- hybrid_rag_chain = (
267
- {"context": itemgetter("full_query") | ensemble_retriever, "full_query": itemgetter("full_query")}
268
- | ml_models["prompt_template"]
269
- | ml_models["llm"]
270
- )
271
-
272
- tasks = [hybrid_rag_chain.ainvoke({"full_query": q}) for q in req.questions]
273
- results = await asyncio.gather(*tasks)
274
- # answers = [parse_llm_response(result.content) for result in results]
275
- answers = []
276
-
277
- for msg in results:
278
- # Safely access the content field
279
- if hasattr(msg, "content"):
280
- answers.append(msg.content.strip())
281
-
282
- return JSONResponse({"answers": answers}, status_code=200)
283
 
284
  @app.get("/", include_in_schema=False)
285
  def root():
 
7
  from contextlib import asynccontextmanager
8
  from dotenv import load_dotenv
9
  from operator import itemgetter
10
+ import requests
11
+ from bs4 import BeautifulSoup
12
+
13
 
14
  from fastapi import FastAPI, Depends, HTTPException, Header
15
  from fastapi.responses import JSONResponse
 
52
 
53
  vector_cache = {}
54
  ml_models = {}
55
+ secret = ""
56
  PRELOAD_URLS = [
57
  "https://hackrx.blob.core.windows.net/assets/Arogya%20Sanjeevani%20Policy%20-%20CIN%20-%20U10200WB1906GOI001713%201.pdf?sv=2023-01-03&st=2025-07-21T08%3A29%3A02Z&se=2025-09-22T08%3A29%3A00Z&sr=b&sp=r&sig=nzrz1K9Iurt%2BBXom%2FB%2BMPTFMFP3PRnIvEsipAX10Ig4%3D",
58
  "https://hackrx.blob.core.windows.net/assets/Super_Splendor_(Feb_2023).pdf?sv=2023-01-03&st=2025-07-21T08%3A10%3A00Z&se=2025-09-22T08%3A10%3A00Z&sr=b&sp=r&sig=vhHrl63YtrEOCsAy%2BpVKr20b3ZUo5HMz1lF9%2BJh6LQ0%3D",
 
176
  # We pass the lifespan function to the FastAPI constructor
177
  app = FastAPI(title="HackRX RAG Server", lifespan=lifespan)
178
 
179
+ def store_secret(url: str):
180
+ url_c = url
181
+ r = requests.get(url_c)
182
+ r.raise_for_status()
183
+ soup = BeautifulSoup(r.text, "html.parser")
184
+ token = (soup.find(id="token") or soup).get_text(strip=True)
185
+ m = re.search(r"[0-9a-fA-F]{64}", token)
186
+ token = m.group(0) if m else token
187
+ secret = token
188
+
189
+
190
  # --- 3. API Key Verification ---
191
  TEAM_API_KEY = os.getenv("TEAM_API_KEY")
192
  TEAM_API_KEY2 = os.getenv("TEAM_API_KEY2")
 
195
  if not authorization.startswith("Bearer "):
196
  raise HTTPException(status_code=401, detail="Invalid Authorization header format")
197
  token = authorization.split("Bearer ")[1]
198
+ if token != TEAM_API_KEY and token != TEAM_API_KEY2 and token != secret:
199
  raise HTTPException(status_code=403, detail="Invalid or missing API key")
200
 
201
 
 
228
  # --- 5. Main API Endpoint ---
229
  @app.post("/api/v1/hackrx/run", response_model=RunResponse, dependencies=[Depends(verify_api_key)])
230
  async def run_hackrx(req: RunRequest):
231
+ doc_url = str(req.documents)
232
+ lower_url = doc_url.lower()
233
+ if "get-secret-token" in lower_url:
234
+ store_secret(doc_url)
235
+ else:
236
+ start_time = time.time()
237
+ # if(doc_url not in vector_cache):
238
+ chunks = load_and_chunk(str(req.documents))
239
+ if not chunks:
240
+ return JSONResponse({"error": "No documents could be processed."}, status_code=400)
241
+ end_time = time.time() - start_time
242
+ print(f"chunking done: {end_time}")
243
+ # if not chunks:
244
+ # return JSONResponse({"error": "No documents could be processed."}, status_code=400)
245
+
246
+
247
+ start_time2 = time.time()
248
+ # ✅ Reuse vectorstore if already cached
249
+ if doc_url in vector_cache:
250
+ print(f"♻ Using cached vectorstore for: {doc_url}")
251
+ vectorstore = vector_cache[doc_url]
252
+ else:
253
+ print(f"📄 Processing new document: {doc_url}")
254
+ # Build vectorstore & save to cache
255
+ vectorstore = await FAISS.afrom_documents(documents=chunks, embedding=ml_models["embedder"])
256
+ vector_cache[doc_url] = vectorstore # store in memory cache
257
+ print(f"✅ Vectorstore cached for: {doc_url}")
258
+ end_time2 = time.time() - start_time2
259
+ print(f"vector done: {end_time2}")
260
+
261
+ # start_time2 = time.time()
262
+ # vectorstore = await FAISS.afrom_documents(
263
+ # documents=chunks,
264
+ # embedding=ml_models["embedder"]
265
+ # )
266
+ # end_time2 = time.time() - start_time2
267
+ # print(f"vector done: {end_time2}")
268
+ # dense_retriever = vectorstore.as_retriever(search_type="mmr",search_kwargs={"k": 8})
269
+ dense_retriever = vectorstore.as_retriever(search_type="mmr",search_kwargs={"k": 14 ,"lambda_mult": 0.7} ) # prev 16
270
+ # dense_retriever = vectorstore.as_retriever(search_type="similarity" ,search_kwargs={"k": 11} )
271
+
272
+
273
+ # Create retrievers using the pre-loaded models from our ml_models dictionary
274
+ keyword_retriever = BM25Retriever.from_documents(chunks)
275
+ keyword_retriever.k = 9 #prev 11
276
+ # dense_retriever = Chroma.from_documents(documents=chunks, embedding=ml_models["embedder"]).as_retriever()
277
+ ensemble_retriever = EnsembleRetriever(retrievers=[keyword_retriever, dense_retriever], weights=[0.3, 0.7],search_kwargs={"k": 14}) #prev 16
278
+ ### to make it faster we are now using our built reranker thats why commenting the code below
279
+ # compression_retriever = ContextualCompressionRetriever(
280
+ # base_retriever=ensemble_retriever, base_compressor=ml_models["reranker_compressor"]
281
+ # )
282
+
283
+ # Define the RAG chain using pre-loaded components
284
+ hybrid_rag_chain = (
285
+ {"context": itemgetter("full_query") | ensemble_retriever, "full_query": itemgetter("full_query")}
286
+ | ml_models["prompt_template"]
287
+ | ml_models["llm"]
288
+ )
289
+
290
+ tasks = [hybrid_rag_chain.ainvoke({"full_query": q}) for q in req.questions]
291
+ results = await asyncio.gather(*tasks)
292
+ # answers = [parse_llm_response(result.content) for result in results]
293
+ answers = []
294
+
295
+ for msg in results:
296
+ # Safely access the content field
297
+ if hasattr(msg, "content"):
298
+ answers.append(msg.content.strip())
299
 
300
+ return JSONResponse({"answers": answers}, status_code=200)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
 
302
  @app.get("/", include_in_schema=False)
303
  def root():