lyimo commited on
Commit
9933a4f
Β·
verified Β·
1 Parent(s): 3d4910d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +1057 -61
app.py CHANGED
@@ -5,6 +5,12 @@ import pandas as pd
5
  from groq import Groq
6
  from PIL import Image
7
  import io
 
 
 
 
 
 
8
 
9
  # Initialize Groq client
10
  client = Groq(
@@ -17,8 +23,44 @@ def encode_image(image):
17
  image.save(buffered, format="JPEG")
18
  return base64.b64encode(buffered.getvalue()).decode('utf-8')
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  # Extract ECG readings from image using Llama Vision model
21
  def analyze_ecg_image(image, vision_model="llama-3.2-90b-vision-preview"):
 
 
22
  if image is None:
23
  return "No image provided."
24
 
@@ -29,8 +71,32 @@ def analyze_ecg_image(image, vision_model="llama-3.2-90b-vision-preview"):
29
  # Encode the image
30
  base64_image = encode_image(image)
31
 
 
 
 
32
  # Create chat completion with vision model
33
- vision_prompt = "Analyze this ECG image carefully. Extract and report all visible parameters, intervals, rhythm patterns, and any abnormalities. Report exact numerical values where visible. Format your response as a structured report with clear sections for different measurements and observations."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
  try:
36
  vision_completion = client.chat.completions.create(
@@ -54,18 +120,328 @@ def analyze_ecg_image(image, vision_model="llama-3.2-90b-vision-preview"):
54
  )
55
 
56
  ecg_analysis = vision_completion.choices[0].message.content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  return ecg_analysis
58
 
59
  except Exception as e:
60
- return f"Error analyzing ECG image: {str(e)}"
61
 
62
  # Generate medical assessment based on ECG readings and patient history
63
  def generate_assessment(ecg_analysis, patient_history=None, chat_model="llama-3.3-70b-versatile"):
 
 
64
  if not ecg_analysis or ecg_analysis.startswith("Error"):
65
  return "Please analyze an ECG image first."
66
 
 
 
 
67
  # Construct prompt based on available information
68
- if patient_history:
69
  prompt = f"""You are a highly trained cardiologist assistant. Based on the ECG analysis below and the patient's history, provide a comprehensive assessment of the patient's cardiac status. Indicate clearly if there are any concerning findings that require immediate medical attention.
70
 
71
  ECG ANALYSIS:
@@ -74,11 +450,28 @@ ECG ANALYSIS:
74
  PATIENT HISTORY:
75
  {patient_history}
76
 
77
- Provide your assessment in the following format:
78
- 1. Summary of findings
79
- 2. Key abnormalities (if any)
80
- 3. Potential clinical implications
81
- 4. Recommendation (including urgency level)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  """
83
  else:
84
  prompt = f"""You are a highly trained cardiologist assistant. Based on the ECG analysis below, provide a comprehensive assessment of the patient's cardiac status. Indicate clearly if there are any concerning findings that require immediate medical attention.
@@ -86,11 +479,28 @@ Provide your assessment in the following format:
86
  ECG ANALYSIS:
87
  {ecg_analysis}
88
 
89
- Provide your assessment in the following format:
90
- 1. Summary of findings
91
- 2. Key abnormalities (if any)
92
- 3. Potential clinical implications
93
- 4. Recommendation (including urgency level)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  """
95
 
96
  try:
@@ -116,17 +526,30 @@ Provide your assessment in the following format:
116
  return f"Error generating assessment: {str(e)}"
117
 
118
  # Doctor's chat interaction with the model about the patient
119
- def doctor_chat(message, chat_history, ecg_analysis, patient_history, chat_model="llama-3.3-70b-versatile"):
 
 
120
  if not ecg_analysis or ecg_analysis.startswith("Error"):
121
  return "Please analyze an ECG image first before starting a chat.", chat_history
122
 
 
 
 
 
 
 
123
  # Prepare chat context
124
  context = f"""ECG ANALYSIS:
125
  {ecg_analysis}
126
 
 
 
 
 
 
127
  """
128
 
129
- if patient_history:
130
  context += f"""PATIENT HISTORY:
131
  {patient_history}
132
 
@@ -140,8 +563,8 @@ def doctor_chat(message, chat_history, ecg_analysis, patient_history, chat_model
140
  }
141
  ]
142
 
143
- # Add chat history to the context
144
- for entry in chat_history:
145
  messages.append({"role": "user", "content": entry[0]})
146
  messages.append({"role": "assistant", "content": entry[1]})
147
 
@@ -166,66 +589,639 @@ def doctor_chat(message, chat_history, ecg_analysis, patient_history, chat_model
166
  return "", chat_history
167
 
168
  # Create Gradio interface
169
- with gr.Blocks(title="Cardiac ECG Analysis System") as app:
170
- gr.Markdown("# Cardiac ECG Analysis System")
 
 
 
171
  gr.Markdown("Upload an ECG image and optional patient history to get an automated analysis and assessment.")
172
 
173
- with gr.Row():
174
- with gr.Column(scale=1):
175
- # Input components
176
- ecg_image = gr.Image(type="pil", label="Upload ECG Image")
177
- vision_model = gr.Dropdown(
178
- choices=["llama-3.2-90b-vision-preview", "llama-3.2-11b-vision-preview"],
179
- value="llama-3.2-90b-vision-preview",
180
- label="Vision Model"
181
- )
182
- analyze_button = gr.Button("Analyze ECG Image")
183
-
184
- patient_history = gr.Textbox(
185
- lines=10,
186
- label="Patient History (optional)",
187
- placeholder="Enter patient's medical history, age, sex, symptoms, medications, etc."
188
- )
189
-
190
- chat_model = gr.Dropdown(
191
- choices=["llama-3.3-70b-versatile", "llama-3.3-8b-versatile"],
192
- value="llama-3.3-70b-versatile",
193
- label="Chat Model"
194
- )
195
- assess_button = gr.Button("Generate Assessment")
196
-
197
- with gr.Column(scale=1):
198
- # Output components
199
- ecg_analysis_output = gr.Textbox(label="ECG Analysis", lines=15)
200
- assessment_output = gr.Textbox(label="Medical Assessment", lines=15)
201
-
202
- gr.Markdown("## Doctor's Consultation")
203
- gr.Markdown("Ask follow-up questions about the patient's ECG results and medical condition.")
204
-
205
- chatbot = gr.Chatbot(label="Consultation")
206
- message = gr.Textbox(
207
- lines=2,
208
- label="Doctor's Question",
209
- placeholder="Ask a question about this patient's cardiac status..."
210
- )
211
- chat_button = gr.Button("Send")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
 
213
  # Set up event handlers
214
  analyze_button.click(
215
  analyze_ecg_image,
216
- inputs=[ecg_image, vision_model],
217
  outputs=ecg_analysis_output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  )
219
 
220
  assess_button.click(
221
  generate_assessment,
222
- inputs=[ecg_analysis_output, patient_history, chat_model],
223
  outputs=assessment_output
224
  )
225
 
226
  chat_button.click(
227
  doctor_chat,
228
- inputs=[message, chatbot, ecg_analysis_output, patient_history, chat_model],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  outputs=[message, chatbot]
230
  )
231
 
 
5
  from groq import Groq
6
  from PIL import Image
7
  import io
8
+ import datetime
9
+ import re
10
+ from dotenv import load_dotenv
11
+
12
+ # Load environment variables from .env file
13
+ load_dotenv()
14
 
15
  # Initialize Groq client
16
  client = Groq(
 
23
  image.save(buffered, format="JPEG")
24
  return base64.b64encode(buffered.getvalue()).decode('utf-8')
25
 
26
+ # Process patient history file
27
+ def process_patient_history(file):
28
+ if file is None:
29
+ return ""
30
+
31
+ try:
32
+ # Check file extension
33
+ file_ext = os.path.splitext(file.name)[1].lower()
34
+
35
+ if file_ext == '.txt':
36
+ # Read text file
37
+ content = file.read().decode('utf-8')
38
+ return content
39
+
40
+ elif file_ext in ['.csv', '.xlsx', '.xls']:
41
+ # Read spreadsheet file
42
+ if file_ext == '.csv':
43
+ df = pd.read_csv(file.name)
44
+ else:
45
+ df = pd.read_excel(file.name)
46
+
47
+ # Convert dataframe to formatted string
48
+ formatted_data = "PATIENT INFORMATION:\n\n"
49
+ for column in df.columns:
50
+ formatted_data += f"{column}: {df.iloc[0][column]}\n"
51
+
52
+ return formatted_data
53
+
54
+ else:
55
+ return "Unsupported file format. Please upload a .txt, .csv, or .xlsx file."
56
+
57
+ except Exception as e:
58
+ return f"Error processing patient history file: {str(e)}"
59
+
60
  # Extract ECG readings from image using Llama Vision model
61
  def analyze_ecg_image(image, vision_model="llama-3.2-90b-vision-preview"):
62
+ # Fixed model - always use llama-3.2-90b-vision-preview
63
+ vision_model = "llama-3.2-90b-vision-preview"
64
  if image is None:
65
  return "No image provided."
66
 
 
71
  # Encode the image
72
  base64_image = encode_image(image)
73
 
74
+ # Get current timestamp
75
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
76
+
77
  # Create chat completion with vision model
78
+ vision_prompt = f"""Analyze this ECG image carefully. You are a cardiologist analyzing an electrocardiogram (ECG).
79
+
80
+ Extract and report all visible parameters, including but not limited to:
81
+ 1. Heart rate
82
+ 2. PR interval
83
+ 3. QRS duration
84
+ 4. QT/QTc interval
85
+ 5. P wave morphology
86
+ 6. ST segment changes
87
+ 7. T wave morphology
88
+ 8. Rhythm classification
89
+ 9. Specific patterns (if any)
90
+
91
+ Report exact numerical values where visible. Format your response as a structured report with clear sections for different measurements and observations. If certain measurements aren't visible in the image, indicate that they cannot be determined.
92
+
93
+ If you notice any abnormalities or concerning patterns, highlight them clearly but avoid making definitive diagnoses.
94
+
95
+ Important formatting instructions:
96
+ - Use proper HTML/Markdown formatting with <strong> tags for headings or important findings
97
+ - Do not use asterisks (**) for emphasis - use proper HTML formatting instead
98
+ - Include this timestamp at the top of your report: {timestamp}
99
+ """
100
 
101
  try:
102
  vision_completion = client.chat.completions.create(
 
120
  )
121
 
122
  ecg_analysis = vision_completion.choices[0].message.content
123
+
124
+ # Process the response to convert any remaining ** to HTML tags
125
+ ecg_analysis = re.sub(r'\*\*([^*]+)\*\*', r'<strong>\1</strong>', ecg_analysis)
126
+
127
+ # Make sure all headers are properly formatted
128
+ ecg_analysis = re.sub(r'^(#+)\s+(.+)
129
+
130
+ # Generate medical assessment based on ECG readings and patient history
131
+ def generate_assessment(ecg_analysis, patient_history=None, chat_model="llama-3.3-70b-versatile"):
132
+ # Fixed model - always use llama-3.3-70b-versatile
133
+ chat_model = "llama-3.3-70b-versatile"
134
+ if not ecg_analysis or ecg_analysis.startswith("Error"):
135
+ return "Please analyze an ECG image first."
136
+
137
+ # Get current timestamp
138
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
139
+
140
+ # Construct prompt based on available information
141
+ if patient_history and patient_history.strip():
142
+ prompt = f"""You are a highly trained cardiologist assistant. Based on the ECG analysis below and the patient's history, provide a comprehensive assessment of the patient's cardiac status. Indicate clearly if there are any concerning findings that require immediate medical attention.
143
+
144
+ ECG ANALYSIS:
145
+ {ecg_analysis}
146
+
147
+ PATIENT HISTORY:
148
+ {patient_history}
149
+
150
+ TIMESTAMP: {timestamp}
151
+
152
+ Provide your assessment with proper formatting:
153
+ <strong>Summary of Findings</strong>
154
+ (Your summary here)
155
+
156
+ <strong>Key Abnormalities</strong>
157
+ (List any abnormalities here)
158
+
159
+ <strong>Potential Clinical Implications</strong>
160
+ (Describe implications here)
161
+
162
+ <strong>Recommendation</strong>
163
+ (Include urgency level)
164
+
165
+ <strong>Differential Considerations</strong>
166
+ (List differentials here)
167
+
168
+ Important formatting instructions:
169
+ - Use proper HTML formatting with <strong> tags for headings
170
+ - Do not use asterisks (**) for emphasis - use proper HTML formatting instead
171
+ - For any urgent findings, use <strong style="color:red"> to highlight them
172
+ """
173
+ else:
174
+ prompt = f"""You are a highly trained cardiologist assistant. Based on the ECG analysis below, provide a comprehensive assessment of the patient's cardiac status. Indicate clearly if there are any concerning findings that require immediate medical attention.
175
+
176
+ ECG ANALYSIS:
177
+ {ecg_analysis}
178
+
179
+ TIMESTAMP: {timestamp}
180
+
181
+ Provide your assessment with proper formatting:
182
+ <strong>Summary of Findings</strong>
183
+ (Your summary here)
184
+
185
+ <strong>Key Abnormalities</strong>
186
+ (List any abnormalities here)
187
+
188
+ <strong>Potential Clinical Implications</strong>
189
+ (Describe implications here)
190
+
191
+ <strong>Recommendation</strong>
192
+ (Include urgency level)
193
+
194
+ <strong>Differential Considerations</strong>
195
+ (List differentials here)
196
+
197
+ Important formatting instructions:
198
+ - Use proper HTML formatting with <strong> tags for headings
199
+ - Do not use asterisks (**) for emphasis - use proper HTML formatting instead
200
+ - For any urgent findings, use <strong style="color:red"> to highlight them
201
+ """
202
+
203
+ try:
204
+ assessment_completion = client.chat.completions.create(
205
+ messages=[
206
+ {
207
+ "role": "system",
208
+ "content": "You are a medical AI assistant specialized in cardiology. Provide accurate, clinically relevant interpretations of ECG data. If there are concerning findings that might indicate a medical emergency, clearly highlight them. Avoid definitive diagnoses but provide reasoned medical assessments based on the data provided."
209
+ },
210
+ {
211
+ "role": "user",
212
+ "content": prompt
213
+ }
214
+ ],
215
+ model=chat_model,
216
+ temperature=0.2, # Lower temperature for more factual responses
217
+ max_completion_tokens=2048,
218
+ )
219
+
220
+ assessment_text = assessment_completion.choices[0].message.content
221
+
222
+ # Process the response to convert any remaining ** to HTML tags
223
+ assessment_text = re.sub(r'\*\*([^*]+)\*\*', r'<strong>\1</strong>', assessment_text)
224
+
225
+ # Make sure all headers are properly formatted
226
+ assessment_text = re.sub(r'^(#+)\s+(.+)
227
+
228
+ # Doctor's chat interaction with the model about the patient
229
+ def doctor_chat(message, chat_history, ecg_analysis, patient_history, assessment, chat_model="llama-3.3-70b-versatile"):
230
+ # Fixed model - always use llama-3.3-70b-versatile
231
+ chat_model = "llama-3.3-70b-versatile"
232
+ if not ecg_analysis or ecg_analysis.startswith("Error"):
233
+ return "Please analyze an ECG image first before starting a chat.", chat_history
234
+
235
+ if not message.strip():
236
+ return "", chat_history
237
+
238
+ # Get current timestamp
239
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
240
+
241
+ # Prepare chat context
242
+ context = f"""ECG ANALYSIS:
243
+ {ecg_analysis}
244
+
245
+ MEDICAL ASSESSMENT:
246
+ {assessment}
247
+
248
+ TIMESTAMP: {timestamp}
249
+
250
+ """
251
+
252
+ if patient_history and patient_history.strip():
253
+ context += f"""PATIENT HISTORY:
254
+ {patient_history}
255
+
256
+ """
257
+
258
+ # Construct full chat history for context
259
+ messages = [
260
+ {
261
+ "role": "system",
262
+ "content": f"You are a medical AI assistant specialized in cardiology. You are helping a doctor interpret ECG results and patient data. Answer the doctor's questions based on the following information:\n\n{context}"
263
+ }
264
+ ]
265
+
266
+ # Add chat history to the context (limited to last 10 exchanges to avoid token limits)
267
+ for entry in chat_history[-10:]:
268
+ messages.append({"role": "user", "content": entry[0]})
269
+ messages.append({"role": "assistant", "content": entry[1]})
270
+
271
+ # Add the current message
272
+ messages.append({"role": "user", "content": message})
273
+
274
+ try:
275
+ chat_completion = client.chat.completions.create(
276
+ messages=messages,
277
+ model=chat_model,
278
+ temperature=0.3,
279
+ max_completion_tokens=1024,
280
+ )
281
+
282
+ response = chat_completion.choices[0].message.content
283
+ chat_history.append((message, response))
284
+ return "", chat_history
285
+
286
+ except Exception as e:
287
+ error_message = f"Error in chat: {str(e)}"
288
+ chat_history.append((message, error_message))
289
+ return "", chat_history
290
+
291
+ # Create Gradio interface
292
+ with gr.Blocks(title="Cardiac ECG Analysis System", theme=gr.themes.Soft()) as app:
293
+ # Session state to store data
294
+ ecg_analysis_state = gr.State("")
295
+
296
+ gr.Markdown("# πŸ«€ Cardiac ECG Analysis System")
297
+ gr.Markdown("Upload an ECG image and optional patient history to get an automated analysis and assessment.")
298
+
299
+ with gr.Tabs():
300
+ with gr.TabItem("πŸ’» Main Interface"):
301
+ with gr.Row():
302
+ with gr.Column(scale=1):
303
+ # Input components
304
+ with gr.Box():
305
+ gr.Markdown("### πŸ“Š ECG Image")
306
+ ecg_image = gr.Image(type="pil", label="Upload ECG Image")
307
+ # Display fixed model info
308
+ gr.Markdown("**Vision Model:** llama-3.2-90b-vision-preview")
309
+ analyze_button = gr.Button("Analyze ECG Image", variant="primary")
310
+
311
+ with gr.Box():
312
+ gr.Markdown("### πŸ“‹ Patient Information")
313
+ patient_history_text = gr.Textbox(
314
+ lines=8,
315
+ label="Patient History (Manual Entry)",
316
+ placeholder="Enter patient's medical history, age, sex, symptoms, medications, etc."
317
+ )
318
+ patient_history_file = gr.File(
319
+ label="Upload Patient History File (Optional, .txt, .csv, or .xlsx)",
320
+ file_types=[".txt", ".csv", ".xlsx", ".xls"]
321
+ )
322
+ load_history_button = gr.Button("Load Patient History from File")
323
+
324
+ with gr.Box():
325
+ gr.Markdown("### 🧠 Assessment Settings")
326
+ # Display fixed model info
327
+ gr.Markdown("**Chat Model:** llama-3.3-70b-versatile")
328
+ assess_button = gr.Button("Generate Assessment", variant="primary")
329
+
330
+ with gr.Column(scale=1):
331
+ # Output components
332
+ with gr.Box():
333
+ gr.Markdown("### πŸ“ˆ ECG Analysis Results")
334
+ ecg_analysis_output = gr.HTML(label="ECG Analysis", elem_id="ecg-analysis")
335
+
336
+ with gr.Box():
337
+ gr.Markdown("### πŸ“ Medical Assessment")
338
+ assessment_output = gr.HTML(label="Assessment", elem_id="assessment-output")
339
+
340
+ gr.Markdown("## πŸ‘¨β€βš•οΈ Doctor's Consultation")
341
+ gr.Markdown("Ask follow-up questions about the patient's ECG results and medical condition.")
342
+
343
+ with gr.Box():
344
+ chatbot = gr.Chatbot(label="Consultation", height=400)
345
+ with gr.Row():
346
+ message = gr.Textbox(
347
+ lines=2,
348
+ label="Doctor's Question",
349
+ placeholder="Ask a question about this patient's cardiac status...",
350
+ scale=4
351
+ )
352
+ chat_button = gr.Button("Send", scale=1, variant="primary")
353
+
354
+ with gr.TabItem("ℹ️ Instructions"):
355
+ gr.Markdown("""
356
+ ## How to Use This Application
357
+
358
+ ### Step 1: Upload and Analyze ECG
359
+ 1. Upload an ECG image using the file uploader in the Main Interface tab
360
+ 2. Select the vision model (90b recommended for best results)
361
+ 3. Click "Analyze ECG Image" to extract readings from the image
362
+
363
+ ### Step 2: Add Patient Information (Optional)
364
+ - Enter patient history directly in the text box, OR
365
+ - Upload a patient history file (.txt, .csv, or .xlsx) and click "Load Patient History from File"
366
+
367
+ ### Step 3: Generate Assessment
368
+ 1. Select the chat model (70b recommended for detailed analysis)
369
+ 2. Click "Generate Assessment" to get an AI-assisted interpretation
370
+
371
+ ### Step 4: Consultation
372
+ - Use the chatbot interface to ask follow-up questions
373
+ - The AI will consider the ECG analysis, patient history, and previous assessment in its responses
374
+
375
+ ### Important Notes
376
+ - This tool is designed to assist healthcare professionals, not replace clinical judgment
377
+ - Always validate AI-generated medical interpretations with proper medical expertise
378
+ - Patient data privacy should be maintained according to relevant regulations
379
+ """)
380
+
381
+ # Set up event handlers
382
+ analyze_button.click(
383
+ analyze_ecg_image,
384
+ inputs=[ecg_image],
385
+ outputs=ecg_analysis_output
386
+ ).then(
387
+ lambda x: x, # Pass through function to update state
388
+ inputs=ecg_analysis_output,
389
+ outputs=ecg_analysis_state
390
+ )
391
+
392
+ def process_and_update_history(file):
393
+ if file is None:
394
+ return "No file uploaded."
395
+ processed_text = process_patient_history(file)
396
+ return processed_text
397
+
398
+ load_history_button.click(
399
+ process_and_update_history,
400
+ inputs=[patient_history_file],
401
+ outputs=[patient_history_text]
402
+ )
403
+
404
+ assess_button.click(
405
+ generate_assessment,
406
+ inputs=[ecg_analysis_output, patient_history_text],
407
+ outputs=assessment_output
408
+ )
409
+
410
+ chat_button.click(
411
+ doctor_chat,
412
+ inputs=[message, chatbot, ecg_analysis_output, patient_history_text, assessment_output],
413
+ outputs=[message, chatbot]
414
+ )
415
+
416
+ # Also trigger chat on Enter key
417
+ message.submit(
418
+ doctor_chat,
419
+ inputs=[message, chatbot, ecg_analysis_output, patient_history_text, assessment_output],
420
+ outputs=[message, chatbot]
421
+ )
422
+
423
+ # Launch the app
424
+ if __name__ == "__main__":
425
+ app.launch()
426
+ , r'<strong>\2</strong>', ecg_analysis, flags=re.MULTILINE)
427
+
428
  return ecg_analysis
429
 
430
  except Exception as e:
431
+ return f"<strong style='color:red'>Error analyzing ECG image:</strong> {str(e)}"
432
 
433
  # Generate medical assessment based on ECG readings and patient history
434
  def generate_assessment(ecg_analysis, patient_history=None, chat_model="llama-3.3-70b-versatile"):
435
+ # Fixed model - always use llama-3.3-70b-versatile
436
+ chat_model = "llama-3.3-70b-versatile"
437
  if not ecg_analysis or ecg_analysis.startswith("Error"):
438
  return "Please analyze an ECG image first."
439
 
440
+ # Get current timestamp
441
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
442
+
443
  # Construct prompt based on available information
444
+ if patient_history and patient_history.strip():
445
  prompt = f"""You are a highly trained cardiologist assistant. Based on the ECG analysis below and the patient's history, provide a comprehensive assessment of the patient's cardiac status. Indicate clearly if there are any concerning findings that require immediate medical attention.
446
 
447
  ECG ANALYSIS:
 
450
  PATIENT HISTORY:
451
  {patient_history}
452
 
453
+ TIMESTAMP: {timestamp}
454
+
455
+ Provide your assessment with proper formatting:
456
+ <strong>Summary of Findings</strong>
457
+ (Your summary here)
458
+
459
+ <strong>Key Abnormalities</strong>
460
+ (List any abnormalities here)
461
+
462
+ <strong>Potential Clinical Implications</strong>
463
+ (Describe implications here)
464
+
465
+ <strong>Recommendation</strong>
466
+ (Include urgency level)
467
+
468
+ <strong>Differential Considerations</strong>
469
+ (List differentials here)
470
+
471
+ Important formatting instructions:
472
+ - Use proper HTML formatting with <strong> tags for headings
473
+ - Do not use asterisks (**) for emphasis - use proper HTML formatting instead
474
+ - For any urgent findings, use <strong style="color:red"> to highlight them
475
  """
476
  else:
477
  prompt = f"""You are a highly trained cardiologist assistant. Based on the ECG analysis below, provide a comprehensive assessment of the patient's cardiac status. Indicate clearly if there are any concerning findings that require immediate medical attention.
 
479
  ECG ANALYSIS:
480
  {ecg_analysis}
481
 
482
+ TIMESTAMP: {timestamp}
483
+
484
+ Provide your assessment with proper formatting:
485
+ <strong>Summary of Findings</strong>
486
+ (Your summary here)
487
+
488
+ <strong>Key Abnormalities</strong>
489
+ (List any abnormalities here)
490
+
491
+ <strong>Potential Clinical Implications</strong>
492
+ (Describe implications here)
493
+
494
+ <strong>Recommendation</strong>
495
+ (Include urgency level)
496
+
497
+ <strong>Differential Considerations</strong>
498
+ (List differentials here)
499
+
500
+ Important formatting instructions:
501
+ - Use proper HTML formatting with <strong> tags for headings
502
+ - Do not use asterisks (**) for emphasis - use proper HTML formatting instead
503
+ - For any urgent findings, use <strong style="color:red"> to highlight them
504
  """
505
 
506
  try:
 
526
  return f"Error generating assessment: {str(e)}"
527
 
528
  # Doctor's chat interaction with the model about the patient
529
+ def doctor_chat(message, chat_history, ecg_analysis, patient_history, assessment, chat_model="llama-3.3-70b-versatile"):
530
+ # Fixed model - always use llama-3.3-70b-versatile
531
+ chat_model = "llama-3.3-70b-versatile"
532
  if not ecg_analysis or ecg_analysis.startswith("Error"):
533
  return "Please analyze an ECG image first before starting a chat.", chat_history
534
 
535
+ if not message.strip():
536
+ return "", chat_history
537
+
538
+ # Get current timestamp
539
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
540
+
541
  # Prepare chat context
542
  context = f"""ECG ANALYSIS:
543
  {ecg_analysis}
544
 
545
+ MEDICAL ASSESSMENT:
546
+ {assessment}
547
+
548
+ TIMESTAMP: {timestamp}
549
+
550
  """
551
 
552
+ if patient_history and patient_history.strip():
553
  context += f"""PATIENT HISTORY:
554
  {patient_history}
555
 
 
563
  }
564
  ]
565
 
566
+ # Add chat history to the context (limited to last 10 exchanges to avoid token limits)
567
+ for entry in chat_history[-10:]:
568
  messages.append({"role": "user", "content": entry[0]})
569
  messages.append({"role": "assistant", "content": entry[1]})
570
 
 
589
  return "", chat_history
590
 
591
  # Create Gradio interface
592
+ with gr.Blocks(title="Cardiac ECG Analysis System", theme=gr.themes.Soft()) as app:
593
+ # Session state to store data
594
+ ecg_analysis_state = gr.State("")
595
+
596
+ gr.Markdown("# πŸ«€ Cardiac ECG Analysis System")
597
  gr.Markdown("Upload an ECG image and optional patient history to get an automated analysis and assessment.")
598
 
599
+ with gr.Tabs():
600
+ with gr.TabItem("πŸ’» Main Interface"):
601
+ with gr.Row():
602
+ with gr.Column(scale=1):
603
+ # Input components
604
+ with gr.Box():
605
+ gr.Markdown("### πŸ“Š ECG Image")
606
+ ecg_image = gr.Image(type="pil", label="Upload ECG Image")
607
+ # Display fixed model info
608
+ gr.Markdown("**Vision Model:** llama-3.2-90b-vision-preview")
609
+ analyze_button = gr.Button("Analyze ECG Image", variant="primary")
610
+
611
+ with gr.Box():
612
+ gr.Markdown("### πŸ“‹ Patient Information")
613
+ patient_history_text = gr.Textbox(
614
+ lines=8,
615
+ label="Patient History (Manual Entry)",
616
+ placeholder="Enter patient's medical history, age, sex, symptoms, medications, etc."
617
+ )
618
+ patient_history_file = gr.File(
619
+ label="Upload Patient History File (Optional, .txt, .csv, or .xlsx)",
620
+ file_types=[".txt", ".csv", ".xlsx", ".xls"]
621
+ )
622
+ load_history_button = gr.Button("Load Patient History from File")
623
+
624
+ with gr.Box():
625
+ gr.Markdown("### 🧠 Assessment Settings")
626
+ # Display fixed model info
627
+ gr.Markdown("**Chat Model:** llama-3.3-70b-versatile")
628
+ assess_button = gr.Button("Generate Assessment", variant="primary")
629
+
630
+ with gr.Column(scale=1):
631
+ # Output components
632
+ with gr.Box():
633
+ gr.Markdown("### πŸ“ˆ ECG Analysis Results")
634
+ ecg_analysis_output = gr.HTML(label="ECG Analysis", elem_id="ecg-analysis")
635
+
636
+ with gr.Box():
637
+ gr.Markdown("### πŸ“ Medical Assessment")
638
+ assessment_output = gr.HTML(label="Assessment", elem_id="assessment-output")
639
+
640
+ gr.Markdown("## πŸ‘¨β€βš•οΈ Doctor's Consultation")
641
+ gr.Markdown("Ask follow-up questions about the patient's ECG results and medical condition.")
642
+
643
+ with gr.Box():
644
+ chatbot = gr.Chatbot(label="Consultation", height=400)
645
+ with gr.Row():
646
+ message = gr.Textbox(
647
+ lines=2,
648
+ label="Doctor's Question",
649
+ placeholder="Ask a question about this patient's cardiac status...",
650
+ scale=4
651
+ )
652
+ chat_button = gr.Button("Send", scale=1, variant="primary")
653
+
654
+ with gr.TabItem("ℹ️ Instructions"):
655
+ gr.Markdown("""
656
+ ## How to Use This Application
657
+
658
+ ### Step 1: Upload and Analyze ECG
659
+ 1. Upload an ECG image using the file uploader in the Main Interface tab
660
+ 2. Select the vision model (90b recommended for best results)
661
+ 3. Click "Analyze ECG Image" to extract readings from the image
662
+
663
+ ### Step 2: Add Patient Information (Optional)
664
+ - Enter patient history directly in the text box, OR
665
+ - Upload a patient history file (.txt, .csv, or .xlsx) and click "Load Patient History from File"
666
+
667
+ ### Step 3: Generate Assessment
668
+ 1. Select the chat model (70b recommended for detailed analysis)
669
+ 2. Click "Generate Assessment" to get an AI-assisted interpretation
670
+
671
+ ### Step 4: Consultation
672
+ - Use the chatbot interface to ask follow-up questions
673
+ - The AI will consider the ECG analysis, patient history, and previous assessment in its responses
674
+
675
+ ### Important Notes
676
+ - This tool is designed to assist healthcare professionals, not replace clinical judgment
677
+ - Always validate AI-generated medical interpretations with proper medical expertise
678
+ - Patient data privacy should be maintained according to relevant regulations
679
+ """)
680
 
681
  # Set up event handlers
682
  analyze_button.click(
683
  analyze_ecg_image,
684
+ inputs=[ecg_image],
685
  outputs=ecg_analysis_output
686
+ ).then(
687
+ lambda x: x, # Pass through function to update state
688
+ inputs=ecg_analysis_output,
689
+ outputs=ecg_analysis_state
690
+ )
691
+
692
+ def process_and_update_history(file):
693
+ if file is None:
694
+ return "No file uploaded."
695
+ processed_text = process_patient_history(file)
696
+ return processed_text
697
+
698
+ load_history_button.click(
699
+ process_and_update_history,
700
+ inputs=[patient_history_file],
701
+ outputs=[patient_history_text]
702
  )
703
 
704
  assess_button.click(
705
  generate_assessment,
706
+ inputs=[ecg_analysis_output, patient_history_text],
707
  outputs=assessment_output
708
  )
709
 
710
  chat_button.click(
711
  doctor_chat,
712
+ inputs=[message, chatbot, ecg_analysis_output, patient_history_text, assessment_output],
713
+ outputs=[message, chatbot]
714
+ )
715
+
716
+ # Also trigger chat on Enter key
717
+ message.submit(
718
+ doctor_chat,
719
+ inputs=[message, chatbot, ecg_analysis_output, patient_history_text, assessment_output],
720
+ outputs=[message, chatbot]
721
+ )
722
+
723
+ # Launch the app
724
+ if __name__ == "__main__":
725
+ app.launch()
726
+ , r'<strong>\2</strong>', assessment_text, flags=re.MULTILINE)
727
+
728
+ return assessment_text
729
+
730
+ except Exception as e:
731
+ return f"<strong style='color:red'>Error generating assessment:</strong> {str(e)}"
732
+
733
+ # Doctor's chat interaction with the model about the patient
734
+ def doctor_chat(message, chat_history, ecg_analysis, patient_history, assessment, chat_model="llama-3.3-70b-versatile"):
735
+ # Fixed model - always use llama-3.3-70b-versatile
736
+ chat_model = "llama-3.3-70b-versatile"
737
+ if not ecg_analysis or ecg_analysis.startswith("Error"):
738
+ return "Please analyze an ECG image first before starting a chat.", chat_history
739
+
740
+ if not message.strip():
741
+ return "", chat_history
742
+
743
+ # Get current timestamp
744
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
745
+
746
+ # Prepare chat context
747
+ context = f"""ECG ANALYSIS:
748
+ {ecg_analysis}
749
+
750
+ MEDICAL ASSESSMENT:
751
+ {assessment}
752
+
753
+ TIMESTAMP: {timestamp}
754
+
755
+ """
756
+
757
+ if patient_history and patient_history.strip():
758
+ context += f"""PATIENT HISTORY:
759
+ {patient_history}
760
+
761
+ """
762
+
763
+ # Construct full chat history for context
764
+ messages = [
765
+ {
766
+ "role": "system",
767
+ "content": f"You are a medical AI assistant specialized in cardiology. You are helping a doctor interpret ECG results and patient data. Answer the doctor's questions based on the following information:\n\n{context}"
768
+ }
769
+ ]
770
+
771
+ # Add chat history to the context (limited to last 10 exchanges to avoid token limits)
772
+ for entry in chat_history[-10:]:
773
+ messages.append({"role": "user", "content": entry[0]})
774
+ messages.append({"role": "assistant", "content": entry[1]})
775
+
776
+ # Add the current message
777
+ messages.append({"role": "user", "content": message})
778
+
779
+ try:
780
+ chat_completion = client.chat.completions.create(
781
+ messages=messages,
782
+ model=chat_model,
783
+ temperature=0.3,
784
+ max_completion_tokens=1024,
785
+ )
786
+
787
+ response = chat_completion.choices[0].message.content
788
+ chat_history.append((message, response))
789
+ return "", chat_history
790
+
791
+ except Exception as e:
792
+ error_message = f"Error in chat: {str(e)}"
793
+ chat_history.append((message, error_message))
794
+ return "", chat_history
795
+
796
+ # Create Gradio interface
797
+ with gr.Blocks(title="Cardiac ECG Analysis System", theme=gr.themes.Soft()) as app:
798
+ # Session state to store data
799
+ ecg_analysis_state = gr.State("")
800
+
801
+ gr.Markdown("# πŸ«€ Cardiac ECG Analysis System")
802
+ gr.Markdown("Upload an ECG image and optional patient history to get an automated analysis and assessment.")
803
+
804
+ with gr.Tabs():
805
+ with gr.TabItem("πŸ’» Main Interface"):
806
+ with gr.Row():
807
+ with gr.Column(scale=1):
808
+ # Input components
809
+ with gr.Box():
810
+ gr.Markdown("### πŸ“Š ECG Image")
811
+ ecg_image = gr.Image(type="pil", label="Upload ECG Image")
812
+ # Display fixed model info
813
+ gr.Markdown("**Vision Model:** llama-3.2-90b-vision-preview")
814
+ analyze_button = gr.Button("Analyze ECG Image", variant="primary")
815
+
816
+ with gr.Box():
817
+ gr.Markdown("### πŸ“‹ Patient Information")
818
+ patient_history_text = gr.Textbox(
819
+ lines=8,
820
+ label="Patient History (Manual Entry)",
821
+ placeholder="Enter patient's medical history, age, sex, symptoms, medications, etc."
822
+ )
823
+ patient_history_file = gr.File(
824
+ label="Upload Patient History File (Optional, .txt, .csv, or .xlsx)",
825
+ file_types=[".txt", ".csv", ".xlsx", ".xls"]
826
+ )
827
+ load_history_button = gr.Button("Load Patient History from File")
828
+
829
+ with gr.Box():
830
+ gr.Markdown("### 🧠 Assessment Settings")
831
+ # Display fixed model info
832
+ gr.Markdown("**Chat Model:** llama-3.3-70b-versatile")
833
+ assess_button = gr.Button("Generate Assessment", variant="primary")
834
+
835
+ with gr.Column(scale=1):
836
+ # Output components
837
+ with gr.Box():
838
+ gr.Markdown("### πŸ“ˆ ECG Analysis Results")
839
+ ecg_analysis_output = gr.HTML(label="ECG Analysis", elem_id="ecg-analysis")
840
+
841
+ with gr.Box():
842
+ gr.Markdown("### πŸ“ Medical Assessment")
843
+ assessment_output = gr.HTML(label="Assessment", elem_id="assessment-output")
844
+
845
+ gr.Markdown("## πŸ‘¨β€βš•οΈ Doctor's Consultation")
846
+ gr.Markdown("Ask follow-up questions about the patient's ECG results and medical condition.")
847
+
848
+ with gr.Box():
849
+ chatbot = gr.Chatbot(label="Consultation", height=400)
850
+ with gr.Row():
851
+ message = gr.Textbox(
852
+ lines=2,
853
+ label="Doctor's Question",
854
+ placeholder="Ask a question about this patient's cardiac status...",
855
+ scale=4
856
+ )
857
+ chat_button = gr.Button("Send", scale=1, variant="primary")
858
+
859
+ with gr.TabItem("ℹ️ Instructions"):
860
+ gr.Markdown("""
861
+ ## How to Use This Application
862
+
863
+ ### Step 1: Upload and Analyze ECG
864
+ 1. Upload an ECG image using the file uploader in the Main Interface tab
865
+ 2. Select the vision model (90b recommended for best results)
866
+ 3. Click "Analyze ECG Image" to extract readings from the image
867
+
868
+ ### Step 2: Add Patient Information (Optional)
869
+ - Enter patient history directly in the text box, OR
870
+ - Upload a patient history file (.txt, .csv, or .xlsx) and click "Load Patient History from File"
871
+
872
+ ### Step 3: Generate Assessment
873
+ 1. Select the chat model (70b recommended for detailed analysis)
874
+ 2. Click "Generate Assessment" to get an AI-assisted interpretation
875
+
876
+ ### Step 4: Consultation
877
+ - Use the chatbot interface to ask follow-up questions
878
+ - The AI will consider the ECG analysis, patient history, and previous assessment in its responses
879
+
880
+ ### Important Notes
881
+ - This tool is designed to assist healthcare professionals, not replace clinical judgment
882
+ - Always validate AI-generated medical interpretations with proper medical expertise
883
+ - Patient data privacy should be maintained according to relevant regulations
884
+ """)
885
+
886
+ # Set up event handlers
887
+ analyze_button.click(
888
+ analyze_ecg_image,
889
+ inputs=[ecg_image],
890
+ outputs=ecg_analysis_output
891
+ ).then(
892
+ lambda x: x, # Pass through function to update state
893
+ inputs=ecg_analysis_output,
894
+ outputs=ecg_analysis_state
895
+ )
896
+
897
+ def process_and_update_history(file):
898
+ if file is None:
899
+ return "No file uploaded."
900
+ processed_text = process_patient_history(file)
901
+ return processed_text
902
+
903
+ load_history_button.click(
904
+ process_and_update_history,
905
+ inputs=[patient_history_file],
906
+ outputs=[patient_history_text]
907
+ )
908
+
909
+ assess_button.click(
910
+ generate_assessment,
911
+ inputs=[ecg_analysis_output, patient_history_text],
912
+ outputs=assessment_output
913
+ )
914
+
915
+ chat_button.click(
916
+ doctor_chat,
917
+ inputs=[message, chatbot, ecg_analysis_output, patient_history_text, assessment_output],
918
+ outputs=[message, chatbot]
919
+ )
920
+
921
+ # Also trigger chat on Enter key
922
+ message.submit(
923
+ doctor_chat,
924
+ inputs=[message, chatbot, ecg_analysis_output, patient_history_text, assessment_output],
925
+ outputs=[message, chatbot]
926
+ )
927
+
928
+ # Launch the app
929
+ if __name__ == "__main__":
930
+ app.launch()
931
+ , r'<strong>\2</strong>', ecg_analysis, flags=re.MULTILINE)
932
+
933
+ return ecg_analysis
934
+
935
+ except Exception as e:
936
+ return f"<strong style='color:red'>Error analyzing ECG image:</strong> {str(e)}"
937
+
938
+ # Generate medical assessment based on ECG readings and patient history
939
+ def generate_assessment(ecg_analysis, patient_history=None, chat_model="llama-3.3-70b-versatile"):
940
+ # Fixed model - always use llama-3.3-70b-versatile
941
+ chat_model = "llama-3.3-70b-versatile"
942
+ if not ecg_analysis or ecg_analysis.startswith("Error"):
943
+ return "Please analyze an ECG image first."
944
+
945
+ # Get current timestamp
946
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
947
+
948
+ # Construct prompt based on available information
949
+ if patient_history and patient_history.strip():
950
+ prompt = f"""You are a highly trained cardiologist assistant. Based on the ECG analysis below and the patient's history, provide a comprehensive assessment of the patient's cardiac status. Indicate clearly if there are any concerning findings that require immediate medical attention.
951
+
952
+ ECG ANALYSIS:
953
+ {ecg_analysis}
954
+
955
+ PATIENT HISTORY:
956
+ {patient_history}
957
+
958
+ TIMESTAMP: {timestamp}
959
+
960
+ Provide your assessment with proper formatting:
961
+ <strong>Summary of Findings</strong>
962
+ (Your summary here)
963
+
964
+ <strong>Key Abnormalities</strong>
965
+ (List any abnormalities here)
966
+
967
+ <strong>Potential Clinical Implications</strong>
968
+ (Describe implications here)
969
+
970
+ <strong>Recommendation</strong>
971
+ (Include urgency level)
972
+
973
+ <strong>Differential Considerations</strong>
974
+ (List differentials here)
975
+
976
+ Important formatting instructions:
977
+ - Use proper HTML formatting with <strong> tags for headings
978
+ - Do not use asterisks (**) for emphasis - use proper HTML formatting instead
979
+ - For any urgent findings, use <strong style="color:red"> to highlight them
980
+ """
981
+ else:
982
+ prompt = f"""You are a highly trained cardiologist assistant. Based on the ECG analysis below, provide a comprehensive assessment of the patient's cardiac status. Indicate clearly if there are any concerning findings that require immediate medical attention.
983
+
984
+ ECG ANALYSIS:
985
+ {ecg_analysis}
986
+
987
+ TIMESTAMP: {timestamp}
988
+
989
+ Provide your assessment with proper formatting:
990
+ <strong>Summary of Findings</strong>
991
+ (Your summary here)
992
+
993
+ <strong>Key Abnormalities</strong>
994
+ (List any abnormalities here)
995
+
996
+ <strong>Potential Clinical Implications</strong>
997
+ (Describe implications here)
998
+
999
+ <strong>Recommendation</strong>
1000
+ (Include urgency level)
1001
+
1002
+ <strong>Differential Considerations</strong>
1003
+ (List differentials here)
1004
+
1005
+ Important formatting instructions:
1006
+ - Use proper HTML formatting with <strong> tags for headings
1007
+ - Do not use asterisks (**) for emphasis - use proper HTML formatting instead
1008
+ - For any urgent findings, use <strong style="color:red"> to highlight them
1009
+ """
1010
+
1011
+ try:
1012
+ assessment_completion = client.chat.completions.create(
1013
+ messages=[
1014
+ {
1015
+ "role": "system",
1016
+ "content": "You are a medical AI assistant specialized in cardiology. Provide accurate, clinically relevant interpretations of ECG data. If there are concerning findings that might indicate a medical emergency, clearly highlight them. Avoid definitive diagnoses but provide reasoned medical assessments based on the data provided."
1017
+ },
1018
+ {
1019
+ "role": "user",
1020
+ "content": prompt
1021
+ }
1022
+ ],
1023
+ model=chat_model,
1024
+ temperature=0.2, # Lower temperature for more factual responses
1025
+ max_completion_tokens=2048,
1026
+ )
1027
+
1028
+ return assessment_completion.choices[0].message.content
1029
+
1030
+ except Exception as e:
1031
+ return f"Error generating assessment: {str(e)}"
1032
+
1033
+ # Doctor's chat interaction with the model about the patient
1034
+ def doctor_chat(message, chat_history, ecg_analysis, patient_history, assessment, chat_model="llama-3.3-70b-versatile"):
1035
+ # Fixed model - always use llama-3.3-70b-versatile
1036
+ chat_model = "llama-3.3-70b-versatile"
1037
+ if not ecg_analysis or ecg_analysis.startswith("Error"):
1038
+ return "Please analyze an ECG image first before starting a chat.", chat_history
1039
+
1040
+ if not message.strip():
1041
+ return "", chat_history
1042
+
1043
+ # Get current timestamp
1044
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
1045
+
1046
+ # Prepare chat context
1047
+ context = f"""ECG ANALYSIS:
1048
+ {ecg_analysis}
1049
+
1050
+ MEDICAL ASSESSMENT:
1051
+ {assessment}
1052
+
1053
+ TIMESTAMP: {timestamp}
1054
+
1055
+ """
1056
+
1057
+ if patient_history and patient_history.strip():
1058
+ context += f"""PATIENT HISTORY:
1059
+ {patient_history}
1060
+
1061
+ """
1062
+
1063
+ # Construct full chat history for context
1064
+ messages = [
1065
+ {
1066
+ "role": "system",
1067
+ "content": f"You are a medical AI assistant specialized in cardiology. You are helping a doctor interpret ECG results and patient data. Answer the doctor's questions based on the following information:\n\n{context}"
1068
+ }
1069
+ ]
1070
+
1071
+ # Add chat history to the context (limited to last 10 exchanges to avoid token limits)
1072
+ for entry in chat_history[-10:]:
1073
+ messages.append({"role": "user", "content": entry[0]})
1074
+ messages.append({"role": "assistant", "content": entry[1]})
1075
+
1076
+ # Add the current message
1077
+ messages.append({"role": "user", "content": message})
1078
+
1079
+ try:
1080
+ chat_completion = client.chat.completions.create(
1081
+ messages=messages,
1082
+ model=chat_model,
1083
+ temperature=0.3,
1084
+ max_completion_tokens=1024,
1085
+ )
1086
+
1087
+ response = chat_completion.choices[0].message.content
1088
+ chat_history.append((message, response))
1089
+ return "", chat_history
1090
+
1091
+ except Exception as e:
1092
+ error_message = f"Error in chat: {str(e)}"
1093
+ chat_history.append((message, error_message))
1094
+ return "", chat_history
1095
+
1096
+ # Create Gradio interface
1097
+ with gr.Blocks(title="Cardiac ECG Analysis System", theme=gr.themes.Soft()) as app:
1098
+ # Session state to store data
1099
+ ecg_analysis_state = gr.State("")
1100
+
1101
+ gr.Markdown("# πŸ«€ Cardiac ECG Analysis System")
1102
+ gr.Markdown("Upload an ECG image and optional patient history to get an automated analysis and assessment.")
1103
+
1104
+ with gr.Tabs():
1105
+ with gr.TabItem("πŸ’» Main Interface"):
1106
+ with gr.Row():
1107
+ with gr.Column(scale=1):
1108
+ # Input components
1109
+ with gr.Box():
1110
+ gr.Markdown("### πŸ“Š ECG Image")
1111
+ ecg_image = gr.Image(type="pil", label="Upload ECG Image")
1112
+ # Display fixed model info
1113
+ gr.Markdown("**Vision Model:** llama-3.2-90b-vision-preview")
1114
+ analyze_button = gr.Button("Analyze ECG Image", variant="primary")
1115
+
1116
+ with gr.Box():
1117
+ gr.Markdown("### πŸ“‹ Patient Information")
1118
+ patient_history_text = gr.Textbox(
1119
+ lines=8,
1120
+ label="Patient History (Manual Entry)",
1121
+ placeholder="Enter patient's medical history, age, sex, symptoms, medications, etc."
1122
+ )
1123
+ patient_history_file = gr.File(
1124
+ label="Upload Patient History File (Optional, .txt, .csv, or .xlsx)",
1125
+ file_types=[".txt", ".csv", ".xlsx", ".xls"]
1126
+ )
1127
+ load_history_button = gr.Button("Load Patient History from File")
1128
+
1129
+ with gr.Box():
1130
+ gr.Markdown("### 🧠 Assessment Settings")
1131
+ # Display fixed model info
1132
+ gr.Markdown("**Chat Model:** llama-3.3-70b-versatile")
1133
+ assess_button = gr.Button("Generate Assessment", variant="primary")
1134
+
1135
+ with gr.Column(scale=1):
1136
+ # Output components
1137
+ with gr.Box():
1138
+ gr.Markdown("### πŸ“ˆ ECG Analysis Results")
1139
+ ecg_analysis_output = gr.HTML(label="ECG Analysis", elem_id="ecg-analysis")
1140
+
1141
+ with gr.Box():
1142
+ gr.Markdown("### πŸ“ Medical Assessment")
1143
+ assessment_output = gr.HTML(label="Assessment", elem_id="assessment-output")
1144
+
1145
+ gr.Markdown("## πŸ‘¨β€βš•οΈ Doctor's Consultation")
1146
+ gr.Markdown("Ask follow-up questions about the patient's ECG results and medical condition.")
1147
+
1148
+ with gr.Box():
1149
+ chatbot = gr.Chatbot(label="Consultation", height=400)
1150
+ with gr.Row():
1151
+ message = gr.Textbox(
1152
+ lines=2,
1153
+ label="Doctor's Question",
1154
+ placeholder="Ask a question about this patient's cardiac status...",
1155
+ scale=4
1156
+ )
1157
+ chat_button = gr.Button("Send", scale=1, variant="primary")
1158
+
1159
+ with gr.TabItem("ℹ️ Instructions"):
1160
+ gr.Markdown("""
1161
+ ## How to Use This Application
1162
+
1163
+ ### Step 1: Upload and Analyze ECG
1164
+ 1. Upload an ECG image using the file uploader in the Main Interface tab
1165
+ 2. Select the vision model (90b recommended for best results)
1166
+ 3. Click "Analyze ECG Image" to extract readings from the image
1167
+
1168
+ ### Step 2: Add Patient Information (Optional)
1169
+ - Enter patient history directly in the text box, OR
1170
+ - Upload a patient history file (.txt, .csv, or .xlsx) and click "Load Patient History from File"
1171
+
1172
+ ### Step 3: Generate Assessment
1173
+ 1. Select the chat model (70b recommended for detailed analysis)
1174
+ 2. Click "Generate Assessment" to get an AI-assisted interpretation
1175
+
1176
+ ### Step 4: Consultation
1177
+ - Use the chatbot interface to ask follow-up questions
1178
+ - The AI will consider the ECG analysis, patient history, and previous assessment in its responses
1179
+
1180
+ ### Important Notes
1181
+ - This tool is designed to assist healthcare professionals, not replace clinical judgment
1182
+ - Always validate AI-generated medical interpretations with proper medical expertise
1183
+ - Patient data privacy should be maintained according to relevant regulations
1184
+ """)
1185
+
1186
+ # Set up event handlers
1187
+ analyze_button.click(
1188
+ analyze_ecg_image,
1189
+ inputs=[ecg_image],
1190
+ outputs=ecg_analysis_output
1191
+ ).then(
1192
+ lambda x: x, # Pass through function to update state
1193
+ inputs=ecg_analysis_output,
1194
+ outputs=ecg_analysis_state
1195
+ )
1196
+
1197
+ def process_and_update_history(file):
1198
+ if file is None:
1199
+ return "No file uploaded."
1200
+ processed_text = process_patient_history(file)
1201
+ return processed_text
1202
+
1203
+ load_history_button.click(
1204
+ process_and_update_history,
1205
+ inputs=[patient_history_file],
1206
+ outputs=[patient_history_text]
1207
+ )
1208
+
1209
+ assess_button.click(
1210
+ generate_assessment,
1211
+ inputs=[ecg_analysis_output, patient_history_text],
1212
+ outputs=assessment_output
1213
+ )
1214
+
1215
+ chat_button.click(
1216
+ doctor_chat,
1217
+ inputs=[message, chatbot, ecg_analysis_output, patient_history_text, assessment_output],
1218
+ outputs=[message, chatbot]
1219
+ )
1220
+
1221
+ # Also trigger chat on Enter key
1222
+ message.submit(
1223
+ doctor_chat,
1224
+ inputs=[message, chatbot, ecg_analysis_output, patient_history_text, assessment_output],
1225
  outputs=[message, chatbot]
1226
  )
1227