singhankur01 commited on
Commit
c6f2a8c
·
verified ·
1 Parent(s): 0423c77

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +138 -214
app.py CHANGED
@@ -1,6 +1,4 @@
1
- # ==============================================================================
2
- # SECTION 1: IMPORTS
3
- # ==============================================================================
4
  import os
5
  import json
6
  import re
@@ -8,238 +6,164 @@ 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
13
 
14
- # --- LangChain and related imports ---
 
 
 
15
  from langchain_google_genai import ChatGoogleGenerativeAI
16
- from langchain_huggingface import HuggingFaceEmbeddings
17
  from langchain_chroma import Chroma
18
  from langchain_community.retrievers import BM25Retriever
19
  from langchain.retrievers import EnsembleRetriever, ContextualCompressionRetriever
20
  from langchain.retrievers.document_compressors import CrossEncoderReranker
 
21
  from langchain.prompts import PromptTemplate
22
 
23
- # --- Local utility imports ---
24
- from utils.DocsLoader import load_and_chunk
25
- from utils.Schemas import RunRequest, RunResponse
26
 
27
  # Load environment variables
28
  load_dotenv()
29
 
30
- # ==============================================================================
31
- # SECTION 2: LIFESPAN MANAGER (MODEL LOADING BYPASSED FOR DEBUGGING)
32
- # ==============================================================================
33
  ml_models = {}
34
 
35
  @asynccontextmanager
36
  async def lifespan(app: FastAPI):
37
- """
38
- Debug version: This function will bypass model loading to test server startup.
39
- """
40
- print("🚀 Lifespan startup: Bypassing model loading for this test.")
41
-
42
- # --- ALL MODEL LOADING IS TEMPORARILY COMMENTED OUT ---
43
- # GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
44
- # if not GOOGLE_API_KEY:
45
- # raise RuntimeError("CRITICAL: Missing GOOGLE_API_KEY in environment secrets!")
46
-
47
- # ml_models["embedder"] = HuggingFaceEmbeddings(model_name="BAAI/bge-base-en-v1.5")
48
- # cross_encoder_model = HuggingFaceCrossEncoder(model_name="BAAI/bge-reranker-large")
49
- # ml_models["reranker_compressor"] = CrossEncoderReranker(model=cross_encoder_model, top_n=5)
50
- # ml_models["llm"] = ChatGoogleGenerativeAI(model="gemini-1.5-flash", api_key=GOOGLE_API_KEY)
51
- # ml_models["prompt_template"] = PromptTemplate.from_template(
52
- # """Your full prompt here..."""
53
- # )
54
-
55
- print("✅ Bypassed model loading. The server should now start.")
56
-
57
- yield
58
-
59
- print("Shutting down.")
60
-
61
- # ==============================================================================
62
- # SECTION 3: FASTAPI APP INITIALIZATION
63
- # ==============================================================================
64
- app = FastAPI(title="Debug RAG Server", lifespan=lifespan)
65
-
66
-
67
- # ==============================================================================
68
- # SECTION 4 & 5: SECURITY AND API ENDPOINT
69
- # ==============================================================================
70
- # Note: The API endpoint will fail if you call it, because the models are not loaded.
71
- # We are only testing if the server can START.
72
-
73
- @app.get("/")
74
- def root():
75
- return {"message": "Debug server is running! Model loading was bypassed."}
76
-
77
-
78
- # import os
79
- # import json
80
- # import re
81
- # from contextlib import asynccontextmanager
82
- # from dotenv import load_dotenv
83
- # from operator import itemgetter
84
-
85
- # import gradio as gr
86
- # from fastapi import FastAPI, Depends, HTTPException, Header
87
- # from fastapi.responses import JSONResponse
88
-
89
- # # Make sure you have these files in a 'utils' folder
90
- # from utils.DocsLoader import load_and_chunk
91
- # from utils.Schemas import RunRequest, RunResponse
92
-
93
- # from langchain_google_genai import ChatGoogleGenerativeAI
94
- # from langchain_huggingface import HuggingFaceEmbeddings # Correct new import
95
- # from langchain_chroma import Chroma
96
- # from langchain_community.retrievers import BM25Retriever
97
- # from langchain.retrievers import EnsembleRetriever, ContextualCompressionRetriever
98
- # from langchain.retrievers.document_compressors import CrossEncoderReranker
99
- # from langchain_community.cross_encoders import HuggingFaceCrossEncoder
100
- # from langchain.prompts import PromptTemplate
101
-
102
-
103
- # # Load environment variables
104
- # load_dotenv()
105
-
106
- # # --- 1. Lifespan Event Handler (The New, Correct Way) ---
107
- # # This dictionary will hold our loaded models
108
- # ml_models = {}
109
-
110
- # @asynccontextmanager
111
- # async def lifespan(app: FastAPI):
112
- # # This code runs ONCE when the application starts up
113
- # print("🚀 Initializing models and prompt template...")
114
 
115
- # # Use a consistent key name that you set in Hugging Face Secrets
116
- # GOOGLE_API_KEY = os.getenv("gemini_api_key")
117
- # if not GOOGLE_API_KEY:
118
- # raise RuntimeError("CRITICAL: Missing GOOGLE_API_KEY in environment secrets!")
119
 
120
- # # Load models into the shared dictionary
121
- # ml_models["embedder"] = HuggingFaceEmbeddings(model_name="BAAI/bge-base-en-v1.5")
122
- # cross_encoder_model = HuggingFaceCrossEncoder(model_name="BAAI/bge-reranker-large")
123
- # ml_models["reranker_compressor"] = CrossEncoderReranker(model=cross_encoder_model, top_n=5)
124
- # ml_models["llm"] = ChatGoogleGenerativeAI(model="gemini-1.5-flash", api_key=GOOGLE_API_KEY)
125
- # ml_models["prompt_template"] = PromptTemplate.from_template(
126
- # """
127
- # You are an expert decision maker Assistant in the domain such as insurance, legal compliance, human resources, and contract management.
128
-
129
- # The customer has submitted a query. If the query has details about age , gender ,procedure ,location , policy duration, then parse it and understand the query properly
130
- # If not, the raw question is provided instead:
131
- # - Query: {full_query}
132
-
133
- # We retrieved the following policy clauses and rules relevant to this case:
134
- # {context}
135
-
136
- # ### Task:
137
- # If the details about age , gender , procedure , location , policy duration are available, do all of the following:
138
- # 1. Decide whether the procedure is covered.
139
- # 2. Estimate the claimable amount.
140
- # 3. Justify with the relevant clause.
141
- # and answer the query precisely as insurance agent.
142
-
143
- # Otherwise, answer the question concisely and clearly using the retrieved context.
144
-
145
- # ### Output format:
146
- # If query involes age , gender , procedure , location , policy duration answer like below:
147
- # {{
148
- # "decision": "approved / rejected",
149
- # "amount": "INR amount or null",
150
- # "justification": "Refer to specific clause"
151
- # Make it a perfect and concise.
152
- # }}
153
- # Else:
154
- # {{
155
- # "response": "Concise natural language answer"
156
- # Make it a perfect and concise.
157
- # }}
158
-
159
- # ##NOTE : Do not mention document id or its page number just mention clauses if applicable .
160
- # """
161
- # )
162
- # print("✅ Models and prompt loaded successfully!")
163
 
164
- # yield
165
 
166
- # # Code below yield runs on shutdown (optional)
167
- # print("Shutting down and cleaning up.")
168
- # ml_models.clear()
169
-
170
- # # --- 2. FastAPI App Instance ---
171
- # # We pass the lifespan function to the FastAPI constructor
172
- # app = FastAPI(title="HackRX RAG Server", lifespan=lifespan)
173
-
174
- # # --- 3. API Key Verification ---
175
- # TEAM_API_KEY = os.getenv("TEAM_API_KEY")
176
- # def verify_api_key(authorization: str = Header(...)):
177
- # if not authorization.startswith("Bearer "):
178
- # raise HTTPException(status_code=401, detail="Invalid Authorization header format")
179
- # token = authorization.split("Bearer ")[1]
180
- # if token != TEAM_API_KEY:
181
- # raise HTTPException(status_code=403, detail="Invalid or missing API key")
182
-
183
- # # --- 4. Parsing Helper ---
184
- # def parse_llm_response(content: str) -> str:
185
- # try:
186
- # # Remove code fences and clean up
187
- # content_cleaned = re.sub(r"^```json|```$", "", content.strip(), flags=re.IGNORECASE).strip()
188
- # data = json.loads(content_cleaned)
189
-
190
- # if isinstance(data, dict):
191
- # if "decision" in data:
192
- # decision = data.get("decision", "N/A").upper()
193
- # amount = data.get("amount", "Not specified")
194
- # justification = data.get("justification", "No justification provided.")
195
- # return f"Decision: {decision}\nAmount: {amount}\nJustification: {justification}"
196
-
197
- # elif "response" in data:
198
- # return data["response"]
199
-
200
- # return "The response was parsed but didn't match expected structure."
201
-
202
- # except json.JSONDecodeError:
203
- # return f"Unstructured response:\n{content.strip()}"
204
-
205
- # except Exception as e:
206
- # return f"An error occurred while processing the response: {str(e)}"
207
-
208
-
209
- # # --- 5. Main API Endpoint ---
210
- # @app.post("/api/v1/hackrx/run", response_model=RunResponse, dependencies=[Depends(verify_api_key)])
211
- # async def run_hackrx(req: RunRequest):
212
- # chunks = load_and_chunk(str(req.documents))
213
- # if not chunks:
214
- # return JSONResponse({"error": "No documents could be processed."}, status_code=400)
215
-
216
- # # Create retrievers using the pre-loaded models from our ml_models dictionary
217
- # keyword_retriever = BM25Retriever.from_documents(chunks)
218
- # dense_retriever = Chroma.from_documents(documents=chunks, embedding=ml_models["embedder"]).as_retriever()
219
- # ensemble_retriever = EnsembleRetriever(retrievers=[keyword_retriever, dense_retriever], weights=[0.3, 0.7])
220
 
221
- # compression_retriever = ContextualCompressionRetriever(
222
- # base_retriever=ensemble_retriever, base_compressor=ml_models["reranker_compressor"]
223
- # )
224
-
225
- # # Define the RAG chain using pre-loaded components
226
- # hybrid_rag_chain = (
227
- # {"context": itemgetter("full_query") | compression_retriever, "full_query": itemgetter("full_query")}
228
- # | ml_models["prompt_template"]
229
- # | ml_models["llm"]
230
- # )
231
 
232
- # answers = []
233
- # for q in req.questions:
234
- # try:
235
- # result = await hybrid_rag_chain.ainvoke({"full_query": q})
236
- # parsed = parse_llm_response(result.content)
237
- # answers.append(parsed)
238
- # except Exception as e:
239
- # return JSONResponse({"error": str(e)}, status_code=500)
240
-
241
- # return JSONResponse({"answers": answers}, status_code=200)
242
-
243
- # @app.get("/", include_in_schema=False)
244
- # def root():
245
- # return {"message": "API is running. Go to /docs for documentation."}
 
1
+
 
 
2
  import os
3
  import json
4
  import re
 
6
  from dotenv import load_dotenv
7
  from operator import itemgetter
8
 
9
+ import gradio as gr
10
  from fastapi import FastAPI, Depends, HTTPException, Header
11
  from fastapi.responses import JSONResponse
12
 
13
+ # Make sure you have these files in a 'utils' folder
14
+ from utils.DocsLoader import load_and_chunk
15
+ from utils.Schemas import RunRequest, RunResponse
16
+
17
  from langchain_google_genai import ChatGoogleGenerativeAI
18
+ from langchain_huggingface import HuggingFaceEmbeddings # Correct new import
19
  from langchain_chroma import Chroma
20
  from langchain_community.retrievers import BM25Retriever
21
  from langchain.retrievers import EnsembleRetriever, ContextualCompressionRetriever
22
  from langchain.retrievers.document_compressors import CrossEncoderReranker
23
+ from langchain_community.cross_encoders import HuggingFaceCrossEncoder
24
  from langchain.prompts import PromptTemplate
25
 
 
 
 
26
 
27
  # Load environment variables
28
  load_dotenv()
29
 
30
+ # --- 1. Lifespan Event Handler (The New, Correct Way) ---
31
+ # This dictionary will hold our loaded models
 
32
  ml_models = {}
33
 
34
  @asynccontextmanager
35
  async def lifespan(app: FastAPI):
36
+ # This code runs ONCE when the application starts up
37
+ print("🚀 Initializing models and prompt template...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
+ # Use a consistent key name that you set in Hugging Face Secrets
40
+ GOOGLE_API_KEY = os.getenv("gemini_api_key")
41
+ if not GOOGLE_API_KEY:
42
+ raise RuntimeError("CRITICAL: Missing GOOGLE_API_KEY in environment secrets!")
43
 
44
+ # Load models into the shared dictionary
45
+ ml_models["embedder"] = HuggingFaceEmbeddings(model_name="BAAI/bge-base-en-v1.5")
46
+ cross_encoder_model = HuggingFaceCrossEncoder(model_name="BAAI/bge-reranker-large")
47
+ ml_models["reranker_compressor"] = CrossEncoderReranker(model=cross_encoder_model, top_n=5)
48
+ ml_models["llm"] = ChatGoogleGenerativeAI(model="gemini-1.5-flash", api_key=GOOGLE_API_KEY)
49
+ ml_models["prompt_template"] = PromptTemplate.from_template(
50
+ """
51
+ You are an expert decision maker Assistant in the domain such as insurance, legal compliance, human resources, and contract management.
52
+
53
+ The customer has submitted a query. If the query has details about age , gender ,procedure ,location , policy duration, then parse it and understand the query properly
54
+ If not, the raw question is provided instead:
55
+ - Query: {full_query}
56
+
57
+ We retrieved the following policy clauses and rules relevant to this case:
58
+ {context}
59
+
60
+ ### Task:
61
+ If the details about age , gender , procedure , location , policy duration are available, do all of the following:
62
+ 1. Decide whether the procedure is covered.
63
+ 2. Estimate the claimable amount.
64
+ 3. Justify with the relevant clause.
65
+ and answer the query precisely as insurance agent.
66
+
67
+ Otherwise, answer the question concisely and clearly using the retrieved context.
68
+
69
+ ### Output format:
70
+ If query involes age , gender , procedure , location , policy duration answer like below:
71
+ {{
72
+ "decision": "approved / rejected",
73
+ "amount": "INR amount or null",
74
+ "justification": "Refer to specific clause"
75
+ Make it a perfect and concise.
76
+ }}
77
+ Else:
78
+ {{
79
+ "response": "Concise natural language answer"
80
+ Make it a perfect and concise.
81
+ }}
82
+
83
+ ##NOTE : Do not mention document id or its page number just mention clauses if applicable .
84
+ """
85
+ )
86
+ print("✅ Models and prompt loaded successfully!")
87
 
88
+ yield
89
 
90
+ # Code below yield runs on shutdown (optional)
91
+ print("Shutting down and cleaning up.")
92
+ ml_models.clear()
93
+
94
+ # --- 2. FastAPI App Instance ---
95
+ # We pass the lifespan function to the FastAPI constructor
96
+ app = FastAPI(title="HackRX RAG Server", lifespan=lifespan)
97
+
98
+ # --- 3. API Key Verification ---
99
+ TEAM_API_KEY = os.getenv("TEAM_API_KEY")
100
+ def verify_api_key(authorization: str = Header(...)):
101
+ if not authorization.startswith("Bearer "):
102
+ raise HTTPException(status_code=401, detail="Invalid Authorization header format")
103
+ token = authorization.split("Bearer ")[1]
104
+ if token != TEAM_API_KEY:
105
+ raise HTTPException(status_code=403, detail="Invalid or missing API key")
106
+
107
+ # --- 4. Parsing Helper ---
108
+ def parse_llm_response(content: str) -> str:
109
+ try:
110
+ # Remove code fences and clean up
111
+ content_cleaned = re.sub(r"^```json|```$", "", content.strip(), flags=re.IGNORECASE).strip()
112
+ data = json.loads(content_cleaned)
113
+
114
+ if isinstance(data, dict):
115
+ if "decision" in data:
116
+ decision = data.get("decision", "N/A").upper()
117
+ amount = data.get("amount", "Not specified")
118
+ justification = data.get("justification", "No justification provided.")
119
+ return f"Decision: {decision}\nAmount: {amount}\nJustification: {justification}"
120
+
121
+ elif "response" in data:
122
+ return data["response"]
123
+
124
+ return "The response was parsed but didn't match expected structure."
125
+
126
+ except json.JSONDecodeError:
127
+ return f"Unstructured response:\n{content.strip()}"
128
+
129
+ except Exception as e:
130
+ return f"An error occurred while processing the response: {str(e)}"
131
+
132
+
133
+ # --- 5. Main API Endpoint ---
134
+ @app.post("/api/v1/hackrx/run", response_model=RunResponse, dependencies=[Depends(verify_api_key)])
135
+ async def run_hackrx(req: RunRequest):
136
+ chunks = load_and_chunk(str(req.documents))
137
+ if not chunks:
138
+ return JSONResponse({"error": "No documents could be processed."}, status_code=400)
139
+
140
+ # Create retrievers using the pre-loaded models from our ml_models dictionary
141
+ keyword_retriever = BM25Retriever.from_documents(chunks)
142
+ dense_retriever = Chroma.from_documents(documents=chunks, embedding=ml_models["embedder"]).as_retriever()
143
+ ensemble_retriever = EnsembleRetriever(retrievers=[keyword_retriever, dense_retriever], weights=[0.3, 0.7])
144
 
145
+ compression_retriever = ContextualCompressionRetriever(
146
+ base_retriever=ensemble_retriever, base_compressor=ml_models["reranker_compressor"]
147
+ )
148
+
149
+ # Define the RAG chain using pre-loaded components
150
+ hybrid_rag_chain = (
151
+ {"context": itemgetter("full_query") | compression_retriever, "full_query": itemgetter("full_query")}
152
+ | ml_models["prompt_template"]
153
+ | ml_models["llm"]
154
+ )
155
 
156
+ answers = []
157
+ for q in req.questions:
158
+ try:
159
+ result = await hybrid_rag_chain.ainvoke({"full_query": q})
160
+ parsed = parse_llm_response(result.content)
161
+ answers.append(parsed)
162
+ except Exception as e:
163
+ return JSONResponse({"error": str(e)}, status_code=500)
164
+
165
+ return JSONResponse({"answers": answers}, status_code=200)
166
+
167
+ @app.get("/", include_in_schema=False)
168
+ def root():
169
+ return {"message": "API is running. Go to /docs for documentation."}