singhankur01 commited on
Commit
40bb5df
·
verified ·
1 Parent(s): 0bfc371

Update app.py

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