Update app.py
Browse files
app.py
CHANGED
@@ -139,66 +139,75 @@ def log_system_usage(tag=""):
|
|
139 |
|
140 |
def clean_response(text: str) -> str:
|
141 |
text = sanitize_utf8(text)
|
|
|
142 |
text = re.sub(r"\[.*?\]|\bNone\b|To analyze the patient record excerpt.*?medications\.|Since the previous attempts.*?\.|I need to.*?medications\.|Retrieving tools.*?\.", "", text, flags=re.DOTALL)
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
sections = {}
|
147 |
-
current_section = None
|
148 |
lines = text.splitlines()
|
|
|
149 |
for line in lines:
|
150 |
line = line.strip()
|
151 |
if not line:
|
152 |
continue
|
153 |
-
|
154 |
-
|
155 |
-
current_section = section_match.group(1)
|
156 |
-
if current_section not in sections:
|
157 |
-
sections[current_section] = []
|
158 |
continue
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
|
|
|
|
169 |
return text if text else ""
|
170 |
|
171 |
def summarize_findings(combined_response: str) -> str:
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
for line in lines:
|
179 |
-
line = line.strip()
|
180 |
-
if not line:
|
181 |
-
continue
|
182 |
-
section_match = re.match(r"###\s*(Missed Diagnoses|Medication Conflicts|Incomplete Assessments|Urgent Follow-up)", line)
|
183 |
-
if section_match:
|
184 |
-
current_section = section_match.group(1)
|
185 |
-
if current_section not in sections:
|
186 |
-
sections[current_section] = []
|
187 |
continue
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
202 |
|
203 |
def init_agent():
|
204 |
logger.info("Initializing model...")
|
@@ -227,7 +236,7 @@ def create_ui(agent):
|
|
227 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
228 |
gr.Markdown("<h1 style='text-align: center;'>🩺 Clinical Oversight Assistant</h1>")
|
229 |
chatbot = gr.Chatbot(label="Detailed Analysis", height=600, type="messages")
|
230 |
-
final_summary = gr.Markdown(label="Summary of
|
231 |
file_upload = gr.File(file_types=[".pdf", ".csv", ".xls", ".xlsx"], file_count="multiple")
|
232 |
msg_input = gr.Textbox(placeholder="Ask about potential oversights...", show_label=False)
|
233 |
send_btn = gr.Button("Analyze", variant="primary")
|
@@ -235,7 +244,7 @@ def create_ui(agent):
|
|
235 |
progress_bar = gr.Progress()
|
236 |
|
237 |
prompt_template = """
|
238 |
-
Analyze the patient record excerpt for
|
239 |
Patient Record Excerpt (Chunk {0} of {1}):
|
240 |
{chunk}
|
241 |
"""
|
@@ -284,13 +293,13 @@ Patient Record Excerpt (Chunk {0} of {1}):
|
|
284 |
for m in chunk_output:
|
285 |
if hasattr(m, 'content') and m.content:
|
286 |
cleaned = clean_response(m.content)
|
287 |
-
if cleaned
|
288 |
-
chunk_response += cleaned + "
|
289 |
elif isinstance(chunk_output, str) and chunk_output.strip():
|
290 |
-
cleaned = clean_response(
|
291 |
-
if cleaned
|
292 |
-
chunk_response += cleaned + "
|
293 |
-
batch_responses.append(chunk_response)
|
294 |
torch.cuda.empty_cache()
|
295 |
gc.collect()
|
296 |
|
@@ -298,14 +307,14 @@ Patient Record Excerpt (Chunk {0} of {1}):
|
|
298 |
if chunk_response:
|
299 |
combined_response += f"--- Analysis for Chunk {chunk_idx} ---\n{chunk_response}\n"
|
300 |
else:
|
301 |
-
combined_response += f"--- Analysis for Chunk {chunk_idx} ---\nNo
|
302 |
history[-1] = {"role": "assistant", "content": combined_response.strip()}
|
303 |
yield history, None, ""
|
304 |
|
305 |
-
if combined_response.strip() and not all("No
|
306 |
history[-1]["content"] = combined_response.strip()
|
307 |
else:
|
308 |
-
history.append({"role": "assistant", "content": "No
|
309 |
|
310 |
summary = summarize_findings(combined_response)
|
311 |
report_path = os.path.join(report_dir, f"{file_hash_value}_report.txt") if file_hash_value else None
|
@@ -317,7 +326,7 @@ Patient Record Excerpt (Chunk {0} of {1}):
|
|
317 |
except Exception as e:
|
318 |
logger.error("Analysis error: %s", e)
|
319 |
history.append({"role": "assistant", "content": f"❌ Error occurred: {str(e)}"})
|
320 |
-
yield history, None, f"
|
321 |
|
322 |
send_btn.click(analyze, inputs=[msg_input, gr.State([]), file_upload], outputs=[chatbot, download_output, final_summary])
|
323 |
msg_input.submit(analyze, inputs=[msg_input, gr.State([]), file_upload], outputs=[chatbot, download_output, final_summary])
|
|
|
139 |
|
140 |
def clean_response(text: str) -> str:
|
141 |
text = sanitize_utf8(text)
|
142 |
+
# Remove unwanted patterns and tool call artifacts
|
143 |
text = re.sub(r"\[.*?\]|\bNone\b|To analyze the patient record excerpt.*?medications\.|Since the previous attempts.*?\.|I need to.*?medications\.|Retrieving tools.*?\.", "", text, flags=re.DOTALL)
|
144 |
+
# Extract only missed diagnoses, ignoring other categories
|
145 |
+
diagnoses = []
|
|
|
|
|
|
|
146 |
lines = text.splitlines()
|
147 |
+
in_diagnoses_section = False
|
148 |
for line in lines:
|
149 |
line = line.strip()
|
150 |
if not line:
|
151 |
continue
|
152 |
+
if re.match(r"###\s*Missed Diagnoses", line):
|
153 |
+
in_diagnoses_section = True
|
|
|
|
|
|
|
154 |
continue
|
155 |
+
if re.match(r"###\s*(Medication Conflicts|Incomplete Assessments|Urgent Follow-up)", line):
|
156 |
+
in_diagnoses_section = False
|
157 |
+
continue
|
158 |
+
if in_diagnoses_section and re.match(r"-\s*.+", line):
|
159 |
+
diagnosis = re.sub(r"^\-\s*", "", line).strip()
|
160 |
+
if diagnosis and not re.match(r"No issues identified", diagnosis, re.IGNORECASE):
|
161 |
+
diagnoses.append(diagnosis)
|
162 |
+
# Join diagnoses into a plain text paragraph
|
163 |
+
text = " ".join(diagnoses)
|
164 |
+
# Clean up extra whitespace and punctuation
|
165 |
+
text = re.sub(r"\s+", " ", text).strip()
|
166 |
+
text = re.sub(r"[^\w\s\.\,\(\)\-]", "", text)
|
167 |
return text if text else ""
|
168 |
|
169 |
def summarize_findings(combined_response: str) -> str:
|
170 |
+
# Split response by chunk analyses
|
171 |
+
chunks = combined_response.split("--- Analysis for Chunk")
|
172 |
+
diagnoses = []
|
173 |
+
for chunk in chunks:
|
174 |
+
chunk = chunk.strip()
|
175 |
+
if not chunk or "No oversights identified" in chunk:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
176 |
continue
|
177 |
+
# Extract missed diagnoses from chunk
|
178 |
+
lines = chunk.splitlines()
|
179 |
+
in_diagnoses_section = False
|
180 |
+
for line in lines:
|
181 |
+
line = line.strip()
|
182 |
+
if not line:
|
183 |
+
continue
|
184 |
+
if re.match(r"###\s*Missed Diagnoses", line):
|
185 |
+
in_diagnoses_section = True
|
186 |
+
continue
|
187 |
+
if re.match(r"###\s*(Medication Conflicts|Incomplete Assessments|Urgent Follow-up)", line):
|
188 |
+
in_diagnoses_section = False
|
189 |
+
continue
|
190 |
+
if in_diagnoses_section and re.match(r"-\s*.+", line):
|
191 |
+
diagnosis = re.sub(r"^\-\s*", "", line).strip()
|
192 |
+
if diagnosis and not re.match(r"No issues identified", diagnosis, re.IGNORECASE):
|
193 |
+
diagnoses.append(diagnosis)
|
194 |
+
|
195 |
+
# Remove duplicates while preserving order
|
196 |
+
seen = set()
|
197 |
+
unique_diagnoses = [d for d in diagnoses if not (d in seen or seen.add(d))]
|
198 |
+
|
199 |
+
if not unique_diagnoses:
|
200 |
+
return "No missed diagnoses were identified in the provided records."
|
201 |
+
|
202 |
+
# Combine into a single paragraph
|
203 |
+
summary = "Missed diagnoses include " + ", ".join(unique_diagnoses[:-1])
|
204 |
+
if len(unique_diagnoses) > 1:
|
205 |
+
summary += f", and {unique_diagnoses[-1]}"
|
206 |
+
elif len(unique_diagnoses) == 1:
|
207 |
+
summary = "Missed diagnoses include " + unique_diagnoses[0]
|
208 |
+
summary += ", all of which require urgent clinical review to prevent potential adverse outcomes."
|
209 |
+
|
210 |
+
return summary.strip()
|
211 |
|
212 |
def init_agent():
|
213 |
logger.info("Initializing model...")
|
|
|
236 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
237 |
gr.Markdown("<h1 style='text-align: center;'>🩺 Clinical Oversight Assistant</h1>")
|
238 |
chatbot = gr.Chatbot(label="Detailed Analysis", height=600, type="messages")
|
239 |
+
final_summary = gr.Markdown(label="Summary of Missed Diagnoses")
|
240 |
file_upload = gr.File(file_types=[".pdf", ".csv", ".xls", ".xlsx"], file_count="multiple")
|
241 |
msg_input = gr.Textbox(placeholder="Ask about potential oversights...", show_label=False)
|
242 |
send_btn = gr.Button("Analyze", variant="primary")
|
|
|
244 |
progress_bar = gr.Progress()
|
245 |
|
246 |
prompt_template = """
|
247 |
+
Analyze the patient record excerpt for missed diagnoses only. Provide a concise, evidence-based summary as a single paragraph without headings or bullet points. Include specific clinical findings (e.g., 'elevated blood pressure (160/95) on page 10'), their potential implications (e.g., 'may indicate untreated hypertension'), and a recommendation for urgent review. Do not include other oversight categories like medication conflicts. If no missed diagnoses are found, state 'No missed diagnoses identified' in a single sentence.
|
248 |
Patient Record Excerpt (Chunk {0} of {1}):
|
249 |
{chunk}
|
250 |
"""
|
|
|
293 |
for m in chunk_output:
|
294 |
if hasattr(m, 'content') and m.content:
|
295 |
cleaned = clean_response(m.content)
|
296 |
+
if cleaned:
|
297 |
+
chunk_response += cleaned + " "
|
298 |
elif isinstance(chunk_output, str) and chunk_output.strip():
|
299 |
+
cleaned = clean_response(chunk_output)
|
300 |
+
if cleaned:
|
301 |
+
chunk_response += cleaned + " "
|
302 |
+
batch_responses.append(chunk_response.strip())
|
303 |
torch.cuda.empty_cache()
|
304 |
gc.collect()
|
305 |
|
|
|
307 |
if chunk_response:
|
308 |
combined_response += f"--- Analysis for Chunk {chunk_idx} ---\n{chunk_response}\n"
|
309 |
else:
|
310 |
+
combined_response += f"--- Analysis for Chunk {chunk_idx} ---\nNo missed diagnoses identified.\n"
|
311 |
history[-1] = {"role": "assistant", "content": combined_response.strip()}
|
312 |
yield history, None, ""
|
313 |
|
314 |
+
if combined_response.strip() and not all("No missed diagnoses identified" in chunk for chunk in combined_response.split("--- Analysis for Chunk")):
|
315 |
history[-1]["content"] = combined_response.strip()
|
316 |
else:
|
317 |
+
history.append({"role": "assistant", "content": "No missed diagnoses identified in the provided records."})
|
318 |
|
319 |
summary = summarize_findings(combined_response)
|
320 |
report_path = os.path.join(report_dir, f"{file_hash_value}_report.txt") if file_hash_value else None
|
|
|
326 |
except Exception as e:
|
327 |
logger.error("Analysis error: %s", e)
|
328 |
history.append({"role": "assistant", "content": f"❌ Error occurred: {str(e)}"})
|
329 |
+
yield history, None, f"Error occurred during analysis: {str(e)}"
|
330 |
|
331 |
send_btn.click(analyze, inputs=[msg_input, gr.State([]), file_upload], outputs=[chatbot, download_output, final_summary])
|
332 |
msg_input.submit(analyze, inputs=[msg_input, gr.State([]), file_upload], outputs=[chatbot, download_output, final_summary])
|