From d442ec275ad1dd6bf98bbbb2625f703ee1543209 Mon Sep 17 00:00:00 2001 From: Katon Minhas Date: Tue, 11 Feb 2025 15:40:55 +0000 Subject: [PATCH] Merged in field-group/code-primary (pull request #389) Field group/code primary * Code breakout function * Merged main into field-group/code-primary * Update prompts * Add admit type * Merged main into field-group/code-primary * Updated code primary prompts * Updated code primary prompt template Approved-by: Alex Galarce --- .../src/investment/one_to_n_funcs.py | 32 ++++++++++++++++++- .../src/prompts/investment_prompts.json | 32 +++++++++---------- .../src/prompts/investment_prompts.py | 21 +++++++++++- 3 files changed, 67 insertions(+), 18 deletions(-) diff --git a/fieldExtraction/src/investment/one_to_n_funcs.py b/fieldExtraction/src/investment/one_to_n_funcs.py index e2aab27..e4883a2 100644 --- a/fieldExtraction/src/investment/one_to_n_funcs.py +++ b/fieldExtraction/src/investment/one_to_n_funcs.py @@ -242,6 +242,35 @@ def get_methodology_breakout(reimbursement_primary_answers: list[dict], filename return methodology_breakout_answers + +def get_code_breakout(answer_dicts: list[dict], filename: str): + code_primary_questions = FieldSet(file_path=config.FIELD_JSON_PATH, field_type="code_primary_breakout").get_prompt_dict() + code_breakout_answers = [] + already_seen = {} + for answer_dict in answer_dicts: + service = answer_dict.get("CONTRACT_SERVICE_CD_OR_DESC") + if service: + code_answer_dict = already_seen.setdefault( + service, + string_utils.universal_json_load( + llm_utils.invoke_claude( + investment_prompts.CODE_PRIMARY_BREAKOUT(service, code_primary_questions), + config.MODEL_ID_CLAUDE35_SONNET, + filename + ) + ) + ) + else: + code_answer_dict = {} + code_breakout_answers.append({**answer_dict, **code_answer_dict}) + + return code_breakout_answers + + + + + + def reimbursement_tin_npi(exhibit_chunk: str, reimbursement_level_fields: FieldSet) -> tuple[dict, FieldSet]: """ Extracts Reimbursement-Level TIN and NPI values from the given exhibit chunk using regex patterns. For multiple TIN/NPIs in Exhibit, assigns the field to be run as part of Reimbursement-Level Primary @@ -296,8 +325,9 @@ def reimbursement_level(exhibit_text, filename, reimbursement_level_fields): # Later, we will only run methodology breakout for those without certain carveout indicators. We will run carveout-specific breakouts for the others methodology_breakout_answers = get_methodology_breakout(carveout_answers, filename) + code_breakout_answers = get_code_breakout(methodology_breakout_answers, filename) - return methodology_breakout_answers + return code_breakout_answers diff --git a/fieldExtraction/src/prompts/investment_prompts.json b/fieldExtraction/src/prompts/investment_prompts.json index a2fd4f7..5b33330 100644 --- a/fieldExtraction/src/prompts/investment_prompts.json +++ b/fieldExtraction/src/prompts/investment_prompts.json @@ -364,8 +364,8 @@ { "field_name": "CPT4_PROC_CD", "relationship": "one_to_n", - "field_type": "TBD", - "prompt": "TBD" + "field_type": "code_primary_breakout", + "prompt": "Identify all Procedure (CPT or HCPCS) Codes in the text: - CPT codes are exactly 5 numeric digits (00100-99999) - HCPCS codes are 1 letter followed by 4 numbers (A0000-V9999)." }, { "field_name": "CPT4_PROC_DESC", @@ -376,8 +376,8 @@ { "field_name": "CPT4_PROC_MOD", "relationship": "one_to_n", - "field_type": "TBD", - "prompt": "TBD" + "field_type": "code_primary_breakout", + "prompt": "Identify all CPT code modifiers in the text: Identify CPT modifiers in the text: - Modifiers are 2-character codes (alphanumeric) appended to the end of a five-character Procedure code." }, { "field_name": "CPT4_PROC_MOD_DESC", @@ -388,8 +388,8 @@ { "field_name": "REVENUE_CD", "relationship": "one_to_n", - "field_type": "TBD", - "prompt": "TBD" + "field_type": "code_primary_breakout", + "prompt": "Identify all Revenue codes (Rev codes) in the text: - 3-4 digit numeric codes - May have 'X' as a wildcard character - Start with digits 0-9." }, { "field_name": "REVENUE_CD_DESC", @@ -398,10 +398,10 @@ "prompt": "TBD" }, { - "field_name": "DIAG_CD_01", + "field_name": "DIAG_CD_LIST", "relationship": "one_to_n", - "field_type": "TBD", - "prompt": "TBD" + "field_type": "code_primary_breakout", + "prompt": "Identify diagnosis codes (ICD-10) in the text: - 3-7 characters in length - First character must be a letter - Characters 2-3 must be numbers - Characters 4-7 (if present) can be numbers or letters." }, { "field_name": "DIAG_CD_DESC", @@ -418,8 +418,8 @@ { "field_name": "FACILITY_GROUPER_CD", "relationship": "one_to_n", - "field_type": "TBD", - "prompt": "TBD" + "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-4) - May include decimal points (like 123.45) - Maximum length of 7 characters." }, { "field_name": "FACILITY_GROUPER_DESC", @@ -430,8 +430,8 @@ { "field_name": "LINE_NDC_NUM", "relationship": "one_to_n", - "field_type": "TBD", - "prompt": "TBD" + "field_type": "code_primary_breakout", + "prompt": "Return a list of any National Drug Code numbers present. Look for an 11-digit number of the format XXXXX-XXXX-XX." }, { "field_name": "LINE_NDC_DESC", @@ -440,10 +440,10 @@ "prompt": "TBD" }, { - "field_name": "CLAIM_ADMIT_TYPE", + "field_name": "CLAIM_ADMIT_TYPE_CD", "relationship": "one_to_n", - "field_type": "TBD", - "prompt": "TBD" + "field_type": "code_primary_breakout", + "prompt": "Identify admit type codes in the text: - Single digit numeric codes (1-9) - Used to indicate hospital admission type" }, { "field_name": "AUTH_ADMIT_TYPE_DESC", diff --git a/fieldExtraction/src/prompts/investment_prompts.py b/fieldExtraction/src/prompts/investment_prompts.py index a31d57c..b140d49 100644 --- a/fieldExtraction/src/prompts/investment_prompts.py +++ b/fieldExtraction/src/prompts/investment_prompts.py @@ -13,6 +13,8 @@ from src.utils.string_utils import extract_text_from_delimiters import json import importlib +PIPE_FORMAT_INSTRUCTIONS = "Feel free to justify your answer, but enclose your final answer in |pipes|. If the question cannot be answered with the given context, return |N/A|." + class Field: def __init__(self, field_dict): # Base Attributes @@ -340,7 +342,24 @@ Use the text in the methodology to populate a JSON dictionary with the following Write your JSON dictionary below: """ -PIPE_FORMAT_INSTRUCTIONS = "Feel free to justify your answer, but enclose your final answer in |pipes|. If the question cannot be answered with the given context, return |N/A|." +def CODE_PRIMARY_BREAKOUT(context, questions): + return f"""Analyze a given medical service, found in a Payer-Provider contract: + +Use the text in the Service to populate a JSON dictionary with the following fields: + +{questions}. + +For each field, return a list of all codes seen for that field. +For the CPT4_PROC_CD and CPT4_PROC_MOD fields, ensure that each item in each list corresponds with each other. + +If any of the codes are not found, do not write a list for that field. Simply populate the field with 'N/A'. + +Here is the Service to analyze: +{context} + +Write the JSON dictionary below: +""" + def ONE_TO_ONE_SINGLE_FIELD_TEMPLATE(context, question): return f"""The following is a contract (or excerpt thereof). Answer the following question: {question}