-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathapp2.py
More file actions
238 lines (195 loc) · 8.58 KB
/
Copy pathapp2.py
File metadata and controls
238 lines (195 loc) · 8.58 KB
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
import os
import sys
import json
import shutil
from typing import List, Dict, Any, Optional
from flask import Flask, request, jsonify
from flask_cors import CORS
from dotenv import load_dotenv, find_dotenv
from pymongo import MongoClient
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter, CharacterTextSplitter
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA, ConversationChain
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationBufferMemory
# ==========================================
# Configuration & Environment Variables
# ==========================================
load_dotenv(find_dotenv())
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
MONGO_URL = os.environ.get("MONGO_URL")
# Directory Paths
DOCS_FOLDER = "./docs/"
PRES_PERSIST_DIRECTORY = "docs/pres_chroma"
MED_PERSIST_DIRECTORY = "docs/chroma/"
MEDICINES_FILE = "medicines.json"
# Ensure OpenAI Key is set
if not OPENAI_API_KEY:
raise ValueError("OPENAI_API_KEY not found in environment variables.")
openai_api_key = OPENAI_API_KEY # For langchain if needed implicitly
# ==========================================
# Initialization & Setup
# ==========================================
def init_mongo_connection() -> Any:
"""Initialize MongoDB connection."""
if not MONGO_URL:
raise ValueError("MONGO_URL not found in environment variables.")
client = MongoClient(MONGO_URL)
db = client["medimate"]
return db["medicines"]
def init_med_vector_store() -> Chroma:
"""
Initialize the vector store for medicines.
Reads from medicines.json, creates embeddings, and returns the Chroma vector store.
"""
if not os.path.exists(MEDICINES_FILE):
print(f"Warning: {MEDICINES_FILE} not found. Medicine recommendation might not work.")
return None
with open(MEDICINES_FILE, "r") as file:
medicines = json.load(file)
med_splits = [json.dumps(medicine) for medicine in medicines]
embedding = OpenAIEmbeddings()
vector_db = Chroma.from_texts(
texts=med_splits,
embedding=embedding,
persist_directory=MED_PERSIST_DIRECTORY
)
return vector_db
def init_conversation_chain() -> ConversationChain:
"""Initialize the general healthcare assistant conversation chain."""
memory = ConversationBufferMemory()
memory.save_context(
{
"input": (
"Bot Instructions:\n"
"1. Introduce Yourself: Begin by introducing yourself as a healthcare assistant from MediMate. 🌡️🏥\n"
"2. Welcome Message: Always start with a warm welcome message.\n"
"3. Symptom Assessment: Assess the user's symptoms when prompted. Ask for details and provide guidance.\n"
"4. Specialization: If needed, guide the user to a specialist or department, and explain next steps.\n"
"5. Emergency Response: In emergencies, prioritize and suggest dialing 108 (or local emergency number) for an ambulance.\n"
"6. One Question at a Time: Encourage users to ask one health-related question at a time.\n"
"7. Set Expectations: Clarify that the bot provides health guidance, not personalized medical advice.\n"
"8. Thank You Message: Remind users to say Thank you and acknowledge their gratitude.\n"
"Our goal is efficient and helpful healthcare assistance."
)
},
{
"output": "Welcome to MediMate! How can I assist you with your health today?"
},
)
chat_llm = ChatOpenAI(temperature=0.9)
return ConversationChain(llm=chat_llm, memory=memory, verbose=False)
# Global Instances
medicines_collection = init_mongo_connection()
med_vectordb = init_med_vector_store()
conversation_chain = init_conversation_chain()
embedding_function = OpenAIEmbeddings()
app = Flask(__name__)
CORS(app)
# ==========================================
# Helper Functions
# ==========================================
def process_pdf_and_create_vector_store(pdf_path: str) -> Chroma:
"""
Loads a PDF, splits it into chunks, and creates a temporary vector store.
"""
loader = PyPDFLoader(pdf_path)
pages = loader.load()
# Split by character first (paragraphs)
char_splitter = CharacterTextSplitter(
separator="\n", chunk_size=1000, chunk_overlap=50, length_function=len
)
docs = char_splitter.split_documents(pages)
# Further split for embeddings
recursive_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=20)
pres_splits = recursive_splitter.split_documents(docs)
pres_vectordb = Chroma.from_documents(
documents=pres_splits,
embedding=embedding_function,
persist_directory=PRES_PERSIST_DIRECTORY
)
return pres_vectordb
def extract_medicines_from_prescription(pres_vectordb: Chroma) -> str:
"""
Queries the prescription vector store to extract medicine names.
"""
qa_chain = RetrievalQA.from_chain_type(
llm=ChatOpenAI(model="gpt-3.5-turbo", temperature=0),
retriever=pres_vectordb.as_retriever()
)
response = qa_chain({"query": "meds name only"})
return response["result"]
def find_medicine_recommendations(medicine_names_text: str) -> List[Dict]:
"""
Searches for medicines in the medicine vector store and retrieves details from MongoDB.
"""
if not med_vectordb:
return []
# Similarity search to find relevant medicines
sim_search = med_vectordb.similarity_search(medicine_names_text, k=5)
sim_search_json = [json.loads(item.page_content) for item in sim_search]
# Collect unique IDs
unique_ids = list({item['_id'] for item in sim_search_json})
# Fetch full details from MongoDB
recommendations = []
for med_id in unique_ids:
medicine = medicines_collection.find_one({"_id": med_id})
if medicine:
recommendations.append(medicine)
return recommendations
# ==========================================
# Routes
# ==========================================
@app.route('/', methods=['GET'])
def welcome():
return "MediMate Backend is Running", 200
@app.route('/chat', methods=['POST'])
def chat():
"""
Main chat endpoint. Handles both general text questions and PDF prescription uploads.
"""
# 1. Handle JSON Question (General Chat)
if request.is_json:
json_data = request.get_json()
question = json_data.get('question', '')
if not question:
return jsonify({"message": "Question is required"}), 400
try:
response_message = conversation_chain.predict(input=question)
return jsonify({"message": response_message}), 200
except Exception as e:
return jsonify({"error": f"Error processing request: {str(e)}"}), 500
# 2. Handle PDF Upload (Prescription Analysis)
elif 'pdf_file' in request.files:
pdf_file = request.files['pdf_file']
if not pdf_file.filename.endswith('.pdf'):
return jsonify({'error': 'Invalid file format. Please upload a PDF.'}, 415)
try:
# Save temporarily
temp_filename = f"temp_{pdf_file.filename}"
pdf_file.save(temp_filename)
try:
# Process PDF
pres_vectordb = process_pdf_and_create_vector_store(temp_filename)
# Extract Medicines
extracted_meds_text = extract_medicines_from_prescription(pres_vectordb)
# Find Recommendations
recommendations = find_medicine_recommendations(extracted_meds_text)
return jsonify({
"message": "Based on the prescription, here are the recommended medicines:",
"recommendation": recommendations,
"extracted_text": extracted_meds_text # Optional: return what was found
}), 200
finally:
# Cleanup
if os.path.exists(temp_filename):
os.remove(temp_filename)
# Note: You might want to cleanup the chroma persistence directory too if it's temporary per request
except Exception as e:
return jsonify({"error": f"Error processing PDF: {str(e)}"}), 500
else:
return jsonify({'error': 'Unsupported request format. Send JSON with "question" or form-data with "pdf_file".'}, 415)
if __name__ == '__main__':
app.run(debug=True)