singhankur01 commited on
Commit
56de5f6
·
verified ·
1 Parent(s): f492d73

Update app.py

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