abjasrees commited on
Commit
bfc2b2f
·
verified ·
1 Parent(s): 3cd5e95

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +18 -401
app.py CHANGED
@@ -1,9 +1,6 @@
1
- # app.py - Interactive Version with HEDIS Feedback
2
- # app.py - Interactive Version with HEDIS Feedback
3
  import os
4
  import tempfile
5
  import hashlib
6
- import json
7
  import gradio as gr
8
 
9
  from embedding_manager import EmbeddingManager
@@ -18,11 +15,6 @@ DEFAULT_MEASURE_YEAR = 2024
18
  _VDB_CACHE = {}
19
  _LAST_HASH = None # optional: for quick reuse/debug
20
 
21
- # Global state for interactive HEDIS analysis
22
- _CURRENT_ENGINE = None
23
- _CURRENT_CRITERIA = None
24
- _ANALYSIS_STATE = "idle" # idle, criteria_review, analysis_complete
25
-
26
  def _pdf_hash(pdf_bytes: bytes) -> str:
27
  return hashlib.sha256(pdf_bytes).hexdigest()
28
 
@@ -58,112 +50,6 @@ def _get_vectordb_from_bytes(pdf_bytes: bytes):
58
  except Exception:
59
  pass
60
 
61
- def clean_json_response(response_text: str) -> dict:
62
- """Clean and parse JSON response from agents."""
63
- if not response_text:
64
- return {"raw_output": "Empty response", "parse_error": "No content received"}
65
-
66
- try:
67
- # Clean the response string
68
- cleaned = str(response_text).strip()
69
-
70
- # Remove markdown code blocks if present
71
- if cleaned.startswith("```json"):
72
- cleaned = cleaned[7:]
73
- elif cleaned.startswith("```"):
74
- cleaned = cleaned[3:]
75
-
76
- if cleaned.endswith("```"):
77
- cleaned = cleaned[:-3]
78
-
79
- # Remove any leading/trailing whitespace and newlines
80
- cleaned = cleaned.strip()
81
-
82
- # Handle quoted JSON strings
83
- if len(cleaned) >= 2 and cleaned.startswith('"') and cleaned.endswith('"'):
84
- try:
85
- # Try to unquote and parse
86
- unquoted = cleaned[1:-1]
87
- # Handle common escape sequences
88
- unquoted = unquoted.replace('\\"', '"').replace('\\n', '\n').replace('\\t', '\t')
89
- test_parse = json.loads(unquoted)
90
- cleaned = unquoted
91
- except:
92
- # If unquoting fails, keep original
93
- pass
94
-
95
- # Try to parse as JSON
96
- return json.loads(cleaned)
97
-
98
- except json.JSONDecodeError as e:
99
- print(f"JSON parsing error: {e}")
100
- print(f"Original response: {repr(response_text)}")
101
- return {"raw_output": str(response_text), "parse_error": f"JSON Parse Error: {str(e)}"}
102
- """Format criteria dictionary into markdown for display."""
103
- if "raw_output" in criteria:
104
- return f"```\n{criteria['raw_output']}\n```"
105
-
106
- markdown = f"# HEDIS Measure Criteria\n\n"
107
-
108
- if "measure" in criteria:
109
- markdown += f"**📋 Measure:** {criteria['measure']}\n\n"
110
-
111
- if "required_tests" in criteria:
112
- markdown += f"## 🔬 Required Tests/Procedures\n"
113
- if isinstance(criteria['required_tests'], list):
114
- for test in criteria['required_tests']:
115
- markdown += f"• {test}\n"
116
- else:
117
- markdown += f"{criteria['required_tests']}\n"
118
- markdown += "\n"
119
-
120
- if "required_medications" in criteria:
121
- markdown += f"## 💊 Required Medications\n"
122
- if isinstance(criteria['required_medications'], list):
123
- for med in criteria['required_medications']:
124
- markdown += f"• {med}\n"
125
- else:
126
- markdown += f"{criteria['required_medications']}\n"
127
- markdown += "\n"
128
-
129
- if "codes" in criteria:
130
- markdown += f"## 🏷️ Relevant Codes\n"
131
- if isinstance(criteria['codes'], dict):
132
- for code_type, codes in criteria['codes'].items():
133
- markdown += f"**{code_type}:** {codes}\n"
134
- else:
135
- markdown += f"{criteria['codes']}\n"
136
- markdown += "\n"
137
-
138
- if "timeframes" in criteria:
139
- markdown += f"## ⏰ Timeframes\n"
140
- if isinstance(criteria['timeframes'], dict):
141
- for requirement, timeframe in criteria['timeframes'].items():
142
- markdown += f"**{requirement}:** {timeframe}\n"
143
- else:
144
- markdown += f"{criteria['timeframes']}\n"
145
- markdown += "\n"
146
-
147
- if "inclusions" in criteria:
148
- markdown += f"## ✅ Inclusion Criteria\n"
149
- if isinstance(criteria['inclusions'], list):
150
- for inclusion in criteria['inclusions']:
151
- markdown += f"• {inclusion}\n"
152
- else:
153
- markdown += f"{criteria['inclusions']}\n"
154
- markdown += "\n"
155
-
156
- if "exclusions" in criteria:
157
- markdown += f"## ❌ Exclusion Criteria\n"
158
- if isinstance(criteria['exclusions'], list):
159
- for exclusion in criteria['exclusions']:
160
- markdown += f"• {exclusion}\n"
161
- else:
162
- markdown += f"{criteria['exclusions']}\n"
163
- markdown += "\n"
164
-
165
- return markdown
166
-
167
  # --- Handlers ---
168
  def generate_patient_summary(pdf_file):
169
  try:
@@ -179,219 +65,23 @@ def generate_patient_summary(pdf_file):
179
  except Exception as e:
180
  return f"❌ Error processing chart: {e}"
181
 
182
- def start_hedis_analysis(pdf_file, measure_name, measurement_year):
183
- """Start interactive HEDIS analysis by getting initial criteria."""
184
- global _CURRENT_ENGINE, _CURRENT_CRITERIA, _ANALYSIS_STATE
185
-
186
  try:
187
  if pdf_file is None:
188
- return (
189
- "⚠️ Please upload a PDF file first.",
190
- gr.update(visible=False),
191
- gr.update(visible=False),
192
- gr.update(visible=False),
193
- gr.update(visible=False)
194
- )
195
  if not measure_name:
196
- return (
197
- "⚠️ Please enter a HEDIS measure code (e.g., COL, BCS, CCS).",
198
- gr.update(visible=False),
199
- gr.update(visible=False),
200
- gr.update(visible=False),
201
- gr.update(visible=False)
202
- )
203
  if not measurement_year:
204
- return (
205
- "⚠️ Please enter a measurement year.",
206
- gr.update(visible=False),
207
- gr.update(visible=False),
208
- gr.update(visible=False),
209
- gr.update(visible=False)
210
- )
211
 
212
  # Reuse embeddings if already built for this file
213
  vectordb = _get_vectordb_from_bytes(pdf_file)
214
 
215
- # Create engine and get initial criteria
216
- _CURRENT_ENGINE = HedisComplianceEngine(vectordb, measure_name, int(measurement_year))
217
-
218
- try:
219
- # Get initial criteria with better error handling
220
- raw_criteria = _CURRENT_ENGINE.get_hedis_criteria_with_feedback()
221
-
222
- # Handle different response types
223
- if isinstance(raw_criteria, dict):
224
- if "raw_output" in raw_criteria:
225
- # Try to clean and re-parse if it's raw output
226
- _CURRENT_CRITERIA = clean_json_response(raw_criteria["raw_output"])
227
- else:
228
- _CURRENT_CRITERIA = raw_criteria
229
- else:
230
- # If it's not a dict, treat as raw output
231
- _CURRENT_CRITERIA = clean_json_response(str(raw_criteria))
232
-
233
- _ANALYSIS_STATE = "criteria_review"
234
-
235
- except Exception as e:
236
- print(f"Error getting criteria: {e}")
237
- return (
238
- f"❌ Error getting HEDIS criteria: {str(e)}\n\nPlease try again or check the measure code.",
239
- gr.update(visible=False),
240
- gr.update(visible=False),
241
- gr.update(visible=False),
242
- gr.update(visible=False)
243
- )
244
-
245
- criteria_display = format_criteria_for_display(_CURRENT_CRITERIA)
246
-
247
- return (
248
- criteria_display,
249
- gr.update(visible=True), # suggestions textbox
250
- gr.update(visible=True), # approve button
251
- gr.update(visible=True), # modify button
252
- gr.update(visible=True) # cancel button
253
- )
254
- except Exception as e:
255
- _ANALYSIS_STATE = "idle"
256
- return (
257
- f"❌ Error getting HEDIS criteria: {e}",
258
- gr.update(visible=False),
259
- gr.update(visible=False),
260
- gr.update(visible=False),
261
- gr.update(visible=False)
262
- )
263
-
264
- def approve_criteria():
265
- """Approve current criteria and proceed with full analysis."""
266
- global _CURRENT_ENGINE, _CURRENT_CRITERIA, _ANALYSIS_STATE
267
-
268
- try:
269
- if _ANALYSIS_STATE != "criteria_review" or _CURRENT_ENGINE is None:
270
- return (
271
- "❌ No criteria to approve. Please start a new analysis.",
272
- gr.update(visible=False),
273
- gr.update(visible=False),
274
- gr.update(visible=False),
275
- gr.update(visible=False)
276
- )
277
-
278
- # Set approved criteria and run full analysis
279
- _CURRENT_ENGINE.hedis_criteria = _CURRENT_CRITERIA
280
- result = _CURRENT_ENGINE.run_full_analysis()
281
- _ANALYSIS_STATE = "analysis_complete"
282
-
283
- return (
284
- f"## ✅ Analysis Complete\n\n{result}",
285
- gr.update(visible=False),
286
- gr.update(visible=False),
287
- gr.update(visible=False),
288
- gr.update(visible=False)
289
- )
290
- except Exception as e:
291
- _ANALYSIS_STATE = "idle"
292
- return (
293
- f"❌ Error running analysis: {e}",
294
- gr.update(visible=False),
295
- gr.update(visible=False),
296
- gr.update(visible=False),
297
- gr.update(visible=False)
298
- )
299
-
300
- def modify_criteria(suggestions):
301
- """Modify criteria based on suggestions."""
302
- global _CURRENT_ENGINE, _CURRENT_CRITERIA, _ANALYSIS_STATE
303
-
304
- try:
305
- if _ANALYSIS_STATE != "criteria_review" or _CURRENT_ENGINE is None:
306
- return (
307
- "❌ No criteria to modify. Please start a new analysis.",
308
- gr.update(visible=False),
309
- gr.update(visible=False),
310
- gr.update(visible=False),
311
- gr.update(visible=False)
312
- )
313
-
314
- if not suggestions.strip():
315
- return (
316
- "⚠️ Please provide suggestions for modification.",
317
- gr.update(visible=True),
318
- gr.update(visible=True),
319
- gr.update(visible=True),
320
- gr.update(visible=True)
321
- )
322
-
323
- try:
324
- # Get modified criteria with better error handling
325
- raw_criteria = _CURRENT_ENGINE.get_hedis_criteria_with_feedback(suggestions.strip())
326
-
327
- # Handle different response types
328
- if isinstance(raw_criteria, dict):
329
- if "raw_output" in raw_criteria:
330
- # Try to clean and re-parse if it's raw output
331
- _CURRENT_CRITERIA = clean_json_response(raw_criteria["raw_output"])
332
- else:
333
- _CURRENT_CRITERIA = raw_criteria
334
- else:
335
- # If it's not a dict, treat as raw output
336
- _CURRENT_CRITERIA = clean_json_response(str(raw_criteria))
337
-
338
- criteria_display = format_criteria_for_display(_CURRENT_CRITERIA)
339
-
340
- except Exception as e:
341
- print(f"Error modifying criteria: {e}")
342
- return (
343
- f"❌ Error modifying criteria: {str(e)}\n\nPlease try again with different suggestions.",
344
- gr.update(visible=True, value=""),
345
- gr.update(visible=True),
346
- gr.update(visible=True),
347
- gr.update(visible=True)
348
- )
349
-
350
- return (
351
- f"## 🔄 Modified Criteria\n\n{criteria_display}",
352
- gr.update(visible=True, value=""), # clear suggestions
353
- gr.update(visible=True),
354
- gr.update(visible=True),
355
- gr.update(visible=True)
356
- )
357
  except Exception as e:
358
- _ANALYSIS_STATE = "idle"
359
- return (
360
- f"❌ Error modifying criteria: {e}",
361
- gr.update(visible=False),
362
- gr.update(visible=False),
363
- gr.update(visible=False),
364
- gr.update(visible=False)
365
- )
366
-
367
- def toggle_analysis_mode(mode):
368
- """Toggle between interactive and standard analysis modes."""
369
- if mode == "Interactive (with criteria review)":
370
- return (
371
- gr.update(visible=True), # start_hedis_btn
372
- gr.update(visible=False), # run_standard_btn
373
- "Select analysis mode and enter measure details, then click 'Start Interactive Analysis' to begin criteria review."
374
- )
375
- else:
376
- return (
377
- gr.update(visible=False), # start_hedis_btn
378
- gr.update(visible=True), # run_standard_btn
379
- "Select analysis mode and enter measure details, then click 'Run Standard Analysis' for direct analysis."
380
- )
381
- """Cancel the current analysis."""
382
- global _CURRENT_ENGINE, _CURRENT_CRITERIA, _ANALYSIS_STATE
383
-
384
- _CURRENT_ENGINE = None
385
- _CURRENT_CRITERIA = None
386
- _ANALYSIS_STATE = "idle"
387
-
388
- return (
389
- "❌ Analysis cancelled. You can start a new analysis.",
390
- gr.update(visible=False),
391
- gr.update(visible=False),
392
- gr.update(visible=False),
393
- gr.update(visible=False)
394
- )
395
 
396
  # --- Gradio Theme ---
397
  simple_theme = gr.themes.Soft(
@@ -409,7 +99,7 @@ simple_theme = gr.themes.Soft(
409
  # --- Interface ---
410
  with gr.Blocks(theme=simple_theme, title="ChartWise AI") as interface:
411
  gr.HTML("<h1 style='text-align:center;color:#1e40af;'>🏥 ChartWise AI</h1>")
412
- gr.HTML("<p style='text-align:center;color:#64748b;'>Patient Chart Analysis &amp; Interactive HEDIS Compliance</p>")
413
 
414
  with gr.Row(equal_height=True):
415
  # Left: Patient Summary
@@ -423,26 +113,9 @@ with gr.Blocks(theme=simple_theme, title="ChartWise AI") as interface:
423
  height=400
424
  )
425
 
426
- def generate_hedis_analysis_original(pdf_file, measure_name, measurement_year):
427
- """Original HEDIS analysis without interactive feedback."""
428
- try:
429
- if pdf_file is None:
430
- return "⚠️ Please upload a PDF file first."
431
- if not measure_name:
432
- return "⚠️ Please enter a HEDIS measure code (e.g., COL, BCS, CCS)."
433
- if not measurement_year:
434
- return "⚠️ Please enter a measurement year."
435
-
436
- # Reuse embeddings if already built for this file
437
- vectordb = _get_vectordb_from_bytes(pdf_file)
438
-
439
- hedis = HedisComplianceEngine(vectordb, measure_name, int(measurement_year))
440
- result = hedis.run() # Use original non-interactive run method
441
- return result
442
- except Exception as e:
443
- return f"❌ Error processing HEDIS analysis: {e}"
444
  with gr.Column():
445
- gr.HTML("<h3 style='color:#1e40af;'>🎯 Interactive HEDIS Analysis</h3>")
446
  hedis_measure = gr.Textbox(
447
  label="HEDIS Measure Code",
448
  placeholder="e.g., COL, BCS, CCS, AAB",
@@ -454,74 +127,18 @@ def generate_hedis_analysis_original(pdf_file, measure_name, measurement_year):
454
  precision=0,
455
  info="e.g., 2024"
456
  )
457
- start_hedis_btn = gr.Button("🎯 Start HEDIS Analysis", variant="secondary")
458
-
459
  hedis_output = gr.Markdown(
460
  label="HEDIS Analysis Results",
461
- value="Enter measure and year, then click 'Start HEDIS Analysis' to begin interactive review.",
462
- height=300
463
  )
464
-
465
- # Interactive feedback components (initially hidden)
466
- with gr.Group():
467
- suggestions_input = gr.Textbox(
468
- label="Suggestions for Criteria Modification",
469
- placeholder="Enter your suggestions to modify the criteria...",
470
- visible=False,
471
- lines=3
472
- )
473
-
474
- with gr.Row():
475
- approve_btn = gr.Button("✅ Approve & Continue", variant="primary", visible=False)
476
- modify_btn = gr.Button("🔄 Modify Criteria", variant="secondary", visible=False)
477
- cancel_btn = gr.Button("❌ Cancel Analysis", visible=False)
478
 
479
  # Wire events
480
- summary_btn.click(
481
- fn=generate_patient_summary,
482
- inputs=[pdf_upload],
483
- outputs=[summary_output]
484
- )
485
-
486
- # Mode toggle
487
- analysis_mode.change(
488
- fn=toggle_analysis_mode,
489
- inputs=[analysis_mode],
490
- outputs=[start_hedis_btn, run_standard_btn, hedis_output]
491
- )
492
-
493
- # Interactive analysis
494
- start_hedis_btn.click(
495
- fn=start_hedis_analysis,
496
- inputs=[pdf_upload, hedis_measure, measurement_year],
497
- outputs=[hedis_output, suggestions_input, approve_btn, modify_btn, cancel_btn]
498
- )
499
-
500
- # Standard analysis
501
- run_standard_btn.click(
502
- fn=generate_hedis_analysis_original,
503
- inputs=[pdf_upload, hedis_measure, measurement_year],
504
- outputs=[hedis_output]
505
- )
506
-
507
- approve_btn.click(
508
- fn=approve_criteria,
509
- inputs=[],
510
- outputs=[hedis_output, suggestions_input, approve_btn, modify_btn, cancel_btn]
511
- )
512
-
513
- modify_btn.click(
514
- fn=modify_criteria,
515
- inputs=[suggestions_input],
516
- outputs=[hedis_output, suggestions_input, approve_btn, modify_btn, cancel_btn]
517
- )
518
-
519
- cancel_btn.click(
520
- fn=cancel_analysis,
521
- inputs=[],
522
- outputs=[hedis_output, suggestions_input, approve_btn, modify_btn, cancel_btn]
523
- )
524
 
525
  # Spaces auto-runs the script; these hints are fine, too:
526
  if __name__ == "__main__":
527
- interface.queue().launch(server_name="0.0.0.0", server_port=int(os.environ.get("GRADIO_SERVER_PORT", "7860")))
 
 
 
 
1
  import os
2
  import tempfile
3
  import hashlib
 
4
  import gradio as gr
5
 
6
  from embedding_manager import EmbeddingManager
 
15
  _VDB_CACHE = {}
16
  _LAST_HASH = None # optional: for quick reuse/debug
17
 
 
 
 
 
 
18
  def _pdf_hash(pdf_bytes: bytes) -> str:
19
  return hashlib.sha256(pdf_bytes).hexdigest()
20
 
 
50
  except Exception:
51
  pass
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  # --- Handlers ---
54
  def generate_patient_summary(pdf_file):
55
  try:
 
65
  except Exception as e:
66
  return f"❌ Error processing chart: {e}"
67
 
68
+ def generate_hedis_analysis(pdf_file, measure_name, measurement_year):
 
 
 
69
  try:
70
  if pdf_file is None:
71
+ return "⚠️ Please upload a PDF file first."
 
 
 
 
 
 
72
  if not measure_name:
73
+ return "⚠️ Please enter a HEDIS measure code (e.g., COL, BCS, CCS)."
 
 
 
 
 
 
74
  if not measurement_year:
75
+ return "⚠️ Please enter a measurement year."
 
 
 
 
 
 
76
 
77
  # Reuse embeddings if already built for this file
78
  vectordb = _get_vectordb_from_bytes(pdf_file)
79
 
80
+ hedis = HedisComplianceEngine(vectordb, measure_name, int(measurement_year))
81
+ result = hedis.run()
82
+ return result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  except Exception as e:
84
+ return f"❌ Error processing HEDIS analysis: {e}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
  # --- Gradio Theme ---
87
  simple_theme = gr.themes.Soft(
 
99
  # --- Interface ---
100
  with gr.Blocks(theme=simple_theme, title="ChartWise AI") as interface:
101
  gr.HTML("<h1 style='text-align:center;color:#1e40af;'>🏥 ChartWise AI</h1>")
102
+ gr.HTML("<p style='text-align:center;color:#64748b;'>Patient Chart Analysis &amp; HEDIS Compliance</p>")
103
 
104
  with gr.Row(equal_height=True):
105
  # Left: Patient Summary
 
113
  height=400
114
  )
115
 
116
+ # Right: HEDIS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  with gr.Column():
118
+ gr.HTML("<h3 style='color:#1e40af;'>🎯 HEDIS Measure Analysis</h3>")
119
  hedis_measure = gr.Textbox(
120
  label="HEDIS Measure Code",
121
  placeholder="e.g., COL, BCS, CCS, AAB",
 
127
  precision=0,
128
  info="e.g., 2024"
129
  )
130
+ hedis_btn = gr.Button("🎯 Run HEDIS Analysis", variant="secondary")
 
131
  hedis_output = gr.Markdown(
132
  label="HEDIS Analysis Results",
133
+ value="Enter measure and year, then click 'Run HEDIS Analysis'.",
134
+ height=400
135
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
  # Wire events
138
+ summary_btn.click(fn=generate_patient_summary, inputs=[pdf_upload], outputs=[summary_output])
139
+ hedis_btn.click(fn=generate_hedis_analysis, inputs=[pdf_upload, hedis_measure, measurement_year], outputs=[hedis_output])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
  # Spaces auto-runs the script; these hints are fine, too:
142
  if __name__ == "__main__":
143
+ interface.queue().launch(server_name="0.0.0.0", server_port=int(os.environ.get("GRADIO_SERVER_PORT", "7860")))
144
+