farhananis005 commited on
Commit
ecd3d25
·
verified ·
1 Parent(s): 089531c

Upload 2 files

Browse files
Files changed (2) hide show
  1. medical_reports_upgraded.py +1079 -0
  2. requirements.txt +12 -0
medical_reports_upgraded.py ADDED
@@ -0,0 +1,1079 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import shutil
3
+ import openai
4
+ import docx
5
+ import base64
6
+ import gradio as gr
7
+ import assemblyai as aai
8
+ from langchain.document_loaders import PyPDFLoader
9
+ from langchain.document_loaders import DirectoryLoader
10
+ from langchain.document_loaders import TextLoader
11
+ from langchain.document_loaders import Docx2txtLoader
12
+ from langchain.vectorstores import FAISS
13
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
14
+ from langchain.chains.question_answering import load_qa_chain
15
+ from langchain.callbacks import get_openai_callback
16
+ from langchain.llms import OpenAI
17
+ from langchain_openai import ChatOpenAI, OpenAIEmbeddings
18
+ from langchain_core.prompts import ChatPromptTemplate
19
+ from pydantic import BaseModel, Field
20
+
21
+ from langchain import PromptTemplate, LLMChain
22
+
23
+ os.environ["TOKENIZERS_PARALLELISM"] = "false"
24
+
25
+ aai.settings.api_key = os.environ.get("AAPI_KEY")
26
+ openai.api_key = os.environ.get("OPENAI_API_KEY")
27
+ embeddings = OpenAIEmbeddings()
28
+ client = OpenAI()
29
+
30
+ upload_dir = "/home/user/app/file/"
31
+ upload_files_vector_db = "/home/user/app/file_db/"
32
+ report_vector_db = "/home/user/app/local_db/"
33
+ soap_dir = "/home/user/app/soap_docs/"
34
+ sbar_dir = "/home/user/app/sbar_docs/"
35
+ temp_reports_dir = "/home/user/app/temp_reports/"
36
+ temp_vector_db = "/home/user/app/temp_db/"
37
+
38
+ directories = [
39
+ upload_dir,
40
+ upload_files_vector_db,
41
+ report_vector_db,
42
+ soap_dir,
43
+ sbar_dir,
44
+ temp_reports_dir,
45
+ temp_vector_db,
46
+ ]
47
+
48
+ # Create each directory if it doesn't already exist
49
+ for directory in directories:
50
+ if not os.path.exists(directory):
51
+ os.makedirs(directory)
52
+ print(f"Created directory: {directory}")
53
+ else:
54
+ print(f"Directory already exists: {directory}")
55
+ llm = ChatOpenAI(model="gpt-4o-mini")
56
+ embedding_model = OpenAIEmbeddings()
57
+ # report_db = FAISS.load_local(report_vector_db, embeddings=embedding_model, allow_dangerous_deserialization=True)
58
+ qa_chain = load_qa_chain(ChatOpenAI(), chain_type="stuff")
59
+
60
+ """# Page 1"""
61
+
62
+
63
+ def save_file(input_file):
64
+ os.makedirs(upload_dir, exist_ok=True)
65
+
66
+ for file in input_file:
67
+ shutil.copy(file.name, upload_dir)
68
+
69
+ return "File(s) saved successfully!"
70
+
71
+
72
+ def vectorise(input_dir, output_dir):
73
+ loader1 = DirectoryLoader(input_dir, glob="./*.pdf", loader_cls=PyPDFLoader)
74
+ document1 = loader1.load()
75
+
76
+ loader2 = DirectoryLoader(input_dir, glob="./*.txt", loader_cls=TextLoader)
77
+ document2 = loader2.load()
78
+
79
+ loader3 = DirectoryLoader(input_dir, glob="./*.docx", loader_cls=Docx2txtLoader)
80
+ document3 = loader3.load()
81
+
82
+ document1.extend(document2)
83
+ document1.extend(document3)
84
+
85
+ text_splitter = RecursiveCharacterTextSplitter(
86
+ chunk_size=1000, chunk_overlap=200, length_function=len
87
+ )
88
+
89
+ docs = text_splitter.split_documents(document1)
90
+ file_db = FAISS.from_documents(docs, embeddings)
91
+ file_db.save_local(output_dir)
92
+
93
+ return "File(s) processed successfully!"
94
+
95
+
96
+ def merge_vectors(vectorDB_path):
97
+ docs_db1 = FAISS.load_local(
98
+ report_vector_db, embeddings, allow_dangerous_deserialization=True
99
+ )
100
+ docs_db2 = FAISS.load_local(
101
+ vectorDB_path, embeddings, allow_dangerous_deserialization=True
102
+ )
103
+ docs_db2.merge_from(docs_db1)
104
+ docs_db2.save_local(report_vector_db)
105
+
106
+
107
+ def formatted_response(docs, response):
108
+ formatted_output = response + "\n\nSources"
109
+
110
+ for i, doc in enumerate(docs):
111
+ source_info = doc.metadata.get("source", "Unknown source")
112
+ page_info = doc.metadata.get("page", None)
113
+
114
+ file_name = source_info.split("/")[-1].strip()
115
+
116
+ if page_info is not None:
117
+ formatted_output += f"\n{file_name}\tpage no {page_info}"
118
+ else:
119
+ formatted_output += f"\n{file_name}"
120
+
121
+ return formatted_output
122
+
123
+
124
+ class AI_Medical_Report(BaseModel):
125
+ patient_name: str = Field(
126
+ ...,
127
+ description="The full name of the patient if provided in the context. Otherwise Unknown",
128
+ )
129
+ soap_report: str = Field(
130
+ ...,
131
+ description="""SOAP reports are a structured way to document patient interactions in healthcare:
132
+ Subjective: Patient’s own description of symptoms and concerns.
133
+ Objective: Factual, measurable data like exam results and vital signs.
134
+ Assessment: The healthcare provider’s diagnosis or clinical impression.
135
+ Plan: Recommended next steps, treatments, or follow-up actions.""",
136
+ )
137
+ sbar_report: str = Field(
138
+ ...,
139
+ description="""SBAR reports are a structured communication tool in healthcare to convey critical information efficiently:
140
+ Situation: Briefly state the current issue or reason for the communication.
141
+ Background: Provide context, such as patient history or relevant background info.
142
+ Assessment: Share your professional assessment of the problem.
143
+ Recommendation: Suggest actions or what you need from the listener.""",
144
+ )
145
+ recommendations_for_doc: str = Field(
146
+ ...,
147
+ description="provide 3 recommendations for the doctor like further questions to ask the patient, follow-up tests etc.",
148
+ )
149
+
150
+
151
+ def assemblyai_STT(audio_url: str) -> str:
152
+ """
153
+ Transcribes an audio file with speaker labels and returns a formatted string.
154
+
155
+ Parameters:
156
+ audio_url (str): URL or path to the audio file to be transcribed.
157
+
158
+ Returns:
159
+ str: A formatted string with each speaker's label and their corresponding text.
160
+ """
161
+ # Configure transcription with speaker labels enabled
162
+ config = aai.TranscriptionConfig(speaker_labels=True)
163
+
164
+ # Perform transcription
165
+ transcript = aai.Transcriber().transcribe(audio_url, config)
166
+
167
+ # Format each utterance into a single string with speaker labels
168
+ transcription_output = "\n".join(
169
+ f"Speaker {utterance.speaker}: {utterance.text}"
170
+ for utterance in transcript.utterances
171
+ )
172
+
173
+ return transcription_output
174
+
175
+
176
+ def generate_report(input_text: str = None, file_path: str = None) -> AI_Medical_Report:
177
+ """
178
+ Generates a SOAP report from text or audio input using OpenAI's GPT-4 model.
179
+
180
+ Args:
181
+ client (OpenAI): Initialized OpenAI client.
182
+ input_text (str, optional): Text input containing the patient case study.
183
+ file_path (str, optional): Path to the audio file. Defaults to None.
184
+ model (str): Model name to use for generating the report. Defaults to "gpt-4o-audio-preview".
185
+
186
+ Returns:
187
+ SOAPExtraction: Parsed SOAP information including patient name, subjective, objective, assessment, plan, and doctor recommendations.
188
+ """
189
+ from openai import OpenAI
190
+
191
+ client = OpenAI()
192
+ try:
193
+ # Prepare message content based on input type
194
+ messages = [
195
+ {
196
+ "role": "system",
197
+ "content": (
198
+ "You are an AI medical assistant designed to help doctors. Your job is to convert the patient information into SOAP and SBAR reports. in the given JSON format"
199
+ ),
200
+ }
201
+ ]
202
+
203
+ if input_text:
204
+ # Text-based input
205
+ messages.append({"role": "user", "content": input_text})
206
+ model = "gpt-4o"
207
+ elif file_path:
208
+ # Audio-based input: load and encode the audio file
209
+ model = "gpt-4o-audio-preview-2024-10-01"
210
+ with open(file_path, "rb") as audio_file:
211
+ wav_data = audio_file.read()
212
+ encoded_string = base64.b64encode(wav_data).decode("utf-8")
213
+ messages.append(
214
+ {
215
+ "role": "user",
216
+ "content": [
217
+ {
218
+ "type": "text",
219
+ "text": "Please generate Medical reports based on the following audio input",
220
+ },
221
+ {
222
+ "type": "input_audio",
223
+ "input_audio": {"data": encoded_string, "format": "wav"},
224
+ },
225
+ ],
226
+ }
227
+ )
228
+ else:
229
+ raise ValueError("Either input_text or file_path must be provided.")
230
+
231
+ # Create completion request
232
+ completion = client.beta.chat.completions.parse(
233
+ model=model,
234
+ modalities=["text"],
235
+ messages=messages,
236
+ response_format=AI_Medical_Report,
237
+ )
238
+
239
+ # Retrieve structured SOAP report
240
+ report = completion.choices[0].message.parsed
241
+ return report
242
+
243
+ except Exception as e:
244
+ print(f"An error occurred: {e}")
245
+ return None
246
+
247
+
248
+ # driver function for making reports
249
+ def report_main(
250
+ input_text: str = None,
251
+ audio_file: str = None,
252
+ transcription_service: str = "OpenAI",
253
+ ) -> tuple:
254
+ """
255
+ Generates a SOAP and SBAR report based on user input, either from text or audio.
256
+
257
+ Args:
258
+ input_text (str, optional): Text input from the user.
259
+ audio_file (str, optional): Path to the audio file (if provided).
260
+ transcription_service (str): Selected transcription service ("AssemblyAI" or "OpenAI").
261
+
262
+ Returns:
263
+ tuple: Contains patient_name, SOAP Report, SBAR_Report,
264
+ doctor_recommendations, and transcription_text (if audio input was used).
265
+ """
266
+ from openai import OpenAI
267
+
268
+ client = OpenAI() # Initialize OpenAI client
269
+
270
+ # Initialize empty strings for the SOAP report components
271
+ patient_name = ""
272
+ soap_report = ""
273
+ sbar_report = ""
274
+ doctor_recommendations = ""
275
+ transcription_text = ""
276
+
277
+ # Process input based on provided input_text or audio_file
278
+ if input_text:
279
+ # Generate SOAP report from text input
280
+ report = generate_report(input_text=input_text)
281
+
282
+ # Assign values from the generated report
283
+ patient_name = report.patient_name
284
+ soap_report = report.soap_report
285
+ sbar_report = report.sbar_report
286
+ doctor_recommendations = report.recommendations_for_doc
287
+
288
+ elif audio_file:
289
+ # Use selected transcription service for audio input
290
+ if transcription_service == "AssemblyAI":
291
+ transcription_text = assemblyai_STT(audio_file)
292
+ report = generate_report(input_text=transcription_text)
293
+ print(transcription_text)
294
+ elif transcription_service == "OpenAI":
295
+ report = generate_report(file_path=audio_file)
296
+ transcription_text = "OpenAI directly accepts Audio Inputs"
297
+ print(transcription_text)
298
+ else:
299
+ raise ValueError(
300
+ "Invalid transcription service specified. Choose 'AssemblyAI' or 'OpenAI'."
301
+ )
302
+
303
+ # Assign values from the generated report
304
+ patient_name = report.patient_name
305
+ soap_report = report.soap_report
306
+ sbar_report = report.sbar_report
307
+ doctor_recommendations = report.recommendations_for_doc
308
+
309
+ else:
310
+ raise ValueError("Either input_text or audio_file must be provided.")
311
+
312
+ # Return structured output in a tuple
313
+ return (
314
+ patient_name,
315
+ soap_report,
316
+ sbar_report,
317
+ doctor_recommendations,
318
+ transcription_text,
319
+ )
320
+
321
+
322
+ # x=generate_report(file_path="/content/Cough.wav")
323
+
324
+
325
+ def delete_dir(dir):
326
+ try:
327
+ shutil.rmtree(dir)
328
+ return "Deleted Successfully"
329
+
330
+ except:
331
+ return "Already Deleted"
332
+
333
+
334
+ def save_reports(file_name, file_content, report_type, destination_folder):
335
+ # Ensure the destination folder exists
336
+ if not os.path.exists(destination_folder):
337
+ os.makedirs(destination_folder)
338
+
339
+ # Define the path for the .docx file in the destination folder
340
+ destination_path = os.path.join(
341
+ destination_folder, f"{report_type}_{file_name}.docx"
342
+ )
343
+
344
+ # Create a new document and add the SOAP response text
345
+ doc = docx.Document()
346
+ doc.add_paragraph(file_content)
347
+
348
+ # Save the document to the specified destination folder
349
+ doc.save(destination_path)
350
+
351
+ # Define and create the path for the temp folder
352
+ if not os.path.exists(temp_reports_dir):
353
+ os.makedirs(temp_reports_dir)
354
+
355
+ # Define the path for the temp copy
356
+ temp_path = os.path.join(temp_reports_dir, f"{report_type}_{file_name}.docx")
357
+
358
+ # Save a copy of the document in the temp folder
359
+ doc.save(temp_path)
360
+
361
+ return f"Successfully saved"
362
+
363
+
364
+ # driver function for save
365
+ def save_reports_main(file_name, soap_report_content, sbar_report_content):
366
+ # Save SOAP report
367
+ soap_result = save_reports(file_name, soap_report_content, "SOAP", soap_dir)
368
+ print(soap_result)
369
+
370
+ # Save SBAR report
371
+ sbar_result = save_reports(file_name, sbar_report_content, "SBAR", sbar_dir)
372
+ print(sbar_result)
373
+
374
+ # Vectorize the reports in the temporary directory
375
+ vectorise(temp_reports_dir, temp_vector_db)
376
+
377
+ # Check if report_vector_db is empty
378
+ if not os.listdir(report_vector_db): # If report_vector_db is empty
379
+ # Copy all contents from temp_vector_db to report_vector_db
380
+ for item in os.listdir(temp_vector_db):
381
+ source_path = os.path.join(temp_vector_db, item)
382
+ destination_path = os.path.join(report_vector_db, item)
383
+ if os.path.isdir(source_path):
384
+ shutil.copytree(source_path, destination_path)
385
+ else:
386
+ shutil.copy2(source_path, destination_path)
387
+ print("Copied contents from temp_vector_db to report_vector_db.")
388
+ else:
389
+ # Call merge_vectors to merge temp_vector_db into report_vector_db
390
+ merge_vectors(temp_vector_db)
391
+ print("Merged temp_vector_db into report_vector_db.")
392
+
393
+ # Clean up by deleting the temporary directories
394
+ delete_dir(temp_reports_dir)
395
+ delete_dir(temp_vector_db)
396
+ print("Deleted temporary directories.")
397
+
398
+ return "Reports saved successfully!"
399
+
400
+
401
+ """#Page 2"""
402
+
403
+
404
+ def refresh_files(docs_dir):
405
+ if not os.path.exists(docs_dir):
406
+ os.makedirs(docs_dir)
407
+
408
+ file_list = []
409
+
410
+ for root, dirs, files in os.walk(docs_dir):
411
+ for file in files:
412
+ file_list.append(file)
413
+ return gr.Dropdown(choices=file_list, interactive=True)
414
+
415
+
416
+ def soap_refresh():
417
+ return refresh_files(soap_dir)
418
+
419
+
420
+ def sbar_refresh():
421
+ return refresh_files(sbar_dir)
422
+
423
+
424
+ def get_content(docs_dir, selected_file_name):
425
+ docx_path = os.path.join(docs_dir, selected_file_name)
426
+
427
+ # Check if the file exists and has a .docx extension
428
+ if not os.path.isfile(docx_path) or not docx_path.endswith(".docx"):
429
+ raise FileNotFoundError(
430
+ f"File {selected_file_name} not found in {docs_dir} or is not a .docx file."
431
+ )
432
+
433
+ try:
434
+ # Open and read the document
435
+ doc = docx.Document(docx_path)
436
+ paragraphs = [paragraph.text for paragraph in doc.paragraphs if paragraph.text]
437
+ return "\n\n".join(
438
+ paragraphs
439
+ ) # Join paragraphs with double newlines for readability
440
+
441
+ except Exception as e:
442
+ raise IOError(f"An error occurred while reading the document: {e}")
443
+
444
+
445
+ def get_soap_report_content(selected_file_name):
446
+ return get_content(soap_dir, selected_file_name)
447
+
448
+
449
+ def get_sbar_report_content(selected_file):
450
+ return get_content(sbar_dir, selected_file)
451
+
452
+
453
+ # Updated generate_response function
454
+ def generate_response(message, history, soap_content):
455
+ from openai import OpenAI
456
+
457
+ client = OpenAI()
458
+
459
+ # Format history as expected by OpenAI's API
460
+ formatted_history = [
461
+ {
462
+ "role": "system",
463
+ "content": "This conversation is based on the following SOAP report content:\n"
464
+ + soap_content,
465
+ }
466
+ ]
467
+ for interaction in history:
468
+ if len(interaction) == 2:
469
+ user, assistant = interaction
470
+ formatted_history.append({"role": "user", "content": user})
471
+ formatted_history.append({"role": "assistant", "content": assistant})
472
+
473
+ # Add the latest user message to the formatted history
474
+ formatted_history.append({"role": "user", "content": message})
475
+
476
+ # Generate the assistant's response with streaming enabled
477
+ response = client.chat.completions.create(
478
+ model="gpt-4o-mini", messages=formatted_history, stream=True
479
+ )
480
+
481
+ partial_message = ""
482
+ for chunk in response:
483
+ if chunk.choices[0].delta.content is not None:
484
+ partial_message += chunk.choices[0].delta.content
485
+ yield partial_message # Yield each chunk as it comes
486
+
487
+
488
+ # Updated handle_chat_message function
489
+ def handle_chat_message(history, message, soap_content):
490
+ response_generator = generate_response(message, history, soap_content)
491
+ new_history = history + [
492
+ [message, ""]
493
+ ] # Initialize with an empty assistant response
494
+ for partial_response in response_generator:
495
+ new_history[-1][1] = partial_response # Update assistant's response in history
496
+ yield new_history, "" # Stream the updated history and clear the text box
497
+
498
+
499
+ def ask_reports(docs_dir, doc_name, question):
500
+ # Construct the path to the docx file
501
+ docx_path = os.path.join(docs_dir, doc_name)
502
+
503
+ # Read and extract text from the .docx file
504
+ doc = docx.Document(docx_path)
505
+ extracted_text = (
506
+ f"You are provided with a medical report of a patient {doc_name}.\n\n"
507
+ )
508
+ text = ""
509
+ for paragraph in doc.paragraphs:
510
+ text += paragraph.text + "\n"
511
+
512
+ # Append the question to extracted text
513
+ extracted_text = (
514
+ extracted_text
515
+ + text
516
+ + "\n\nUse the report to answer the following question:\n"
517
+ + question
518
+ )
519
+
520
+ if not text:
521
+ return "Failed to retrieve text from document."
522
+ from openai import OpenAI
523
+
524
+ client = OpenAI()
525
+ # Prepare the messages for the chat completion request
526
+ messages = [
527
+ {
528
+ "role": "system",
529
+ "content": "You are a helpful assistant with medical expertise.",
530
+ },
531
+ {"role": "user", "content": extracted_text},
532
+ ]
533
+
534
+ # Use the ChatCompletion API to get a response
535
+ try:
536
+ completion = client.chat.completions.create(
537
+ model="gpt-4o",
538
+ messages=messages,
539
+ )
540
+ answer = completion.choices[0].message
541
+ except Exception as e:
542
+ return f"An error occurred: {e}"
543
+
544
+ return answer
545
+
546
+
547
+ def ask_soap(selected_file, question):
548
+ # Logic to answer the question based on the selected SOAP file
549
+ return f"Answer to '{question}' based on {selected_file}"
550
+
551
+
552
+ def ask_sbar(selected_file, question):
553
+ # Logic to answer the question based on the selected SBAR file
554
+ return f"Answer to '{question}' based on {selected_file}"
555
+
556
+
557
+ """# page 3"""
558
+
559
+
560
+ def local_search(question):
561
+ embeddings = OpenAIEmbeddings()
562
+ file_db = FAISS.load_local(
563
+ report_vector_db, embeddings, allow_dangerous_deserialization=True
564
+ )
565
+ docs = file_db.similarity_search(question)
566
+
567
+ chain = load_qa_chain(llm, chain_type="stuff")
568
+
569
+ with get_openai_callback() as cb:
570
+ response = chain.run(input_documents=docs, question=question)
571
+ print(cb)
572
+
573
+ return formatted_response_local(docs, response)
574
+
575
+
576
+ def formatted_response_local(docs, response):
577
+ formatted_output = response + "\n\nSources"
578
+
579
+ for i, doc in enumerate(docs):
580
+ source_info = doc.metadata.get("source", "Unknown source")
581
+ page_info = doc.metadata.get("page", None)
582
+
583
+ file_name = source_info.split("/")[-1].strip()
584
+
585
+ if page_info is not None:
586
+ formatted_output += f"\n{file_name}\tpage no {page_info}"
587
+ else:
588
+ formatted_output += f"\n{file_name}"
589
+
590
+ return formatted_output
591
+
592
+
593
+ def local_gpt(question):
594
+ template = """Question: {question}
595
+ Answer: Let's think step by step."""
596
+
597
+ prompt = PromptTemplate(template=template, input_variables=["question"])
598
+
599
+ llm_chain = LLMChain(prompt=prompt, llm=llm)
600
+ response = llm_chain.run(question)
601
+
602
+ return response
603
+
604
+
605
+ """# Page 4"""
606
+
607
+
608
+ def save2_docs(docs):
609
+
610
+ import shutil
611
+ import os
612
+
613
+ output_dir = upload_dir
614
+
615
+ if os.path.exists(output_dir):
616
+ shutil.rmtree(output_dir)
617
+
618
+ if not os.path.exists(output_dir):
619
+ os.makedirs(output_dir)
620
+
621
+ for doc in docs:
622
+ shutil.copy(doc.name, output_dir)
623
+
624
+ return "Successful!"
625
+
626
+
627
+ global agent2
628
+
629
+
630
+ def create2_agent():
631
+
632
+ from langchain.chat_models import ChatOpenAI
633
+ from langchain.chains.conversation.memory import ConversationSummaryBufferMemory
634
+ from langchain.chains import ConversationChain
635
+
636
+ global agent2
637
+
638
+ llm = ChatOpenAI(model_name="gpt-4o-mini")
639
+ memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=1500)
640
+ agent2 = ConversationChain(llm=llm, memory=memory, verbose=True)
641
+
642
+ return "Successful!"
643
+
644
+
645
+ def search2_docs(prompt, question, state):
646
+
647
+ from langchain.embeddings.openai import OpenAIEmbeddings
648
+ from langchain.vectorstores import FAISS
649
+ from langchain.callbacks import get_openai_callback
650
+
651
+ global agent2
652
+ agent2 = agent2
653
+
654
+ state = state or []
655
+
656
+ embeddings = OpenAIEmbeddings()
657
+ docs_db = FAISS.load_local(
658
+ upload_files_vector_db, embeddings, allow_dangerous_deserialization=True
659
+ )
660
+ docs = docs_db.similarity_search(question)
661
+
662
+ prompt += "\n\n"
663
+ prompt += question
664
+ prompt += "\n\n"
665
+ prompt += str(docs)
666
+
667
+ with get_openai_callback() as cb:
668
+ response = agent2.predict(input=prompt)
669
+ print(cb)
670
+
671
+ return formatted_response(docs, question, response, state)
672
+
673
+
674
+ def delete2_docs():
675
+
676
+ import shutil
677
+
678
+ path1 = upload_dir
679
+ path2 = upload_files_vector_db
680
+
681
+ try:
682
+ shutil.rmtree(path1)
683
+ shutil.rmtree(path2)
684
+ return "Deleted Successfully"
685
+
686
+ except:
687
+ return "Already Deleted"
688
+
689
+
690
+ def process2_docs():
691
+
692
+ from langchain.document_loaders import PyPDFLoader
693
+ from langchain.document_loaders import DirectoryLoader
694
+ from langchain.document_loaders import TextLoader
695
+ from langchain.document_loaders import Docx2txtLoader
696
+ from langchain.document_loaders.csv_loader import CSVLoader
697
+ from langchain.document_loaders import UnstructuredExcelLoader
698
+ from langchain.vectorstores import FAISS
699
+ from langchain.embeddings.openai import OpenAIEmbeddings
700
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
701
+
702
+ loader1 = DirectoryLoader(upload_dir, glob="./*.pdf", loader_cls=PyPDFLoader)
703
+ document1 = loader1.load()
704
+
705
+ loader2 = DirectoryLoader(upload_dir, glob="./*.txt", loader_cls=TextLoader)
706
+ document2 = loader2.load()
707
+
708
+ loader3 = DirectoryLoader(upload_dir, glob="./*.docx", loader_cls=Docx2txtLoader)
709
+ document3 = loader3.load()
710
+
711
+ loader4 = DirectoryLoader(upload_dir, glob="./*.csv", loader_cls=CSVLoader)
712
+ document4 = loader4.load()
713
+
714
+ loader5 = DirectoryLoader(
715
+ upload_dir, glob="./*.xlsx", loader_cls=UnstructuredExcelLoader
716
+ )
717
+ document5 = loader5.load()
718
+
719
+ document1.extend(document2)
720
+ document1.extend(document3)
721
+ document1.extend(document4)
722
+ document1.extend(document5)
723
+
724
+ text_splitter = RecursiveCharacterTextSplitter(
725
+ chunk_size=1000, chunk_overlap=200, length_function=len
726
+ )
727
+
728
+ docs = text_splitter.split_documents(document1)
729
+ embeddings = OpenAIEmbeddings()
730
+
731
+ docs_db = FAISS.from_documents(docs, embeddings)
732
+ docs_db.save_local(upload_files_vector_db)
733
+
734
+ return "Successful!"
735
+
736
+
737
+ def formatted_response(docs, question, response, state):
738
+
739
+ formatted_output = response + "\n\nSources"
740
+
741
+ for i, doc in enumerate(docs):
742
+ source_info = doc.metadata.get("source", "Unknown source")
743
+ page_info = doc.metadata.get("page", None)
744
+
745
+ doc_name = source_info.split("/")[-1].strip()
746
+
747
+ if page_info is not None:
748
+ formatted_output += f"\n{doc_name}\tpage no {page_info}"
749
+ else:
750
+ formatted_output += f"\n{doc_name}"
751
+
752
+ state.append((question, formatted_output))
753
+ return state, state
754
+
755
+
756
+ """# UI"""
757
+
758
+ import gradio as gr
759
+
760
+ css = """
761
+ .col {
762
+ max-width: 70%;
763
+ margin: 0 auto;
764
+ display: flex;
765
+ flex-direction: column;
766
+ justify-content: center;
767
+ align-items: center;
768
+ }
769
+ """
770
+
771
+ # Define the Gradio interface
772
+ with gr.Blocks(css=css) as demo:
773
+ gr.Markdown("## <center>Medical App</center>")
774
+ # Page 1----------------------------------------------------------------------
775
+ with gr.Tab("SOAP and SBAR Note Creation"):
776
+ # Tab for generating from audio
777
+ with gr.Tab("From Audio"):
778
+ with gr.Row():
779
+ with gr.Column():
780
+ audio_file = gr.Audio(label="Audio Input", type="filepath")
781
+ with gr.Column():
782
+ transcription_service = gr.Dropdown(
783
+ label="Select Transcription Service",
784
+ choices=["OpenAI", "AssemblyAI"],
785
+ value="OpenAI",
786
+ )
787
+ gr.Markdown(
788
+ "<small>Upload an audio file or select a transcription service.</small>"
789
+ )
790
+ generate_with_audio_button = gr.Button(
791
+ "Generate Report", variant="primary"
792
+ )
793
+
794
+ # Shared output containers
795
+ audio_patient_name_box = gr.Textbox(
796
+ label="Patient Name",
797
+ interactive=True,
798
+ placeholder="Generated Patient Name",
799
+ lines=1,
800
+ )
801
+ with gr.Row():
802
+ with gr.Column():
803
+ audio_soap_report_box = gr.Textbox(
804
+ label="SOAP Report",
805
+ interactive=True,
806
+ placeholder="Generated SOAP Report",
807
+ lines=10,
808
+ )
809
+ with gr.Column():
810
+ audio_sbar_report_box = gr.Textbox(
811
+ label="SBAR Report",
812
+ interactive=True,
813
+ placeholder="Generated SBAR Report",
814
+ lines=10,
815
+ )
816
+
817
+ audio_doctor_recommendations_box = gr.Textbox(
818
+ label="Doctor Recommendations",
819
+ interactive=False,
820
+ placeholder="Recommendations",
821
+ lines=5,
822
+ )
823
+ # audio_transcription_box = gr.Textbox(label="Transcription Text", interactive=False, placeholder="Transcribed Text", lines=5)
824
+
825
+ # Click event for audio
826
+ generate_with_audio_button.click(
827
+ fn=report_main,
828
+ inputs=[audio_file, transcription_service],
829
+ outputs=[
830
+ audio_patient_name_box,
831
+ audio_soap_report_box,
832
+ audio_sbar_report_box,
833
+ audio_doctor_recommendations_box,
834
+ # audio_transcription_box
835
+ ],
836
+ )
837
+
838
+ # Tab for generating from text input
839
+ with gr.Tab("From Transcript"):
840
+ with gr.Column():
841
+ input_text = gr.Textbox(
842
+ label="Patient Case Study (Text Input)",
843
+ placeholder="Enter the patient case study here...",
844
+ lines=7,
845
+ )
846
+ gr.Markdown(
847
+ "<small>Enter the patient's details, symptoms, and any relevant information.</small>"
848
+ )
849
+ generate_with_text_button = gr.Button(
850
+ "Generate Report", variant="primary"
851
+ )
852
+
853
+ # Shared output containers for this tab
854
+ patient_name_box_text = gr.Textbox(
855
+ label="Patient Name",
856
+ interactive=True,
857
+ placeholder="Generated Patient Name",
858
+ lines=1,
859
+ )
860
+ with gr.Row():
861
+ with gr.Column():
862
+ soap_report_box_text = gr.Textbox(
863
+ label="SOAP Report",
864
+ interactive=True,
865
+ placeholder="Generated SOAP Report",
866
+ lines=10,
867
+ )
868
+ with gr.Column():
869
+ sbar_report_box_text = gr.Textbox(
870
+ label="SBAR Report",
871
+ interactive=True,
872
+ placeholder="Generated SBAR Report",
873
+ lines=10,
874
+ )
875
+
876
+ doctor_recommendations_box_text = gr.Textbox(
877
+ label="Doctor Recommendations",
878
+ interactive=False,
879
+ placeholder="Recommendations",
880
+ lines=5,
881
+ )
882
+
883
+ # Click event for text
884
+ generate_with_text_button.click(
885
+ fn=report_main,
886
+ inputs=[input_text],
887
+ outputs=[
888
+ patient_name_box_text,
889
+ soap_report_box_text,
890
+ sbar_report_box_text,
891
+ doctor_recommendations_box_text,
892
+ ],
893
+ )
894
+ # Add Save Report Button
895
+ with gr.Row():
896
+ save_button = gr.Button("Save Report", variant="secondary")
897
+ save_message = gr.Textbox(
898
+ label="Save Status",
899
+ interactive=False,
900
+ placeholder="Status of the save operation",
901
+ lines=1,
902
+ )
903
+
904
+ # Click event for Save Report Button using `patient_name_box_text` as the file name
905
+ save_button.click(
906
+ fn=save_reports_main,
907
+ inputs=[patient_name_box_text, soap_report_box_text, sbar_report_box_text],
908
+ outputs=[save_message],
909
+ )
910
+
911
+ # Page 2----------------------------------------------------------------------
912
+ ####|
913
+ with gr.Tab("SOAP and SBAR Queries"):
914
+
915
+ with gr.Tab("Query SOAP Reports"):
916
+ with gr.Row():
917
+ with gr.Column():
918
+ soap_refresh_button = gr.Button("Refresh")
919
+ ask_soap_input = gr.Dropdown(label="Choose File")
920
+ soap_content_display = gr.Textbox(
921
+ label="SOAP Report Content",
922
+ interactive=False,
923
+ placeholder="Report content will appear here...",
924
+ lines=5,
925
+ )
926
+ with gr.Column():
927
+ # Chatbot for Q&A
928
+ soap_chatbot = gr.Chatbot(label="SOAP Chatbot")
929
+ soap_chat_input = gr.Textbox(
930
+ label="Ask a question",
931
+ placeholder="Enter your question here...",
932
+ )
933
+ clear = gr.ClearButton([soap_chat_input, soap_chatbot])
934
+
935
+ # Refresh button for SOAP file dropdown
936
+ soap_refresh_button.click(
937
+ fn=soap_refresh, inputs=None, outputs=ask_soap_input
938
+ )
939
+
940
+ # Display selected SOAP report content
941
+ ask_soap_input.change(
942
+ fn=get_soap_report_content,
943
+ inputs=ask_soap_input,
944
+ outputs=soap_content_display,
945
+ )
946
+
947
+ # Handle chatbot input submission with streaming response
948
+ soap_chat_input.submit(
949
+ handle_chat_message,
950
+ inputs=[soap_chatbot, soap_chat_input, soap_content_display],
951
+ outputs=[soap_chatbot, soap_chat_input],
952
+ )
953
+
954
+ # Query SBAR Reports Tab
955
+ with gr.Tab("Query SBAR Reports"):
956
+ with gr.Row():
957
+ with gr.Column():
958
+ sbar_refresh_button = gr.Button("Refresh")
959
+ ask_sbar_input = gr.Dropdown(label="Choose File")
960
+ sbar_content_display = gr.Textbox(
961
+ label="SBAR Report Content",
962
+ interactive=False,
963
+ placeholder="Report content will appear here...",
964
+ lines=5,
965
+ )
966
+ with gr.Column():
967
+ # Chatbot for SBAR Q&A
968
+ sbar_chatbot = gr.Chatbot(label="SBAR Chatbot")
969
+ sbar_chat_input = gr.Textbox(
970
+ label="Ask a question",
971
+ placeholder="Enter your question here...",
972
+ )
973
+ clear_sbar = gr.ClearButton([sbar_chat_input, sbar_chatbot])
974
+
975
+ # Refresh button for SBAR file dropdown
976
+ sbar_refresh_button.click(
977
+ fn=sbar_refresh, inputs=None, outputs=ask_sbar_input
978
+ )
979
+
980
+ # Display selected SBAR report content
981
+ ask_sbar_input.change(
982
+ fn=get_sbar_report_content,
983
+ inputs=ask_sbar_input,
984
+ outputs=sbar_content_display,
985
+ )
986
+
987
+ # Handle chatbot input submission with streaming response
988
+ sbar_chat_input.submit(
989
+ handle_chat_message,
990
+ inputs=[
991
+ sbar_chatbot,
992
+ sbar_chat_input,
993
+ sbar_content_display,
994
+ ], # Pass the SBAR content
995
+ outputs=[sbar_chatbot, sbar_chat_input],
996
+ )
997
+
998
+ # Page 3----------------------------------------------------------------------
999
+ ####|Chatbot to query all SOAP and SBAR reports (RAG). Chatbot can ask OpenAI for answers directly
1000
+ with gr.Tab("All Queries"):
1001
+ with gr.Column(elem_classes="col"):
1002
+ local_search_input = gr.Textbox(label="Enter Question here")
1003
+ local_search_button = gr.Button("Search")
1004
+ local_search_output = gr.Textbox(label="Output")
1005
+
1006
+ local_gpt_button = gr.Button("Ask ChatGPT")
1007
+ local_gpt_output = gr.Textbox(label="Output")
1008
+
1009
+ local_search_button.click(
1010
+ local_search, inputs=local_search_input, outputs=local_search_output
1011
+ )
1012
+ local_gpt_button.click(
1013
+ local_gpt, inputs=local_search_input, outputs=local_gpt_output
1014
+ )
1015
+
1016
+ # Page 4----------------------------------------------------------------------
1017
+ ####|
1018
+ with gr.Tab("Documents Queries"):
1019
+ with gr.Column(elem_classes="col"):
1020
+
1021
+ with gr.Tab("Upload and Process Documents"):
1022
+ with gr.Column():
1023
+ docs2_upload_input = gr.Files(label="Upload File(s)")
1024
+ docs2_upload_button = gr.Button("Upload")
1025
+ docs2_upload_output = gr.Textbox(label="Output")
1026
+
1027
+ docs2_process_button = gr.Button("Process")
1028
+ docs2_process_output = gr.Textbox(label="Output")
1029
+
1030
+ create2_agent_button = gr.Button("Create Agent")
1031
+ create2_agent_output = gr.Textbox(label="Output")
1032
+
1033
+ gr.ClearButton(
1034
+ [
1035
+ docs2_upload_input,
1036
+ docs2_upload_output,
1037
+ docs2_process_output,
1038
+ create2_agent_output,
1039
+ ]
1040
+ )
1041
+
1042
+ docs2_upload_button.click(
1043
+ save2_docs,
1044
+ inputs=docs2_upload_input,
1045
+ outputs=docs2_upload_output,
1046
+ )
1047
+ docs2_process_button.click(
1048
+ process2_docs, inputs=None, outputs=docs2_process_output
1049
+ )
1050
+ create2_agent_button.click(
1051
+ create2_agent, inputs=None, outputs=create2_agent_output
1052
+ )
1053
+
1054
+ with gr.Tab("Query Documents"):
1055
+ with gr.Column():
1056
+ docs2_prompt_input = gr.Textbox(label="Custom Prompt")
1057
+
1058
+ docs2_chatbot = gr.Chatbot(label="Chats")
1059
+ docs2_state = gr.State()
1060
+
1061
+ docs2_search_input = gr.Textbox(label="Enter Question")
1062
+ docs2_search_button = gr.Button("Search")
1063
+
1064
+ docs2_delete_button = gr.Button("Delete")
1065
+ docs2_delete_output = gr.Textbox(label="Output")
1066
+
1067
+ gr.ClearButton(
1068
+ [docs2_prompt_input, docs2_search_input, docs2_delete_output]
1069
+ )
1070
+ docs2_search_button.click(
1071
+ search2_docs,
1072
+ inputs=[docs2_prompt_input, docs2_search_input, docs2_state],
1073
+ outputs=[docs2_chatbot, docs2_state],
1074
+ )
1075
+ docs2_delete_button.click(
1076
+ delete2_docs, inputs=None, outputs=docs2_delete_output
1077
+ )
1078
+
1079
+ demo.launch(debug=True)
requirements.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ pypdf
2
+ langchain
3
+ PyPDF2
4
+ docx2txt
5
+ gradio
6
+ faiss-cpu
7
+ openai==1.52.2
8
+ assemblyai
9
+ python-docx
10
+ langchain-community
11
+ tiktoken
12
+ langchain-openai==0.2.4