Merged in feature/remove-global-lesser-process (pull request #851)

Feature/remove global lesser process

* Deprecate GLOBAL_LESSER_OF

* remove deprecated

* Remove deprecated

* Merged main into feature/remove-global-lesser-process

* Black formatter

* remove deprecated

* Remove check redundant lesser

* Remove prompt_smart_chunked

* Black format

* Update logging


Approved-by: Sha Brown
This commit is contained in:
Katon Minhas
2026-01-27 18:37:38 +00:00
parent 929ff4abf8
commit 55533d5a24
10 changed files with 9 additions and 1095 deletions
@@ -72,28 +72,6 @@ def prompt_exhibit_level_breakout(
return exhibit_level_answers
def prompt_exhibit_lesser(
exhibit_text: str,
exhibit_level_answers: dict,
filename: str,
):
logging.debug(f"Running Exhibit-Level Lesser for {filename}:")
prompt = prompt_templates.EXHIBIT_LEVEL_LESSER_OF(exhibit_text)
llm_answer_raw = llm_utils.invoke_claude(
prompt, "legacy_sonnet", filename, max_tokens=8192
)
logging.debug(f"LLM raw output for {filename}: {llm_answer_raw}")
try:
exhibit_lesser_of_answer = string_utils.extract_text_from_delimiters(
llm_answer_raw, Delimiter.PIPE
)
except:
exhibit_lesser_of_answer = "N/A"
exhibit_level_answers["EXHIBIT_LESSER_OF_STATEMENT"] = exhibit_lesser_of_answer
return exhibit_level_answers
def prompt_dynamic_primary(
exhibit_text: str, field: Field, constants: Constants, filename: str, TEMPLATE
):
@@ -399,43 +377,6 @@ def prompt_full_context(
return full_context_answers_dict
def is_exhibit_lesser_of_redundant(
reimb_term: str, exhibit_lesser_of: str, filename: str
) -> bool:
"""Use LLM to determine if exhibit-level lesser-of statement is redundant with the reimbursement term.
This is a second step of filtering, after checking for exact substring matches.
Small variations in punctuation may make it past the first filter, so in those cases
where it's determined NOT to contain the substring, we use LLM to check if the
semantic meaning is the same.
Args:
reimb_term (str): The original reimbursement term.
exhibit_lesser_of (str): The exhibit-level lesser-of statement.
filename (str): The name of the file being processed, for logging.
Returns:
bool: True if the exhibit-level lesser-of statement is redundant, False otherwise.
"""
prompt = prompt_templates.CHECK_REDUNDANT_LESSER(reimb_term, exhibit_lesser_of)
response = llm_utils.invoke_claude(
prompt,
"haiku_latest",
filename=filename,
cache=True,
instruction=prompt_templates.CHECK_REDUNDANT_LESSER_INSTRUCTION(),
)
logging.debug(f"LLM response for redundancy check in {filename}: {response}")
return (
string_utils.extract_text_from_delimiters(response, Delimiter.PIPE)
.strip()
.upper()
== "YES"
)
def validate_reimbursements_for_llm(answer_dict: dict[str, str], filename: str) -> bool:
"""Use LLM to determine if a service has actual reimbursement methodology.
@@ -553,77 +494,6 @@ def prompt_exhibit_header(page_content, EXHIBIT_HEADER_MARKERS, filename):
return llm_answer_extracted
def prompt_smart_chunked(
answers_dict, new_field_groups, field_group, one_to_one_chunks, filename
):
"""Extract field values from chunked contract text using appropriate prompt templates.
Selects single-field or multi-field prompt templates based on the number of fields
in the group and processes the corresponding text chunk to extract structured answers.
Args:
answers_dict (dict): Existing answers dictionary, mutated in place with new field values.
new_field_groups (dict): Field group definitions containing fields and their prompts.
field_group (str): Name of the field group to process.
one_to_one_chunks (dict): Text chunks mapped to field groups.
filename (str): Source filename for logging and LLM attribution.
Returns:
dict: The mutated answers_dict with extracted field values or error messages.
"""
fields = new_field_groups[field_group].get("fields", [])
questions = {}
for field in fields:
questions[field.field_name] = field.prompt
context = one_to_one_chunks[field_group]
context = context[0 : min(config.MAX_CONTEXT_LENGTH, len(context)) - 1]
# prepare prompt
if len(fields) == 1:
question = questions[
fields[0].field_name
] # Take the one question for the single field included
prompt = prompt_templates.ONE_TO_ONE_SINGLE_FIELD_TEMPLATE(context, question)
elif len(fields) > 1:
prompt = prompt_templates.ONE_TO_ONE_MULTI_FIELD_TEMPLATE(context, questions)
else:
raise ValueError("Field group has no defined fields")
try:
field_group_answer_raw = llm_utils.invoke_claude(
prompt, "sonnet_latest", filename, 8192
)
if len(fields) == 1:
field_group_answer_parsed = string_utils.extract_text_from_delimiters(
field_group_answer_raw, Delimiter.PIPE
)
field_group_answer_dict = {fields[0].field_name: field_group_answer_parsed}
elif len(fields) > 1:
field_group_answer_dict = string_utils.json_parsing_search(
field_group_answer_raw, [field.field_name for field in fields]
)
# Validate field names coming out of Claude:
expected_fields = {field.field_name for field in fields}
received_fields = set(field_group_answer_dict.keys())
if expected_fields != received_fields:
missing = expected_fields - received_fields
extra = received_fields - expected_fields
raise ValueError(f"Field mismatch - Missing: {missing}, Extra: {extra}")
# Load up answers dict
for field in fields:
field_answer = field_group_answer_dict[field.field_name]
answers_dict[field.field_name] = field_answer
except Exception as e:
logging.error(f"Error processing field {field}: {type(e)}, {str(e)}")
for field in fields:
answers_dict[field.field_name] = f"Error: {str(e)}"
return answers_dict
def prompt_date_fix(date: str) -> str:
"""Processes dates using `prompt_templates.date_fix_prompt`.
@@ -662,107 +532,6 @@ def prompt_derived_term_date(
return string_utils.extract_text_from_delimiters(response, Delimiter.PIPE)
def prompt_global_lesser_of_breakout(
global_lesser_of_stmt: str, constants: Constants, filename: str
) -> dict[str, str]:
"""Parse global lesser-of statement into structured methodology fields using LLM breakout.
This function reuses the same METHODOLOGY_BREAKOUT prompt template used for individual
service-level reimbursement terms to extract structured fields from contract-wide lesser-of
constraints. The extracted fields follow the same structure as methodology_breakout fields
defined in the field JSON.
The function works by:
1. Creating a mock service term "Global Lesser Of (all services)" to fit the breakout template
2. Loading methodology_breakout fields from the field JSON configuration
3. Invoking Claude with the METHODOLOGY_BREAKOUT prompt to parse the statement
4. Adding "[GLOBAL LESSER OF]" prefix to REIMB_TERM for tracking/identification
5. Returning the parsed fields or a minimal dict with just REIMB_TERM if parsing fails
Args:
global_lesser_of_stmt (str): Global lesser-of statement extracted from contract.
Example: "At no time will reimbursement exceed 100% of billed charges"
constants (Constants): Configuration object containing valid codes and business rules
used during methodology breakout parsing
filename (str): Source filename (e.g., "contract_001.txt"). Used for:
- Logging context
- LLM request attribution
- Error tracking
Returns:
dict[str, str]: Dictionary of methodology breakout fields extracted from the statement.
Always includes REIMB_TERM with "[GLOBAL LESSER OF]" prefix. Other fields depend on
what's defined as field_type="methodology_breakout" in the field JSON (e.g.,
REIMB_PCT_RATE, REIMB_FEE_RATE, UNIT_OF_MEASURE, etc.)
If parsing fails, returns: {"REIMB_TERM": "[GLOBAL LESSER OF] {original_statement}"}
Side Effects:
- Makes LLM API call via llm_utils.invoke_claude()
- Logs debug information about prompt and response (via logging_utils context)
- Logs error if parsing fails but continues execution
Notes:
- Uses the same METHODOLOGY_BREAKOUT template as one-to-n service-level terms
- The mock service term "Global Lesser Of (all services)" allows the global statement
to fit the [service, term] tuple expected by METHODOLOGY_BREAKOUT
- Parsing failures are non-fatal - returns minimal dict to allow processing to continue
- The "[GLOBAL LESSER OF]" prefix in REIMB_TERM distinguishes these from service-level terms
- This function is typically called from row_funcs.apply_global_lesser_of() which uses
the results to create duplicate rows via create_global_lesser_of_row()
- The specific methodology fields returned depend on the field_type="methodology_breakout"
definitions in the field JSON configuration
Example:
>>> stmt = "At no time will reimbursement exceed 100% of billed charges"
>>> constants = Constants(...)
>>> result = run_global_lesser_of_breakout(stmt, constants, "contract.txt")
>>> result["REIMB_TERM"]
'[GLOBAL LESSER OF] At no time will reimbursement exceed 100% of billed charges'
>>> result["AARETE_DERIVED_REIMB_METHOD"]
'billed charges'
>>> result["REIMB_PCT_RATE"]
'100'
>>> # Other fields depend on methodology_breakout field definitions in JSON
"""
# Create a mock service term for the breakout
mock_service = "Global Lesser Of (all services)"
prompt = prompt_templates.METHODOLOGY_BREAKOUT(
[mock_service, global_lesser_of_stmt],
FieldSet(
file_path=config.FIELD_JSON_PATH, field_type="methodology_breakout"
).print_prompt_dict(constants),
)
logging.debug(
f"Running global lesser of breakout for {filename} with prompt: {prompt}"
)
llm_response = llm_utils.invoke_claude(
prompt,
"sonnet_latest",
filename,
cache=True,
instruction=prompt_templates.METHODOLOGY_BREAKOUT_INSTRUCTION(),
usage_label="GLOBAL_LESSER_OF_BREAKOUT",
)
try:
breakout_results = string_utils.universal_json_load(llm_response)
result = breakout_results[0] if breakout_results else {}
# Add the global lesser of statement as the REIMB_TERM
result["REIMB_TERM"] = f"[GLOBAL LESSER OF] {global_lesser_of_stmt}"
return result
except:
logging.error(
f"Failed to parse global lesser of breakout response for {filename}. Response: {llm_response}"
)
return {"REIMB_TERM": f"[GLOBAL LESSER OF] {global_lesser_of_stmt}"}
def prompt_dynamic_assignment(
service_term: str,
reimb_term: str,
@@ -912,7 +681,7 @@ def prompt_lesser_of_check(
- exhibit_reference: str | None (exhibit name if target is OTHER)
"""
# DEBUG: Log what we're sending
logging.info(
logging.debug(
f"LESSER_OF_CHECK input: service='{service_term[:50]}...', reimb_term='{reimb_term[:100]}...'"
)
@@ -78,34 +78,6 @@ def prompt_exhibit_level_breakout(
return exhibit_level_answers
def prompt_exhibit_lesser(
exhibit_text: str,
exhibit_level_answers: dict,
filename: str,
):
logging.debug(f"Running Exhibit-Level Lesser for {filename}:")
prompt = prompt_templates.EXHIBIT_LEVEL_LESSER_OF(exhibit_text)
llm_answer_raw = llm_utils.invoke_claude(
prompt,
"legacy_sonnet",
filename,
max_tokens=8192,
cache=True,
instruction=prompt_templates.EXHIBIT_LEVEL_LESSER_OF_INSTRUCTION(),
usage_label="EXHIBIT_LEVEL_LESSER_OF",
)
logging.debug(f"LLM raw output for {filename}: {llm_answer_raw}")
try:
exhibit_lesser_of_answer = string_utils.extract_text_from_delimiters(
llm_answer_raw, Delimiter.PIPE
)
except:
exhibit_lesser_of_answer = "N/A"
exhibit_level_answers["EXHIBIT_LESSER_OF_STATEMENT"] = exhibit_lesser_of_answer
return exhibit_level_answers
def prompt_dynamic_primary(
exhibit_text: str, field: Field, constants: Constants, filename: str, TEMPLATE
):
@@ -439,43 +411,6 @@ def prompt_full_context(
return full_context_answers_dict
def is_exhibit_lesser_of_redundant(
reimb_term: str, exhibit_lesser_of: str, filename: str
) -> bool:
"""Use LLM to determine if exhibit-level lesser-of statement is redundant with the reimbursement term.
This is a second step of filtering, after checking for exact substring matches.
Small variations in punctuation may make it past the first filter, so in those cases
where it's determined NOT to contain the substring, we use LLM to check if the
semantic meaning is the same.
Args:
reimb_term (str): The original reimbursement term.
exhibit_lesser_of (str): The exhibit-level lesser-of statement.
filename (str): The name of the file being processed, for logging.
Returns:
bool: True if the exhibit-level lesser-of statement is redundant, False otherwise.
"""
prompt = prompt_templates.CHECK_REDUNDANT_LESSER(reimb_term, exhibit_lesser_of)
response = llm_utils.invoke_claude(
prompt,
"haiku_latest",
filename=filename,
cache=True,
instruction=prompt_templates.CHECK_REDUNDANT_LESSER_INSTRUCTION(),
)
logging.debug(f"LLM response for redundancy check in {filename}: {response}")
return (
string_utils.extract_text_from_delimiters(response, Delimiter.PIPE)
.strip()
.upper()
== "YES"
)
def validate_reimbursements_for_llm(answer_dict: dict[str, str], filename: str) -> bool:
"""Use LLM to determine if a service has actual reimbursement methodology.
@@ -606,89 +541,6 @@ def prompt_exhibit_header(page_content, EXHIBIT_HEADER_MARKERS, filename):
return llm_answer_extracted
def prompt_smart_chunked(
answers_dict, new_field_groups, field_group, one_to_one_chunks, filename
):
"""Extract field values from chunked contract text using appropriate prompt templates.
Selects single-field or multi-field prompt templates based on the number of fields
in the group and processes the corresponding text chunk to extract structured answers.
Args:
answers_dict (dict): Existing answers dictionary, mutated in place with new field values.
new_field_groups (dict): Field group definitions containing fields and their prompts.
field_group (str): Name of the field group to process.
one_to_one_chunks (dict): Text chunks mapped to field groups.
filename (str): Source filename for logging and LLM attribution.
Returns:
dict: The mutated answers_dict with extracted field values or error messages.
"""
fields = new_field_groups[field_group].get("fields", [])
questions = {}
for field in fields:
questions[field.field_name] = field.prompt
context = one_to_one_chunks[field_group]
context = context[0 : min(config.MAX_CONTEXT_LENGTH, len(context)) - 1]
# prepare prompt
if len(fields) == 1:
question = questions[
fields[0].field_name
] # Take the one question for the single field included
prompt = prompt_templates.ONE_TO_ONE_SINGLE_FIELD_TEMPLATE(context, question)
elif len(fields) > 1:
prompt = prompt_templates.ONE_TO_ONE_MULTI_FIELD_TEMPLATE(context, questions)
else:
raise ValueError("Field group has no defined fields")
try:
# Use appropriate instruction based on single vs multi-field
instruction = (
prompt_templates.ONE_TO_ONE_SINGLE_FIELD_INSTRUCTION()
if len(fields) == 1
else prompt_templates.ONE_TO_ONE_MULTI_FIELD_INSTRUCTION()
)
field_group_answer_raw = llm_utils.invoke_claude(
prompt,
"sonnet_latest",
filename,
8192,
cache=True,
instruction=instruction,
usage_label="ONE_TO_ONE_CHUNKED",
)
if len(fields) == 1:
field_group_answer_parsed = string_utils.extract_text_from_delimiters(
field_group_answer_raw, Delimiter.PIPE
)
field_group_answer_dict = {fields[0].field_name: field_group_answer_parsed}
elif len(fields) > 1:
field_group_answer_dict = string_utils.json_parsing_search(
field_group_answer_raw, [field.field_name for field in fields]
)
# Validate field names coming out of Claude:
expected_fields = {field.field_name for field in fields}
received_fields = set(field_group_answer_dict.keys())
if expected_fields != received_fields:
missing = expected_fields - received_fields
extra = received_fields - expected_fields
raise ValueError(f"Field mismatch - Missing: {missing}, Extra: {extra}")
# Load up answers dict
for field in fields:
field_answer = field_group_answer_dict[field.field_name]
answers_dict[field.field_name] = field_answer
except Exception as e:
logging.error(f"Error processing field {field}: {type(e)}, {str(e)}")
for field in fields:
answers_dict[field.field_name] = f"Error: {str(e)}"
return answers_dict
def prompt_date_fix(date: str) -> str:
"""Processes dates using `prompt_templates.date_fix_prompt`.
@@ -741,107 +593,6 @@ def prompt_derived_term_date(
return string_utils.extract_text_from_delimiters(response, Delimiter.PIPE)
def prompt_global_lesser_of_breakout(
global_lesser_of_stmt: str, constants: Constants, filename: str
) -> dict[str, str]:
"""Parse global lesser-of statement into structured methodology fields using LLM breakout.
This function reuses the same METHODOLOGY_BREAKOUT prompt template used for individual
service-level reimbursement terms to extract structured fields from contract-wide lesser-of
constraints. The extracted fields follow the same structure as methodology_breakout fields
defined in the field JSON.
The function works by:
1. Creating a mock service term "Global Lesser Of (all services)" to fit the breakout template
2. Loading methodology_breakout fields from the field JSON configuration
3. Invoking Claude with the METHODOLOGY_BREAKOUT prompt to parse the statement
4. Adding "[GLOBAL LESSER OF]" prefix to REIMB_TERM for tracking/identification
5. Returning the parsed fields or a minimal dict with just REIMB_TERM if parsing fails
Args:
global_lesser_of_stmt (str): Global lesser-of statement extracted from contract.
Example: "At no time will reimbursement exceed 100% of billed charges"
constants (Constants): Configuration object containing valid codes and business rules
used during methodology breakout parsing
filename (str): Source filename (e.g., "contract_001.txt"). Used for:
- Logging context
- LLM request attribution
- Error tracking
Returns:
dict[str, str]: Dictionary of methodology breakout fields extracted from the statement.
Always includes REIMB_TERM with "[GLOBAL LESSER OF]" prefix. Other fields depend on
what's defined as field_type="methodology_breakout" in the field JSON (e.g.,
REIMB_PCT_RATE, REIMB_FEE_RATE, UNIT_OF_MEASURE, etc.)
If parsing fails, returns: {"REIMB_TERM": "[GLOBAL LESSER OF] {original_statement}"}
Side Effects:
- Makes LLM API call via llm_utils.invoke_claude()
- Logs debug information about prompt and response (via logging_utils context)
- Logs error if parsing fails but continues execution
Notes:
- Uses the same METHODOLOGY_BREAKOUT template as one-to-n service-level terms
- The mock service term "Global Lesser Of (all services)" allows the global statement
to fit the [service, term] tuple expected by METHODOLOGY_BREAKOUT
- Parsing failures are non-fatal - returns minimal dict to allow processing to continue
- The "[GLOBAL LESSER OF]" prefix in REIMB_TERM distinguishes these from service-level terms
- This function is typically called from row_funcs.apply_global_lesser_of() which uses
the results to create duplicate rows via create_global_lesser_of_row()
- The specific methodology fields returned depend on the field_type="methodology_breakout"
definitions in the field JSON configuration
Example:
>>> stmt = "At no time will reimbursement exceed 100% of billed charges"
>>> constants = Constants(...)
>>> result = run_global_lesser_of_breakout(stmt, constants, "contract.txt")
>>> result["REIMB_TERM"]
'[GLOBAL LESSER OF] At no time will reimbursement exceed 100% of billed charges'
>>> result["AARETE_DERIVED_REIMB_METHOD"]
'billed charges'
>>> result["REIMB_PCT_RATE"]
'100'
>>> # Other fields depend on methodology_breakout field definitions in JSON
"""
# Create a mock service term for the breakout
mock_service = "Global Lesser Of (all services)"
prompt = prompt_templates.METHODOLOGY_BREAKOUT(
[mock_service, global_lesser_of_stmt],
FieldSet(
file_path=config.FIELD_JSON_PATH, field_type="methodology_breakout"
).print_prompt_dict(constants),
)
logging.debug(
f"Running global lesser of breakout for {filename} with prompt: {prompt}"
)
llm_response = llm_utils.invoke_claude(
prompt,
"sonnet_latest",
filename,
cache=True,
instruction=prompt_templates.METHODOLOGY_BREAKOUT_INSTRUCTION(),
usage_label="GLOBAL_LESSER_OF_BREAKOUT",
)
try:
breakout_results = string_utils.universal_json_load(llm_response)
result = breakout_results[0] if breakout_results else {}
# Add the global lesser of statement as the REIMB_TERM
result["REIMB_TERM"] = f"[GLOBAL LESSER OF] {global_lesser_of_stmt}"
return result
except:
logging.error(
f"Failed to parse global lesser of breakout response for {filename}. Response: {llm_response}"
)
return {"REIMB_TERM": f"[GLOBAL LESSER OF] {global_lesser_of_stmt}"}
def prompt_dynamic_assignment(
service_term: str,
reimb_term: str,
@@ -991,7 +742,7 @@ def prompt_lesser_of_check(
- exhibit_reference: str | None (exhibit name if target is OTHER)
"""
# DEBUG: Log what we're sending
logging.info(
logging.debug(
f"LESSER_OF_CHECK input: service='{service_term[:50]}...', reimb_term='{reimb_term[:100]}...'"
)
+1 -251
View File
@@ -71,41 +71,12 @@ def prompt_exhibit_level_breakout(
llm_answer_raw = llm_utils.invoke_claude(
prompt=prompt, model_id="sonnet_latest", filename=filename
)
print("Facility Adjustment: ", llm_answer_raw)
llm_answer_final = string_utils.universal_json_load(llm_answer_raw)
exhibit_level_answers.update(llm_answer_final)
return exhibit_level_answers
def prompt_exhibit_lesser(
exhibit_text: str,
exhibit_level_answers: dict,
filename: str,
):
logging.debug(f"Running Exhibit-Level Lesser for {filename}:")
prompt = prompt_templates.EXHIBIT_LEVEL_LESSER_OF(exhibit_text)
llm_answer_raw = llm_utils.invoke_claude(
prompt,
"legacy_sonnet",
filename,
max_tokens=8192,
cache=True,
instruction=prompt_templates.EXHIBIT_LEVEL_LESSER_OF_INSTRUCTION(),
usage_label="EXHIBIT_LEVEL_LESSER_OF",
)
logging.debug(f"LLM raw output for {filename}: {llm_answer_raw}")
try:
exhibit_lesser_of_answer = string_utils.extract_text_from_delimiters(
llm_answer_raw, Delimiter.PIPE
)
except:
exhibit_lesser_of_answer = "N/A"
exhibit_level_answers["EXHIBIT_LESSER_OF_STATEMENT"] = exhibit_lesser_of_answer
return exhibit_level_answers
def prompt_dynamic_primary(
exhibit_text: str, field: Field, constants: Constants, filename: str, TEMPLATE
):
@@ -420,43 +391,6 @@ def prompt_full_context(
return full_context_answers_dict
def is_exhibit_lesser_of_redundant(
reimb_term: str, exhibit_lesser_of: str, filename: str
) -> bool:
"""Use LLM to determine if exhibit-level lesser-of statement is redundant with the reimbursement term.
This is a second step of filtering, after checking for exact substring matches.
Small variations in punctuation may make it past the first filter, so in those cases
where it's determined NOT to contain the substring, we use LLM to check if the
semantic meaning is the same.
Args:
reimb_term (str): The original reimbursement term.
exhibit_lesser_of (str): The exhibit-level lesser-of statement.
filename (str): The name of the file being processed, for logging.
Returns:
bool: True if the exhibit-level lesser-of statement is redundant, False otherwise.
"""
prompt = prompt_templates.CHECK_REDUNDANT_LESSER(reimb_term, exhibit_lesser_of)
response = llm_utils.invoke_claude(
prompt,
"haiku_latest",
filename=filename,
cache=True,
instruction=prompt_templates.CHECK_REDUNDANT_LESSER_INSTRUCTION(),
)
logging.debug(f"LLM response for redundancy check in {filename}: {response}")
return (
string_utils.extract_text_from_delimiters(response, Delimiter.PIPE)
.strip()
.upper()
== "YES"
)
def validate_reimbursements_for_llm(answer_dict: dict[str, str], filename: str) -> bool:
"""Use LLM to determine if a service has actual reimbursement methodology.
@@ -587,89 +521,6 @@ def prompt_exhibit_header(page_content, EXHIBIT_HEADER_MARKERS, filename):
return llm_answer_extracted
def prompt_smart_chunked(
answers_dict, new_field_groups, field_group, one_to_one_chunks, filename
):
"""Extract field values from chunked contract text using appropriate prompt templates.
Selects single-field or multi-field prompt templates based on the number of fields
in the group and processes the corresponding text chunk to extract structured answers.
Args:
answers_dict (dict): Existing answers dictionary, mutated in place with new field values.
new_field_groups (dict): Field group definitions containing fields and their prompts.
field_group (str): Name of the field group to process.
one_to_one_chunks (dict): Text chunks mapped to field groups.
filename (str): Source filename for logging and LLM attribution.
Returns:
dict: The mutated answers_dict with extracted field values or error messages.
"""
fields = new_field_groups[field_group].get("fields", [])
questions = {}
for field in fields:
questions[field.field_name] = field.prompt
context = one_to_one_chunks[field_group]
context = context[0 : min(config.MAX_CONTEXT_LENGTH, len(context)) - 1]
# prepare prompt
if len(fields) == 1:
question = questions[
fields[0].field_name
] # Take the one question for the single field included
prompt = prompt_templates.ONE_TO_ONE_SINGLE_FIELD_TEMPLATE(context, question)
elif len(fields) > 1:
prompt = prompt_templates.ONE_TO_ONE_MULTI_FIELD_TEMPLATE(context, questions)
else:
raise ValueError("Field group has no defined fields")
try:
# Use appropriate instruction based on single vs multi-field
instruction = (
prompt_templates.ONE_TO_ONE_SINGLE_FIELD_INSTRUCTION()
if len(fields) == 1
else prompt_templates.ONE_TO_ONE_MULTI_FIELD_INSTRUCTION()
)
field_group_answer_raw = llm_utils.invoke_claude(
prompt,
"sonnet_latest",
filename,
8192,
cache=True,
instruction=instruction,
usage_label="ONE_TO_ONE_CHUNKED",
)
if len(fields) == 1:
field_group_answer_parsed = string_utils.extract_text_from_delimiters(
field_group_answer_raw, Delimiter.PIPE
)
field_group_answer_dict = {fields[0].field_name: field_group_answer_parsed}
elif len(fields) > 1:
field_group_answer_dict = string_utils.json_parsing_search(
field_group_answer_raw, [field.field_name for field in fields]
)
# Validate field names coming out of Claude:
expected_fields = {field.field_name for field in fields}
received_fields = set(field_group_answer_dict.keys())
if expected_fields != received_fields:
missing = expected_fields - received_fields
extra = received_fields - expected_fields
raise ValueError(f"Field mismatch - Missing: {missing}, Extra: {extra}")
# Load up answers dict
for field in fields:
field_answer = field_group_answer_dict[field.field_name]
answers_dict[field.field_name] = field_answer
except Exception as e:
logging.error(f"Error processing field {field}: {type(e)}, {str(e)}")
for field in fields:
answers_dict[field.field_name] = f"Error: {str(e)}"
return answers_dict
def prompt_date_fix(date: str) -> str:
"""Processes dates using `prompt_templates.date_fix_prompt`.
@@ -722,107 +573,6 @@ def prompt_derived_term_date(
return string_utils.extract_text_from_delimiters(response, Delimiter.PIPE)
def prompt_global_lesser_of_breakout(
global_lesser_of_stmt: str, constants: Constants, filename: str
) -> dict[str, str]:
"""Parse global lesser-of statement into structured methodology fields using LLM breakout.
This function reuses the same METHODOLOGY_BREAKOUT prompt template used for individual
service-level reimbursement terms to extract structured fields from contract-wide lesser-of
constraints. The extracted fields follow the same structure as methodology_breakout fields
defined in the field JSON.
The function works by:
1. Creating a mock service term "Global Lesser Of (all services)" to fit the breakout template
2. Loading methodology_breakout fields from the field JSON configuration
3. Invoking Claude with the METHODOLOGY_BREAKOUT prompt to parse the statement
4. Adding "[GLOBAL LESSER OF]" prefix to REIMB_TERM for tracking/identification
5. Returning the parsed fields or a minimal dict with just REIMB_TERM if parsing fails
Args:
global_lesser_of_stmt (str): Global lesser-of statement extracted from contract.
Example: "At no time will reimbursement exceed 100% of billed charges"
constants (Constants): Configuration object containing valid codes and business rules
used during methodology breakout parsing
filename (str): Source filename (e.g., "contract_001.txt"). Used for:
- Logging context
- LLM request attribution
- Error tracking
Returns:
dict[str, str]: Dictionary of methodology breakout fields extracted from the statement.
Always includes REIMB_TERM with "[GLOBAL LESSER OF]" prefix. Other fields depend on
what's defined as field_type="methodology_breakout" in the field JSON (e.g.,
REIMB_PCT_RATE, REIMB_FEE_RATE, UNIT_OF_MEASURE, etc.)
If parsing fails, returns: {"REIMB_TERM": "[GLOBAL LESSER OF] {original_statement}"}
Side Effects:
- Makes LLM API call via llm_utils.invoke_claude()
- Logs debug information about prompt and response (via logging_utils context)
- Logs error if parsing fails but continues execution
Notes:
- Uses the same METHODOLOGY_BREAKOUT template as one-to-n service-level terms
- The mock service term "Global Lesser Of (all services)" allows the global statement
to fit the [service, term] tuple expected by METHODOLOGY_BREAKOUT
- Parsing failures are non-fatal - returns minimal dict to allow processing to continue
- The "[GLOBAL LESSER OF]" prefix in REIMB_TERM distinguishes these from service-level terms
- This function is typically called from row_funcs.apply_global_lesser_of() which uses
the results to create duplicate rows via create_global_lesser_of_row()
- The specific methodology fields returned depend on the field_type="methodology_breakout"
definitions in the field JSON configuration
Example:
>>> stmt = "At no time will reimbursement exceed 100% of billed charges"
>>> constants = Constants(...)
>>> result = run_global_lesser_of_breakout(stmt, constants, "contract.txt")
>>> result["REIMB_TERM"]
'[GLOBAL LESSER OF] At no time will reimbursement exceed 100% of billed charges'
>>> result["AARETE_DERIVED_REIMB_METHOD"]
'billed charges'
>>> result["REIMB_PCT_RATE"]
'100'
>>> # Other fields depend on methodology_breakout field definitions in JSON
"""
# Create a mock service term for the breakout
mock_service = "Global Lesser Of (all services)"
prompt = prompt_templates.METHODOLOGY_BREAKOUT(
[mock_service, global_lesser_of_stmt],
FieldSet(
file_path=config.FIELD_JSON_PATH, field_type="methodology_breakout"
).print_prompt_dict(constants),
)
logging.debug(
f"Running global lesser of breakout for {filename} with prompt: {prompt}"
)
llm_response = llm_utils.invoke_claude(
prompt,
"sonnet_latest",
filename,
cache=True,
instruction=prompt_templates.METHODOLOGY_BREAKOUT_INSTRUCTION(),
usage_label="GLOBAL_LESSER_OF_BREAKOUT",
)
try:
breakout_results = string_utils.universal_json_load(llm_response)
result = breakout_results[0] if breakout_results else {}
# Add the global lesser of statement as the REIMB_TERM
result["REIMB_TERM"] = f"[GLOBAL LESSER OF] {global_lesser_of_stmt}"
return result
except:
logging.error(
f"Failed to parse global lesser of breakout response for {filename}. Response: {llm_response}"
)
return {"REIMB_TERM": f"[GLOBAL LESSER OF] {global_lesser_of_stmt}"}
def prompt_dynamic_assignment(
service_term: str,
reimb_term: str,
@@ -972,7 +722,7 @@ def prompt_lesser_of_check(
- exhibit_reference: str | None (exhibit name if target is OTHER)
"""
# DEBUG: Log what we're sending
logging.info(
logging.debug(
f"LESSER_OF_CHECK input: service='{service_term[:50]}...', reimb_term='{reimb_term[:100]}...'"
)
@@ -17,59 +17,6 @@ from src.pipelines.shared.extraction.vision_funcs import get_image_array_based_a
from src.prompts.fieldset import Field, FieldSet
def extract_global_lesser_of(
text_dict: str, first_reimbursement_page: str, filename: str
) -> dict:
"""Extract global "lesser of" statements that apply contract-wide.
Args:
contract_text (str): The full text of the contract to process.
filename (str): The name of the file being processed.
Returns:
dict: A dictionary containing the extracted "lesser of" statement.
"""
global_lesser_of_field = Field.load_from_file(
file_path=config.FIELD_JSON_PATH, field_name="GLOBAL_LESSER_OF_STATEMENT"
)
context = "\n".join(
[
page_text
for page_num, page_text in text_dict.items()
if int(page_num) < int(first_reimbursement_page)
]
)
# Process using existing full context logic
prompt = prompt_templates.ONE_TO_ONE_SINGLE_FIELD_TEMPLATE(
context,
field_prompt=global_lesser_of_field.prompt,
)
logging.debug(
f"Extracting global lesser of statement for {filename} with prompt: {prompt}"
)
response = llm_utils.invoke_claude(
prompt,
"sonnet_latest",
filename,
cache=True,
instruction=prompt_templates.ONE_TO_ONE_SINGLE_FIELD_INSTRUCTION(),
usage_label="ONE_TO_ONE_SINGLE_FIELD",
)
logging.debug(f"Response for global lesser of statement extraction: {response}")
return {
"GLOBAL_LESSER_OF_STATEMENT": string_utils.extract_text_from_delimiters(
response, Delimiter.PIPE
)
or "N/A"
}
def pass_smart_chunked_to_full_context(answers_dict, one_to_one_fields):
# even after running the vision api, if the effective date is still N/A, we need to run the prompt again with full context
if "EFFECTIVE_DT" in answers_dict and string_utils.is_empty(
@@ -208,8 +155,6 @@ def run_full_context_fields(
(e.g., "California""CA", "New York""NY")
5. **Special Case Breakout**: Processes any "special_case_primary" fields that require additional
parsing (e.g., complex reimbursement terms)
6. **Separate Field Handling**: Processes "full_context_separate" fields individually
(e.g., GLOBAL_LESSER_OF_STATEMENT extraction)
Args:
one_to_one_fields (FieldSet): Complete set of one-to-one fields to process. The function
@@ -224,13 +169,10 @@ def run_full_context_fields(
dict[str, str]: Dictionary mapping field names to extracted values. Common fields include:
- PAYER_STATE: Two-letter state abbreviation or value from config.STATE[0] if single-state batch
- PROVIDER_STATE: Two-letter state abbreviation
- GLOBAL_LESSER_OF_STATEMENT: Global lesser-of constraint text or "N/A"
- Any special_case_primary fields with their breakout results appended
Side Effects:
- Makes LLM API call via prompt_calls.prompt_full_context()
- May make additional LLM calls for special case breakouts via one_to_one_breakout()
- May make additional LLM call for GLOBAL_LESSER_OF_STATEMENT via extract_global_lesser_of()
- Logs extraction progress and results via logging_utils context
Notes:
@@ -238,7 +180,6 @@ def run_full_context_fields(
- State normalization uses string_utils.normalize_state_to_abbreviation() which handles
full names, abbreviations, and common misspellings
- Special case breakout is only triggered for fields with field_type="special_case_primary"
- GLOBAL_LESSER_OF_STATEMENT is always processed separately due to its unique extraction logic
- Full context fields are processed together in a single LLM call for efficiency
Example:
@@ -300,14 +241,6 @@ def run_full_context_fields(
# Handle separate one-to-one fields that require individual processing
separate_fields = one_to_one_fields.filter(field_type="full_context_separate")
for field in separate_fields.fields:
if field.field_name == "GLOBAL_LESSER_OF_STATEMENT":
# Add global lesser of extraction
global_lesser_of_results = extract_global_lesser_of(
text_dict, first_reimbursement_page, filename
)
full_context_answers_dict.update(global_lesser_of_results)
return full_context_answers_dict
@@ -397,64 +330,6 @@ def one_to_one_breakout(
return full_context_answers_dict
def create_global_lesser_of_row(
original_row: pd.Series, global_methodology_breakout: dict[str, str]
) -> pd.Series:
"""Create a duplicate reimbursement row with global lesser-of constraint applied.
This function supports the global lesser-of logic by creating an additional reimbursement
row for services that didn't originally have lesser-of constraints. When a contract has
a global lesser-of statement (e.g., "At no time will reimbursement exceed 100% of billed charges"),
this function creates a new row that applies that constraint to existing reimbursement terms which
lack their own lesser-of logic
The workflow:
1. Copies all fields from the original row (preserving REIMB_ID, SERVICE_TERM, etc.)
2. Overwrites methodology fields with values from global_methodology_breakout
3. Sets LESSER_OF_IND="Y" to indicate lesser-of logic applies
4. Adds GLOBAL_LESSER_OF_APPLIED="Y" flag for tracking
This allows the final output to have two rows for the same service:
- Original row: Standard reimbursement without lesser-of constraint
- New row: Same service with global lesser-of constraint applied
Args:
original_row (pd.Series): Original reimbursement row with LESSER_OF_IND="N".
Contains fields like REIMB_ID, SERVICE_TERM, REIMB_PCT_RATE, etc.
global_methodology_breakout (dict[str, str]): Results from
run_global_lesser_of_breakout() containing methodology fields to apply.
Common fields include: REIMB_PCT_RATE, REIMB_FEE_RATE, UNIT_OF_MEASURE, etc.
Returns:
pd.Series: New reimbursement row with:
- All original fields copied (REIMB_ID, SERVICE_TERM, etc. unchanged)
- Methodology fields updated with global_methodology_breakout values
- LESSER_OF_IND="Y"
- GLOBAL_LESSER_OF_APPLIED="Y"
Side Effects:
None - creates a new Series without mutating the input
Notes:
- The function does a shallow copy of the original row
- Only fields present in global_methodology_breakout are overwritten
- This function is typically called from row_funcs.apply_global_lesser_of()
which handles the iteration over all eligible rows
"""
new_row = original_row.copy()
# Update with global lesser of methodology breakout results.
for field, value in global_methodology_breakout.items():
new_row[field] = value
# Ensure lesser of indicator is set
new_row["LESSER_OF_IND"] = "Y"
# Add flag to track that this is a global lesser of row
new_row["GLOBAL_LESSER_OF_APPLIED"] = "Y"
return new_row
def get_aarete_derived_termination_dt(one_to_one_results: dict):
"""Derive standardized termination date from effective date and term duration.
@@ -92,104 +92,4 @@ def merge_one_to_one_into_one_to_n(
lambda row_val: str(v) if string_utils.is_empty(row_val) else row_val
)
# Handle global lesser of injection
global_lesser_of = one_to_one_results.get("GLOBAL_LESSER_OF_STATEMENT")
if global_lesser_of and global_lesser_of not in ["N/A", "UNKNOWN"]:
logging.debug(
f"[{one_to_one_results.get('FILE_NAME', 'UNKNOWN')}] Injecting global lesser of logic: {global_lesser_of}"
)
one_to_n_results = inject_global_lesser_of_rows(
one_to_n_results,
global_lesser_of,
constants,
one_to_one_results.get("FILE_NAME", "UNKNOWN"),
)
else:
logging.debug(
f"[{one_to_one_results.get('FILE_NAME', 'UNKNOWN')}] No global lesser of logic found or applicable."
)
# Initialize the tracking flag when no global lesser of logic is found
one_to_n_results["GLOBAL_LESSER_OF_APPLIED"] = "N"
return one_to_n_results
def inject_global_lesser_of_rows(
one_to_n_results: pd.DataFrame,
global_lesser_of_stmt: str,
constants: Constants,
filename: str,
) -> pd.DataFrame:
"""Create new rows with global lesser of constraint for REIMB_IDs that lack lesser of logic.
For each row where LESSER_OF_IND="N", creates a duplicate row with the global lesser
of statement applied via methodology breakout. The original row is preserved with
LESSER_OF_IND updated to "Y" (since it's now subject to lesser of) and GLOBAL_LESSER_OF_APPLIED="N".
The new row has LESSER_OF_IND="Y" and GLOBAL_LESSER_OF_APPLIED="Y". Both rows
share the same REIMB_ID.
Args:
one_to_n_results (pd.DataFrame): DataFrame containing one-to-n results with a 'LESSER_OF_IND' column.
global_lesser_of_stmt (str): Global lesser of statement extracted from contract (e.g., "lesser of billed or allowable")
filename (str): Name of the file being processed, used for logging and debugging.
Returns:
pd.DataFrame: Updated one_to_n_results with new rows injected for global lesser of logic.
"""
# Sanity Check: Ensure LESSER_OF_IND is populated
if "LESSER_OF_IND" in one_to_n_results.columns:
# Find rows that need global lesser of injection
rows_needing_injection = one_to_n_results[
one_to_n_results["LESSER_OF_IND"] == "N"
].copy()
elif "LESSER_OF_IND" not in one_to_n_results.columns:
if "SERVICE_TERM" not in one_to_n_results.columns:
return one_to_n_results
else:
one_to_n_results["LESSER_OF_IND"] = "N"
rows_needing_injection = one_to_n_results.copy()
if rows_needing_injection.empty:
logging.debug(f"No rows needing global lesser of injection for {filename}.")
# Still need to set tracking flag for all existing rows
one_to_n_results["GLOBAL_LESSER_OF_APPLIED"] = "N"
return one_to_n_results
# Update original rows that lacked lesser of: they're now subject to lesser of due to
# global constraint
one_to_n_results.loc[one_to_n_results["LESSER_OF_IND"] == "N", "LESSER_OF_IND"] = (
"Y"
)
# Set tracking flag for all existing rows (both original injected rows and rows that already had lesser of logic) to "N"
# New rows will get "Y" for this flag in create_global_lesser_of_row
one_to_n_results["GLOBAL_LESSER_OF_APPLIED"] = "N"
# Run methodology breakout on global statement (once)
global_methodology_breakout = prompt_calls.prompt_global_lesser_of_breakout(
global_lesser_of_stmt, constants, filename
)
# Create new rows for each REIMB_ID that was lacking lesser of logic
new_rows = []
seen_reimb_ids = set()
for _, original_row in rows_needing_injection.iterrows():
reimb_id = original_row.get("REIMB_ID")
if reimb_id in seen_reimb_ids:
continue # Skip if we've already processed this REIMB_ID
seen_reimb_ids.add(reimb_id)
new_row = one_to_one_funcs.create_global_lesser_of_row(
original_row, global_methodology_breakout
)
new_rows.append(new_row)
# Append new rows to results
if new_rows:
new_rows_df = pd.DataFrame(new_rows)
one_to_n_results = pd.concat([one_to_n_results, new_rows_df], ignore_index=True)
logging.debug(f"Injected {len(new_rows)} global lesser of rows for {filename}.")
else:
logging.debug(
f"No new rows created for global lesser of injection for {filename}."
)
return one_to_n_results
-6
View File
@@ -55,12 +55,6 @@
"field_type": "methodology_breakout",
"prompt": "Write the full name of the Fee Schedule here. Include any secondary part of the fee schedule such as clinical lab, outpatient imaging, DMEPOS, encounter, surgeon, clinical diagnostic lab, parenteral and enteral nutrition (PEN), ASC, Part B Drug, Behavioral health, obstetrical, dental, transportation, etc. CHIP, STAR, and other Programs are not fee schedules. Allowed amounts or allowable charges that are not set by CMS, Medicaid, or Medicare do not constitute a fee schedule. Only use exact text from the Reimbursement Term."
},
{
"field_name": "GLOBAL_LESSER_OF_STATEMENT",
"relationship": "one_to_one",
"field_type": "full_context_separate",
"prompt": "Find 'Lesser of' language that applies to ALL Services in the contract. Be careful not to include 'Lesser of' phrasing that only applies to the specific Exhibit. If you find a true contract-wide global constraint, extract it exactly. Most contracts will return 'N/A' - only extract if genuinely global."
},
{
"field_name": "GREATER_OF_IND",
"relationship": "one_to_n",
-122
View File
@@ -55,9 +55,6 @@ def get_cacheable_instructions():
if config.FIELDS in ["all", "one_to_n"]:
try:
cacheable_instructions["EXHIBIT_LEVEL"] = EXHIBIT_LEVEL_INSTRUCTION()
cacheable_instructions["EXHIBIT_LEVEL_LESSER_OF"] = (
EXHIBIT_LEVEL_LESSER_OF_INSTRUCTION()
)
except Exception:
pass
@@ -67,9 +64,6 @@ def get_cacheable_instructions():
cacheable_instructions["VALIDATE_REIMBURSEMENTS"] = (
VALIDATE_REIMBURSEMENTS_INSTRUCTION()
)
cacheable_instructions["CHECK_REDUNDANT_LESSER"] = (
CHECK_REDUNDANT_LESSER_INSTRUCTION()
)
cacheable_instructions["LOB_RELATIONSHIP"] = LOB_RELATIONSHIP_INSTRUCTION()
except Exception:
pass
@@ -169,53 +163,6 @@ def EXHIBIT_LEVEL_INSTRUCTION() -> str:
)
def EXHIBIT_LEVEL_LESSER_OF(context):
return f"""Analyze a section of a contract.
Look for exhibit-wide payment constraints that apply universally to ALL services in this exhibit, regardless of service category or line of business.
Valid exhibit-wide constraints have language like:
- 'All services under this exhibit are subject to the lesser of...'
- 'Except as otherwise provided in this Exhibit, the Allowed Amount for Covered Services shall be the lesser of...'
- 'Payment for any service in this exhibit shall not exceed...'
- 'At no time shall Plan pay an amount that exceeds...'
DO NOT extract constraints that:
- Apply only to specific lines of business or service categories
- Are part of individual service methodologies
If you find a true exhibit-wide constraint, return the component that represents the actual payment limitation, excluding administrative processing language such as:
- 'less any applicable co-payments, deductibles and coinsurance'
- 'minus patient responsibility amounts'
**GUIDANCE FOR ABBREVIATED TERMS:**
- 'billed' can be expanded to 'Provider's billed charges'
- 'allowable' typically refers to amounts defined elsewhere in the contract and is not an acceptable exhibit-wide constraint due to its nonspecificity
- Other generic terms like 'contracted rate' or 'negotiated amount' should be avoided unless they represent specific, quantifiable constraints
- Specific percentages of external fee schedules (e.g., '105% of Medicare rates') are acceptable constraints
Examples:
- 'all rates will be reimbursed at the lesser of billed or allowable' → return 'Provider's billed charges'
- 'Lesser of 105% of Medicare rates or allowable charges' → return '105% of Medicare rates'
- 'Lesser of Provider's charges or contracted rates' → return 'Provider's billed charges'
- 'At no time shall payment exceed Provider's billed charges' → return 'Provider's billed charges'
If no exhibit-wide constraint exists, return 'N/A'.
Here is the Exhibit to analyze:
{context}
Briefly explain your answer, then put your final answer in |pipes|
"""
def EXHIBIT_LEVEL_LESSER_OF_INSTRUCTION() -> str:
return (
"[OBJECTIVE]\n"
"Identify presence of exhibit-level 'lesser of' or 'not to exceed' statements. Return answer in pipes."
)
def DYNAMIC_PRIMARY_HEADER(context, field_name, field_prompt):
return f"""Extract attributes from the provided text, paying close attention to detail.
@@ -1166,40 +1113,6 @@ Contract text:
"""
def GROUP_TIN_NPI_TEMPLATE(key_sections: str, provider_list: str) -> str:
return f"""
Identify which provider is the MAIN CONTRACTING GROUP in this contract.
Below is a list of all providers found in the contract:
{provider_list}
Based on the key sections of the contract (first and last parts where party definitions and signatures are usually located):
{key_sections.replace('"', "'")}
Look for these indicators to identify the main contracting group:
1. Provider explicitly referred to as "Provider", "Group", or "Group Practice" in party definitions
2. Provider appearing in signature blocks or with authorized signatories
3. Provider referenced with phrases like "hereby agrees", "enters into agreement with", or "party of the first/second part"
4. Entity described as representing multiple practitioners or locations
5. Entity mentioned most frequently throughout the key sections
In case of multiple potential matches, prioritize:
- Entities with both TIN and NPI over those with only one identifier
- Entities described as medical groups over individual practitioners
- Entities appearing in formal contract party definitions over those mentioned elsewhere
Determine which provider is the main contracting entity/group.
Return the name, TIN, and NPI of the main group provider in JSON format like this:
|{{"NAME": "Provider Name", "TIN": "123456789", "NPI": "1234567890"}}|
If you cannot confidently determine the main group, return null values:
|{{"NAME": null, "TIN": null, "NPI": null}}|
Feel free to justify your answer, but enclose your final answer in |pipes|.
"""
def FULL_CONTEXT_CLAIM_TYPES_ADDITIONAL_INSTRUCTION():
return "Additionally Check the contract text for the provider name from the preamble or signature section to help determine the claim type."
@@ -1500,41 +1413,6 @@ def DATE_FIX_INSTRUCTION() -> str:
)
def CHECK_REDUNDANT_LESSER(reimb_term, exhibit_lesser_of):
return f"""You are analyzing whether an exhibit-level lesser-of constraint is semantically redundant with a reimbursement methodology.
REIMBURSEMENT METHODOLOGY: {reimb_term}
EXHIBIT CONSTRAINT: {exhibit_lesser_of}
Determine if the exhibit constraint is already covered by the reimbursement methodology.
CRITICAL: Pay careful attention to NUMERICAL DIFFERENCES. Different percentages (e.g., 90% vs 100%) are NOT redundant even if they both reference billed charges.
Examples of REDUNDANT language:
- Reimb: "lesser of fee schedule or billed charges" + Exhibit: "not to exceed billed charges" → REDUNDANT
- Reimb: "payment shall not exceed provider's charges" + Exhibit: "lesser of rate or charges" → REDUNDANT
- Reimb: "100% of billed charges not to exceed usual and customary" + Exhibit: "not to exceed billed charges" → REDUNDANT
Examples of NON-REDUNDANT language:
- Reimb: "100% of billed charges" + Exhibit: "90% of billed charges" → NOT REDUNDANT (90% is more restrictive than 100%)
- Reimb: "billed charges not to exceed usual and customary" + Exhibit: "90% of billed charges" → NOT REDUNDANT (90% is a specific cap vs general UCR limit)
- Reimb: "fee schedule rate" + Exhibit: "90% of billed charges" → NOT REDUNDANT (different constraint types)
IMPORTANT: If the exhibit constraint introduces ANY new numerical limit, percentage, or dollar amount not present in the reimbursement methodology, it is NOT redundant.
Answer YES only if the exhibit constraint adds NO additional limitation beyond what's already in the reimbursement methodology.
Briefly explain your reasoning, then return YES or NO in |pipes|."""
def CHECK_REDUNDANT_LESSER_INSTRUCTION() -> str:
return (
"[OBJECTIVE]\n"
"Decide if two texts are semantically redundant with respect to 'lesser of' meaning. Return YES/NO in pipes."
)
def CARVEOUT_CHECK(
service_term: str, reimb_term: str, carveout_definitions, special_case_definitions
):
+1 -3
View File
@@ -1184,9 +1184,7 @@ def get_one_to_one_analysis(testbed, results):
["PROV_GROUP_TIN", "PROV_GROUP_NPI", "PROV_GROUP_NAME_FULL"]
)
one_to_one_fields = [
f
for f in one_to_one_fields
if f not in {"TIN", "NPI", "NAME", "CLIENT_NAME", "GLOBAL_LESSER_OF_STATEMENT"}
f for f in one_to_one_fields if f not in {"TIN", "NPI", "NAME", "CLIENT_NAME"}
]
# Create filtered dataframes with only one-to-one fields
+5 -5
View File
@@ -391,7 +391,7 @@ def invoke_claude_with_image_array(
base64_images,
usage_label=usage_label,
)
logging.info(
logging.debug(
f"Effective Date Response from local Claude 3 for {filename}: {response}"
)
else:
@@ -707,12 +707,12 @@ def ec2_claude_3_and_up(
]:
if attempt < max_retries - 1:
delay = (2**attempt + random.uniform(0, 1)) * initial_delay
logging.info(
logging.warning(
f"Attempt {attempt + 1} failed. Retrying in {delay:.2f} seconds..."
)
time.sleep(delay)
else:
logging.info(
logging.warning(
f"Switching to next runtime due to {error_code}: {str(e)}."
)
break # Switch to the next runtime
@@ -744,7 +744,7 @@ def ec2_claude_3_and_up(
# Timeout errors should probably retry
if attempt < max_retries - 1:
delay = (2**attempt + random.uniform(0, 1)) * initial_delay
logging.info(
logging.warning(
f"Attempt {attempt + 1} failed due to timeout. Retrying in {delay:.2f} seconds..."
)
time.sleep(delay)
@@ -755,7 +755,7 @@ def ec2_claude_3_and_up(
if error_code in ["ThrottlingException", "TooManyRequestsException"]:
if attempt < max_retries - 1:
delay = (2**attempt + random.uniform(0, 1)) * initial_delay
logging.info(
logging.warning(
f"Attempt {attempt + 1} failed due to rate limiting. Retrying in {delay:.2f} seconds..."
)
time.sleep(delay)
-1
View File
@@ -781,7 +781,6 @@ QC_YES_NO_COLUMNS = [
"OUTLIER_FIRST_DOLLAR_IND",
"STOP_LOSS_FIRST_DOLLAR_IND",
"COMPOUND_SPLIT_IND",
"GLOBAL_LESSER_OF_APPLIED",
]
# Standard Line of Business values