Merged in bugfix/dynamic-reimbursement-primary-nv-issues (pull request #687)

Bugfix/dynamic reimbursement primary nv issues

* Add reimb_level_prompt to Field class for dynamic reimbursement handling

* Add reimb_level_prompt for dynamic extraction of LOBs, Networks, Products, and Programs

* Merged main into bugfix/dynamic-reimbursement-primary-nv-issues


Approved-by: Katon Minhas
This commit is contained in:
Alex Galarce
2025-09-02 14:06:45 +00:00
parent 611da13d2e
commit 785ffe85ba
3 changed files with 43 additions and 16 deletions
+34 -16
View File
@@ -34,7 +34,9 @@ def add_full_context_field(one_to_one_fields, field_to_add, answer_dicts):
# Special Handling for Program and Product:
# If there are multiple non-empty AARETE_DERIVED_LOB values in the contract, don't ask for Program or Product full context
if field_to_add.field_name in ["PROGRAM", "PRODUCT"]:
lob_values = [answer_dict.get("AARETE_DERIVED_LOB", "") for answer_dict in answer_dicts]
lob_values = [
answer_dict.get("AARETE_DERIVED_LOB", "") for answer_dict in answer_dicts
]
non_empty_lobs = [lob for lob in lob_values if not string_utils.is_empty(lob)]
if len(non_empty_lobs) > 1:
return one_to_one_fields
@@ -117,15 +119,15 @@ def get_dynamic_one_to_one_fields(answer_dicts):
file_path=config.FIELD_JSON_PATH,
field_name=field.base_field,
)
one_to_one_fields = add_full_context_field(one_to_one_fields, field_to_add, answer_dicts)
one_to_one_fields = add_full_context_field(
one_to_one_fields, field_to_add, answer_dicts
)
return one_to_one_fields
def prompt_dynamic_primary(exhibit_text, constants, field, filename, TEMPLATE):
# Prompt Exhibit Header
prompt = TEMPLATE(
exhibit_text, field.field_name, field.get_prompt(constants)
)
prompt = TEMPLATE(exhibit_text, field.field_name, field.get_prompt(constants))
logging.debug(f"Dynamic primary prompt for {filename}; {field}: {prompt}")
claude_answer_raw = llm_utils.invoke_claude(prompt, "sonnet_latest", filename)
logging.debug(f"Claude answer for {filename}; {field}: {claude_answer_raw}")
@@ -170,7 +172,11 @@ def get_dynamic_primary(
exhibit_header_answer = "N/A"
else:
exhibit_header_answer = prompt_dynamic_primary(
exhibit_header, constants, field, filename, prompt_templates.DYNAMIC_PRIMARY_HEADER
exhibit_header,
constants,
field,
filename,
prompt_templates.DYNAMIC_PRIMARY_HEADER,
)
# If there is exactly ONE Non-N/A answer in the header
@@ -185,20 +191,29 @@ def get_dynamic_primary(
and "|" in exhibit_header_answer
):
field.update_valid_values(exhibit_header_answer)
field.prompt = (
field.prompt
+ ". Ensure ALL values that apply to the specific reimbursement term are included."
)
if field.reimb_level_prompt:
field.prompt = field.reimb_level_prompt
else:
field.prompt = (
field.prompt
+ ". Ensure ALL values that apply to the specific reimbursement term are included."
)
reimbursement_level_fields.add_field(field)
dynamic_fields.remove_field(field.field_name)
# If there are ZERO Non-N/A answers in the header
else:
exhibit_text_answer = prompt_dynamic_primary(
exhibit_chunk, constants, field, filename, prompt_templates.DYNAMIC_PRIMARY_TEXT
exhibit_chunk,
constants,
field,
filename,
prompt_templates.DYNAMIC_PRIMARY_TEXT,
)
# If there is at least ONE Non-N/A answers in the Exhibit
if not string_utils.is_empty(exhibit_text_answer):
field.update_valid_values(exhibit_text_answer + " | N/A")
if field.reimb_level_prompt:
field.prompt = field.reimb_level_prompt
reimbursement_level_fields.add_field(field)
dynamic_fields.remove_field(field.field_name)
# If the field is not found, add to Exhibit-Level answers (the answer will be N/A or similar)
@@ -216,7 +231,7 @@ def get_dynamic_answers(
constants: Constants,
):
"""
Processes dynamic fields from exhibit header and chunk text.
Processes dynamic (code and provider info) fields from exhibit header and chunk text.
Args:
exhibit_chunk (str): The main text chunk of the exhibit
@@ -247,10 +262,13 @@ def get_dynamic_answers(
if not string_utils.is_empty(answer):
field = dynamic_fields.get_field(field_name)
field.update_valid_values(answer)
field.prompt = (
field.prompt
+ ". Ensure ALL values that apply to the specific reimbursement term are included."
)
if field.reimb_level_prompt:
field.prompt = field.reimb_level_prompt
else:
field.prompt = (
field.prompt
+ ". Ensure ALL values that apply to the specific reimbursement term are included."
)
reimbursement_level_fields.add_field(field)
dynamic_fields.remove_field(field_name)
+5
View File
@@ -43,6 +43,11 @@ class Field:
self.relationship = field_dict["relationship"]
self.field_type = field_dict["field_type"]
self.prompt = field_dict["prompt"] if "prompt" in field_dict else None
self.reimb_level_prompt = (
field_dict["reimb_level_prompt"]
if "reimb_level_prompt" in field_dict
else None
)
self.valid_values = (
field_dict["valid_values"] if "valid_values" in field_dict else None
)
@@ -112,6 +112,7 @@
"relationship" : "one_to_n",
"field_type" : "dynamic_primary",
"prompt" : "Extract ONLY LOBs that are **explicitly mentioned** in the text. Do NOT extract based on Programs, Products, context, implications, or any other form of inference. Choose LOBs ONLY from the following values: {valid_values}. ",
"reimb_level_prompt": "What LOB applies specifically to this service-reimbursement pair? Extract only LOBs that are directly associated with this particular service or methodology. Choose ONLY from: {valid_values}. If no LOB is specifically associated with this service-reimbursement pair, return 'N/A'.",
"valid_values" : "VALID_LOBS"
},
{
@@ -127,6 +128,7 @@
"relationship" : "one_to_n",
"field_type" : "dynamic_primary",
"prompt" : "List any Networks explicitly stated in the text. Choose ONLY from the following: {valid_values}.",
"reimb_level_prompt": "What Network applies specifically to this service-reimbursement pair? Look for network references that are directly associated with this particular service or methodology. Choose ONLY from: {valid_values}. If no network is specifically associated with this service-reimbursement pair, return 'N/A'.",
"valid_values" : "VALID_NETWORKS"
},
{
@@ -142,6 +144,7 @@
"relationship" : "one_to_n",
"field_type" : "dynamic_primary",
"prompt" : "List any Products explicitly stated in the text. Look for the brand name of a health plan or product. Do not include the name of the entire healthplan (such as 'Molina', 'Centene' etc) - rather, look for the Product that these healthplans offer. Valid products include, but are not limited to, the following: {valid_values}. If any of the values in this list are mentioned, return them exactly as written in the list. Use reasonable judgment for obvious synonyms and equivalent terms.",
"reimb_level_prompt" : "What Product applies specifically to this service-reimbursement pair? Look for product references that are directly associated with this particular service or methodology. Choose ONLY from: {valid_values}. If no product is specifically associated with this service-reimbursement pair, return 'N/A'.",
"valid_values" : "VALID_PRODUCTS"
},
{
@@ -156,6 +159,7 @@
"relationship" : "one_to_n",
"field_type" : "dynamic_primary",
"prompt" : "List any Programs explicitly stated in the text. Choose ONLY from the following: {valid_values}. Note that 'Medicare' does NOT mean 'Medicare Advantage'. If only 'Medicare' is seen, do not write 'Medicare Advantage'. Choose the answer from the list that most closely matches what is found in the text. Use reasonable judgment for obvious synonyms and equivalent terms (e.g., 'Medicare-Medicaid Program' matches 'Medicare-Medicaid Plan').",
"reimb_level_prompt" : "What Program applies specifically to this service-reimbursement pair? Look for program references that are directly associated with this particular service or reimbursement methodology. Choose ONLY from: {valid_values}. Note: 'Medicare' alone does NOT mean 'Medicare Advantage'. Use reasonable judgment for synonyms (e.g., 'Medicare-Medicaid Program' matches 'Medicare-Medicaid Plan'). If no program is specifically associated with this service-reimbursement pair, return 'N/A'.",
"valid_values" : "VALID_PROGRAMS"
},
{