Ali2206 commited on
Commit
ae5e718
·
verified ·
1 Parent(s): b833614

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +208 -71
app.py CHANGED
@@ -1,4 +1,8 @@
1
-
 
 
 
 
2
  import gradio as gr
3
  from typing import List
4
  from concurrent.futures import ThreadPoolExecutor, as_completed
@@ -7,6 +11,19 @@ import shutil
7
  import re
8
  import psutil
9
  import subprocess
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  # Persistent directory
12
  persistent_dir = "/data/hf_cache"
@@ -56,6 +73,7 @@ def extract_priority_pages(file_path: str, max_pages: int = 20) -> str:
56
  text_chunks.append(f"=== Page {i} ===\n{page_text.strip()}")
57
  return "\n\n".join(text_chunks)
58
  except Exception as e:
 
59
  return f"PDF processing error: {str(e)}"
60
 
61
  def convert_file_to_json(file_path: str, file_type: str) -> str:
@@ -83,29 +101,31 @@ def convert_file_to_json(file_path: str, file_type: str) -> str:
83
  result = json.dumps({"filename": os.path.basename(file_path), "rows": content})
84
  else:
85
  result = json.dumps({"error": f"Unsupported file type: {file_type}"})
 
86
  with open(cache_path, "w", encoding="utf-8") as f:
87
  f.write(result)
88
  return result
89
  except Exception as e:
 
90
  return json.dumps({"error": f"Error processing {os.path.basename(file_path)}: {str(e)}"})
91
 
92
  def log_system_usage(tag=""):
93
  try:
94
  cpu = psutil.cpu_percent(interval=1)
95
  mem = psutil.virtual_memory()
96
- print(f"[{tag}] CPU: {cpu}% | RAM: {mem.used // (1024**2)}MB / {mem.total // (1024**2)}MB")
97
  result = subprocess.run(
98
  ["nvidia-smi", "--query-gpu=memory.used,memory.total,utilization.gpu", "--format=csv,nounits,noheader"],
99
  capture_output=True, text=True
100
  )
101
  if result.returncode == 0:
102
  used, total, util = result.stdout.strip().split(", ")
103
- print(f"[{tag}] GPU: {used}MB / {total}MB | Utilization: {util}%")
104
  except Exception as e:
105
- print(f"[{tag}] GPU/CPU monitor failed: {e}")
106
 
107
  def init_agent():
108
- print("🔁 Initializing model...")
109
  log_system_usage("Before Load")
110
  default_tool_path = os.path.abspath("data/new_tool.json")
111
  target_tool_path = os.path.join(tool_cache_dir, "new_tool.json")
@@ -124,33 +144,59 @@ def init_agent():
124
  )
125
  agent.init_model()
126
  log_system_usage("After Load")
127
- print("✅ Agent Ready")
128
  return agent
129
 
130
- def create_ui(agent):
131
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
132
- gr.Markdown("<h1 style='text-align: center;'>🩺 Clinical Oversight Assistant</h1>")
133
- chatbot = gr.Chatbot(label="Analysis", height=600, type="messages")
134
- file_upload = gr.File(file_types=[".pdf", ".csv", ".xls", ".xlsx"], file_count="multiple")
135
- msg_input = gr.Textbox(placeholder="Ask about potential oversights...", show_label=False)
136
- send_btn = gr.Button("Analyze", variant="primary")
137
- download_output = gr.File(label="Download Full Report")
138
-
139
- def analyze(message: str, history: list, files: list):
140
- history = history + [{"role": "user", "content": message},
141
- {"role": "assistant", "content": "⏳ Analyzing records for potential oversights..."}]
142
- yield history, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
 
144
- extracted = ""
145
- file_hash_value = ""
146
- if files:
147
- with ThreadPoolExecutor(max_workers=4) as executor:
148
- futures = [executor.submit(convert_file_to_json, f.name, f.name.split(".")[-1].lower()) for f in files]
149
- results = [sanitize_utf8(f.result()) for f in as_completed(futures)]
150
- extracted = "\n".join(results)
151
- file_hash_value = file_hash(files[0].name)
 
 
 
 
 
 
 
152
 
153
- prompt = f"""Review these medical records and identify EXACTLY what might have been missed:
154
  1. List potential missed diagnoses
155
  2. Flag any medication conflicts
156
  3. Note incomplete assessments
@@ -161,54 +207,145 @@ Medical Records:
161
 
162
  ### Potential Oversights:
163
  """
164
- response_chunks = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  try:
166
- for chunk in agent.run_gradio_chat(
167
- message=prompt,
168
- history=[],
169
- temperature=0.2,
170
- max_new_tokens=1024,
171
- max_token=4096,
172
- call_agent=False,
173
- conversation=[]
174
- ):
175
- if not chunk:
176
- continue
177
- if isinstance(chunk, str):
178
- response_chunks.append(chunk)
179
- elif isinstance(chunk, list):
180
- response_chunks.extend([c.content for c in chunk if hasattr(c, 'content')])
181
- partial_response = "".join(response_chunks)
182
- cleaned_partial = partial_response.split("[TOOL_CALLS]")[0].strip()
183
- if cleaned_partial:
184
- history[-1] = {"role": "assistant", "content": cleaned_partial}
185
- yield history, None
186
  except Exception as e:
187
- history[-1] = {"role": "assistant", "content": f"❌ Error: {str(e)}"}
188
- yield history, None
189
- return
190
 
191
- full_response = "".join(response_chunks)
192
- final_output = full_response.split("[TOOL_CALLS]")[0].strip()
193
- if not final_output:
194
- final_output = "No clear oversights identified. Recommend comprehensive review."
195
- history[-1] = {"role": "assistant", "content": final_output}
196
 
197
- report_path = os.path.join(report_dir, f"{file_hash_value}_report.txt") if file_hash_value else None
198
- yield history, report_path if report_path and os.path.exists(report_path) else None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
 
200
- send_btn.click(analyze, inputs=[msg_input, gr.State([]), file_upload], outputs=[chatbot, download_output])
201
- msg_input.submit(analyze, inputs=[msg_input, gr.State([]), file_upload], outputs=[chatbot, download_output])
202
  return demo
203
 
204
  if __name__ == "__main__":
205
- print("🚀 Launching app...")
206
- agent = init_agent()
207
- demo = create_ui(agent)
208
- demo.queue(api_open=False).launch(
209
- server_name="0.0.0.0",
210
- server_port=7860,
211
- show_error=True,
212
- allowed_paths=[report_dir],
213
- share=False
214
- )
 
 
 
 
 
1
+ import sys
2
+ import os
3
+ import pandas as pd
4
+ import pdfplumber
5
+ import json
6
  import gradio as gr
7
  from typing import List
8
  from concurrent.futures import ThreadPoolExecutor, as_completed
 
11
  import re
12
  import psutil
13
  import subprocess
14
+ import logging
15
+ from datetime import datetime
16
+
17
+ # Configure logging
18
+ logging.basicConfig(
19
+ level=logging.INFO,
20
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
21
+ handlers=[
22
+ logging.StreamHandler(),
23
+ logging.FileHandler('clinical_oversight.log')
24
+ ]
25
+ )
26
+ logger = logging.getLogger(__name__)
27
 
28
  # Persistent directory
29
  persistent_dir = "/data/hf_cache"
 
73
  text_chunks.append(f"=== Page {i} ===\n{page_text.strip()}")
74
  return "\n\n".join(text_chunks)
75
  except Exception as e:
76
+ logger.error(f"Error extracting pages from PDF: {str(e)}")
77
  return f"PDF processing error: {str(e)}"
78
 
79
  def convert_file_to_json(file_path: str, file_type: str) -> str:
 
101
  result = json.dumps({"filename": os.path.basename(file_path), "rows": content})
102
  else:
103
  result = json.dumps({"error": f"Unsupported file type: {file_type}"})
104
+
105
  with open(cache_path, "w", encoding="utf-8") as f:
106
  f.write(result)
107
  return result
108
  except Exception as e:
109
+ logger.error(f"Error converting {file_type} file to JSON: {str(e)}")
110
  return json.dumps({"error": f"Error processing {os.path.basename(file_path)}: {str(e)}"})
111
 
112
  def log_system_usage(tag=""):
113
  try:
114
  cpu = psutil.cpu_percent(interval=1)
115
  mem = psutil.virtual_memory()
116
+ logger.info(f"[{tag}] CPU: {cpu}% | RAM: {mem.used // (1024**2)}MB / {mem.total // (1024**2)}MB")
117
  result = subprocess.run(
118
  ["nvidia-smi", "--query-gpu=memory.used,memory.total,utilization.gpu", "--format=csv,nounits,noheader"],
119
  capture_output=True, text=True
120
  )
121
  if result.returncode == 0:
122
  used, total, util = result.stdout.strip().split(", ")
123
+ logger.info(f"[{tag}] GPU: {used}MB / {total}MB | Utilization: {util}%")
124
  except Exception as e:
125
+ logger.error(f"[{tag}] GPU/CPU monitor failed: {e}")
126
 
127
  def init_agent():
128
+ logger.info("🔁 Initializing model...")
129
  log_system_usage("Before Load")
130
  default_tool_path = os.path.abspath("data/new_tool.json")
131
  target_tool_path = os.path.join(tool_cache_dir, "new_tool.json")
 
144
  )
145
  agent.init_model()
146
  log_system_usage("After Load")
147
+ logger.info("✅ Agent Ready")
148
  return agent
149
 
150
+ def format_response_for_ui(response: str) -> str:
151
+ """Formats the raw response for clean display in the UI"""
152
+ # Remove any tool call metadata
153
+ cleaned = response.split("[TOOL_CALLS]")[0].strip()
154
+
155
+ # If we have a structured response, format it nicely
156
+ if "Potential missed diagnoses" in cleaned or "Flagged medication conflicts" in cleaned:
157
+ # Add markdown formatting for better readability
158
+ formatted = []
159
+ for line in cleaned.split("\n"):
160
+ if line.startswith("Potential missed diagnoses"):
161
+ formatted.append(f"### 🔍 Potential Missed Diagnoses")
162
+ elif line.startswith("Flagged medication conflicts"):
163
+ formatted.append(f"\n### ⚠️ Flagged Medication Conflicts")
164
+ elif line.startswith("Incomplete assessments"):
165
+ formatted.append(f"\n### 📋 Incomplete Assessments")
166
+ elif line.startswith("Highlighted abnormal results"):
167
+ formatted.append(f"\n### ❗ Abnormal Results Needing Follow-up")
168
+ else:
169
+ formatted.append(line)
170
+ return "\n".join(formatted)
171
+ return cleaned
172
+
173
+ def analyze(message: str, history: list, files: list):
174
+ start_time = datetime.now()
175
+ logger.info(f"Starting analysis for message: {message[:100]}...")
176
+ if files:
177
+ logger.info(f"Processing {len(files)} uploaded files")
178
+
179
+ history = history + [{"role": "user", "content": message},
180
+ {"role": "assistant", "content": "⏳ Analyzing records for potential oversights..."}]
181
+ yield history, None
182
 
183
+ extracted = ""
184
+ file_hash_value = ""
185
+ if files:
186
+ try:
187
+ with ThreadPoolExecutor(max_workers=4) as executor:
188
+ futures = [executor.submit(convert_file_to_json, f.name, f.name.split(".")[-1].lower()) for f in files]
189
+ results = [sanitize_utf8(f.result()) for f in as_completed(futures)]
190
+ extracted = "\n".join(results)
191
+ file_hash_value = file_hash(files[0].name)
192
+ logger.info(f"Processed {len(files)} files, extracted {len(extracted)} characters")
193
+ except Exception as e:
194
+ logger.error(f"Error processing files: {str(e)}")
195
+ history[-1] = {"role": "assistant", "content": f"❌ Error processing files: {str(e)}"}
196
+ yield history, None
197
+ return
198
 
199
+ prompt = f"""Review these medical records and identify EXACTLY what might have been missed:
200
  1. List potential missed diagnoses
201
  2. Flag any medication conflicts
202
  3. Note incomplete assessments
 
207
 
208
  ### Potential Oversights:
209
  """
210
+ logger.info(f"Generated prompt with {len(prompt)} characters")
211
+
212
+ response_chunks = []
213
+ try:
214
+ logger.info("Starting model inference...")
215
+ for chunk in agent.run_gradio_chat(
216
+ message=prompt,
217
+ history=[],
218
+ temperature=0.2,
219
+ max_new_tokens=1024,
220
+ max_token=4096,
221
+ call_agent=False,
222
+ conversation=[]
223
+ ):
224
+ if not chunk:
225
+ continue
226
+ if isinstance(chunk, str):
227
+ response_chunks.append(chunk)
228
+ elif isinstance(chunk, list):
229
+ response_chunks.extend([c.content for c in chunk if hasattr(c, 'content')])
230
+
231
+ partial_response = "".join(response_chunks)
232
+ formatted_partial = format_response_for_ui(partial_response)
233
+
234
+ if formatted_partial:
235
+ history[-1] = {"role": "assistant", "content": formatted_partial}
236
+ yield history, None
237
+
238
+ full_response = "".join(response_chunks)
239
+ logger.info(f"Full model response received: {full_response[:500]}...")
240
+
241
+ final_output = format_response_for_ui(full_response)
242
+ if not final_output or len(final_output) < 20: # Very short response
243
+ final_output = "No clear oversights identified. Recommend comprehensive review."
244
+ logger.info("No significant findings detected in analysis")
245
+
246
+ history[-1] = {"role": "assistant", "content": final_output}
247
+
248
+ # Save report
249
+ report_path = None
250
+ if file_hash_value:
251
+ report_path = os.path.join(report_dir, f"{file_hash_value}_report.txt")
252
  try:
253
+ with open(report_path, "w", encoding="utf-8") as f:
254
+ f.write(final_output)
255
+ logger.info(f"Saved report to {report_path}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  except Exception as e:
257
+ logger.error(f"Error saving report: {str(e)}")
 
 
258
 
259
+ elapsed = (datetime.now() - start_time).total_seconds()
260
+ logger.info(f"Analysis completed in {elapsed:.2f} seconds")
261
+ yield history, report_path if report_path and os.path.exists(report_path) else None
 
 
262
 
263
+ except Exception as e:
264
+ logger.error(f"Error during analysis: {str(e)}", exc_info=True)
265
+ history[-1] = {"role": "assistant", "content": f"❌ Error during analysis: {str(e)}"}
266
+ yield history, None
267
+
268
+ def create_ui(agent):
269
+ with gr.Blocks(theme=gr.themes.Soft(), title="Clinical Oversight Assistant") as demo:
270
+ gr.Markdown("<h1 style='text-align: center;'>🩺 Clinical Oversight Assistant</h1>")
271
+ gr.Markdown("""
272
+ <div style='text-align: center; margin-bottom: 20px;'>
273
+ Upload medical records and receive analysis of potential oversights, including:<br>
274
+ - Missed diagnoses - Medication conflicts - Incomplete assessments - Abnormal results needing follow-up
275
+ </div>
276
+ """)
277
+
278
+ with gr.Row():
279
+ with gr.Column(scale=2):
280
+ file_upload = gr.File(
281
+ label="Upload Medical Records",
282
+ file_types=[".pdf", ".csv", ".xls", ".xlsx"],
283
+ file_count="multiple",
284
+ interactive=True
285
+ )
286
+ msg_input = gr.Textbox(
287
+ placeholder="Ask about potential oversights...",
288
+ show_label=False,
289
+ lines=3,
290
+ max_lines=5
291
+ )
292
+ send_btn = gr.Button("Analyze", variant="primary")
293
+
294
+ with gr.Column(scale=3):
295
+ chatbot = gr.Chatbot(
296
+ label="Analysis Results",
297
+ height=600,
298
+ bubble_full_width=False,
299
+ show_copy_button=True
300
+ )
301
+ download_output = gr.File(
302
+ label="Download Full Report",
303
+ interactive=False
304
+ )
305
+
306
+ # Examples for quick testing
307
+ examples = gr.Examples(
308
+ examples=[
309
+ ["Are there any potential missed diagnoses in these records?"],
310
+ ["What medication conflicts should I be aware of?"],
311
+ ["Are there any incomplete assessments in this case?"]
312
+ ],
313
+ inputs=[msg_input],
314
+ label="Example Questions"
315
+ )
316
+
317
+ send_btn.click(
318
+ analyze,
319
+ inputs=[msg_input, gr.State([]), file_upload],
320
+ outputs=[chatbot, download_output]
321
+ )
322
+ msg_input.submit(
323
+ analyze,
324
+ inputs=[msg_input, gr.State([]), file_upload],
325
+ outputs=[chatbot, download_output]
326
+ )
327
+
328
+ # Add some footer text
329
+ gr.Markdown("""
330
+ <div style='text-align: center; margin-top: 20px; color: #666; font-size: 0.9em;'>
331
+ Note: This tool provides preliminary analysis only. Always verify findings with complete clinical evaluation.
332
+ </div>
333
+ """)
334
 
 
 
335
  return demo
336
 
337
  if __name__ == "__main__":
338
+ logger.info("🚀 Launching Clinical Oversight Assistant...")
339
+ try:
340
+ agent = init_agent()
341
+ demo = create_ui(agent)
342
+ demo.queue(api_open=False).launch(
343
+ server_name="0.0.0.0",
344
+ server_port=7860,
345
+ show_error=True,
346
+ allowed_paths=[report_dir],
347
+ share=False
348
+ )
349
+ except Exception as e:
350
+ logger.error(f"Failed to launch application: {str(e)}", exc_info=True)
351
+ raise