From 94251faa03cc99426f3ea55e73775d0e174a4724 Mon Sep 17 00:00:00 2001 From: Mayank Aamseek Date: Tue, 26 Aug 2025 15:43:53 +0000 Subject: [PATCH] Merged in feature/grouper-fields (pull request #658) Feature/grouper fields * prompt changes * print statements removed * merged with main * merge issue fixed * updated grouper prompts * updated grouper fields in fields order * updated breakout prompt * Merged main into feature/grouper-fields * reverted grouper cd prompt * updated goruper severity prompt * Merge remote-tracking branch 'origin/main' into feature/grouper-fields * updated grouper breakout function * error fixes * Merged main into feature/grouper-fields * Merged main into feature/grouper-fields * updated grouper_pct_rate and grouper_base_rate * removed empty array post processing fix * Merged main into feature/grouper-fields * post-processing for grouper rate modified * updated grouper type prompt * Merged main into feature/grouper-fields * grouper version prompt updated * Merged main into feature/grouper-fields * OPPS as APC * Merged main into feature/grouper-fields Approved-by: Katon Minhas --- .../constants/investment_columns.py | 7 ++ .../constants/mappings/valid_special.json | 3 +- .../constants/special_case_configs.py | 12 ++++ fieldExtraction/src/codes/code_funcs.py | 48 +++++++++++-- .../src/investment/file_processing.py | 4 +- .../src/investment/one_to_n_funcs.py | 67 +++++++++++++++++++ fieldExtraction/src/investment/postprocess.py | 2 + .../src/investment/postprocessing_funcs.py | 48 +++++++++++++ .../src/investment/tin_npi_funcs.py | 2 +- .../src/prompts/investment_prompts.json | 62 +++++++++++++++-- .../src/prompts/prompt_templates.py | 32 +++++++++ 11 files changed, 275 insertions(+), 12 deletions(-) diff --git a/fieldExtraction/constants/investment_columns.py b/fieldExtraction/constants/investment_columns.py index bd777c4..c0e2b78 100644 --- a/fieldExtraction/constants/investment_columns.py +++ b/fieldExtraction/constants/investment_columns.py @@ -105,10 +105,17 @@ COLUMN_ORDER = [ "GROUPER_TYPE", "GROUPER_CD", "GROUPER_CD_DESC", + "GROUPER_PCT_RATE", + "GROUPER_BASE_RATE", "GROUPER_VERSION", + "AARETE_DERIVED_GROUPER_VERSION", "GROUPER_ALTERNATIVE_LEVEL_OF_CARE", + "GROUPER_SEVERITY_IND", "GROUPER_SEVERITY", "GROUPER_RISK_OF_MORTALITY_SUBCLASS", + "GROUPER_TRANSFER_IND", + "GROUPER_READMISSIONS_IND", + "GROUPER_HAC_IND", "OUTLIER_TERMS", "OUTLIER_FIRST_DOLLAR_IND", "RANGE_NBR_DAYS", diff --git a/fieldExtraction/constants/mappings/valid_special.json b/fieldExtraction/constants/mappings/valid_special.json index 89630ee..53ba938 100644 --- a/fieldExtraction/constants/mappings/valid_special.json +++ b/fieldExtraction/constants/mappings/valid_special.json @@ -9,6 +9,7 @@ "PREMIUM": "Describes a premium to the payment. Look for the term 'Premium'.", "ADDITION": "Describes a payment where an additional amount is added to the base reimbursement rate for individual claims. This can be a fixed dollar amount, a percentage of the base reimbursement, or a variable amount derived from the base rate that may change over time based on specified conditions, schedules, or specific services or procedures. Exclude cases where: \n- No specific amount or percentage tied to the base reimbursement of an individual claim is stated, \n- It is unclear whether the payment applies to individual claims, or \n- The payment is dependent on total compensation to Participating IPA Providers.", "TRIGGER_CAP": "Describes the payments that trigger a complete shift to a new payment methodology after a defined quantitative threshold is reached. Unlike supplemental payments, this fundamentally changes how reimbursement is calculated beyond the threshold. Valid thresholds can be based on dollar amounts, number of days, visits, units, or other measurable metrics. However, the following scenarios are NOT considered trigger caps: (1) Payment variations based solely on the timing of an event, such as discharge or admission dates (e.g., different payment if discharge occurs before or after a certain day of the month); (2) Thresholds based on the number of births (e.g., 'If the number of deliveries exceeds 17 per 1,000 members, an additional payment of $2,193 per birth will apply'); (3) Administrative pricing mechanisms such as Charge Description Master (CDM) increases or decreases; (4) Reimbursement limits tied to Acquisition Cost validations (e.g., payments capped at cost plus a percentage); (5) Provisions labeled as 'Stop-Loss' in either the service description or reimbursement text — even if a threshold and rate change are present — as these are considered risk protection mechanisms rather than true shifts in payment methodology; 6) Conditional changes in payment based on service context (e.g., ER vs. admission, Observation vs. surgery) without a numeric threshold do not qualify or Conditional reimbursement rules based on service combinations. Look for language indicating a clear change in the payment methodology when a defined quantitative threshold is exceeded, such as 'when charges exceed X, pay Y', 'above X, reimburse at Y%', or 'over X threshold, payment becomes Y','when charges exceed $50,000, pay 80%' or 'after 3 days, payment changes to $1,200 per diem'.", - "RATE_ESCALATOR": "Describes a payment arrangement where the reimbursement rate automatically increases by a specified percentage annually after an payment rate defined for the initial term. Exception: Does not include predetermined fixed payment amounts scheduled at specific dates (e.g., $860 in 2008, $925 in 2009, $994 in 2010)." + "RATE_ESCALATOR": "Describes a payment arrangement where the reimbursement rate automatically increases by a specified percentage annually after an payment rate defined for the initial term. Exception: Does not include predetermined fixed payment amounts scheduled at specific dates (e.g., $860 in 2008, $925 in 2009, $994 in 2010).", + "GROUPER": "Describes service or payment methodology that uses a classification system to group similar services or diagnoses for reimbursement purposes. Look for terms like 'Grouper', 'Classification System', or specific grouper names (e.g., 'DRG', 'APC')." } } \ No newline at end of file diff --git a/fieldExtraction/constants/special_case_configs.py b/fieldExtraction/constants/special_case_configs.py index f63c3b4..cf68eaa 100644 --- a/fieldExtraction/constants/special_case_configs.py +++ b/fieldExtraction/constants/special_case_configs.py @@ -44,6 +44,9 @@ TRIGGER_CAP_QUESTIONS = FieldSet( RATE_ESCALATOR_QUESTIONS = FieldSet( file_path=config.FIELD_JSON_PATH, field_type="rate_escalator" ).print_prompt_dict() +GROUPER_QUESTIONS = FieldSet( + file_path=config.FIELD_JSON_PATH, field_type="grouper" +).print_prompt_dict() CARVEOUT_CONFIGS = { @@ -244,6 +247,15 @@ SPECIAL_CASE_CONFIGS = { "term_key": "methodology", "use_two_stage": False, # Legacy processing - this case does NOT gets its own breakout separate from the main methodology breakout }, + "GROUPER": { + "questions": GROUPER_QUESTIONS, + "methodology_prompt_template": prompt_templates.REIMB_TERM_BREAKOUT, + "fee_schedule_prompt_template": prompt_templates.FEE_SCHEDULE_BREAKOUT, + "carveout_ind": False, + "default_ind": False, + "term_key": "service and methodology", + "use_two_stage": True, + }, } diff --git a/fieldExtraction/src/codes/code_funcs.py b/fieldExtraction/src/codes/code_funcs.py index 4a05f69..7ef69e2 100644 --- a/fieldExtraction/src/codes/code_funcs.py +++ b/fieldExtraction/src/codes/code_funcs.py @@ -11,7 +11,6 @@ from constants.constants import Constants from constants.delimiters import Delimiter from src.prompts.fieldset import FieldSet - def clean_service(service, constants: Constants) -> str: """ Cleans the service string by removing unnecessary terms and formatting it for further processing. @@ -615,13 +614,52 @@ def code_breakout(merged_results: pd.DataFrame, constants: Constants): # Fill empty AARETE_DERIVED_CLAIM_TYPE_CD with mode answer_dicts = fill_claim_type(merged_results.to_dict(orient="records")) - final_answer_dicts = [] + answer_dicts_with_code = [] for answer_dict in answer_dicts: code_answer_dict = extract_codes_from_service(answer_dict, constants) answer_dict.update(code_answer_dict) + answer_dicts_with_code.append(answer_dict) + + return pd.DataFrame(answer_dicts_with_code) + + +def grouper_breakout(results_with_code: pd.DataFrame): + """ + Processes a DataFrame to extract and fill in grouper-related information in cases where we got a grouper_cd from code_breakout + but didn't get grouper fields from methodology + grouper breakout as in special case grouper was not identified and extracted. + Args: + results_with_code (pd.DataFrame): DataFrame containing the answer dictionaries with code fields + Returns: + pd.DataFrame: DataFrame with updated grouper fields. + """ + + # try grouper fields again if they are empty and grouper_cd is not empty + answer_dicts_with_code = results_with_code.to_dict(orient="records") + final_answer_dicts = [] + GROUPER_QUESTIONS = FieldSet( + file_path=config.FIELD_JSON_PATH, field_type="grouper" + ).print_prompt_dict() + for answer_dict in answer_dicts_with_code: + if ( + string_utils.is_empty(answer_dict.get("GROUPER_TYPE")) + and not string_utils.is_empty(answer_dict.get("GROUPER_CD")) + ): + # run groper breakout prompt + grouper_breakout_prompt = prompt_templates.GROUPER_BREAKOUT( + answer_dict.get("SERVICE_TERM", ""), + answer_dict.get("REIMB_TERM", ""), + GROUPER_QUESTIONS + ) + claude_answer_raw = llm_utils.invoke_claude( + grouper_breakout_prompt, "sonnet_latest", "" + ) + try: + grouper_answer = string_utils.universal_json_load(claude_answer_raw) + except Exception as e: + grouper_answer = {"GROUPER_TYPE": e} + + answer_dict.update(grouper_answer) final_answer_dicts.append(answer_dict) - # Convert back to DataFrame - final_answer_df = pd.DataFrame(final_answer_dicts) + return pd.DataFrame(final_answer_dicts) - return final_answer_df diff --git a/fieldExtraction/src/investment/file_processing.py b/fieldExtraction/src/investment/file_processing.py index 327a380..1c44a02 100644 --- a/fieldExtraction/src/investment/file_processing.py +++ b/fieldExtraction/src/investment/file_processing.py @@ -68,9 +68,11 @@ def process_file(file_object, constants: Constants, run_timestamp): ) ################## CODES ################## - final_results = code_funcs.code_breakout(merged_results, constants) + results_with_code = code_funcs.code_breakout(merged_results, constants) print(f"{datetime_str()} Codes Complete - {filename}") + final_results = code_funcs.grouper_breakout(results_with_code) + ################## POSTPROCESS ################## final_df = postprocess.postprocess(final_results, constants) print(f"{datetime_str()} Postprocessing Complete - {filename}") diff --git a/fieldExtraction/src/investment/one_to_n_funcs.py b/fieldExtraction/src/investment/one_to_n_funcs.py index 0be0191..3f2d320 100644 --- a/fieldExtraction/src/investment/one_to_n_funcs.py +++ b/fieldExtraction/src/investment/one_to_n_funcs.py @@ -1270,6 +1270,10 @@ def process_special_cases( logging.info(f"Running OUTLIERS breakout for {service} - {methodology}") outlier_results = run_outliers_breakout(service, methodology, filename) special_case_results.update(outlier_results) + elif case == "GROUPER": + logging.info(f"Running GROUPER breakout for {service} - {methodology}") + grouper_results = run_grouper_breakout(service, methodology, filename) + special_case_results.update(grouper_results) else: # Other/future special cases can go here pass @@ -1327,6 +1331,69 @@ def run_trigger_cap_breakout(service: str, methodology: str, filename: str) -> d return string_utils.universal_json_load(llm_response) or {} +def run_grouper_breakout(service: str, methodology: str, filename: str) -> dict: + """ + Runs focused GROUPER analysis using specialized prompt. + + PURPOSE: + - Analyzes ONLY grouper conditions and associated codes + - Ignores standard methodology details (handled separately in Stage 1) + - Uses laser-focused prompt optimized for grouper scenarios + + GROUPER DEFINITION: + - A set of codes that determine payment based on grouping logic + - Example: "Grouper codes APR-DRG 001-1, 001-2, etc. for Transplants" + + FIELDS EXTRACTED: + - GROUPER_TYPE: The grouper type used for grouping + - GROUPER_VERSION: The version of the grouper used (e.g., "MS-DRG Version 40") + - AARETE_DERIVED_GROUPER_VERSION: The derived grouper version used for reimbursement (e.g. 40) + - GROUPER_ALTERNATIVE_LEVEL_OF_CARE: The alternative level of care grouper used + - GROUPER_SEVERITY_IND: Indicates if severity is considered in grouper codes + - GROUPER_SEVERITY: The severity level of the grouper codes + - GROUPER_RISK_OF_MORTALITY_SUBCLASS: The risk of mortality subclass for the grouper codes + - GROUPER_PCT_RATE: The reimbursement percentage rate applied to grouper codes + - GROUPER_BASE_RATE: The base reimbursement rate applied to grouper codes + - GROUPER_TRANSFER_IND: Indicates if transfer cases are handled differently + - GROUPER_READMISSIONS_IND: Indicates if readmissions are handled differently + - GROUPER_HAC_IND: Indicates if Hospital-Acquired Conditions (HAC) are considered + + PROMPT DESIGN: + - Focused instructions for grouper identification + - Handles multiple codes and descriptions in one grouping logic e.g multiple APR-DRG codes + + Args: + service: Service description containing grouper codes + methodology: Payment methodology containing grouper information + filename: For logging and LLM context + + Returns: + dict: Grouper fields (GROUPER_TYPE, GROUPER_VERSION, etc.) + + Example: + Input: + service="Angioplasty MSDRG 246-251, 273-274" + methodology="provider shall be paid the lessor of a) $57,117 or b) 100% of covered billed charges." + Output: + {"GROUPER_TYPE": "MSDRG", + "GROUPER_VERSION": "Version 40", + "AARETE_DERIVED_GROUPER_VERSION": "40", + "GROUPER_SEVERITY_IND": "Y", + "GROUPER_SEVERITY": "Major Complications or Comorbidities (MCC)", + "GROUPER_PCT_RATE": "100%", + "GROUPER_BASE_RATE": "$57,117"} + """ + grouper_questions = FieldSet( + file_path=config.FIELD_JSON_PATH, field_type="grouper" + ).print_prompt_dict() + prompt = prompt_templates.GROUPER_BREAKOUT( + service, methodology, grouper_questions + ) + logging.debug(f"Running GROUPER breakout in {filename} with prompt: {prompt}") + llm_response = llm_utils.invoke_claude(prompt, "sonnet_latest", filename) + logging.debug(f"LLM Response for GROUPER breakout in {filename}: {llm_response}") + return string_utils.universal_json_load(llm_response) or {} + def run_outliers_breakout(service: str, methodology: str, filename: str) -> dict: """ Runs focused OUTLIERS analysis using specialized prompt. diff --git a/fieldExtraction/src/investment/postprocess.py b/fieldExtraction/src/investment/postprocess.py index 90668f1..a99b2ce 100644 --- a/fieldExtraction/src/investment/postprocess.py +++ b/fieldExtraction/src/investment/postprocess.py @@ -80,6 +80,8 @@ def postprocess(df, constants: Constants): # Add GREATER_OF_IND df["GREATER_OF_IND"] = "N" + df = postprocessing_funcs.update_grouper_base_rate_and_grouper_pct_rate(df) + # Standardize output column order - this should ALWAYS be the final postprocessing step df = postprocessing_funcs.reorder_columns(df, COLUMN_ORDER) diff --git a/fieldExtraction/src/investment/postprocessing_funcs.py b/fieldExtraction/src/investment/postprocessing_funcs.py index 4d36214..4080d8b 100644 --- a/fieldExtraction/src/investment/postprocessing_funcs.py +++ b/fieldExtraction/src/investment/postprocessing_funcs.py @@ -742,3 +742,51 @@ def deduplicate_provider_columns(df: pd.DataFrame) -> pd.DataFrame: " | ".join(deduped_list) if deduped_list else "" ) return df + +def update_grouper_base_rate_and_grouper_pct_rate(df): + """ + Update GROUPER_PCT_RATE and GROUPER_BASE_RATE columns based on grouper conditions. + + Creates new columns and populates them when: + - AARETE_DERIVED_REIMB_METHOD is 'Grouper', OR + - GROUPER_TYPE is not empty (using string_utils.is_empty) AND not empty array/empty array string, OR + - GROUPER_CD is not empty (using string_utils.is_empty) AND not empty array/empty array string + + Args: + df: DataFrame to update + + Returns: + DataFrame with updated GROUPER_PCT_RATE and GROUPER_BASE_RATE columns + """ + # Required columns for the operation + required_cols = [ + "AARETE_DERIVED_REIMB_METHOD", + "REIMB_PCT_RATE", + "REIMB_CONVERSION_FACTOR", + "GROUPER_TYPE" + # "GROUPER_CD" + ] + + # Check if all required columns exist + if not all(col in df.columns for col in required_cols): + # Initialize empty columns and return if required columns are missing + df["GROUPER_PCT_RATE"] = "" + df["GROUPER_BASE_RATE"] = "" + return df + + # Create grouper condition mask + grouper_mask = ( + (df["AARETE_DERIVED_REIMB_METHOD"] == "Grouper") & ( + (~string_utils.is_empty(df["GROUPER_TYPE"]) & df["GROUPER_TYPE"].ne("[]")) | + (~string_utils.is_empty(df["GROUPER_CD"]) & df["GROUPER_CD"].ne("[]")) + )) + + # Initialize new columns with empty strings + df["GROUPER_PCT_RATE"] = "" + df["GROUPER_BASE_RATE"] = "" + + # Update values where grouper condition is met + df.loc[grouper_mask, "GROUPER_PCT_RATE"] = df.loc[grouper_mask, "REIMB_PCT_RATE"] + df.loc[grouper_mask, "GROUPER_BASE_RATE"] = df.loc[grouper_mask, "REIMB_CONVERSION_FACTOR"] + + return df \ No newline at end of file diff --git a/fieldExtraction/src/investment/tin_npi_funcs.py b/fieldExtraction/src/investment/tin_npi_funcs.py index 4921cf3..d8eda84 100644 --- a/fieldExtraction/src/investment/tin_npi_funcs.py +++ b/fieldExtraction/src/investment/tin_npi_funcs.py @@ -414,7 +414,7 @@ def get_provider_info(text_dict: dict, page_num: str, filename: str) -> list: # Add IS_GROUP flag based on ON_SIGNATURE_PAGE and page_num for provider in providers: - if int(page_num) <= 2 or provider.get("ON_SIGNATURE_PAGE", "N") == "Y": + if float(page_num) <= 2.0 or provider.get("ON_SIGNATURE_PAGE", "N") == "Y": provider["IS_GROUP"] = "Y" else: provider["IS_GROUP"] = "N" diff --git a/fieldExtraction/src/prompts/investment_prompts.json b/fieldExtraction/src/prompts/investment_prompts.json index 6fa3153..8e200fc 100644 --- a/fieldExtraction/src/prompts/investment_prompts.json +++ b/fieldExtraction/src/prompts/investment_prompts.json @@ -506,20 +506,74 @@ { "field_name": "GROUPER_TYPE", "relationship": "one_to_n", - "field_type": "code_primary_breakout", - "prompt": "What is the Diagnosis-Related Group (DRG) type mentioned in the context? It is generally either MS-DRG or APR-DRG." + "field_type": "grouper", + "prompt": "Extract all grouper types from the Service and Methodology. Look for MS-DRG, APR-DRG, DRG, APC, APG, EAPG, OPPS and similar grouper methodologies. Follow these rules: 1) Standardize all MS-DRG variations (MSDRG, MS DRG, MSDRGs, etc.) to 'MS-DRG'. 2) Standardize all APR-DRG variations (APRDRG, APR DRG, APRDRGs, etc.) to 'APR-DRG'. 3) For other DRG variants (Ohio Medicaid DRG, Non-weighted DRGs, TDH DRG, etc.), extract only 'DRG' without prefixes. 4) Remove all descriptive prefixes - for 'Surgical MSDRGs' return 'MS-DRG', for 'Enhanced APR-DRG' return 'APR-DRG'. 5) In case of OPPS, return APC as a grouper type. 6) When both MS-DRG/APR-DRG and generic DRG are present in same context, extract only the specific one (MS-DRG or APR-DRG), not both. 7) Extract only abbreviations, not full forms. If multiple different grouper types are found, separate them with commas. Do not return empty arrays, empty strings, or brackets. If no grouper methodology is found, return only N/A." }, { "field_name": "GROUPER_CD", "relationship": "one_to_n", "field_type": "code_primary_breakout", - "prompt": "Identify grouper codes (DRGs) in the text: - Basic format: 3 digits (like 123) - May include severity/risk indicators (like 123-1) - May include decimal points (like 123.4) - Maximum length of 5 characters." + "prompt": "Identify grouper codes (DRGs) in the text: - Basic format: 3 digits (like 123) - May include severity/risk indicators (like 123-1) - May include decimal points (like 123.4) - Maximum length of 5 characters." }, { "field_name": "GROUPER_CD_DESC", "relationship": "one_to_n", "field_type": "TBD", - "prompt": "TBD" + "prompt": "Identify any descriptions associated with the grouper codes referenced for reimbursements. This may include the full name of the grouper, such as 'MS-DRG 123 - Heart Failure with Major Complications'. If no description is provided, return N/A." + }, + { + "field_name": "GROUPER_VERSION", + "relationship": "one_to_n", + "field_type": "grouper", + "prompt": "Extract the exact grouper version or release terminology from the Service and Methodology as it appears in the contract. Look for version references like 'MS-DRG Version 40', 'APR-DRG Version 34', 'APC 2023', 'DRG v38', or similar version indicators. Include the complete version phrase exactly as written. If multiple versions are mentioned, separate them with commas. If no version is mentioned, return any additional information present related to grouper type, e.g., 'Ohio Medicaid DRG', 'NYS APR-DRG'. Return 'N/A' if no version or additional information related to grouper type is specified." + }, + { + "field_name": "AARETE_DERIVED_GROUPER_VERSION", + "relationship": "one_to_n", + "field_type": "grouper", + "prompt": "Extract only the specific version number or year from any grouper version references in the Service and Methodology. Look for and extract only: 2-digit version numbers (like '40' from 'MS-DRG Version 40'), 4-digit years (like '2024' from 'APC 2024'), or the word 'current' when referring to the most recent version. Return just the number/year/word without any additional text. If multiple versions exist, separate them with commas.. Return N/A if no version is specified." + }, + { + "field_name": "GROUPER_ALTERNATIVE_LEVEL_OF_CARE", + "relationship": "one_to_n", + "field_type": "grouper", + "prompt": "Extract any reimbursement language from the Service and Methodology that references alternative level of care situations. Look for terms like 'alternative level of care', 'ALC', 'alternative care', 'alternative treatment', 'no longer requiring intensive inpatient services', 'patient ready for discharge but requiring alternative placement', or similar language indicating a patient is in an inpatient setting but no longer needs intensive inpatient services. Extract the exact language as it appears. If multiple references exist, separate them with commas. Return N/A if no alternative level of care language is found." + }, + { + "field_name": "GROUPER_SEVERITY_IND", + "relationship": "one_to_n", + "field_type": "grouper", + "prompt": "Does the Service and Methodology contain any grouper severity level information that influences reimbursement? Look for references to severity levels, complications/comorbidities (CC/MCC), acuity levels, risk adjustments, or clinical complexity indicators within grouper systems. Return Y if severity information is present, N if explicitly absent, or N/A if no grouper methodology is mentioned." + }, + { + "field_name": "GROUPER_SEVERITY", + "relationship": "one_to_n", + "field_type": "grouper", + "prompt": "Extract any contract language from the Service and Methodology that references severity levels within grouper systems that influence reimbursement. Look for terms like 'severity level', 'severity of illness', 'acuity level', 'DRG acuity level', 'complications and comorbidities', 'CC/MCC', 'risk adjustment', 'clinical complexity', 'with/without complications', or similar severity-related language. Extract the exact language as it appears. If the severity references are codes or levels, include the grouper type context (APR-DRG, MS-DRG, APC, etc.) with the severity information. If multiple severity references exist, separate them with commas. Return N/A if no severity levels are specified." + }, + { + "field_name": "GROUPER_RISK_OF_MORTALITY_SUBCLASS", + "relationship": "one_to_n", + "field_type": "grouper", + "prompt": "Extract any language from the Service and Methodology that references patient risk of mortality subclasses or mortality-based adjustments within grouper systems that impact reimbursement. Look for terms like 'risk of mortality', 'mortality subclass', 'mortality adjustment', 'ROM', 'mortality risk level', 'death probability', 'mortality indicator', or similar mortality-related classifications used by grouper methodologies. Extract the exact language as it appears. If multiple mortality references exist, separate them with commas. Return N/A if no risk of mortality language is specified." + }, + { + "field_name": "GROUPER_TRANSFER_IND", + "relationship": "one_to_n", + "field_type": "grouper", + "prompt": "Extract any language from the Service and Methodology that references patient transfers between facilities and their impact on grouper-based reimbursement. Look for terms like 'transfer DRG', 'transfer rules', 'transfer adjustment', 'transferred to/from another facility', 'inter-facility transfer', 'transfer payment', 'post-acute transfer', or similar transfer-related language that affects payment calculations. Extract the exact language as it appears. Return N/A if no transfer language is specified." + }, + { + "field_name": "GROUPER_READMISSIONS_IND", + "relationship": "one_to_n", + "field_type": "grouper", + "prompt": "Extract any language from the Service and Methodology that references readmissions and their impact on reimbursement. Look for terms like 'readmission policy', 'readmission adjustment', 'readmission penalty', 'readmission reduction', '30-day readmission', 'unplanned readmission', 'readmission exclusion', or similar readmission-related language that affects payment. Extract the exact language as it appears. Return N/A if no readmission language is specified." + }, + { + "field_name": "GROUPER_HAC_IND", + "relationship": "one_to_n", + "field_type": "grouper", + "prompt": "Extract any language from the Service and Methodology that references Hospital-Acquired Conditions and their impact on reimbursement. Look for terms like 'hospital-acquired condition', 'HAC', 'preventable condition', 'HAC adjustment', 'HAC penalty', 'nosocomial infection', 'acquired condition', 'preventable complication', or similar hospital-acquired condition language that affects payment. Extract the exact language as it appears. Return N/A if no HAC language is specified." }, { "field_name": "NDC_CD", diff --git a/fieldExtraction/src/prompts/prompt_templates.py b/fieldExtraction/src/prompts/prompt_templates.py index 7d8a52d..9ff0692 100644 --- a/fieldExtraction/src/prompts/prompt_templates.py +++ b/fieldExtraction/src/prompts/prompt_templates.py @@ -1100,6 +1100,38 @@ Return your answers in valid JSON format using the exact field names from the qu **Important:** Base your answers ONLY on the outlier terms provided. Do not make assumptions or add information not present in the contract language. """ +def GROUPER_BREAKOUT( + service, methodology, questions +): + """ + Use this prompt for grouper fields that are derived directly from the combination of Service and Methodology + + """ + return f"""Analyze the given Service and Methodology to identify grouper-based reimbursement information, and answer the following question(s): + +Populate a JSON dictionary with the following fields: +{questions} + +Instructions for grouper extraction: +- Extract exact terminology as it appears in the contract text +- For derived fields: extract only the specific identifier/number without additional text +- For multiple values: separate with commas, not arrays +- Return N/A when no relevant information is found + +Here is the Service and Methodology to analyze: +Service: {service.replace('"', "'")} +Methodology: {methodology.replace('"', "'")} + +**Response Format:** +Return your answers in valid JSON format using the exact field names from the questions as keys. For any information not found in the outlier terms, use 'N/A' as the value. + +**Example Response Format:** +{{ + "FIELD_NAME_1": "extracted_value_1", + "FIELD_NAME_2": "extracted_value_2", + "FIELD_NAME_3": "N/A" +}} +""" def LOB_RELATIONSHIP(exhibit_text, dynamic_primary_values): return f"""You will be presented with an exhibit from a Payer-Provider contract.