Update hedis_engine.py
Browse files- hedis_engine.py +31 -43
hedis_engine.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
# hedis_engine.py
|
|
|
|
| 2 |
import os
|
| 3 |
import json
|
| 4 |
from crewai import Agent, Task, Crew, Process
|
|
@@ -7,11 +8,13 @@ from langchain_community.vectorstores import Chroma
|
|
| 7 |
from langchain_openai import ChatOpenAI
|
| 8 |
|
| 9 |
|
| 10 |
-
class
|
| 11 |
"""
|
| 12 |
-
|
| 13 |
(2) extracts patient evidence from embeddings, and
|
| 14 |
(3) produces a markdown analysis report.
|
|
|
|
|
|
|
| 15 |
"""
|
| 16 |
def __init__(self, vectordb: Chroma, measure_code: str, measurement_year: int):
|
| 17 |
self.vectordb = vectordb
|
|
@@ -144,10 +147,10 @@ CRITICAL FORMATTING REQUIREMENTS:
|
|
| 144 |
# Remove any leading/trailing whitespace and newlines
|
| 145 |
result_str = result_str.strip()
|
| 146 |
|
| 147 |
-
#
|
| 148 |
if len(result_str) >= 2 and result_str.startswith('"') and result_str.endswith('"'):
|
| 149 |
-
# Check if it's actually a quoted JSON string by trying to parse the unquoted version
|
| 150 |
try:
|
|
|
|
| 151 |
unquoted = result_str[1:-1]
|
| 152 |
# Handle escaped quotes in the JSON
|
| 153 |
unquoted = unquoted.replace('\\"', '"').replace('\\n', '\n')
|
|
@@ -160,6 +163,18 @@ CRITICAL FORMATTING REQUIREMENTS:
|
|
| 160 |
# Try to parse as JSON
|
| 161 |
criteria_json = json.loads(result_str)
|
| 162 |
return criteria_json
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
def get_hedis_criteria_simple(self, suggestions: str = "") -> dict:
|
| 164 |
"""
|
| 165 |
Simplified method to get HEDIS criteria with minimal string formatting.
|
|
@@ -214,12 +229,6 @@ CRITICAL FORMATTING REQUIREMENTS:
|
|
| 214 |
except Exception as e:
|
| 215 |
print(f"Simple method error: {e}")
|
| 216 |
return {"raw_output": str(result) if 'result' in locals() else "No result", "parse_error": str(e)}
|
| 217 |
-
print(f"JSON parsing error: {e}")
|
| 218 |
-
print(f"Raw result type: {type(result)}")
|
| 219 |
-
print(f"Raw result: {repr(str(result))}")
|
| 220 |
-
print(f"Cleaned result: {repr(result_str if 'result_str' in locals() else 'N/A')}")
|
| 221 |
-
# If not valid JSON, return as string
|
| 222 |
-
return {"raw_output": str(result), "parse_error": f"JSON parsing failed: {str(e)}"}
|
| 223 |
|
| 224 |
def display_criteria(self, criteria: dict) -> None:
|
| 225 |
"""Display the HEDIS criteria in a user-friendly format."""
|
|
@@ -396,19 +405,21 @@ CRITICAL FORMATTING REQUIREMENTS:
|
|
| 396 |
print(f"\n✅ Analysis complete!")
|
| 397 |
return str(result)
|
| 398 |
|
| 399 |
-
def
|
| 400 |
"""
|
| 401 |
-
|
|
|
|
| 402 |
"""
|
|
|
|
| 403 |
hedis_task = Task(
|
| 404 |
description=(
|
| 405 |
-
|
| 406 |
"- Required tests or procedures\n"
|
| 407 |
"- Diagnosis/procedure codes\n"
|
| 408 |
"- Required medications\n"
|
| 409 |
"- Inclusion and exclusion criteria\n"
|
| 410 |
-
|
| 411 |
-
|
| 412 |
"Return JSON with fields: 'measure','required_tests','required_medications',"
|
| 413 |
"'codes','timeframes','inclusions','exclusions'."
|
| 414 |
),
|
|
@@ -419,8 +430,8 @@ CRITICAL FORMATTING REQUIREMENTS:
|
|
| 419 |
|
| 420 |
patient_task = Task(
|
| 421 |
description=(
|
| 422 |
-
|
| 423 |
-
|
| 424 |
),
|
| 425 |
expected_output="Structured list of patient evidence with dates.",
|
| 426 |
agent=self.patient_analyzer,
|
|
@@ -429,8 +440,8 @@ CRITICAL FORMATTING REQUIREMENTS:
|
|
| 429 |
|
| 430 |
chart_analysis_task = Task(
|
| 431 |
description=(
|
| 432 |
-
|
| 433 |
-
|
| 434 |
"Sections:\n"
|
| 435 |
"### Diagnoses Found\n"
|
| 436 |
"### Procedures Found\n"
|
|
@@ -459,27 +470,4 @@ CRITICAL FORMATTING REQUIREMENTS:
|
|
| 459 |
"measurement_year": self.measurement_year,
|
| 460 |
}
|
| 461 |
result = crew.kickoff(inputs=inputs)
|
| 462 |
-
return str(result)
|
| 463 |
-
|
| 464 |
-
def run(self) -> str:
|
| 465 |
-
"""
|
| 466 |
-
Entry point - runs non-interactive analysis by default for app compatibility.
|
| 467 |
-
Use run_interactive() for interactive mode.
|
| 468 |
-
"""
|
| 469 |
-
return self.run_non_interactive()
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
# Usage example:
|
| 473 |
-
"""
|
| 474 |
-
# Initialize the engine
|
| 475 |
-
vectordb = your_chroma_db # Your Chroma vector database
|
| 476 |
-
engine = InteractiveHedisComplianceEngine(
|
| 477 |
-
vectordb=vectordb,
|
| 478 |
-
measure_code="CDC-A1C",
|
| 479 |
-
measurement_year=2024
|
| 480 |
-
)
|
| 481 |
-
|
| 482 |
-
# Run interactive analysis
|
| 483 |
-
result = engine.run()
|
| 484 |
-
print(result)
|
| 485 |
-
"""
|
|
|
|
| 1 |
# hedis_engine.py
|
| 2 |
+
# hedis_engine.py - Updated with Interactive Features
|
| 3 |
import os
|
| 4 |
import json
|
| 5 |
from crewai import Agent, Task, Crew, Process
|
|
|
|
| 8 |
from langchain_openai import ChatOpenAI
|
| 9 |
|
| 10 |
|
| 11 |
+
class HedisComplianceEngine:
|
| 12 |
"""
|
| 13 |
+
Crew that: (1) states official HEDIS measure criteria,
|
| 14 |
(2) extracts patient evidence from embeddings, and
|
| 15 |
(3) produces a markdown analysis report.
|
| 16 |
+
|
| 17 |
+
Now includes interactive feedback capabilities.
|
| 18 |
"""
|
| 19 |
def __init__(self, vectordb: Chroma, measure_code: str, measurement_year: int):
|
| 20 |
self.vectordb = vectordb
|
|
|
|
| 147 |
# Remove any leading/trailing whitespace and newlines
|
| 148 |
result_str = result_str.strip()
|
| 149 |
|
| 150 |
+
# Handle quoted JSON strings
|
| 151 |
if len(result_str) >= 2 and result_str.startswith('"') and result_str.endswith('"'):
|
|
|
|
| 152 |
try:
|
| 153 |
+
# Try to unquote and parse
|
| 154 |
unquoted = result_str[1:-1]
|
| 155 |
# Handle escaped quotes in the JSON
|
| 156 |
unquoted = unquoted.replace('\\"', '"').replace('\\n', '\n')
|
|
|
|
| 163 |
# Try to parse as JSON
|
| 164 |
criteria_json = json.loads(result_str)
|
| 165 |
return criteria_json
|
| 166 |
+
|
| 167 |
+
except json.JSONDecodeError as e:
|
| 168 |
+
print(f"Primary method failed, trying simplified approach...")
|
| 169 |
+
try:
|
| 170 |
+
return self.get_hedis_criteria_simple(suggestions)
|
| 171 |
+
except Exception as fallback_error:
|
| 172 |
+
print(f"Fallback method also failed: {fallback_error}")
|
| 173 |
+
return {"raw_output": str(result), "parse_error": f"Primary: {str(e)}, Fallback: {str(fallback_error)}"}
|
| 174 |
+
except Exception as e:
|
| 175 |
+
print(f"Unexpected error: {e}")
|
| 176 |
+
return {"raw_output": str(result) if 'result' in locals() else "No result", "parse_error": str(e)}
|
| 177 |
+
|
| 178 |
def get_hedis_criteria_simple(self, suggestions: str = "") -> dict:
|
| 179 |
"""
|
| 180 |
Simplified method to get HEDIS criteria with minimal string formatting.
|
|
|
|
| 229 |
except Exception as e:
|
| 230 |
print(f"Simple method error: {e}")
|
| 231 |
return {"raw_output": str(result) if 'result' in locals() else "No result", "parse_error": str(e)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 232 |
|
| 233 |
def display_criteria(self, criteria: dict) -> None:
|
| 234 |
"""Display the HEDIS criteria in a user-friendly format."""
|
|
|
|
| 405 |
print(f"\n✅ Analysis complete!")
|
| 406 |
return str(result)
|
| 407 |
|
| 408 |
+
def run(self) -> str:
|
| 409 |
"""
|
| 410 |
+
Entry point - runs original non-interactive analysis for app compatibility.
|
| 411 |
+
Use run_interactive() for interactive mode.
|
| 412 |
"""
|
| 413 |
+
# Original tasks
|
| 414 |
hedis_task = Task(
|
| 415 |
description=(
|
| 416 |
+
"Provide the official criteria for HEDIS measure {measure_code}, including:\n"
|
| 417 |
"- Required tests or procedures\n"
|
| 418 |
"- Diagnosis/procedure codes\n"
|
| 419 |
"- Required medications\n"
|
| 420 |
"- Inclusion and exclusion criteria\n"
|
| 421 |
+
"- Timeframes for each requirement (e.g., within 10 years of {measurement_year})\n\n"
|
| 422 |
+
"You MUST compute and include absolute date ranges using {measurement_year}.\n"
|
| 423 |
"Return JSON with fields: 'measure','required_tests','required_medications',"
|
| 424 |
"'codes','timeframes','inclusions','exclusions'."
|
| 425 |
),
|
|
|
|
| 430 |
|
| 431 |
patient_task = Task(
|
| 432 |
description=(
|
| 433 |
+
"Using the patient_chart_search tool, extract only the tests, diagnoses, procedures, "
|
| 434 |
+
"medications, and dates relevant to {measure_code}. Only include measure related info from the patient charts"
|
| 435 |
),
|
| 436 |
expected_output="Structured list of patient evidence with dates.",
|
| 437 |
agent=self.patient_analyzer,
|
|
|
|
| 440 |
|
| 441 |
chart_analysis_task = Task(
|
| 442 |
description=(
|
| 443 |
+
"Create a comprehensive markdown report analyzing the patient chart for HEDIS {measure_code} "
|
| 444 |
+
"for measurement year {measurement_year}.\n\n"
|
| 445 |
"Sections:\n"
|
| 446 |
"### Diagnoses Found\n"
|
| 447 |
"### Procedures Found\n"
|
|
|
|
| 470 |
"measurement_year": self.measurement_year,
|
| 471 |
}
|
| 472 |
result = crew.kickoff(inputs=inputs)
|
| 473 |
+
return str(result)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|