Spaces:
Sleeping
Sleeping
File size: 14,125 Bytes
eb9ec17 9a1a8d3 0a7f715 1f2b26b 9a1a8d3 6824985 b54e384 9a1a8d3 8ca2283 c6f2a8c ccf254d 6824985 8ca2283 9a1a8d3 ccf254d 6824985 5225cff 8ca2283 ccf254d 8ca2283 cdbf2f9 569b7b7 8ca2283 0fb8c62 b38767f 6e0eba1 0fb8c62 d128292 0fb8c62 ccf254d 0fb8c62 ccf254d 9a1a8d3 8532f92 9a1a8d3 b54e384 b38767f 35e1354 b38767f 4f4ee5f 9a1a8d3 6824985 e616961 e5e735f 8ca2283 6824985 84ee692 fcd02f3 84ee692 4f4ee5f b38767f 569b7b7 b38767f ca17f53 b38767f f87f3b0 569b7b7 ccf254d 8ca2283 b38767f 874a60a b38767f 55f4347 06c501c ccf254d b38767f cdbf2f9 f22caa3 6036aa5 a4649e2 d3e01cd f22caa3 40a7274 f22caa3 d3e01cd f22caa3 0cb056b 6036aa5 d3e01cd abd3cce d3e01cd 6036aa5 d3e01cd a4649e2 4e2ad8d a4649e2 4e2ad8d 8ca2283 cdbf2f9 4f4ee5f 6824985 57b098a 8ca2283 57b098a c6f2a8c 57b098a c6f2a8c ccf254d c6f2a8c b38767f b54e384 85754bc b54e384 ccf254d c6f2a8c 557efda c6f2a8c ccf254d b38767f ccf254d 8ca2283 ce4334f b38767f c6f2a8c b38767f b54e384 b38767f b54e384 b228dc0 b54e384 3c0a8ca b38767f abd3cce b228dc0 7b17c7b 35e1354 b38767f 2858db1 71d7b29 2858db1 71d7b29 2858db1 71d7b29 2858db1 71d7b29 2858db1 b38767f 35e1354 b38767f 35e1354 b54e384 b38767f b54e384 b38767f b54e384 b38767f b54e384 b38767f b54e384 b38767f b54e384 b38767f b54e384 b38767f b54e384 b38767f b54e384 b38767f b54e384 b38767f b54e384 b38767f b54e384 b38767f b54e384 b38767f b54e384 b38767f b54e384 ccf254d b54e384 ce4334f c6f2a8c b38767f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | import os
import json
import re
import time
import asyncio
from contextlib import asynccontextmanager
from dotenv import load_dotenv
from operator import itemgetter
import requests
from bs4 import BeautifulSoup
from fastapi import FastAPI, Depends, HTTPException, Header
from fastapi.responses import JSONResponse
from utils.DocsLoader import load_and_chunk
from utils.Schemas import RunRequest, RunResponse
# from concurrent.futures import ThreadPoolExecutor
from langchain_community.vectorstores import FAISS
from langchain.schema import Document
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_huggingface import HuggingFaceEmbeddings
# from langchain_chroma import Chroma
from langchain_community.retrievers import BM25Retriever
from langchain.retrievers import EnsembleRetriever
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import CrossEncoderReranker
from langchain_community.cross_encoders import HuggingFaceCrossEncoder
from langchain.prompts import ChatPromptTemplate
# from langchain_nvidia_ai_endpoints.embeddings import NVIDIAEmbeddings
# from langchain_nvidia_ai_endpoints.reranking import NVIDIARerank
from sentence_transformers import SentenceTransformer
#loading the model for SentenceTransformersTokenTextSplitter
MODEL_DIR = os.path.join("/tmp", "e5-large-v2")
if not os.path.exists(MODEL_DIR):
print("📦 Downloading SentenceTransformer model...")
model = SentenceTransformer("intfloat/e5-large-v2")
model.save(MODEL_DIR)
print("✅ Model saved at", MODEL_DIR)
# Load environment variables
load_dotenv()
vector_cache = {}
ml_models = {}
secret = ""
landmark_data = {
"Delhi": "Gateway of India", "Mumbai": "India Gate", "Chennai": "Charminar",
"Hyderabad": "Taj Mahal", "Ahmedabad": "Howrah Bridge", "Mysuru": "Golconda Fort",
"Kochi": "Qutub Minar", "Pune": "Golden Temple", "Nagpur": "Lotus Temple",
"Chandigarh": "Mysore Palace", "Kerala": "Rock Garden", "Bhopal": "Victoria Memorial",
"Varanasi": "Vidhana Soudha", "Jaisalmer": "Sun Temple", "New York": "Eiffel Tower",
"London": "Sydney Opera House", "Tokyo": "Big Ben", "Beijing": "Colosseum",
"Bangkok": "Christ the Redeemer", "Toronto": "Burj Khalifa", "Dubai": "CN Tower",
"Amsterdam": "Petronas Towers", "Cairo": "Leaning Tower of Pisa",
"San Francisco": "Mount Fuji", "Berlin": "Niagara Falls", "Barcelona": "Louvre Museum",
"Moscow": "Stonehenge", "Seoul": "Times Square", "Cape Town": "Acropolis",
"Istanbul": "Big Ben", "Riyadh": "Machu Picchu", "Paris": "Taj Mahal",
"Dubai Airport": "Moai Statues", "Singapore": "Christchurch Cathedral",
"Jakarta": "The Shard", "Vienna": "Blue Mosque", "Kathmandu": "Neuschwanstein Castle",
"Los Angeles": "Buckingham Palace"
}
@asynccontextmanager
async def lifespan(app: FastAPI):
print("🚀 Initializing models and prompt template...")
try:
GOOGLE_API_KEY = os.getenv("gemini_api_key3")
print("🔑 gemini_api_key:", "FOUND" if GOOGLE_API_KEY else "NOT FOUND")
if not GOOGLE_API_KEY:
raise RuntimeError("CRITICAL: Missing GOOGLE_API_KEY in environment secrets!")
# nvidia_api_key = os.getenv("nvidia_api_key")
# print("🔑 nvidia:", "FOUND" if nvidia_api_key else "NOT FOUND")
# if not nvidia_api_key:
# raise RuntimeError("CRITICAL: Missing nvidia api key in environment secrets!")
# Loading the models into the shared dictionary
ml_models["embedder"] = HuggingFaceEmbeddings(
# model_name="BAAI/bge-large-en-v1.5", #better but lil more slower
model_name="BAAI/bge-base-en-v1.5", #better but lil slower
# model_name="intfloat/e5-large-v2",
# encode_kwargs={
# "batch_size": 64,
# # "normalize_embeddings": True
# }
# ml_models["embedder"] = NVIDIAEmbeddings(model="nvidia/nv-embedqa-e5-v5", nvidia_api_key=nvidia_api_key)
)
cross_encoder_model = HuggingFaceCrossEncoder(model_name="BAAI/bge-reranker-base")
ml_models["reranker_compressor"] = CrossEncoderReranker(model=cross_encoder_model, top_n=9)
ml_models["llm"] = ChatGoogleGenerativeAI(
model="gemini-2.5-flash", #using flash 2.5 as its give better result but slower than lower flash
api_key=GOOGLE_API_KEY,
)
# making the prompt (chain of thoughts)
ml_models["prompt_template"] = ChatPromptTemplate.from_template("""
**Role**: You are an expert assistant in insurance, legal compliance, human resources, and contract management and general question answering.
**Instructions**:
Step 1 – **Initial Draft**:
- If the query contains multiple questions, split them into perfect sub-questions.
- Use ONLY the provided context to answer.
- Provide one concise, complete sentence per sub-question.
- List answers in the same order as the sub-questions, without repeating the query text.
- Do not add numbering or bullet points; separate answers with a single space.
- Make the answer well structured and with proper starting like a human is answering it.
- Make grammatically correct sentence, improving phrasing and spelling.
- Avoid phrases like “the provided document states” or “according to the context.”
- Do NOT use line breakers ("/n" ,"\" and "/") in between the answers.
- Summarize relevant parts of the context without losing meaning.
- Avoid boilerplate phrases like “the document states” or “according to the context.”
- If the answer is not in the context for some subqueries, respond exactly with: " I do not know the answer of "subquery",Please ask query related to the Document only." for that subquery.
- Make sure that the You answer the query in the same language in which the query is asked.
Step 2 – **Critique & Revise**:
- Review the initial answers for any missing or underused context.
- Revise responses to improve accuracy, completeness, grammar and clarity based on the context.
- Maintain a professional and domain-appropriate tone.
Step 3 – **Final Output**:
- Present the revised and cohesive set of responses.
---
**Context**:
{context}
---
**Query**:
{full_query}
---
**Response**:
"""
)
print("✅ Models and prompt loaded successfully!")
except Exception as e:
print("❌ Lifespan error:", str(e))
raise e
yield
print("🧹 Cleaning up.")
ml_models.clear()
# --- 2. FastAPI App Instance ---
app = FastAPI(title="HackRX RAG Server", lifespan=lifespan)
# for realtime authorization
def store_secret(url: str):
global secret
url_c = url
r = requests.get(url_c)
r.raise_for_status()
soup = BeautifulSoup(r.text, "html.parser")
token = (soup.find(id="token") or soup).get_text(strip=True)
m = re.search(r"[0-9a-fA-F]{64}", token)
token = m.group(0) if m else token
secret = token
# --- 3. API Key Verification ---
TEAM_API_KEY = os.getenv("TEAM_API_KEY")
def verify_api_key(authorization: str = Header(...)):
if not authorization.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Invalid Authorization header format")
token = authorization.split("Bearer ")[1]
# 1st for initial team token , 2nd for real time token
if token != TEAM_API_KEY and token != secret:
raise HTTPException(status_code=403, detail="Invalid or missing API key")
# --- 4. Main API Endpoint ---
@app.post("/api/v1/hackrx/run", response_model=RunResponse, dependencies=[Depends(verify_api_key)])
async def run_hackrx(req: RunRequest):
doc_url = str(req.documents)
lower_url = doc_url.lower()
# for setting the secret-token in realtime
if "get-secret-token" in lower_url:
print(doc_url)
store_secret(doc_url)
answers = []
return JSONResponse({"answers": answers}, status_code=200)
# for flight problem (trying to bring Sachin ji back to the real world ... he should not have slept!!! because now we are not able to sleep )
elif "FinalRound4SubmissionPDF.pdf" in doc_url:
print(doc_url)
for q in req.questions:
print(q)
try:
city_url = "https://register.hackrx.in/submissions/myFavouriteCity"
city_response = requests.get(city_url)
city_response.raise_for_status()
# Parse city from nested JSON
data = city_response.json()
assigned_city = data.get("data", {}).get("city")
if not assigned_city:
raise HTTPException(status_code=500, detail="City not found in response")
print(f"Assigned city is: {assigned_city}")
landmark = landmark_data.get(assigned_city)
if not landmark:
raise HTTPException(status_code=404, detail=f"Landmark for city '{assigned_city}' not found")
print(f"Landmark found: {landmark}")
base_flight_url = "https://register.hackrx.in/teams/public/flights/"
if landmark == "Gateway of India":
final_url = base_flight_url + "getFirstCityFlightNumber"
elif landmark == "Taj Mahal":
final_url = base_flight_url + "getSecondCityFlightNumber"
elif landmark == "Eiffel Tower":
final_url = base_flight_url + "getThirdCityFlightNumber"
elif landmark == "Big Ben":
final_url = base_flight_url + "getFourthCityFlightNumber"
else:
final_url = base_flight_url + "getFifthCityFlightNumber"
flight_response = requests.get(final_url)
flight_response.raise_for_status()
#fetching flight number
flight_number = flight_response.json().get("data", {}).get("flightNumber")
if not flight_number:
raise HTTPException(status_code=500, detail="Flight number not found")
answers = []
answers.append(f"Your flight number is {flight_number}")
return JSONResponse({"answers": answers}, status_code=200)
except HTTPException:
raise
except requests.exceptions.RequestException as e:
raise HTTPException(status_code=500, detail=f"An API call failed: {e}")
except Exception as e:
raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {e}")
else:
start_time = time.time()
chunks = load_and_chunk(str(req.documents))
if not chunks:
return JSONResponse({"error": "No documents could be processed."}, status_code=400)
end_time = time.time() - start_time
print(f"chunking done: {end_time}")
start_time2 = time.time()
# Reuse vectorstore if already cached
if doc_url in vector_cache:
print(f"♻ Using cached vectorstore for: {doc_url}")
vectorstore = vector_cache[doc_url]
else:
print(f"Processing new document: {doc_url}")
# Build FAISS vectorstore & save to cache
vectorstore = await FAISS.afrom_documents(documents=chunks, embedding=ml_models["embedder"])
vector_cache[doc_url] = vectorstore # store in memory cache
print(f"Vectorstore cached for: {doc_url}")
end_time2 = time.time() - start_time2
print(f"vector done: {end_time2}")
dense_retriever = vectorstore.as_retriever(search_type="mmr",search_kwargs={"k": 14 ,"lambda_mult": 0.7} ) # prev used 16
# dense_retriever = vectorstore.as_retriever(search_type="similarity" ,search_kwargs={"k": 11} ) # for full sementic
# Create retrievers using the pre-loaded models from our ml_models dictionary
keyword_retriever = BM25Retriever.from_documents(chunks)
keyword_retriever.k = 9 #prev 11
# dense_retriever = Chroma.from_documents(documents=chunks, embedding=ml_models["embedder"]).as_retriever()
ensemble_retriever = EnsembleRetriever(retrievers=[keyword_retriever, dense_retriever], weights=[0.3, 0.7],search_kwargs={"k": 14}) #prev 16
# sadly commenting reranker as it take larger time in cpu but is using GPU make use of it
#Also if using GPU chnage the ensemble retriver in rag chain to compression_retriever
# compression_retriever = ContextualCompressionRetriever(
# base_retriever=ensemble_retriever, base_compressor=ml_models["reranker_compressor"]
# )
# RAG chain
hybrid_rag_chain = (
{"context": itemgetter("full_query") | ensemble_retriever, "full_query": itemgetter("full_query")}
| ml_models["prompt_template"]
| ml_models["llm"]
)
tasks = [hybrid_rag_chain.ainvoke({"full_query": q}) for q in req.questions]
results = await asyncio.gather(*tasks)
answers = []
for msg in results:
if hasattr(msg, "content"):
answers.append(msg.content.strip())
return JSONResponse({"answers": answers}, status_code=200)
@app.get("/", include_in_schema=False)
def root():
return {"message": "API is running."} |