491abd31c2
Feature/dynamic codes final * Move Claim Type to dynamic code group * Update CLaim Type and Bill TYpe prompts * Prompt optimization * Prompt tuning * streamline * Format * Merged dev into feature/dynamic-codes * test claim and bill type * Merged dev into DAIP2-2186-service-claim-type-testing * Merged dev into feature/dynamic-codes * prompt updated for claim type * Merge branch 'feature/dynamic-codes' into DAIP2-2186-service-claim-type-testing * Merged dev into DAIP2-2186-service-claim-type-testing * Remove print * Switch to map * Merged dev into feature/dynamic-codes-final * prompt inconsistency fixed Approved-by: Praneel Panchigar Approved-by: Siddhant Medar
2883 lines
141 KiB
Python
2883 lines
141 KiB
Python
from typing import Any, Callable, Optional, Tuple
|
||
|
||
import src.config as config
|
||
from src.constants.constants import Constants
|
||
from src.prompts.fieldset import FieldSet
|
||
from src.utils import json_utils, string_utils
|
||
|
||
# JSON format instructions for standardized LLM outputs
|
||
JSON_DICT_FORMAT_INSTRUCTIONS = """Return your final answer as a valid JSON dictionary with the specified field names as keys.
|
||
- Use "N/A" for fields where the requested information doesn't apply to this context or cannot be clearly identified.
|
||
- Ensure the JSON is properly formatted with double quotes around keys and string values."""
|
||
|
||
JSON_LIST_FORMAT_INSTRUCTIONS = """Return your final answer as a valid JSON list (array).
|
||
- If multiple values are found, include all of them as separate items in the list.
|
||
- If no values are found, return ["N/A"].
|
||
- Ensure the JSON is properly formatted with square brackets and double quotes around string values."""
|
||
|
||
JSON_DICT_WITH_LISTS_FORMAT_INSTRUCTIONS = """Return your final answer as a valid JSON dictionary where values can be lists (arrays).
|
||
- Use lists for fields that may contain multiple values.
|
||
- Use "N/A" for fields where the requested information doesn't apply or cannot be clearly identified
|
||
- Ensure the JSON is properly formatted with double quotes around keys and string values."""
|
||
|
||
JSON_LIST_OF_DICTS_FORMAT_INSTRUCTIONS = """Return your final answer as a valid JSON list (array) of dictionaries.
|
||
- Each item in the list should be a dictionary with the specified field names as keys.
|
||
- If multiple items are found, include all of them as separate dictionaries in the list.
|
||
- Use "N/A" for fields within each dictionary where the information doesn't apply.
|
||
- Ensure the JSON is properly formatted with square brackets, curly braces, and double quotes.
|
||
- The Keys and Values must be strings, and not lists."""
|
||
|
||
|
||
# Parser helper functions for prompt templates
|
||
def _json_dict_parser(raw_output: str) -> dict:
|
||
"""Parse JSON dictionary from LLM output (without field normalization).
|
||
|
||
For field-aware parsing, use _create_json_dict_parser() instead.
|
||
"""
|
||
return json_utils.parse_json_dict(raw_output)
|
||
|
||
|
||
def _json_list_parser(raw_output: str) -> list:
|
||
"""Parse JSON list from LLM output (without field normalization).
|
||
|
||
For field-aware parsing, use _create_json_list_parser() instead.
|
||
"""
|
||
return json_utils.parse_json_list(raw_output)
|
||
|
||
|
||
def _create_json_dict_parser(field_names: list[str] | None = None):
|
||
"""Create a JSON dict parser with field names bound for normalization."""
|
||
|
||
def parser(raw_output: str) -> dict:
|
||
return json_utils.parse_json_dict(raw_output, field_names=field_names)
|
||
|
||
return parser
|
||
|
||
|
||
def _create_json_list_parser(
|
||
field_name: str | None = None, expected_format: str | None = None
|
||
):
|
||
"""Create a JSON list parser with field name bound for normalization."""
|
||
|
||
def parser(raw_output: str) -> list:
|
||
return json_utils.parse_json_list(
|
||
raw_output, field_name=field_name, expected_format=expected_format
|
||
)
|
||
|
||
return parser
|
||
|
||
|
||
# Module-level cached Constants instance for instruction functions
|
||
# This is lazily initialized on first use and reused across all instruction calls
|
||
_cached_constants = None
|
||
|
||
|
||
def set_constants_for_instruction_cache(constants: Optional[Constants]) -> None:
|
||
"""Set the Constants instance used by _get_constants (e.g. for tests or
|
||
contract-by-contract runs that want to inject or clear between runs).
|
||
Pass None to clear the cache so the next _get_constants() creates a new instance.
|
||
"""
|
||
global _cached_constants
|
||
_cached_constants = constants
|
||
|
||
|
||
def _get_constants() -> Constants:
|
||
"""Get or create the cached Constants instance for instruction functions."""
|
||
global _cached_constants
|
||
if _cached_constants is None:
|
||
_cached_constants = Constants()
|
||
return _cached_constants
|
||
|
||
|
||
def _get_fields_text(field_type: str) -> str:
|
||
"""Load fields from investment_prompts.json and format them with resolved valid_values.
|
||
|
||
Args:
|
||
field_type: The field_type to filter by (e.g., 'methodology_breakout', 'fee_schedule_breakout')
|
||
|
||
Returns:
|
||
Formatted string of field definitions with resolved valid_values
|
||
"""
|
||
constants = _get_constants()
|
||
fields = FieldSet(
|
||
file_path="src/prompts/investment_prompts.json",
|
||
relationship="one_to_n",
|
||
field_type=field_type,
|
||
)
|
||
return fields.print_prompt_dict(constants)
|
||
|
||
|
||
def get_cacheable_instructions():
|
||
"""
|
||
Returns a dictionary of all cacheable instructions for cache warming.
|
||
This should be called once before parallel processing starts.
|
||
|
||
Returns:
|
||
dict: Dictionary mapping cache keys to instruction text
|
||
"""
|
||
|
||
cacheable_instructions = {}
|
||
|
||
# REIMBURSEMENT_PRIMARY instruction
|
||
if config.FIELDS in ["all", "one_to_n"]:
|
||
try:
|
||
cacheable_instructions["REIMBURSEMENT_PRIMARY"] = (
|
||
REIMBURSEMENT_PRIMARY_INSTRUCTION()
|
||
)
|
||
except Exception as e:
|
||
pass # Skip if fields not available
|
||
|
||
# METHODOLOGY_BREAKOUT instruction
|
||
if config.FIELDS in ["all", "one_to_n"]:
|
||
try:
|
||
cacheable_instructions["METHODOLOGY_BREAKOUT"] = (
|
||
METHODOLOGY_BREAKOUT_INSTRUCTION()
|
||
)
|
||
except Exception:
|
||
pass
|
||
|
||
# DYNAMIC_CODE_ASSIGNMENT instruction
|
||
if config.FIELDS in ["all", "one_to_n"]:
|
||
try:
|
||
cacheable_instructions["DYNAMIC_CODE_ASSIGNMENT"] = (
|
||
DYNAMIC_CODE_ASSIGNMENT_INSTRUCTION()
|
||
)
|
||
except Exception:
|
||
pass
|
||
|
||
# Secondary breakout instructions
|
||
if config.FIELDS in ["all", "one_to_n"]:
|
||
try:
|
||
cacheable_instructions["FEE_SCHEDULE_BREAKOUT"] = (
|
||
FEE_SCHEDULE_BREAKOUT_INSTRUCTION()
|
||
)
|
||
cacheable_instructions["GROUPER_BREAKOUT"] = GROUPER_BREAKOUT_INSTRUCTION()
|
||
except Exception:
|
||
pass
|
||
|
||
# service term split instruction
|
||
if config.FIELDS in ["all", "one_to_n"]:
|
||
try:
|
||
cacheable_instructions["SPLIT_SERVICE_TERM"] = (
|
||
SPLIT_SERVICE_TERM_INSTRUCTION()
|
||
)
|
||
except Exception:
|
||
pass
|
||
|
||
# OUTLIER_BREAKOUT instruction
|
||
if config.FIELDS in ["all", "one_to_n"]:
|
||
try:
|
||
cacheable_instructions["OUTLIER_BREAKOUT"] = OUTLIER_BREAKOUT_INSTRUCTION()
|
||
except Exception as e:
|
||
pass
|
||
|
||
# EXHIBIT_LEVEL + LESSER_OF
|
||
if config.FIELDS in ["all", "one_to_n"]:
|
||
try:
|
||
cacheable_instructions["EXHIBIT_LEVEL"] = EXHIBIT_LEVEL_INSTRUCTION()
|
||
except Exception:
|
||
pass
|
||
|
||
# Validation / split / checks / relationships
|
||
if config.FIELDS in ["all", "one_to_n"]:
|
||
try:
|
||
cacheable_instructions["VALIDATE_REIMBURSEMENTS"] = (
|
||
VALIDATE_REIMBURSEMENTS_INSTRUCTION()
|
||
)
|
||
cacheable_instructions["LOB_RELATIONSHIP"] = LOB_RELATIONSHIP_INSTRUCTION()
|
||
except Exception:
|
||
pass
|
||
|
||
# Dynamic primary + carveout
|
||
if config.FIELDS in ["all", "one_to_n"]:
|
||
try:
|
||
cacheable_instructions["DYNAMIC_PRIMARY"] = DYNAMIC_PRIMARY_INSTRUCTION()
|
||
cacheable_instructions["CARVEOUT_CHECK"] = CARVEOUT_CHECK_INSTRUCTION()
|
||
cacheable_instructions["DYNAMIC_ASSIGNMENT"] = (
|
||
DYNAMIC_ASSIGNMENT_INSTRUCTION()
|
||
)
|
||
cacheable_instructions["REIMB_DATES_ASSIGNMENT"] = (
|
||
REIMB_DATES_ASSIGNMENT_INSTRUCTION()
|
||
)
|
||
cacheable_instructions["LESSER_OF_DISTRIBUTION"] = (
|
||
LESSER_OF_DISTRIBUTION_INSTRUCTION()
|
||
)
|
||
cacheable_instructions["LESSER_OF_CHECK"] = LESSER_OF_CHECK_INSTRUCTION()
|
||
except Exception:
|
||
pass
|
||
|
||
# TIN_NPI_TEMPLATE instruction
|
||
if config.FIELDS in ["all", "one_to_one"]:
|
||
try:
|
||
cacheable_instructions["TIN_NPI_TEMPLATE"] = TIN_NPI_TEMPLATE_INSTRUCTION()
|
||
except Exception as e:
|
||
pass
|
||
|
||
# ONE_TO_ONE templates (single and multi-field)
|
||
if config.FIELDS in ["all", "one_to_one"]:
|
||
try:
|
||
cacheable_instructions["ONE_TO_ONE_SINGLE_FIELD"] = (
|
||
ONE_TO_ONE_SINGLE_FIELD_INSTRUCTION()
|
||
)
|
||
except Exception:
|
||
pass
|
||
|
||
# Preprocessing instructions (exhibit headers, linkage)
|
||
try:
|
||
cacheable_instructions["EXHIBIT_HEADER"] = EXHIBIT_HEADER_INSTRUCTION()
|
||
cacheable_instructions["EXHIBIT_LINKAGE"] = EXHIBIT_LINKAGE_INSTRUCTION()
|
||
cacheable_instructions["EXHIBIT_TITLE_MATCH"] = (
|
||
EXHIBIT_TITLE_MATCH_INSTRUCTION()
|
||
)
|
||
except Exception:
|
||
pass
|
||
|
||
# Utility instructions (date fixes, provider matching)
|
||
try:
|
||
cacheable_instructions["DATE_FIX"] = DATE_FIX_INSTRUCTION()
|
||
cacheable_instructions["DERIVED_TERM_DATE"] = DERIVED_TERM_DATE_INSTRUCTION()
|
||
cacheable_instructions["CHECK_PROVIDER_NAME_MATCH"] = (
|
||
CHECK_PROVIDER_NAME_MATCH_INSTRUCTION()
|
||
)
|
||
cacheable_instructions["SPECIAL_CASE_ASSIGNMENT"] = (
|
||
SPECIAL_CASE_ASSIGNMENT_INSTRUCTION()
|
||
)
|
||
cacheable_instructions["SPLIT_REIMB_DATES"] = SPLIT_REIMB_DATES_INSTRUCTION()
|
||
except Exception:
|
||
pass
|
||
|
||
# Code extraction instructions
|
||
try:
|
||
cacheable_instructions["CODE_EXPLICIT"] = CODE_EXPLICIT_INSTRUCTION()
|
||
cacheable_instructions["CODE_CATEGORY"] = CODE_CATEGORY_INSTRUCTION()
|
||
cacheable_instructions["CODE_IMPLICIT_SPECIAL"] = (
|
||
CODE_IMPLICIT_SPECIAL_INSTRUCTION()
|
||
)
|
||
cacheable_instructions["CODE_IMPLICIT"] = CODE_IMPLICIT_INSTRUCTION()
|
||
cacheable_instructions["CODE_IMPLICIT_ARBITRATION"] = (
|
||
CODE_IMPLICIT_ARBITRATION_INSTRUCTION()
|
||
)
|
||
cacheable_instructions["CODE_LAST_CHECK"] = CODE_LAST_CHECK_INSTRUCTION()
|
||
cacheable_instructions["FILL_BILL_TYPE"] = FILL_BILL_TYPE_INSTRUCTION()
|
||
except Exception:
|
||
pass
|
||
|
||
return cacheable_instructions
|
||
|
||
|
||
#####################################################################################
|
||
################################## PRIMARY PROMPTS ##################################
|
||
#####################################################################################
|
||
|
||
|
||
def EXHIBIT_LEVEL_INSTRUCTION() -> str:
|
||
"""Static instruction for EXHIBIT_LEVEL prompt caching.
|
||
Contains all formatting rules and general instructions.
|
||
"""
|
||
return f"""[OBJECTIVE]
|
||
Extract Exhibit-Level attributes from a section of a contract.
|
||
|
||
[EXHIBIT LEVEL DETERMINATION]
|
||
- For each field, ONLY extract an answer if that answer applies to EVERY rate in the Exhibit. Some ways to determine if the answer applies to the entire Exhibit:
|
||
- The answer is clearly written in the Header or Title of the Exhibit.
|
||
- It is stated in a sentence or paragraph that the answer applies to all rates.
|
||
- The answer for each field must be clearly and explicitly written. Do not attempt to deduce the answer from other related values:
|
||
E.g. If the Exhibit only contains Services related to "Home", "Dialysis", and "Skilled Nursing Facility", then the Claim Type is not "Ancillary" even though those are all Ancillary Services.
|
||
In the example above, the Claim Type answer is not written explicitly, so the answer should be "N/A".
|
||
|
||
[GENERAL INSTRUCTIONS]
|
||
- Return the correct answer. There will only be one correct answer.
|
||
- If a list of valid values is provided, ONLY answer with the EXACT value from that list.
|
||
- If there are no correct answers for a given field, write 'N/A' as the answer for that field.
|
||
- Be consistent and deterministic; avoid creative paraphrasing.
|
||
|
||
[OUTPUT FORMAT]
|
||
Briefly explain your answer, then put the final answer in the properly-formatted JSON dictionary.
|
||
{JSON_DICT_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def EXHIBIT_LEVEL(
|
||
context,
|
||
fields,
|
||
field_names: list[str] | None = None,
|
||
) -> Tuple[str, str, Callable[[str], dict]]:
|
||
"""Returns dynamic content for exhibit level extraction.
|
||
Call EXHIBIT_LEVEL_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Args:
|
||
context: The exhibit text to analyze.
|
||
fields: Formatted string of field definitions.
|
||
field_names: Optional list of field names for format-aware normalization.
|
||
|
||
Returns:
|
||
Tuple of (context_text, prompt_string, parser_function).
|
||
"""
|
||
context_text = f"""[CONTEXT]
|
||
Here is the text to analyze:
|
||
{context.replace('"', "'")}"""
|
||
|
||
attributes_text = f"""[ATTRIBUTES]
|
||
Here are the attributes to be included in the dictionary, and instructions on how to correctly answer:
|
||
{fields}"""
|
||
|
||
parser = _create_json_dict_parser(field_names) if field_names else _json_dict_parser
|
||
return (context_text, attributes_text, parser)
|
||
|
||
|
||
def DYNAMIC_PRIMARY_INSTRUCTION() -> str:
|
||
"""Static instruction for DYNAMIC_PRIMARY prompt caching.
|
||
Contains extraction rules for text-based fields (allows obvious assumptions).
|
||
"""
|
||
return f"""[OBJECTIVE]
|
||
Extract attribute values for dynamic fields from contract text, paying close attention to detail.
|
||
|
||
[GENERAL INSTRUCTIONS]
|
||
- Return all valid, explicitly stated values. If multiple values are found, include all of them as separate items in the JSON list.
|
||
- If none of the valid values appear explicitly in the text, return ["N/A"].
|
||
- Make appropriate and obvious assumptions, but don't take huge leaps of logic.
|
||
- For example, you can assume that "Children's Health Insurance Program Perinate (CHIP-P)" is equivalent to "CHIP Perinate", but do not assume that "Children's Health Insurance Program Perinate (CHIP-P)" is equivalent to "Medicaid".
|
||
|
||
[OUTPUT FORMAT]
|
||
Briefly explain your answer before putting the final answer in a properly-formatted JSON list.
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def DYNAMIC_PRIMARY(
|
||
context,
|
||
field_name,
|
||
field_prompt,
|
||
) -> Tuple[str, str, Callable[[str], list]]:
|
||
"""Returns dynamic content for text-based dynamic primary extraction.
|
||
Call DYNAMIC_PRIMARY_TEXT_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Args:
|
||
context: The exhibit text to analyze.
|
||
field_name: The field name being extracted (used for format-aware normalization).
|
||
field_prompt: The prompt/description for the field.
|
||
|
||
Returns:
|
||
Tuple of (context_text, prompt_string, parser_function).
|
||
"""
|
||
context_text = f"""[CONTEXT]
|
||
Here is the text to analyze:
|
||
{context.replace('"', "'")}"""
|
||
|
||
attribute_text = f"""[ATTRIBUTE TO EXTRACT]
|
||
{field_name} : {field_prompt}"""
|
||
|
||
# Create parser with field_name bound for format-aware normalization
|
||
parser = _create_json_list_parser(field_name=field_name)
|
||
return (context_text, attribute_text, parser)
|
||
|
||
|
||
def REIMB_DATES_ASSIGNMENT_INSTRUCTION() -> str:
|
||
"""Static instruction for REIMB_DATES_ASSIGNMENT prompt caching.
|
||
Contains all date assignment rules and examples.
|
||
"""
|
||
return f"""[OBJECTIVE]
|
||
Identify which date range applies to a specific Reimbursement Term.
|
||
You will be shown a Reimbursement Term and the section of a contract from which that term was extracted.
|
||
Your task is to determine which date range applies to THIS SPECIFIC reimbursement term.
|
||
|
||
[CRITICAL RULES FOR DATE ASSIGNMENT]
|
||
|
||
1. **TABLE/SECTION GROUPING** - MOST IMPORTANT:
|
||
If multiple reimbursement terms appear in the SAME TABLE or under the SAME SECTION HEADER, they ALL share the SAME date range.
|
||
|
||
Example:
|
||
"For services during the period January 16, 2018 through June 30, 2018.
|
||
[Table with: Ambulatory Surgery Services Rate: 193.75%, Cost Items Rate: 100%, Default Pricing Rate: 50%]"
|
||
|
||
→ ALL three rates (193.75%, 100%, 50%) get "January 16, 2018 through June 30, 2018"
|
||
→ Even if you see OTHER date ranges elsewhere in the contract, these terms are in THIS table, so they get THIS date range ONLY
|
||
|
||
2. **PROXIMITY PRIORITY**:
|
||
Assign the date range that appears CLOSEST to and IMMEDIATELY BEFORE the reimbursement term.
|
||
- If the term is in a table, use the date range that introduces that specific table
|
||
- If the term is in a paragraph, use the date range mentioned in that paragraph
|
||
- DO NOT use date ranges from other tables or sections
|
||
|
||
3. **AVOID CROSS-SECTION CONTAMINATION**:
|
||
Contracts often have multiple sections with different date ranges:
|
||
|
||
"For services during January 16, 2018 through June 30, 2018:
|
||
[Table 1 with rates A, B, C]
|
||
|
||
For services during July 1, 2018 through June 30, 2019:
|
||
[Table 2 with rates D, E, F]"
|
||
|
||
→ Terms in Table 1 (A, B, C) get ONLY "January 16, 2018 through June 30, 2018"
|
||
→ Terms in Table 2 (D, E, F) get ONLY "July 1, 2018 through June 30, 2019"
|
||
→ DO NOT assign both date ranges to the same term
|
||
→ DO NOT assign dates from Table 2 to terms in Table 1
|
||
|
||
4. **RELATED TERMS IN SAME CONTEXT**:
|
||
If the reimbursement term is a follow-up or clarification to a previous term in the same section (e.g., "Cost Items Rate for codes with Status Indicator F4" following "Ambulatory Surgery Services Rate" in the same table), it shares the SAME date range as the other terms in that section.
|
||
|
||
5. **"SAME AS EFFECTIVE DATE"**:
|
||
If you see "Effective Date: Same as the Effective Date of the Agreement", return exactly:
|
||
"Same as the Effective Date of the Agreement"
|
||
|
||
6. **NO DATES IN SECTION**:
|
||
If no specific date range appears near this reimbursement term (not in the same paragraph or table section), return "N/A"
|
||
|
||
7. **DATE FORMAT PRIORITY**:
|
||
Always prioritize regular date formats over CY/FY formats.
|
||
- Regular formats: "July 1, 2018", "January 1, 2018 through December 31, 2018", etc.
|
||
- CY/FY formats (lower priority): "FY2018", "CY2019", "CY2018-CY2019", etc.
|
||
|
||
If a regular date exists in the section, use it. If ONLY CY/FY format is available, return "N/A".
|
||
|
||
[OUTPUT FORMAT]
|
||
First, identify:
|
||
1. What section or table is this reimbursement term in?
|
||
2. What date range introduces or applies to that specific section/table?
|
||
3. Are there other date ranges in different sections? (These should NOT be assigned to this term)
|
||
|
||
Then, return a properly-formatted JSON dictionary where the key is "REIMB_DATES" and the value is the correct date range for THIS SPECIFIC term.
|
||
Return ONLY the single date range that applies to this term. Do NOT return multiple date ranges separated by commas.
|
||
If there is no date range for this term, return "N/A".
|
||
{JSON_DICT_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def REIMB_DATES_ASSIGNMENT(
|
||
service_term: str,
|
||
reimb_term: str,
|
||
field_prompt: str,
|
||
exhibit_text_simplified: str,
|
||
page_num: str,
|
||
) -> Tuple[str, str, Callable[[str], dict]]:
|
||
"""Returns dynamic content for REIMB_DATES assignment.
|
||
Call REIMB_DATES_ASSIGNMENT_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Args:
|
||
service_term: Service description
|
||
reimb_term: Reimbursement term text
|
||
field_prompt: Prompt/definition for REIMB_DATES field
|
||
exhibit_text_simplified: Simplified exhibit text
|
||
page_num: Page number
|
||
|
||
Returns:
|
||
Tuple of (context_text, prompt_string, parser_function).
|
||
"""
|
||
context_text = f"""[CONTEXT]
|
||
Here is the section of the contract. Examine this section closely and identify which date range applies to the given Reimbursement Term.
|
||
[START CONTEXT]
|
||
{exhibit_text_simplified}
|
||
[END CONTEXT]"""
|
||
|
||
question_text = f"""[REIMB_DATES FIELD DEFINITION]
|
||
{field_prompt}
|
||
|
||
[REIMBURSEMENT TERM TO ANALYZE]
|
||
This is the term you need to find the date range for. It appears on page {page_num} of the text.
|
||
Service Term: "{service_term}"
|
||
Reimbursement Term: "{reimb_term}" """
|
||
|
||
# Create parser with REIMB_DATES field name bound for format-aware normalization
|
||
parser = _create_json_dict_parser(field_names=["REIMB_DATES"])
|
||
return (context_text, question_text, parser)
|
||
|
||
|
||
def DYNAMIC_ASSIGNMENT_INSTRUCTION() -> str:
|
||
return f"""[OBJECTIVE]
|
||
Identify which field value is associated with a given Reimbursement Term.
|
||
|
||
[INSTRUCTIONS]
|
||
You will be shown a Reimbursement Term, and the section of a contract from which that Reimbursement Term was pulled from.
|
||
Closely examine the contract text, find the Reimbursement Term, and decide which field value or values apply to the Reimbursement Term.
|
||
If there are multiple values, return them as a list (array) in the JSON dictionary value.
|
||
|
||
CRITICAL CONSIDERATIONS:
|
||
1. EXHIBIT HEADER CONTEXT - Pay close attention to exhibit headers (e.g., "MEDICARE PRODUCT ATTACHMENT") that appear at the beginning of the exhibit section. These headers indicate the scope and context for ALL reimbursement terms within that exhibit. If an exhibit header clearly specifies a line of business (e.g., "MEDICARE PRODUCT ATTACHMENT"), assign values consistent with that header (e.g., "Medicare" for LOB) for all terms in that exhibit. The exhibit header value should be ADDITIVE - it should be included as one of multiple values when other sources (service term, reimbursement term, or subsection context) also provide relevant values. For example, if the exhibit header indicates "Medicare" and the service term or subsection context indicates "Duals", the final assignment should be ["Medicare", "Duals"] (combining both values in a JSON list). The exhibit header value is always included and does not override other valid values.
|
||
|
||
2. SUBSECTION CONTEXT - Examine the exhibit text for the subsection header, introductory paragraph, or section introduction that is CLOSEST to the reimbursement term.
|
||
|
||
RELATIONSHIP HIERARCHY:
|
||
- The exhibit header (instruction #1) is ALWAYS additive - it is included in the final assignment.
|
||
- The subsection context is ADDITIVE with respect to the exhibit header (combines with it).
|
||
- The subsection context OVERRIDES conflicting information in the reimbursement term or service term.
|
||
- The reimbursement term (instruction #3) takes LEAST precedence when exhibit header and subsection context provide clear answers.
|
||
|
||
STRUCTURAL BOUNDARY RULE - FOLLOW THESE STEPS:
|
||
1. Identify the structural element containing the reimbursement term (e.g., "Table 1", "Table 2").
|
||
2. Trace backwards from that element to find the MOST RECENT section marker (e.g., "4.", "3.", "Section A", numbered items like "1.", "2.", "3.").
|
||
3. If you find a section marker, that creates a HARD BOUNDARY - subsection context from BEFORE that marker is COMPLETELY INVALIDATED and must be ignored, even if it mentioned the table.
|
||
4. Only consider subsection context that appears AFTER the most recent section marker and BEFORE the structural element.
|
||
5. If no section marker is found, then use the closest subsection context before the structural element.
|
||
|
||
Section markers (numbered items like "1.", "2.", "3.", "4.") create hard boundaries. Example: If "4. Allowable Charges...for any Medicare inpatient admission" appears before "Table 2", you MUST use ONLY context from section 4. Completely ignore subsection context from earlier sections (like "For Covered Services rendered to Covered Person who are eligible for Medicare and enrolled in a Medicare Plan that may include coverage for both Medicare and Medicaid") even if that earlier text mentioned "Table 2" - the section 4 marker invalidates all earlier context.
|
||
|
||
When a relevant subsection context is found WITHIN THE SAME structural boundary with extremely clear wording about eligibility or program coverage, it should:
|
||
- ADD to the exhibit header value (e.g., exhibit header "Medicare" + subsection "both Medicare and Medicaid" = ["Medicare", "Duals"])
|
||
- OVERRIDE conflicting information in the service term or reimbursement term (e.g., if subsection says "Duals" but service term says "Medicaid only", assign ["Duals"] based on subsection)
|
||
|
||
3. REIMBURSEMENT TERM CONTEXT - Examine the Reimbursement Term itself for context about what value should be assigned to the field. This is a broad instruction to consider the reimbursement term as a primary source of information. However, if the exhibit header (instruction #1) and nearest subsection context (instruction #2) provide clear answers, the reimbursement term context takes LEAST precedence. The reimbursement term context should be considered in addition to the context provided by the exhibit header and subsection context, but should not override them when they are clear and explicit.
|
||
|
||
4. Look for what field the Service actually *applies to*. This is not the same as the Schedule used as a basis for calculation. For example, many LOBs use Medicaid or Medicare as a basis for their calculation. The presence of 'Medicaid' or 'Medicare' as a rate does not on it's own guarantee that the Reimbursement Term is 'Medicaid' or 'Medicare'.
|
||
|
||
5. DUALS VALUE FORMATTING: If the answer appears to be a combination like Medicare with Duals or Medicaid with Duals, return them as a JSON list with both values (e.g., ["Medicare", "Duals"] or ["Medicaid", "Duals"]) - including both values adds important context. Only return ["Duals"] alone when it is obvious that Duals alone is sufficient and no additional context is needed.
|
||
|
||
[PROGRAM FIELD SPECIFIC INSTRUCTIONS - Instructions 6-9 below are PRIMARILY for PROGRAM field assignment, though they may loosely apply to other fields. For LOB field assignment, prioritize instructions 1-5 above these instructions.]
|
||
|
||
6. USE THE SERVICE_TERM AS CONTEXT: The Service Term shows which programs are mentioned together. If the Service Term explicitly lists specific programs (e.g., "STAR, CHIP HMO, CHIP PERINATE, and STAR+PLUS"), those are the programs that apply to reimbursement terms in that section. Do NOT assign programs that are mentioned in completely separate sections of the contract.
|
||
|
||
7. LOOK FOR PROXIMITY: Programs must be explicitly mentioned in the SAME sentence or paragraph as the Reimbursement Term. If a reimbursement term appears in a section that lists specific programs (e.g., "STAR, CHIP HMO, CHIP PERINATE, and STAR+PLUS: Covered Services..."), only assign those programs, NOT programs mentioned in separate sections (e.g., "Medicare Advantage (Molina Medicare Options) and MA-SNP...").
|
||
|
||
8. AVOID CROSS-CONTAMINATION: If the Service Term mentions Medicaid programs (STAR, CHIP, CHIPP, STAR+PLUS, etc.), do NOT assign Medicare programs (MA, MASNP) unless they are explicitly mentioned together in the same context. Similarly, if the Service Term mentions Medicare programs, do NOT assign Medicaid programs unless explicitly mentioned together.
|
||
|
||
9. "ALL PROGRAMS" AMBIGUITY - CRITICAL: If you see phrases like "for all Health Plan products & programs" in a table or section, this phrase means "all programs in THIS SECTION", NOT "all programs in the entire document".
|
||
- If the table appears AFTER a section that says "STAR, CHIP HMO, CHIP PERINATE, and STAR+PLUS" and BEFORE a section that says "Medicare Advantage", the table applies ONLY to the Medicaid programs (STAR, CHIP, CHIPP, STAR+PLUS) mentioned in that section.
|
||
- If the table appears AFTER a section that says "Medicare Advantage", the table applies ONLY to the Medicare programs mentioned in that section.
|
||
- NEVER assign programs from separate sections just because you see "all programs" - always determine which section the reimbursement term belongs to based on its position in the document.
|
||
|
||
[OUTPUT FORMAT]
|
||
Briefly explain your answer, then return a properly-formatted JSON dictionary where the key is the field name and the value is the answer.
|
||
If there are multiple correct answers, return them as a list (array) within the dictionary value.
|
||
{JSON_DICT_WITH_LISTS_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def DYNAMIC_ASSIGNMENT(
|
||
service_term: str,
|
||
reimb_term: str,
|
||
field_name: str,
|
||
field_prompt: str,
|
||
exhibit_text_simplified: str,
|
||
page_num: str,
|
||
) -> Tuple[str, str, Callable[[str], dict]]:
|
||
"""
|
||
Returns prompt for dynamic assignment extraction.
|
||
Call DYNAMIC_ASSIGNMENT_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Args:
|
||
service_term: Service description
|
||
reimb_term: Reimbursement term text
|
||
field_name: Name of the field being assigned (used for format-aware normalization)
|
||
field_prompt: Prompt/definition for the field
|
||
exhibit_text_simplified: Simplified exhibit text
|
||
page_num: Page number
|
||
|
||
Returns:
|
||
Tuple of (context_text, prompt_string, parser_function).
|
||
"""
|
||
context_text = f"""[CONTEXT]
|
||
Here is the section of the contract. Examine this section closely and pick the correct answer or answers for the given Reimbursement Term.
|
||
[START CONTEXT]
|
||
{exhibit_text_simplified}
|
||
[END CONTEXT]"""
|
||
|
||
question_text = f"""[REIMBURSEMENT_TERM]
|
||
Here is the Reimbursement Term to analyze. You can find it on page {page_num} of the text.
|
||
Service Term: "{service_term}"
|
||
Reimbursement Term: "{reimb_term}"
|
||
|
||
[{field_name} INFORMATION]
|
||
Here is the definition and valid values. Use this information to help you find the right answer.
|
||
{field_name} : {field_prompt}"""
|
||
|
||
# Create parser with field_name bound for format-aware normalization
|
||
# The dict will contain {field_name: value}, so we need to normalize that field
|
||
parser = _create_json_dict_parser(field_names=[field_name])
|
||
return (context_text, question_text, parser)
|
||
|
||
|
||
def REIMBURSEMENT_PRIMARY(context) -> Tuple[str, Callable[[str], list]]:
|
||
"""
|
||
Returns prompt for reimbursement primary extraction.
|
||
Call REIMBURSEMENT_PRIMARY_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON list output. Should return a list of dicts of strings
|
||
"""
|
||
prompt = f"""
|
||
[START CONTEXT]
|
||
{context.replace('"', "'")}
|
||
[END CONTEXT]
|
||
|
||
Briefly talk through your reasoning. Then return a properly formatted JSON list of dictionaries with all extracted SERVICE_TERM AND REIMB_TERM values.
|
||
"""
|
||
|
||
# Use helper function to create parser for list[dict[str,str]]
|
||
parser = _create_json_list_parser(
|
||
field_name=None, expected_format="list[dict[str,str]]"
|
||
)
|
||
return (prompt, parser)
|
||
|
||
|
||
def REIMBURSEMENT_PRIMARY_INSTRUCTION() -> str:
|
||
return f"""[OBJECTIVE]
|
||
Extract Reimbursement Terms from a section of a payer-provider contract
|
||
The output from this prompt will be used to populate downstream fields in the system.
|
||
|
||
[REIMBURSEMENT TERMS]
|
||
A Reimbursement Term is a statement in the contract describing how a certain Service or class of Services should be paid. Reimbursement Terms ALWAYS consist of two parts:
|
||
SERVICE_TERM: Describes the medical service or procedure that is being reimbursed. It can be very general (like 'Covered Services') or very specific (like 'CPT 98765').
|
||
REIMB_TERM: Describes the method by which the price of the Service is calculated. It can be a simple dollar value (like '$100 per unit') or a percentage of some other value (like '100% of the Medicare Fee Schedule').
|
||
|
||
[IMPORTANT INSTRUCTIONS]
|
||
- Number one rule: ERR ON THE SIDE OF INCLUSION! Return whatever relevant information is available.
|
||
- Ensure that the COMPLETE REIMB_TERM values are captured EXACTLY as they are written in the contract. Return only EXACT word-for-word text from the contract.
|
||
- Sometimes, the SERVICE_TERM may not be explicitly written and instead simply implied. When this is the case, default the SERVICE_TERM to "Covered Services". If needed, add additional relevant detail (e.g. "Inpatient Covered Services", "Hospital Covered Services", etc.)
|
||
- NEVER return a partial sentence - include the entire sentence even if only part of the sentence is necessary.
|
||
- It's ok if the SERVICE_TERM shows up in the REIMB_TERM. Prioritize maintaining complete sentences.
|
||
- Do not leave out any relevant information from either the SERVICE_TERM or REIMB_TERM even if it is not present in the vicinity. e.g. answer "$100 per visit" rather than just "$100". If table headers OR table rows provide ANY relevant information (like code type, provider type, hospital name, provider name, Tax ID, legal name, etc), include that in the SERVICE_TERM or REIMB_TERM as appropriate.
|
||
- When a unit of measure (e.g., "PMPM", "per member per month", "per visit", "per diem", "per case") appears in a table header, column label, or surrounding context, always append it to the REIMB_TERM for each rate in that table. For example, if the header says "Payment PMPM" and a row shows "$31.51", the REIMB_TERM should be "$31.51 PMPM", not just "$31.51".
|
||
- Include all SERVICE_TERM and REIMB_TERM pairs mentioned, even if they are redundant or overlapping. There should be at least one entry for each SERVICE_TERM and REIMB_TERM pair mentioned.
|
||
- If different reimbursement percentages or dollar values are mentioned for different effective dates, create separate entries for each effective date.
|
||
- For the tables: Extract EVERY ROW as a separate entry, including all line items, subtotals, totals, adjustments, charges, calculated ratios, and all relevant information from every column in that row. ALWAYS include column headers and subheaders as part of the SERVICE_TERM to provide full context for the service, forming a complete sentence if necessary. If a row contains multiple reimbursement rate types (e.g., different rate columns such as facility vs non-facility, professional vs technical, inpatient vs outpatient, etc.), create separate entries for EACH rate type using the corresponding rate column header in the SERVICE_TERM. When rows involve different providers/entities, create separate entries for EACH provider/entity and include the provider/entity name in the SERVICE_TERM (e.g., "Provider Name – Service Category").
|
||
- If a reimbursement statement appears either BEFORE or AFTER the table and applies to all listed codes (e.g., "for all codes listed below" or "for all codes listed above" or "codes identified below"), then:
|
||
• Extract each table row as a separate entry, AND
|
||
• Apply that statement to EVERY row by incorporating it into the REIMB_TERM for each entry.
|
||
• Ensure the REIMB_TERM for each row reflects this shared reimbursement language exactly as written.
|
||
Example: If the statement says "For all codes listed below, reimbursement is 100% of Medicare," then each row must have REIMB_TERM = "100% of Medicare".
|
||
- GROUPING RULE: If multiple services are explicitly listed together (e.g., "Reference Lab and DME") AND share the same rate statement, extract as ONE entry preserving the exact combined SERVICE_TERM. Do not split them.
|
||
- SEPARATION RULE: If services appear in separate sentences or have different reimbursement methods, extract as separate entries.
|
||
- ENUMERATED SUB-CASES: When a paragraph introduces a service category and then lists sub-cases (i, ii, iii or a, b, c or 1, 2, 3) with different rates, extract each sub-case as a separate entry. For SERVICE_TERM, use the overall service description with only a brief sub-case label (e.g., "primary procedure", "second and subsequent procedures") - do NOT include billing logic, conditional clauses, or payment-determination language in the SERVICE_TERM. For REIMB_TERM, extract only the payment method (rate, percentage, dollar amount) - do NOT include the conditional clause that precedes it.
|
||
- Examples:
|
||
• "Lab and Imaging Services: 110% of Medicare" → ONE entry with SERVICE_TERM "Lab and Imaging Services"
|
||
• "Emergency Services: $500 per visit" → ONE separate entry
|
||
• "Surgical Services: 150% of Medicare" → ONE separate entry
|
||
|
||
[EXCLUSIONS]
|
||
- NEVER return reimbursement terms that are labeled as Examples,appear in an EXAMPLE section or presented as the sample calculations. These are not actual reimbursements and should not be included in the output.
|
||
- NEVER return reimbursements from the Reciprocity Agreements section of the contract. These are not actual reimbursements and should not be included in the output.
|
||
|
||
[SPECIAL CASES]
|
||
Some types of Reimbursements are special and require additional thought.
|
||
- Stop Loss and Outliers - These reimbursements usually consist of multiple rates and/or thresholds. When a Stop-Loss or Outlier is found, include the entire statement (which may be multiple sentences) as a single REIMB_TERM.
|
||
- Discounts, Escalators and Premiums - These will also include multiple rates. Capture all relevant numbers
|
||
|
||
[OUTPUT FORMAT]
|
||
You MUST return your answer as a JSON list of dictionaries. Each dictionary must contain exactly two keys: "SERVICE_TERM" and "REIMB_TERM". Both values must be strings (not lists).
|
||
|
||
Example output format:
|
||
[
|
||
{{"SERVICE_TERM": "Service 1", "REIMB_TERM": "Reimb 1"}},
|
||
{{"SERVICE_TERM": "Service 2", "REIMB_TERM": "Reimb 2"}}
|
||
]
|
||
|
||
{JSON_LIST_OF_DICTS_FORMAT_INSTRUCTIONS}
|
||
|
||
[ANALYSIS CONTEXT]"""
|
||
|
||
|
||
def LESSER_OF_DISTRIBUTION_INSTRUCTION() -> str:
|
||
return f"""TASK: Identify "lesser of" or "not to exceed" statements that apply to a service.
|
||
|
||
DEFINITIONS:
|
||
"Lesser of" / "not to exceed" = overarching payment rules specifying payment at the lesser of multiple values or capped amounts.
|
||
Examples: "lesser of rates below and billed charges", "shall not exceed 110% of Medicare"
|
||
|
||
DECISION LOGIC:
|
||
|
||
0. UNDERSTAND THE TASK
|
||
|
||
We are looking for overarching "lesser of" or "not to exceed" constraints that apply to SERVICE.
|
||
METHODOLOGY is the base rate - we may wrap it with constraints we find.
|
||
|
||
1. COLLECT APPLICABLE STATEMENTS
|
||
|
||
From cross-exhibit templates (if provided):
|
||
→ Include these-they're already verified to apply.
|
||
|
||
From intra-exhibit text:
|
||
→ INCLUDE statements referencing "this exhibit", "rates below", "contracted rates" (generic)
|
||
→ EXCLUDE any statement that references a specific exhibit name (Attachment A, Schedule B, Exhibit 2, etc.)
|
||
|
||
CRITICAL: If a lesser-of statement mentions "Attachment A", "Schedule B", or any exhibit name → IGNORE IT.
|
||
Example to IGNORE: "lesser of payment rates established in Attachment A or 100% of Medicare"
|
||
Example to USE: "lesser of contracted rates or billed charges"
|
||
|
||
2. FILTER BY SERVICE SCOPE
|
||
|
||
Keep only statements whose scope matches SERVICE:
|
||
- "All services in this exhibit" / "All services" / "All covered services" → applies to any service
|
||
- "Inpatient services only" → does NOT apply to outpatient services
|
||
- Lesser-of statements containing "listed below", "rates below", "set forth below"
|
||
→ applies to ANY service rate that appears after that statement in the exhibit text,
|
||
regardless of section headers
|
||
|
||
EXCEPTION CLAUSE HANDLING:
|
||
- If a lesser-of statement has an exception clause (e.g., "except for services in Table X",
|
||
"excluding procedures listed below"), and SERVICE appears in that exception list:
|
||
|
||
a) The ENTIRE payment rule in that sentence/paragraph does NOT apply to SERVICE.
|
||
This includes all comparisons, caps, and conditions within that grammatical unit.
|
||
|
||
b) ONLY overarching constraints still apply to SERVICE.
|
||
Test: Can this constraint be read independently without referencing the excepted statement?
|
||
If YES → it still applies
|
||
If NO → it's part of the excepted rule and does NOT apply
|
||
|
||
c) ALWAYS re-scan the exhibit text for standalone constraints after excluding the payment rule.
|
||
Do not stop at step (a) - even if the main rule is excluded, independent constraints MUST be applied.
|
||
|
||
Examples:
|
||
- SEPARATE sentence: "In no event shall reimbursement exceed the amount billed by Provider."
|
||
→ Still applies (passes independence test - standalone constraint)
|
||
- SAME sentence: "Compensation shall be the lesser of 80% of the scheduled fee or usual
|
||
and customary charges, capped at the Provider's submitted amount"
|
||
→ Does NOT apply (entire grammatical unit is the excepted rule)
|
||
|
||
If nothing applies → return N/A
|
||
|
||
CONDITIONAL METHODOLOGY RULE:
|
||
-If a “lesser of” or “not to exceed” statement:
|
||
→Applies only under a specific condition (e.g., “where there is no rate,” “if not listed,” “when no fee schedule exists”), and
|
||
→Defines an alternative reimbursement method rather than limiting the base METHODOLOGY,
|
||
-Treat it as a separate reimbursement methodology.
|
||
-Do NOT apply it as a constraint to the primary METHODOLOGY.
|
||
-Do NOT merge it into the output.
|
||
-Only include “lesser of” or “not to exceed” language when it operates as an overarching ceiling that applies to the base METHODOLOGY itself.
|
||
|
||
3. COMBINE & FORMAT
|
||
|
||
Substitution:
|
||
- Replace generic "rates" / "contracted rates" with METHODOLOGY value
|
||
- Preserve original phrasing exactly - do not paraphrase, abbreviate, or alter wording
|
||
- Do not include service code or description in the output
|
||
- Deduplicate identical constraints
|
||
- Join multiple terms preserving meaning through nesting or other structures: e.g., "[the lesser of [the greater of A or B] and C]", "lesser of X, Y, or Z". Ensure brackets and nesting are used appropriately to maintain the original meaning.
|
||
|
||
Validation:
|
||
- The output string MUST contain METHODOLOGY value
|
||
- If METHODOLOGY is a simple rate (e.g., "$30.00") and is missing from the final string, prepend it
|
||
- If METHODOLOGY is a complex formula (e.g., "80% of State Medicaid fee schedule", "the lesser of (i) X or (ii) Y"), verify it appears as the base of the output
|
||
- The output MUST contain the FULL original METHODOLOGY - every rate, tier, and condition.
|
||
The lesser-of constraint wraps METHODOLOGY as an outer ceiling; it never replaces or summarizes it.
|
||
Before returning, verify METHODOLOGY appears intact. If any part is missing, re-nest:
|
||
"the lesser of [full METHODOLOGY] or [constraint ceiling]"
|
||
|
||
Examples:
|
||
- ["lesser of $100, billed charges, or 110% Medicare"]
|
||
- ["$30.00 not to exceed Provider's billed charges"]
|
||
- ["the lesser of [the greater of 100% of the State Medicaid fee schedule or Affinity's standard fee schedule] and billed charges"]
|
||
- ["N/A"]
|
||
|
||
Example - Intra-exhibit statement references another exhibit → N/A:
|
||
- METHODOLOGY: "per visit basis at payment rates established in Attachment A"
|
||
- CROSS-EXHIBIT TEMPLATES: None
|
||
- EXHIBIT TEXT contains: "lesser of payment rates established in Attachment A or 100% of Medicare"
|
||
- Analysis: The only lesser-of statement found references "Attachment A" → IGNORE IT
|
||
- Result: ["N/A"] (no applicable statements remain)
|
||
|
||
RETURN ["N/A"] WHEN:
|
||
- No applicable lesser-of statements found
|
||
- All statements found reference other exhibits by name (and no cross-exhibit templates provided)
|
||
- Statements found but scoped to different service types
|
||
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def LESSER_OF_DISTRIBUTION(
|
||
service_term: str,
|
||
reimb_term: str,
|
||
page_num: str,
|
||
exhibit_text: str,
|
||
cross_exhibit_lesser_of: list[dict],
|
||
) -> Tuple[str, str, Callable[[str], list]]:
|
||
"""
|
||
Returns prompt for lesser-of distribution.
|
||
Call LESSER_OF_DISTRIBUTION_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Returns:
|
||
Tuple of (context_text, prompt_string, parser_function).
|
||
"""
|
||
context_text = f"""EXHIBIT TEXT:
|
||
{exhibit_text}"""
|
||
|
||
# Format cross-exhibit templates
|
||
if cross_exhibit_lesser_of:
|
||
cross_exhibit_text = "\n".join(
|
||
[
|
||
f"- {stmt['reimb_term']} (scope: {stmt['scope']}, page {stmt.get('page_num', 'unknown')})"
|
||
for stmt in cross_exhibit_lesser_of
|
||
]
|
||
)
|
||
cross_exhibit_section = f"""
|
||
CROSS-EXHIBIT TEMPLATES (pre-verified, include if applicable):
|
||
{cross_exhibit_text}
|
||
"""
|
||
else:
|
||
cross_exhibit_section = "CROSS-EXHIBIT TEMPLATES: None"
|
||
|
||
inputs_text = f"""INPUTS:
|
||
- SERVICE: {service_term}
|
||
- METHODOLOGY: {reimb_term}
|
||
- PAGE: {page_num}
|
||
- {cross_exhibit_section}
|
||
|
||
---
|
||
|
||
Analyze the inputs above. Briefly explain your reasoning, then provide your final answer as a JSON list.
|
||
"""
|
||
|
||
return (context_text, inputs_text, _json_list_parser)
|
||
|
||
|
||
def LESSER_OF_CHECK_INSTRUCTION() -> str:
|
||
return f"""Classify lesser-of payment statements from healthcare contracts.
|
||
|
||
**IMPORTANT: Check BOTH Service AND Reimbursement fields for exhibit references.**
|
||
|
||
**Classification Rules (check in order):**
|
||
|
||
1. **EXHIBIT_SPECIFIC (OTHER)** - References a different specific exhibit
|
||
- Check BOTH fields for: "contained in [Exhibit]", "in Attachment [X]", "for [Schedule] services", "described in [Appendix]"
|
||
- Extract exact exhibit name → exhibit_reference
|
||
- NOTE: External fee schedule references (e.g., "Company's fee schedule", "Payer's fee schedule", "Provider's fee schedule") are reimbursement methodologies, NOT exhibit references
|
||
|
||
2. **EXHIBIT_SPECIFIC (CURRENT)** - References only this exhibit
|
||
- Phrases: "this exhibit", "rates below", "listed below", "set forth below", "in this schedule"
|
||
- These are templates/preambles that apply to rates defined within the same exhibit
|
||
- NOT standalone rates-they reference other rates rather than being complete themselves
|
||
|
||
3. **STANDALONE** - A complete, self-contained reimbursement rate
|
||
- This IS a rate (a fee schedule entry, a line item)
|
||
- Has BOTH service identification AND complete reimbursement methodology
|
||
- Answers the question: "How much do we pay for X?"
|
||
- Would make sense as one row in a rate table alongside other rates
|
||
- Can be specific ("MRI", "CPT 70450") OR broad ("Covered Services", "All Services")
|
||
- Even if it says "all services" - if it's functioning as a single rate entry within an exhibit, it's STANDALONE
|
||
- No external exhibit/attachment references
|
||
- Must NOT contain "listed below", "rates below", "set forth below" (these indicate a template, not a complete rate)
|
||
|
||
4. **GLOBAL** - A contract-wide constraint that governs ALL other rates
|
||
- This is a RULE ABOUT rates, not a rate itself
|
||
- Applies across the ENTIRE contract (all exhibits, all services, all fee schedules)
|
||
- Answers the question: "What ceiling/constraint applies to every payment we make?"
|
||
- Would NOT make sense as a row in a rate table - it's a meta-rule governing the table
|
||
- Typically found in general terms, master agreement sections, or contract-wide provisions
|
||
- NO exhibit/attachment/schedule mentions in EITHER field
|
||
|
||
**Examples:**
|
||
|
||
Service: "services described in Attachment B - Facility Services"
|
||
Reimbursement: "payment shall be at lesser of contracted rates or 100% of billed charges"
|
||
→ EXHIBIT_SPECIFIC (OTHER), exhibit_reference: "Attachment B - Facility Services"
|
||
|
||
Service: "All services rendered under this Agreement"
|
||
Reimbursement: "In no event shall payment exceed the lesser of billed charges or the contracted rate"
|
||
→ GLOBAL - this is a contract-wide ceiling/constraint, not a rate itself; it modifies how all other rates are applied
|
||
|
||
Service: "All claims submitted to Payer"
|
||
Reimbursement: "Notwithstanding any rates set forth in the attached Exhibits, payment shall not exceed the lesser of billed charges or 100% of Medicare"
|
||
→ GLOBAL - → GLOBAL - "Notwithstanding...attached Exhibits" indicates this constrains rates defined elsewhere
|
||
|
||
Service: "Services"
|
||
Reimbursement: "Services in this exhibit: lesser of rates below or Medicare"
|
||
→ EXHIBIT_SPECIFIC (CURRENT)
|
||
|
||
Service: "MRI"
|
||
Reimbursement: "lesser of $1000 or Medicare"
|
||
→ STANDALONE (specific service + complete rate)
|
||
|
||
Service: "emergency room services Covered Services"
|
||
Reimbursement: "lesser of Allowable Charges or 100% of Medicare fee schedule"
|
||
→ STANDALONE (complete rate)
|
||
|
||
Service: "Medicare Product Covered Services"
|
||
Reimbursement: "lesser of Allowable Charges or Company's fee schedule in effect on date of service"
|
||
→ STANDALONE - "Company's fee schedule" is an external reimbursement benchmark, not a contract exhibit reference
|
||
|
||
**CRITICAL GUIDANCE:**
|
||
- GLOBAL is rare. Most contracts will have STANDALONE or EXHIBIT_SPECIFIC entries.
|
||
- The presence of "all services" or "covered services" does NOT automatically mean GLOBAL.
|
||
- GLOBAL must be a constraint that governs OTHER rates across the contract, not a rate itself.
|
||
- When in doubt between STANDALONE and GLOBAL, ask: "Is this a rate, or a rule about rates?" If it's a rate, choose STANDALONE.
|
||
|
||
**OUTPUT FORMAT:**
|
||
Briefly explain your reasoning, then return a properly-formatted JSON dictionary with the following structure:
|
||
{{
|
||
"service_term": "<service term>",
|
||
"reimb_term": "<reimbursement term>",
|
||
"scope": "<one of: GLOBAL, EXHIBIT_SPECIFIC, STANDALONE>",
|
||
"target_exhibit": "<one of: ALL, CURRENT, OTHER, or null>",
|
||
"exhibit_reference": "<exhibit name if OTHER, else null>"
|
||
}}
|
||
|
||
{JSON_DICT_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def LESSER_OF_CHECK(
|
||
service_term: str,
|
||
reimb_term: str,
|
||
exhibit_title: str,
|
||
) -> Tuple[str, str, Callable[[str], dict]]:
|
||
"""
|
||
Returns prompt for lesser-of check classification.
|
||
Call LESSER_OF_CHECK_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Returns:
|
||
Tuple of (context_text, prompt_string, parser_function).
|
||
"""
|
||
context_text = f"""**Current Exhibit:** {exhibit_title}"""
|
||
|
||
question_text = f"""**Service:** {service_term}
|
||
**Reimbursement:** {reimb_term}"""
|
||
|
||
return (context_text, question_text, _json_dict_parser)
|
||
|
||
|
||
def EXHIBIT_TITLE_MATCH_INSTRUCTION() -> str:
|
||
"""Static instruction for EXHIBIT_TITLE_MATCH prompt caching.
|
||
Contains all matching rules and examples.
|
||
"""
|
||
return f"""[OBJECTIVE]
|
||
Determine if two exhibit references refer to the same exhibit.
|
||
|
||
[MATCHING RULES]
|
||
Consider the following when matching:
|
||
- Exact matches: "Exhibit B" matches "Exhibit B"
|
||
- Partial matches: "Exhibit B" matches "Exhibit B - Outpatient Services"
|
||
- Descriptive matches: "Outpatient Services" matches "Exhibit B - Outpatient Services"
|
||
- Different identifiers: "Exhibit A" does NOT match "Exhibit B"
|
||
- Different services: "Inpatient Services" does NOT match "Outpatient Services"
|
||
|
||
[OUTPUT FORMAT]
|
||
Briefly explain your reasoning, then return ONLY "YES" or "NO" as a single item in a JSON list.
|
||
Examples: ["YES"] or ["NO"]
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def EXHIBIT_TITLE_MATCH(
|
||
current_exhibit_title: str, target_exhibit_reference: str
|
||
) -> Tuple[str, Callable[[str], list]]:
|
||
"""Returns ONLY dynamic content for exhibit title matching.
|
||
Call EXHIBIT_TITLE_MATCH_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON list output.
|
||
"""
|
||
prompt = f"""[CONTEXT]
|
||
Current Exhibit Title: "{current_exhibit_title}"
|
||
Target Exhibit Reference: "{target_exhibit_reference}" """
|
||
|
||
return (prompt, _json_list_parser)
|
||
|
||
|
||
#####################################################################################
|
||
################################# BREAKOUT PROMPTS ##################################
|
||
#####################################################################################
|
||
|
||
|
||
def METHODOLOGY_BREAKOUT(
|
||
service_term: str, reimb_term: str
|
||
) -> Tuple[str, Callable[[str], list]]:
|
||
"""
|
||
Returns ONLY dynamic content for methodology breakout extraction.
|
||
Call METHODOLOGY_BREAKOUT_INSTRUCTION() separately for the cached instruction.
|
||
|
||
All fields run by this prompt will end up becoming their own row, rather than being distributed across the rows of the exhibit.
|
||
Note: questions/fields are now included in METHODOLOGY_BREAKOUT_INSTRUCTION() for caching.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON list output.
|
||
"""
|
||
prompt = f"""Here is the text to analyze and respond to:
|
||
Service Term: {service_term}
|
||
Reimbursement Term: {reimb_term}
|
||
|
||
Explain your reasoning as you work through the context. After your explanation, include the heading "FINAL REIMBURSEMENT METHODOLOGY:" followed by a properly formatted JSON list with your final output."""
|
||
|
||
# Use helper function to create parser for list[dict[str,str]]
|
||
parser = _create_json_list_parser(
|
||
field_name=None, expected_format="list[dict[str,str]]"
|
||
)
|
||
return (prompt, parser)
|
||
|
||
|
||
def METHODOLOGY_BREAKOUT_INSTRUCTION() -> str:
|
||
"""Static instruction for METHODOLOGY_BREAKOUT prompt caching.
|
||
Contains all extraction rules, field definitions with resolved valid_values.
|
||
Field definitions are loaded from investment_prompts.json.
|
||
"""
|
||
# Load fields from investment_prompts.json with resolved valid_values
|
||
fields_text = _get_fields_text("methodology_breakout")
|
||
|
||
return f"""[OBJECTIVE]
|
||
Analyze a given term from a Payer-Provider contract and extract key fields.
|
||
|
||
[INSTRUCTION]
|
||
- Always identify **all enforceable reimbursement clauses**, whether they appear as standalone sentences or within a comparative structure.
|
||
- When a reimbursement methodology includes **multiple enforceable limits**-for example, "100% of billed charges not to exceed $500"-treat **each distinct limit** as a separate reimbursement methodology. Output one dictionary for "100% of billed charges" and another for "$500 cap".
|
||
- If a clause includes a **"lesser of" (or similar comparative)** structure:
|
||
- Carefully extract **every individual reimbursement clause within the comparison**.
|
||
- For example: "Lesser of (i) 80% of billed charges, (ii) Medicare rate, or (iii) $400" should yield **three separate JSON dictionaries**.
|
||
- **Do not skip any clause**. Every item compared under "lesser of", "greater of", "equal to", or similar comparative logic must be represented as its own dictionary in the output list.
|
||
- If the comparative structure includes nested comparisons (e.g., "the lesser of [the greater of A or B] and C"), each one of A,B and C must be extracted as its own dictionary. LESSER_OF and GREATER_OF are determined only by the immediate parent comparison. So even if it is part of outer LESSER_OF comparison, LESSER_OF should be "N" if GREATER_OF is "Y". In the case of the above example, dictionaries A and B should have GREATER_OF as "Y" and LESSER_OF as "N". Dictionary C should have LESSER_OF as "Y" and GREATER_OF as "N".
|
||
- **IMPORTANT**: For each component, classify the `AARETE_DERIVED_REIMB_METHOD` based on the INDIVIDUAL component, not the overall structure.
|
||
- If the methodology includes **additional reimbursement terms** beyond the "lesser of" structure (e.g., per diem charges, or carve-out payments):
|
||
- **These must also be extracted as separate JSON dictionaries** in the same list.
|
||
- These are often found in follow-on sentences or clauses and may not be part of a comparative limit but are still enforceable and reimbursable.
|
||
- For each dictionary, only one of the following fields should be present: REIMB_FEE_RATE, REIMB_PCT_RATE, or REIMB_CONVERSION_FACTOR. The others should be "N/A".
|
||
- Do not repeat the same reimbursement information in different output entries. For example, if 'cost of vaccines plus a $5 administration fee' is present and $5 is already captured as an addition, do not create a separate dictionary with $5 as a flat fee. Include it only once as an addition.
|
||
- For direct calculations like a flat fee of "$150.00 + $8.00", calculate the total ($158.00) and include only this flat fee rate in one dictionary.
|
||
- When multiple rates are expressed in different time units and are mathematically equivalent via a standard time conversion (e.g., "$50 per 30 min" and "$100 per hour" where $50 × 2 = $100), treat them as a single reimbursement methodology. Extract only the most granular (smallest) time unit. If the values are not mathematically equivalent, extract each as a separate dictionary.
|
||
If AARETE_DERIVED_REIMB_METHOD is Grouper, the dollar amount refers to REIMB_CONVERSION_FACTOR. If AARETE_DERIVED_REIMB_METHOD is Flat Rate or similar, the dollar amount refers to a REIMB_FEE_RATE.
|
||
|
||
[FIELDS]
|
||
Populate a list of JSON dictionaries with the following fields:
|
||
{fields_text}
|
||
|
||
[OUTPUT FORMAT]
|
||
The output should always be a JSON list/array of dictionaries, even when there is only one methodology.
|
||
{JSON_LIST_OF_DICTS_FORMAT_INSTRUCTIONS}
|
||
|
||
[CONTEXT]"""
|
||
|
||
|
||
def DYNAMIC_CODE_ASSIGNMENT_INSTRUCTION() -> str:
|
||
"""Static instruction for DYNAMIC_CODE_ASSIGNMENT prompt caching.
|
||
Contains extraction rules and dynamic_code field definitions with resolved valid_values.
|
||
Field definitions are loaded from investment_prompts.json.
|
||
"""
|
||
fields_text = _get_fields_text("dynamic_code")
|
||
|
||
return f"""[OBJECTIVE]
|
||
Analyze a Service Term from a Payer-Provider contract and classify it as one or more of the listed field values.
|
||
|
||
[INSTRUCTION]
|
||
- The answer for each field must be clearly and explicitly written. Do not attempt to deduce the answer from other related values.
|
||
- If there are multiple clear and obvious answers, use a list (array).
|
||
- If a field has valid values, choose ONLY from those valid values.
|
||
- Make appropriate assumptions only if the answer is obvious.
|
||
|
||
[FIELDS]
|
||
Populate a JSON dictionary with the following fields:
|
||
{fields_text}
|
||
|
||
[OUTPUT FORMAT]
|
||
Briefly explain your reasoning, then output a properly formatted JSON dictionary.
|
||
{JSON_DICT_WITH_LISTS_FORMAT_INSTRUCTIONS}
|
||
|
||
[CONTEXT]"""
|
||
|
||
|
||
def DYNAMIC_CODE_ASSIGNMENT(service_term: str) -> Tuple[str, Callable[[str], dict]]:
|
||
"""Returns ONLY dynamic content for dynamic code assignment extraction.
|
||
Call DYNAMIC_CODE_ASSIGNMENT_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
prompt = f"""Here is the text to analyze and respond to:
|
||
Service Term: {service_term}
|
||
|
||
Explain your reasoning as you work through the context. After your explanation, include the heading "FINAL DYNAMIC CODE ASSIGNMENT:" followed by a properly formatted JSON dictionary with your final output."""
|
||
|
||
dynamic_code_fields = FieldSet(config.FIELD_JSON_PATH, field_type="dynamic_code")
|
||
field_names = [field.field_name for field in dynamic_code_fields.fields]
|
||
parser = _create_json_dict_parser(field_names=field_names)
|
||
return (prompt, parser)
|
||
|
||
|
||
def FEE_SCHEDULE_BREAKOUT_INSTRUCTION() -> str:
|
||
"""Static instruction for FEE_SCHEDULE_BREAKOUT prompt caching.
|
||
Contains objective, field definitions with resolved valid_values, and output format rules.
|
||
Field definitions are loaded from investment_prompts.json.
|
||
"""
|
||
# Load fields from investment_prompts.json with resolved valid_values
|
||
fields_text = _get_fields_text("fee_schedule_breakout")
|
||
|
||
return f"""[OBJECTIVE]
|
||
Analyze a given reimbursement methodology from a Payer-Provider contract and extract fee schedule-specific attributes into JSON fields.
|
||
Use explicit contract language only and return 'N/A' when information is missing.
|
||
|
||
[FIELDS]
|
||
Populate a JSON dictionary with the following fields:
|
||
{fields_text}
|
||
|
||
[OUTPUT FORMAT]
|
||
{JSON_DICT_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def FEE_SCHEDULE_BREAKOUT(
|
||
methodology: str, fee_schedule: str
|
||
) -> Tuple[str, Callable[[str], dict]]:
|
||
"""Returns ONLY dynamic content for fee schedule breakout.
|
||
Call FEE_SCHEDULE_BREAKOUT_INSTRUCTION() separately for the cached instruction.
|
||
Note: Field definitions are now included in FEE_SCHEDULE_BREAKOUT_INSTRUCTION() for caching.
|
||
|
||
Args:
|
||
methodology: The reimbursement methodology string (already normalized to string in prompt_calls)
|
||
fee_schedule: The fee schedule string (already normalized to string in prompt_calls)
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
prompt = f"""[CONTEXT]
|
||
Analyze and respond to the following text, specifically for {fee_schedule} Fee Schedule:
|
||
Methodology: {methodology.replace('"', "'")}"""
|
||
|
||
# Extract field names for format-aware normalization
|
||
fee_schedule_fields = FieldSet(
|
||
config.FIELD_JSON_PATH, field_type="fee_schedule_breakout"
|
||
)
|
||
field_names = [field.field_name for field in fee_schedule_fields.fields]
|
||
parser = _create_json_dict_parser(field_names=field_names)
|
||
return (prompt, parser)
|
||
|
||
|
||
def GROUPER_BREAKOUT_INSTRUCTION() -> str:
|
||
"""Static instruction for GROUPER_BREAKOUT prompt caching.
|
||
Contains objective, extraction rules, field definitions, and output format.
|
||
Field definitions are loaded from investment_prompts.json.
|
||
"""
|
||
fields_text = _get_fields_text("grouper_breakout")
|
||
|
||
return f"""[OBJECTIVE]
|
||
Analyze service and reimbursement terms to identify grouper-based reimbursement information.
|
||
Extract grouper-based reimbursement attributes into JSON fields using exact contract terminology.
|
||
Return 'N/A' when information is missing.
|
||
|
||
[KEY EXTRACTION RULES]
|
||
- 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
|
||
|
||
[FIELDS]
|
||
Populate a JSON dictionary with the following fields:
|
||
{fields_text}
|
||
|
||
[OUTPUT FORMAT]
|
||
Briefly explain your answer, then return a valid JSON dictionary using the exact field names from the questions as keys. For any information not found in the Term, return 'N/A'."""
|
||
|
||
|
||
def GROUPER_BREAKOUT(service, term) -> Tuple[str, Callable[[str], dict]]:
|
||
"""Returns ONLY dynamic content for grouper breakout.
|
||
Call GROUPER_BREAKOUT_INSTRUCTION() separately for the cached instruction.
|
||
Note: Field definitions are now included in GROUPER_BREAKOUT_INSTRUCTION() for caching.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
prompt = f"""[CONTEXT]
|
||
Here are the service and reimbursement terms to analyze:
|
||
SERVICE: {service}
|
||
REIMBURSEMENT TERM: {term}"""
|
||
|
||
# Extract field names for format-aware normalization
|
||
grouper_fields = FieldSet(config.FIELD_JSON_PATH, field_type="grouper_breakout")
|
||
field_names = [field.field_name for field in grouper_fields.fields]
|
||
parser = _create_json_dict_parser(field_names=field_names)
|
||
return (prompt, parser)
|
||
|
||
|
||
def SPECIAL_CASE_BREAKOUT(
|
||
term, questions, field_names: list[str] | None = None
|
||
) -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Generic prompt for special case breakout fields when no additional descriptions are needed.
|
||
|
||
Args:
|
||
term: The term text to analyze
|
||
questions: Formatted string of field definitions
|
||
field_names: Optional list of field names for format-aware normalization.
|
||
If None, will attempt to extract from questions string.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
prompt = f"""[OBJECTIVE]
|
||
Analyze a given term from a Payer-Provider contract:
|
||
|
||
[FIELDS]
|
||
Populate a JSON dictionary with the following fields:
|
||
{questions}
|
||
|
||
[CONTEXT]
|
||
Here is the term to analyze:
|
||
{term}
|
||
|
||
[OUTPUT FORMAT]
|
||
Briefly explain your answer, then put your final answer in JSON dictionary format.
|
||
"""
|
||
|
||
# Create parser with field_names bound for format-aware normalization
|
||
parser = _create_json_dict_parser(field_names=field_names)
|
||
return (prompt, parser)
|
||
|
||
|
||
def RATE_ESCALATOR_BREAKOUT(term) -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Creates a prompt to extract rate escalator field information from a rate escalator statement.
|
||
|
||
Args:
|
||
term: The term text to analyze
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
rate_escalator_fields = FieldSet(
|
||
config.FIELD_JSON_PATH, field_type="rate_escalator_breakout"
|
||
)
|
||
questions = rate_escalator_fields.print_prompt_dict()
|
||
field_names = [field.field_name for field in rate_escalator_fields.fields]
|
||
return SPECIAL_CASE_BREAKOUT(term, questions, field_names=field_names)
|
||
|
||
|
||
def TRIGGER_CAP_BREAKOUT(term) -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Returns prompt and parser for trigger cap breakout.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
trigger_cap_fields = FieldSet(
|
||
config.FIELD_JSON_PATH, field_type="trigger_cap_breakout"
|
||
)
|
||
questions = trigger_cap_fields.print_prompt_dict()
|
||
field_names = [field.field_name for field in trigger_cap_fields.fields]
|
||
return SPECIAL_CASE_BREAKOUT(term, questions, field_names=field_names)
|
||
|
||
|
||
def OUTLIER_BREAKOUT_INSTRUCTION() -> str:
|
||
"""Static instruction for OUTLIER_BREAKOUT prompt caching.
|
||
Contains objective, extraction guidelines, and field definitions.
|
||
Field definitions are loaded from investment_prompts.json.
|
||
"""
|
||
# Load fields from investment_prompts.json with resolved valid_values
|
||
fields_text = _get_fields_text("outlier_breakout")
|
||
|
||
return f"""[OBJECTIVE]
|
||
Analyze a healthcare payer-provider contract to extract outlier payment information. Outlier payments are additional supplemental reimbursements triggered when claims exceed predetermined thresholds for cost, length of stay, or resource utilization.
|
||
|
||
**Extraction Guidelines:**
|
||
- Extract exact field values (numbers, percentages, dollar amounts) as specified
|
||
- Convert time units to days when requested (e.g., 'two weeks' = '14')
|
||
- Include code types when extracting exclusions (e.g., 'CPT 12345')
|
||
- For frequency fields, use exact contract language (e.g., 'per admission', 'per day')
|
||
- Base answers ONLY on information explicitly stated or directly inferable from the contract text
|
||
|
||
[FIELDS]
|
||
Populate a JSON dictionary with the following fields:
|
||
{fields_text}
|
||
|
||
[OUTPUT FORMAT]
|
||
Briefly explain your answer, then provide the extracted outlier payment information in JSON format. For any information not found in the Term, return 'N/A'."""
|
||
|
||
|
||
def OUTLIER_BREAKOUT(term) -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Returns ONLY dynamic content for outlier breakout extraction.
|
||
Call OUTLIER_BREAKOUT_INSTRUCTION() separately for the cached instruction.
|
||
Note: Field definitions are now included in OUTLIER_BREAKOUT_INSTRUCTION() for caching.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
prompt = f"""[CONTEXT]
|
||
Here is the text to analyze and respond to:
|
||
{term}"""
|
||
|
||
# Extract field names for format-aware normalization
|
||
outlier_fields = FieldSet(config.FIELD_JSON_PATH, field_type="outlier_breakout")
|
||
field_names = [field.field_name for field in outlier_fields.fields]
|
||
parser = _create_json_dict_parser(field_names=field_names)
|
||
return (prompt, parser)
|
||
|
||
|
||
def FACILITY_ADJUSTMENT_BREAKOUT(term) -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Returns prompt and parser for facility adjustment breakout.
|
||
|
||
Args:
|
||
term: The facility adjustment term text to analyze
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
facility_adjustment_fields = FieldSet(
|
||
config.FIELD_JSON_PATH, field_type="facility_adjustment_breakout"
|
||
)
|
||
questions = facility_adjustment_fields.print_prompt_dict()
|
||
# Extract field names for format-aware normalization
|
||
field_names = [field.field_name for field in facility_adjustment_fields.fields]
|
||
return SPECIAL_CASE_BREAKOUT(term, questions, field_names=field_names)
|
||
|
||
|
||
def SEQUESTRATION_BREAKOUT(term) -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Returns prompt and parser for sequestration breakout.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
sequestration_fields = FieldSet(
|
||
config.FIELD_JSON_PATH, field_type="sequestration_breakout"
|
||
)
|
||
questions = sequestration_fields.print_prompt_dict()
|
||
field_names = [field.field_name for field in sequestration_fields.fields]
|
||
return SPECIAL_CASE_BREAKOUT(term, questions, field_names=field_names)
|
||
|
||
|
||
def DISCOUNT_BREAKOUT(term) -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Returns prompt and parser for discount breakout.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
discount_fields = FieldSet(config.FIELD_JSON_PATH, field_type="discount_breakout")
|
||
questions = discount_fields.print_prompt_dict()
|
||
field_names = [field.field_name for field in discount_fields.fields]
|
||
return SPECIAL_CASE_BREAKOUT(term, questions, field_names=field_names)
|
||
|
||
|
||
def PREMIUM_BREAKOUT(term) -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Returns prompt and parser for premium breakout.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
premium_fields = FieldSet(config.FIELD_JSON_PATH, field_type="premium_breakout")
|
||
questions = premium_fields.print_prompt_dict()
|
||
field_names = [field.field_name for field in premium_fields.fields]
|
||
return SPECIAL_CASE_BREAKOUT(term, questions, field_names=field_names)
|
||
|
||
|
||
def ADDITION_BREAKOUT(term) -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Returns prompt and parser for addition breakout.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
addition_fields = FieldSet(config.FIELD_JSON_PATH, field_type="addition_breakout")
|
||
questions = addition_fields.print_prompt_dict()
|
||
field_names = [field.field_name for field in addition_fields.fields]
|
||
return SPECIAL_CASE_BREAKOUT(term, questions, field_names=field_names)
|
||
|
||
|
||
def STOP_LOSS_BREAKOUT(term) -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Returns prompt and parser for stop loss breakout.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
stop_loss_fields = FieldSet(config.FIELD_JSON_PATH, field_type="stop_loss_breakout")
|
||
questions = stop_loss_fields.print_prompt_dict()
|
||
field_names = [field.field_name for field in stop_loss_fields.fields]
|
||
return SPECIAL_CASE_BREAKOUT(term, questions, field_names=field_names)
|
||
|
||
|
||
#####################################################################################
|
||
################################### CODE PROMPTS ####################################
|
||
#####################################################################################
|
||
|
||
|
||
def CODE_EXPLICIT_INSTRUCTION() -> str:
|
||
"""Static instruction for CODE_EXPLICIT prompt caching.
|
||
Contains objective, instructions, and field definitions.
|
||
Field definitions are loaded from investment_prompts.json (field_type=code_primary_breakout).
|
||
"""
|
||
# Load fields from investment_prompts.json with resolved valid_values
|
||
fields_text = _get_fields_text("code_primary_breakout")
|
||
|
||
return f"""[OBJECTIVE]
|
||
Extract explicit procedure, revenue, diagnosis, and other healthcare codes from medical service descriptions.
|
||
|
||
[INSTRUCTIONS]
|
||
- For each field, return a list of all codes EXPLICITLY written for that field. The code itself must be written, not language that describes the code.
|
||
- FIELD ASSIGNMENT (map by code system, not by the word "procedure" or
|
||
"diagnosis" in the text):
|
||
- PROCEDURE_CD (CPT4): Only CPT (5 digits or 5 alphanumeric, e.g. 99213, 0124A)
|
||
and HCPCS (1 letter + 4 alphanumeric, e.g. J1098, S2900). Put these ONLY in
|
||
PROCEDURE_CD.
|
||
- DIAG_CD: Includes ICD-10-CM (letter then digits, e.g. Z00.00)
|
||
and ICD-10-PCS (7 alphanumeric characters). When the text mentions both
|
||
diagnosis/procedure codes and uses "ICD-10" or lists codes that look like
|
||
ICD-10 (e.g. 7-character alphanumeric), put those in DIAG_CD. Do NOT put
|
||
ICD-10 codes in PROCEDURE_CD.
|
||
- When the same sentence or list contains both ICD-10-style codes and
|
||
CPT/HCPCS-style codes (5-char), assign each by format: ICD-10 to DIAG_CD,
|
||
CPT/HCPCS to PROCEDURE_CD. Do not put all codes in one field.
|
||
- REVENUE_CD: Revenue codes only (3-4 digits, may end in X). When in doubt:
|
||
PROCEDURE_CD = CPT/HCPCS only; DIAG_CD = ICD-10 only (including ICD-10-PCS).
|
||
- Note that codes may be present, but not explicitly labeled as codes. For example, you may simply see "J1098", which is a PROCEDURE_CD. You may see "155" which is a REVENUE_CD, etc.
|
||
- If the text explicitly gives a range of codes (e.g. "Surgery codes 10021 to 69990", "codes X to Y", "X–Y"), return that range in the format 'LowestCode-HighestCode' (e.g. 10021-69990). A single range string is acceptable and preferred when the source text describes a single range; do not list every code in the range.
|
||
- If the text gives a range of codes without listing each code individually, return the range in the format 'LowestCode-HighestCode'.
|
||
- If the text describes an entire Code Category (not one specific code) based on a start letter, return the Category in the form "Category: X". e.g. "A-Codes" --> "Category: A", "K-Codes" --> "Category K", etc.
|
||
- If the text explicitly says that there is no published or established rate, write "NOT_ESTABLISHED" for the PROCEDURE_CD value.
|
||
- If any of the codes are not found, do not write a list for that field. Simply populate the field with an empty list [].
|
||
- If a standalone 3-digit code appears without explicit labels (e.g., "DRG," "revenue," "Rev code"), analyze the surrounding context for clues. Look for any direct or indirect references-no matter how subtle-that may suggest a connection to either a Revenue Code or a Grouper Code. If any contextual clues imply relevance to either classification, categorize the code accordingly.
|
||
- Return only valid codes that match the stated format (e.g. CPT exactly 5 digits, HCPCS 1 letter + 4 digits); do not invent or guess codes.
|
||
|
||
[FIELDS]
|
||
Populate a JSON dictionary with the following fields:
|
||
{fields_text}
|
||
|
||
[OUTPUT FORMAT]
|
||
Briefly explain your answer before putting the final answer in JSON dictionary format."""
|
||
|
||
|
||
def CODE_EXPLICIT(service, methodology) -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Returns ONLY dynamic content for code explicit extraction.
|
||
Call CODE_EXPLICIT_INSTRUCTION() separately for the cached instruction.
|
||
Note: Field definitions are now included in CODE_EXPLICIT_INSTRUCTION() for caching.
|
||
|
||
Args:
|
||
service: The service term (already normalized to string upstream)
|
||
methodology: The reimbursement methodology (already normalized to string upstream)
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
prompt = f"""[CONTEXT]
|
||
Here is the Service and Methodology to analyze:
|
||
Service: {service.replace('"', "'")}
|
||
Methodology: {methodology.replace('"', "'")}"""
|
||
|
||
return (prompt, _json_dict_parser)
|
||
|
||
|
||
def CODE_CATEGORY_INSTRUCTION() -> str:
|
||
"""Static instruction for CODE_CATEGORY prompt caching."""
|
||
return f"""[OBJECTIVE]
|
||
Match a medical Service Term to its corresponding Procedure Code description based on code categories.
|
||
|
||
[INSTRUCTIONS]
|
||
1. Determine if the descriptions are needed. If the Service Term mentions the Code Category (a single-character e.g. A, B, C, ...), without any additional subcategories or descriptors, then there is no need to reference the descriptions. For example, if the Service Term is "A-Codes", the answer is ["A0000-A9999"]. This represents the full range of A-Codes.
|
||
|
||
2. If there are additional subcategories or descriptors accompanying the code category, match them to the closest item or items from the VALID DESCRIPTIONS.
|
||
- There can be multiple correct answers. When this is the case, return them all as separate items in the JSON list. This may be phrased like "Service1/Service2" or "Service1 and Service2", in which case both Services must be considered.
|
||
- There may be no correct answers. When this is the case, return the full range of codes in that category as a single item in the list (e.g. ["A0000-A9999"], etc.)
|
||
|
||
[OUTPUT FORMAT]
|
||
Explain your answer in 1-2 sentences. Write your final answer in a properly-formatted JSON list.
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def CODE_CATEGORY(service, choices) -> Tuple[str, Callable[[str], list]]:
|
||
"""Call CODE_CATEGORY_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Args:
|
||
service: The service term (already normalized to string upstream)
|
||
choices: The valid descriptions to choose from
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON list output.
|
||
"""
|
||
prompt = f"""[VALID DESCRIPTIONS]
|
||
Here are the descriptions to choose from:
|
||
{choices}
|
||
|
||
[CONTEXT]
|
||
Here is the Service Term to analyze:
|
||
{service.replace('"', "'")}"""
|
||
|
||
return (prompt, _json_list_parser)
|
||
|
||
|
||
def CODE_IMPLICIT_SPECIAL_INSTRUCTION() -> str:
|
||
"""Static instruction for CODE_IMPLICIT_SPECIAL prompt caching."""
|
||
return f"""[OBJECTIVE]
|
||
Classify a medical Service Term into special predefined categories for implicit code mapping.
|
||
|
||
[INSTRUCTIONS]
|
||
- If the Service refers to the broad categories of Drugs, Medications, Injectible Medications, Pharmaceuticals or similar, without referring to a more specific drug category, respond "Drugs". Do NOT use this category if vaccines are included.
|
||
- If the Service refers to Drugs and/or Vaccines, Immunizations, or similar, without referring to a more specific drug or vaccine category, respond "Vaccines".
|
||
- If the Service refers to Physical, Occupational, and Speech Therapy (aka PT/OT/ST), respond "PT/OT/ST". All three must be present to be considered in this group.
|
||
- If the Service refers to just one of these 3 (PT, OT, or ST), respond with "PT", "OT", or "ST"
|
||
- If the Service refers to Multiple procedures, bilateral procedures, or similar, without referring to a single specific procedure or type of procedure, respond "Surgery"
|
||
- If none of the above cases apply, respond "N/A". If any of the above are mentioned but only in the context of being excluded from the service, respond "N/A".
|
||
|
||
[OUTPUT FORMAT]
|
||
Briefly explain your answer, then put your final answer as a single item in a JSON list.
|
||
Examples: ["Drugs"], ["Vaccines"], ["PT/OT/ST"], ["PT"], ["Surgery"], ["N/A"]
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def CODE_IMPLICIT_SPECIAL(service) -> Tuple[str, Callable[[str], list]]:
|
||
"""Call CODE_IMPLICIT_SPECIAL_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON list output.
|
||
"""
|
||
prompt = f"""[CONTEXT]
|
||
Here is the Service to analyze:
|
||
SERVICE: {service}"""
|
||
|
||
return (prompt, _json_list_parser)
|
||
|
||
|
||
def CODE_IMPLICIT_INSTRUCTION() -> str:
|
||
"""Static instruction for CODE_IMPLICIT prompt caching."""
|
||
return f"""[OBJECTIVE]
|
||
Match a medical Service Term to its corresponding Procedure Code description using implicit matching logic.
|
||
|
||
[INSTRUCTIONS]
|
||
1. Determine which part of the Service Term is the Service itself. The term may include additional confusing information that is irrelevant. Here are some things to look out for:
|
||
- The Provider Type is NOT the Service being provided. For example, in "Medical Services provided by an Anesthesiologist", the Service part is simply "Medical Services". The fact that they are provided by an Anesthesiologist is irrelevant.
|
||
- The Place of Service is NOT the Service being provided. For example, in "Anesthesia Services provided in an Outpatient Laboratory", the Service part is simply "Anesthesia Services". The fact that they are provided in an Outpatient Laboratory is irrelevant.
|
||
- Any other Line of Business (e.g. Medicare, Medicaid) or Program (e.g. CHIP, STAR) information is not relevant to the Service.
|
||
- There may be multiple Services within the Service term. This may be phrased like "Service1/Service2" or "Service1 and Service2", in which case both Services must be considered.
|
||
|
||
2. Once you have isolated the Service part of the text and determined that it is not generic, match it to the corresponding item or items from the VALID DESCRIPTIONS. Here's how to do it:
|
||
- IGNORE distracting terms like "Services" or "Procedures" in both the Service and the Description. They are meaningless for the purpose of this exercise.
|
||
- For each Description in VALID DESCRIPTIONS, ask yourself the following question: Is the Description SYNONYMOUS with the Service?
|
||
- **Note**: Do NOT simply categorize the Service into the Description that it falls under. You are looking specifically for synonymous Service-Description matches. For example, "Nutritional Evaluations" is not synonymous with "Evaluations", because not ALL "Evaluations" are "Nutritional Evaluations".
|
||
- There can be multiple correct answers. When this is the case, return them all as separate items in the JSON list.
|
||
- There may be no correct answers, especially when the Service is much more specific than any of the descriptions. When this is the case, simply return an empty list [].
|
||
|
||
[OUTPUT FORMAT]
|
||
Explain your answer, ensuring each point in the instructions is addressed. Then return your final answer in a properly-formatted JSON list.
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def CODE_IMPLICIT(service, choices) -> Tuple[str, Callable[[str], list]]:
|
||
"""Call CODE_IMPLICIT_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Args:
|
||
service: The service term (already normalized to string upstream)
|
||
choices: The valid descriptions to choose from
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON list output.
|
||
"""
|
||
prompt = f"""[VALID DESCRIPTIONS]
|
||
Here are the descriptions to choose from:
|
||
{choices}
|
||
|
||
[CONTEXT]
|
||
Here is the Service Term to analyze:
|
||
{service.replace('"', "'")}"""
|
||
|
||
return (prompt, _json_list_parser)
|
||
|
||
|
||
def CODE_IMPLICIT_ARBITRATION_INSTRUCTION() -> str:
|
||
"""Static instruction for CODE_IMPLICIT_ARBITRATION prompt caching."""
|
||
return """[OBJECTIVE]
|
||
Choose the most appropriate code-set candidate(s) for the given Service Term from the listed candidates, or indicate that none are appropriate.
|
||
|
||
[INSTRUCTIONS]
|
||
- Each candidate has a source (Category, Special, Level 1, Level 2) and procedure/revenue codes with descriptions.
|
||
- Return the value(s) that most accurately describe the service. Prefer the candidate that is most appropriate and narrowest when one clearly fits; when two candidates are equally relevant and complementary (e.g. one Level 1 set and one Level 2 set that both apply to the service), choose the single candidate that best represents the combined intent, or the narrowest single candidate that covers the service. Do not return all candidates when that would be overly broad.
|
||
- Aim for most accurate and not too broad: avoid choosing a candidate that is redundant or overly broad if a better option exists.
|
||
- If no candidate is appropriate (e.g. all are placeholders or too broad and none match the service), return no_match.
|
||
- Return exactly one of: {"chosen_index": N} where N is the 0-based index of the chosen candidate, or {"no_match": true}.
|
||
|
||
[OUTPUT FORMAT]
|
||
Briefly explain your answer, then return your final answer as a valid JSON dictionary with keys chosen_index (integer) or no_match (boolean). Only one key should be present.
|
||
{JSON_DICT_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def CODE_IMPLICIT_ARBITRATION(
|
||
service: str, candidates_text: str
|
||
) -> Tuple[str, Callable[[str], dict]]:
|
||
"""Build prompt and parser for implicit code arbitration.
|
||
|
||
Call CODE_IMPLICIT_ARBITRATION_INSTRUCTION() separately for the cached instruction.
|
||
Uses the same field-aware dict parser as other code prompts; code_funcs interprets
|
||
the parsed dict (chosen_index vs no_match).
|
||
|
||
Args:
|
||
service: The service term.
|
||
candidates_text: Formatted list of candidates (source + code sets).
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function). Parser returns normalized dict
|
||
with keys chosen_index (str) and/or no_match (str) per FIELD_FORMAT_MAPPING.
|
||
"""
|
||
prompt = f"""[SERVICE TERM]
|
||
{service.replace('"', "'")}
|
||
|
||
[CANDIDATES]
|
||
{candidates_text}
|
||
|
||
Choose the most appropriate candidate by index (or no_match if none are appropriate)."""
|
||
|
||
parser = _create_json_dict_parser(field_names=["chosen_index", "no_match"])
|
||
return (prompt, parser)
|
||
|
||
|
||
def CODE_LAST_CHECK_INSTRUCTION() -> str:
|
||
"""Static instruction for CODE_LAST_CHECK prompt caching."""
|
||
return f"""[OBJECTIVE]
|
||
Analyze a given healthcare-related term to determine if it represents a specific medical service.
|
||
|
||
[CLASSIFICATION RULES]
|
||
- Return "Specific" if the term is a specific medical service or category of services
|
||
- Return "Generic" if the term is:
|
||
- A generic medical service
|
||
- Not a service at all
|
||
- The name of a hospital or other provider
|
||
- A nonsensical or non-service phrase
|
||
|
||
[OUTPUT FORMAT]
|
||
Briefly explain your answer, then put your final answer as a single item in a JSON list.
|
||
Examples: ["Specific"] or ["Generic"]
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def CODE_LAST_CHECK(service) -> Tuple[str, Callable[[str], Any]]:
|
||
"""Call CODE_LAST_CHECK_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Uses format-aware list parser with expected_format='str' so single-item list
|
||
(e.g. ['Specific'] or ['Generic']) is normalized to a string. Parser returns str.
|
||
"""
|
||
prompt = f"""[SERVICE TO ANALYZE]
|
||
{service}"""
|
||
|
||
return (
|
||
prompt,
|
||
_create_json_list_parser(field_name="CODE_LAST_CHECK", expected_format="str"),
|
||
)
|
||
|
||
|
||
def FILL_BILL_TYPE_INSTRUCTION() -> str:
|
||
"""Static instruction for FILL_BILL_TYPE prompt caching."""
|
||
return f"""[OBJECTIVE]
|
||
Analyze a given medical Service Term to determine which Bill Type Code Description it falls under.
|
||
|
||
[INSTRUCTIONS]
|
||
- While determining the bill type code, first prioritize the place where the service is taking place, that will always take precedence over the service only terminology. For example in home dialysis, we can recognize home as place of service and only need to identify that for code determination. However if place of service is not available fall back on the service term as long as it is clearly and unambiguously referred to by the service and is present in [VALID DESCRIPTIONS].
|
||
- Answer with the Description or Descriptions from [VALID DESCRIPTIONS] only if it is clearly and unambiguously referred to by the Service. Do not attempt much inference, the answer will be very clear if it is present.
|
||
- It is highly likely that none of the Descriptions will be a clear and unambiguous match. When this is the case, return an empty list: []
|
||
- Here are some examples:
|
||
- "Ambulatory Surgery Procedures" --> ["Ambulatory Surgery Center"]
|
||
- "Hospital Services" --> ["Inpatient Hospital", "Outpatient Hospital"]
|
||
- "home dialysis" --> ["Home"]
|
||
- "dialysis" --> ["Dialysis Center"]
|
||
|
||
[OUTPUT FORMAT]
|
||
Briefly explain your answer, then return your final answer in a properly-formatted JSON list.
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def FILL_BILL_TYPE(
|
||
service, choices, reimb_term=None, exhibit_text=None
|
||
) -> Tuple[str, Callable[[str], list]]:
|
||
"""Call FILL_BILL_TYPE_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Args:
|
||
service: The service term (already normalized to string upstream)
|
||
choices: The valid bill type descriptions to choose from
|
||
reimb_term: The reimbursement term (already normalized to string upstream, optional)
|
||
exhibit_text: Additional exhibit context (optional)
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON list output.
|
||
"""
|
||
# Build reimbursement term section conditionally
|
||
reimb_term_section = ""
|
||
if reimb_term and not string_utils.is_empty(reimb_term):
|
||
reimb_term_section = f"""
|
||
[REIMBURSEMENT TERM]
|
||
Here is the Reimbursement Term associated with the Service Term:
|
||
{reimb_term.replace('"', "'")}
|
||
"""
|
||
|
||
# Build exhibit context section conditionally
|
||
exhibit_context_section = ""
|
||
if exhibit_text:
|
||
exhibit_context_section = f"""
|
||
[EXHIBIT CONTEXT]
|
||
If the Service Term and Reimbursement Term together do not provide sufficient information, examine the following exhibit text for additional context about where the service is performed:
|
||
{exhibit_text.replace('"', "'")}
|
||
"""
|
||
|
||
# service is already normalized to string upstream
|
||
prompt = f"""[VALID DESCRIPTIONS]
|
||
Here are the descriptions to choose from:
|
||
{choices}
|
||
|
||
[SERVICE TERM TO ANALYZE]
|
||
{service.replace('"', "'")}
|
||
{reimb_term_section}{exhibit_context_section}"""
|
||
|
||
return (prompt, _json_list_parser)
|
||
|
||
|
||
#####################################################################################
|
||
################################ ONE-TO-ONE-PROMPTS #################################
|
||
#####################################################################################
|
||
|
||
|
||
def ONE_TO_ONE_SINGLE_FIELD_TEMPLATE(
|
||
context: str, field_prompt: dict[str, str], field_name: str | None = None
|
||
) -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Returns prompt and parser for one-to-one single field extraction.
|
||
|
||
Args:
|
||
context: Contract text context
|
||
field_prompt: Dictionary mapping field name to prompt question
|
||
field_name: Optional field name for field-aware normalization
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
prompt = f"""The following is a contract (or excerpt thereof).
|
||
Answer the following question: {field_prompt}
|
||
|
||
## START CONTRACT TEXT ##
|
||
{context}
|
||
## END CONTRACT TEXT ##
|
||
|
||
Briefly explain your answer, then put the final answer in a properly-formatted JSON dictionary, where the key is the field name and value is the answer. If there is no valid answer, return "N/A".
|
||
"""
|
||
|
||
# Use field-aware parser if field_name is provided
|
||
if field_name:
|
||
parser = _create_json_dict_parser(field_names=[field_name])
|
||
else:
|
||
parser = _json_dict_parser
|
||
return (prompt, parser)
|
||
|
||
|
||
def ONE_TO_ONE_SINGLE_FIELD_INSTRUCTION() -> str:
|
||
return (
|
||
"[OBJECTIVE]\n"
|
||
"Answer a single, deterministic question about the contract text. Return result in JSON dictionary format as instructed."
|
||
)
|
||
|
||
|
||
def TIN_NPI_TEMPLATE_INSTRUCTION() -> str:
|
||
return """
|
||
Analyze the following healthcare payer-provider contract text and extract provider entities - these are healthcare providers who deliver services, NOT payers/insurance companies who reimburse for services.
|
||
|
||
IMPORTANT DISTINCTION:
|
||
- Providers: Physicians, physician groups, hospitals, clinics who DELIVER healthcare, CEOs and other signatories that may sign on behalf of any of the previous entities
|
||
- Payers: Insurance companies, health plans who PAY for healthcare services
|
||
|
||
[EXTRACTION INSTRUCTIONS]
|
||
- First, analyze the text thoroughly and identify all Provider Entities
|
||
- A Provider Entity is considered valid as long as either a TIN, NPI, or NAME is present. If any one or two of those fields are missing, use "N/A" for those fields.
|
||
- Return TIN and NPIs that are found on the page even if they aren't explicitly labeled as such.
|
||
- If the table contains multiple name columns (e.g. 'System Name' and 'Facility Name'), YOU MUST extract the specific 'Facility Name' or 'Hospital Name' column.
|
||
- IMPORTANT VALIDATION REQUIREMENTS:
|
||
* TIN must be exactly 9 digits (no more, no less)
|
||
* NPI must be exactly 10 digits (no more, no less)
|
||
* CAREFULLY double-check the digit count for each TIN and NPI before including them in the response
|
||
|
||
[FORMATTING INSTRUCTIONS]
|
||
- Briefly explain your reasoning. After your explanation, include the heading "FINAL PROVIDER INFO:" followed by a properly formatted JSON array with your final output
|
||
- In your response, include ONLY ONE SINGLE properly formatted JSON object. CRITICAL: The object MUST be of the form **dict[list[str, str]]**
|
||
- Each associated TIN-NPI-Name combination must be an object (DICT) within this array
|
||
- All provider objects must have these exact keys: "TIN", "NPI", "NAME"
|
||
- ALl dict values must be strings - if there are multiple different NAMEs or NPIs for an identical TIN, put them in different dictionaries.
|
||
- Use "N/A" for any missing values
|
||
- Strip ALL non-digit characters from any found TIN or NPI (e.g., remove hyphens, spaces, parentheses).
|
||
- Your final JSON array must begin with an opening bracket '[' and end with a closing bracket ']'
|
||
- Make sure the JSON array is correctly formatted with commas between objects
|
||
- Do not include any JSON notation elsewhere in your response
|
||
|
||
Example of correct format:
|
||
FINAL PROVIDER INFO:
|
||
[
|
||
{
|
||
"TIN": "123456789",
|
||
"NPI": "1234567890",
|
||
"NAME": "Main Provider Group"
|
||
},
|
||
{
|
||
"TIN": "124681012",
|
||
"NPI": "N/A",
|
||
"NAME": "Main Provider Group"
|
||
},
|
||
{
|
||
"TIN": "987654321",
|
||
"NPI": "N/A,
|
||
"NAME": "Dr. John Smith",
|
||
},
|
||
{
|
||
"TIN": "N/A",
|
||
"NPI": "1234567890",
|
||
"NAME": "Jane Doe, CRNA"
|
||
}
|
||
]
|
||
|
||
Contract text:"""
|
||
|
||
|
||
def TIN_NPI_TEMPLATE(
|
||
context, questions, payer_name
|
||
) -> Tuple[str, Callable[[str], list]]:
|
||
"""
|
||
Returns prompt for TIN/NPI extraction.
|
||
Call TIN_NPI_TEMPLATE_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON list output (list of dicts).
|
||
"""
|
||
# Build payer-specific instruction for the prompt
|
||
payer_instruction = ""
|
||
if not string_utils.is_empty(payer_name):
|
||
payer_instruction = f'\n\nIMPORTANT: "{payer_name}" is the PAYER (insurance company) in this contract. Do NOT extract TIN or NPI information for "{payer_name}" - only extract information for healthcare providers who deliver services.'
|
||
|
||
prompt = f"""[PAYER CONTEXT]{payer_instruction}
|
||
|
||
[FIELDS TO EXTRACT]
|
||
{questions}
|
||
|
||
Contract text:
|
||
{context.replace('"', "'" )}
|
||
"""
|
||
|
||
return (prompt, _json_list_parser)
|
||
|
||
|
||
def HSC_CLAIM_TYPES_ADDITIONAL_INSTRUCTION():
|
||
return " IMPORTANT: Prioritize the title and header as the primary source for determining claim type. Look for terms like 'Professional', 'Physician', 'Hospital', 'Facility', 'Institutional', 'Ancillary', 'DME', 'Home Health', 'Laboratory' in the title or header. If the title/header clearly indicates the claim type, use that. Otherwise, check the preamble, provider name, or signature section to help determine the claim type."
|
||
|
||
|
||
def HSC_ADDITIONAL_INSTRUCTIONS(allow_na):
|
||
if allow_na:
|
||
return " Only provide an answer if that answer clearly applies to the ENTIRE contract. Otherwise, answer N/A. If there are multiple correct answers, return them as a JSON list (array) in the dictionary value."
|
||
else:
|
||
return " Do NOT leave this field N/A. Choose the most appropriate answer based on the context. If there are multiple answers, return them as a JSON list (array) in the dictionary value."
|
||
|
||
|
||
def HSC_DYNAMIC_PRIMARY_INSTRUCTION():
|
||
"""Additional instructions for dynamic primary fields (LOB, PRODUCT, PROGRAM, NETWORK) when passed to 1:1 processing.
|
||
|
||
These instructions emphasize that values should only be extracted if they explicitly
|
||
refer to the entire contract and all terms, and to ignore template language or general
|
||
definitions that don't specifically apply to this contract.
|
||
"""
|
||
return (
|
||
" CRITICAL: Only extract values if they explicitly refer to the ENTIRE contract "
|
||
"and apply to ALL terms and services covered by this specific contract. "
|
||
"IGNORE any mentions that are part of template language, general definitions, "
|
||
"or boilerplate text that describes general capabilities but does not specifically "
|
||
"apply to this contract. If the value is only mentioned in definitions, examples, "
|
||
"or general descriptions without explicit application to this contract's terms, "
|
||
"return 'N/A'. Only extract values that are clearly stated as applying to this "
|
||
"specific contract and all its terms."
|
||
)
|
||
|
||
|
||
def CHECK_PROVIDER_NAME_MATCH_INSTRUCTION() -> str:
|
||
"""Static instruction for CHECK_PROVIDER_NAME_MATCH prompt caching.
|
||
Contains all matching rules and critical exclusions.
|
||
"""
|
||
return f"""[OBJECTIVE]
|
||
Determine if two provider names refer to the same healthcare organization.
|
||
|
||
[MATCHING RULES]
|
||
1. PROVIDER NAME A must appear in PROVIDER NAME B (either as an exact match or close variation).
|
||
2. If PROVIDER NAME B contains multiple names (which may appear separated by delimiters in the input), check each name individually.
|
||
3. Legal entity suffixes (LLC, Inc, PA, PC, etc.) can be ignored when matching.
|
||
4. D/B/A / "doing business as" variations count as matches (e.g., "Lakeview Hospital" matches "Hospital Corporation of Utah d.b.a. Lakeview Hospital").
|
||
5. Acronyms that clearly represent the same entity count as matches (e.g., "MHS" matches "Memorial Hospital System").
|
||
6. Common word variations count as matches (e.g., "St. Mark's" matches "Saint Mark's").
|
||
|
||
[CRITICAL - DO NOT MATCH]
|
||
- Parent company or holding company names that do NOT appear in PROVIDER NAME B, even if they own the listed entities.
|
||
- Health system names that are NOT in PROVIDER NAME B, even if the listed hospitals belong to that system.
|
||
- Corporate umbrella names unless they are explicitly listed in PROVIDER NAME B.
|
||
|
||
[OUTPUT FORMAT]
|
||
Briefly explain your reasoning. Then return ONLY 'Y' (match found) or 'N' (no match found) as a single item in a JSON list.
|
||
Examples: ["Y"] or ["N"]
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def CHECK_PROVIDER_NAME_MATCH_PROMPT(
|
||
provider_name: str, hybrid_smart_chunking_provider_group_name: str
|
||
) -> Tuple[str, Callable[[str], list]]:
|
||
"""Returns ONLY dynamic content for provider name matching.
|
||
Call CHECK_PROVIDER_NAME_MATCH_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON list output.
|
||
"""
|
||
prompt = f"""[CONTEXT]
|
||
PROVIDER NAME A: {provider_name}
|
||
PROVIDER NAME B: {hybrid_smart_chunking_provider_group_name}
|
||
|
||
Note: PROVIDER NAME B is typically a single name but may contain multiple names. Determine if PROVIDER NAME A matches PROVIDER NAME B (or ANY name if multiple are present)."""
|
||
|
||
return (prompt, _json_list_parser)
|
||
|
||
|
||
def JOINT_FIELD_TEMPLATE(
|
||
context: str, questions: dict[str, str], consistency_rule: str = ""
|
||
) -> str:
|
||
rule_section = (
|
||
f"\n\nCONSISTENCY RULES — these fields must be logically consistent with each other:\n{consistency_rule}\n"
|
||
if consistency_rule
|
||
else ""
|
||
)
|
||
return f"""The following is a contract (or excerpt thereof). Answer the following questions: {questions}{rule_section}
|
||
|
||
## START CONTRACT TEXT ##
|
||
{context.replace('"', "'")}
|
||
## END CONTRACT TEXT ##
|
||
|
||
Reason through each answer carefully, ensuring all values are consistent with each other before finalizing. Explanation MUST NOT contain any curly brackets '{{' or '}}'. Replace them with '()' instead.
|
||
|
||
The JSON should:
|
||
- Include every key
|
||
- Use 'N/A' for any question where the requested information doesn't apply to this context
|
||
- Use 'NOT_FOUND' where explicitly instructed
|
||
- Contain only the final answers, not explanations
|
||
|
||
Example format:
|
||
I analyzed the payment structure and found...
|
||
Given the monthly rate and fixed term, I will calculate...
|
||
|
||
{{
|
||
"PAYMENT_TYPE": "Monthly",
|
||
"AMOUNT": "5000"
|
||
}}
|
||
"""
|
||
|
||
|
||
def JOINT_FIELD_INSTRUCTION() -> str:
|
||
return (
|
||
"[OBJECTIVE]\n"
|
||
"Extract multiple related fields from a contract in a single pass, ensuring the values are "
|
||
"logically consistent with each other. Apply all consistency rules in the prompt before "
|
||
"finalizing your answers. Follow JSON-only output rules."
|
||
)
|
||
|
||
|
||
def EXTRACT_AMENDMENT_NUM_FROM_FILENAME(filename) -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Returns prompt and parser for extracting amendment number from filename.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
prompt = f"""Filename: {filename}"""
|
||
|
||
return (prompt, _json_dict_parser)
|
||
|
||
|
||
def EXTRACT_AMENDMENT_NUM_FROM_FILENAME_INSTRUCTION() -> str:
|
||
return """[OBJECTIVE]\n
|
||
Analyze the given filename string to extract the amendment number or identifier of the contract document if it exists.
|
||
Amendment identifiers can be numeric, letters only, or alphanumeric. Follow these guidelines:
|
||
- Numeric: '3', '12', '1' (with leading zeros dropped, e.g., '001' → '1')
|
||
- Letters only: 'A', 'AB', 'B'
|
||
- Alphanumeric: '3A', '2B', '1A2'
|
||
- Roman numerals should be converted to numbers (e.g., 'IV' → '4')
|
||
- Return the amendment identifier exactly as it appears in the filename, applying the above rules.
|
||
If no amendment number or identifier is found, return N/A.
|
||
Briefly explain your reasoning, then put your final answer in JSON dictionary format as follows:
|
||
{{
|
||
"amendment_number": "<extracted amendment number/identifier or N/A>"
|
||
}}"""
|
||
|
||
|
||
#####################################################################################
|
||
############################### PREPROCESSING PROMPTS ###############################
|
||
#####################################################################################
|
||
|
||
|
||
def EXHIBIT_HEADER_INSTRUCTION() -> str:
|
||
"""Static instruction for EXHIBIT_HEADER prompt caching.
|
||
Returns formatted instructions for extracting headers from chunked contract pages.
|
||
"""
|
||
return """[OBJECTIVE]
|
||
Extract section headers from contract page chunks with boundary markers.
|
||
Return a JSON dict in |pipes| with boundary numbers as keys and header as values.
|
||
|
||
Each chunk is separated by a boundary marker in the format ===BOUNDARY_X_HEADER=== where X is the boundary number.
|
||
|
||
The following keywords and phrases should be considered as exhibit/attachment headers:
|
||
* EXHIBIT
|
||
* ATTACHMENT
|
||
* ARTICLE
|
||
* AMENDMENT
|
||
* SCHEDULE
|
||
* ADDENDUM
|
||
* APPENDIX
|
||
* PARTICIPATING PROVIDER AGREEMENT
|
||
|
||
Rules for Inclusion:
|
||
* Header can be multi-lined, include all lines that are part of the header
|
||
* Include ALL text that appears to be part of a section header
|
||
* Full header names and numbers/letters (e.g., "Attachment C: Commercial-Exchange")
|
||
* Document titles, agreement names, and section identifiers
|
||
* Any words that specify what type of information is contained, such as "Compensation Schedule" or "Definitions"
|
||
* Provider/Entity names when listed with exhibit information
|
||
* Keep exact capitalization and formatting as shown in document
|
||
|
||
Rules for Exclusion:
|
||
* Do NOT include body text or paragraph content
|
||
* Do NOT include page numbers
|
||
* Do NOT include docusign IDs and other metadata
|
||
* Do NOT include partial sentences or continuation text
|
||
* Do NOT include form field labels like "Provider's Legal Name" or "Authorized Representative's Signature"
|
||
|
||
[OUTPUT FORMAT]
|
||
Return a JSON dictionary enclosed in |pipes| where:
|
||
- Keys are boundary numbers (e.g., "1", "2", "3")
|
||
- Values are the header strings found in that chunk
|
||
- If no headers are found in a chunk, return "N/A" for that key
|
||
- Return {} if no headers are found in any chunk
|
||
|
||
Example output:
|
||
|{"1": "PROVIDER SERVICES AGREEMENT\\nSIGNATURE PAGE", "2": "N/A", "3": "Attachment A: Fee Schedule"}|"""
|
||
|
||
|
||
def EXHIBIT_HEADER(context) -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Returns ONLY dynamic content for exhibit header extraction.
|
||
Call EXHIBIT_HEADER_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Args:
|
||
context (str): Contract text with boundary markers separating chunks
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
prompt = f"""[CONTEXT]
|
||
Here are the chunks to analyze:
|
||
{context.replace('"', "'")}
|
||
|
||
Return ONLY the |{{dictionary}}| followed by a brief explanation."""
|
||
|
||
return (prompt, _json_dict_parser)
|
||
|
||
|
||
def EXHIBIT_LINKAGE_INSTRUCTION() -> str:
|
||
"""Static instruction for EXHIBIT_LINKAGE prompt caching.
|
||
Contains all decision rules for determining meaningful differences.
|
||
"""
|
||
return f"""[OBJECTIVE]
|
||
Determine if two exhibit headers are meaningfully different from each other.
|
||
|
||
[DECISION RULES]
|
||
* The headers may refer to Attachment, Exhibit, Schedule, Addendum, or similar.
|
||
* If the highest-level number is identical, then the Headers ARE meaningfully different. For example, "Exhibit B-1" and "Exhibit B-2" are meaningfully different because they are both Exhibit B but have different sub-exhibits.
|
||
* If the headers have the same numbers, but different descriptors, they are NOT meaningfully different. For example, "Attachment A" and "Attachment A: Professional" are NOT meaningfully different because they are both attachment A.
|
||
* If one header is a continuation of the other (e.g. "Continued", "CONT'D"), they are NOT meaningfully different.
|
||
* If Header 1 directly references Header 2 in a way that links the two, then they are NOT meaningfully different. This linkage also applies if Header 2 references Header 1.
|
||
|
||
[OUTPUT FORMAT]
|
||
Return ["Y"] if the two Headers are meaningfully different. Return ["N"] if they are not.
|
||
Briefly explain your answer, then return your final answer as a single item in a JSON list.
|
||
Examples: ["Y"] or ["N"]
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def EXHIBIT_LINKAGE(header1, header2) -> Tuple[str, Callable[[str], list]]:
|
||
"""Returns ONLY dynamic content for exhibit linkage comparison.
|
||
Call EXHIBIT_LINKAGE_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON list output.
|
||
"""
|
||
prompt = f"""[CONTEXT]
|
||
Header 1:
|
||
"{header1}"
|
||
|
||
Header 2:
|
||
"{header2}" """
|
||
|
||
return (prompt, _json_list_parser)
|
||
|
||
|
||
def EXHIBIT_HEADER_DEDUP_INSTRUCTION() -> str:
|
||
"""Static instruction for EXHIBIT_HEADER_DEDUP prompt caching.
|
||
Returns formatted instructions for deduplicating exhibit headers across pages.
|
||
"""
|
||
return (
|
||
"[OBJECTIVE]\n"
|
||
"Deduplicate a dictionary of page-number-to-header mappings from a contract.\n"
|
||
"Keep only the first page occurrence of each unique section.\n"
|
||
"Return the deduplicated JSON dict in |pipes|."
|
||
)
|
||
|
||
|
||
def EXHIBIT_HEADER_DEDUP(header_dict_str):
|
||
"""
|
||
Returns prompt for deduplicating exhibit headers across contract pages.
|
||
Call EXHIBIT_HEADER_DEDUP_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Args:
|
||
header_dict_str (str): JSON string of the exhibit header dictionary
|
||
mapping page numbers to lists of headers.
|
||
|
||
Returns:
|
||
str: Formatted prompt for LLM to deduplicate headers and return as JSON dict
|
||
"""
|
||
return f"""You are given a JSON dictionary where keys are page numbers (as strings) and values are lists containing header text extracted from each page of a contract document.
|
||
|
||
Multiple consecutive pages may belong to the same section and will have similar or identical headers repeated on each page, sometimes with minor variations.
|
||
|
||
Your task is to deduplicate this dictionary so that each unique section appears only once, on the first page where it starts.
|
||
|
||
[RULES FOR IDENTIFYING DUPLICATES]
|
||
* Two headers belong to the same section if they share the same core section identifier AND the same content-type descriptors after case-normalization.
|
||
* Minor case variations do NOT make headers different sections.
|
||
* Extra sub-lines that are facility names, entity names, or provider names do NOT make it a different section - these are just contextual metadata repeated on continuation pages.
|
||
* However, sub-lines that describe the TYPE OF CONTENT or SERVICE CATEGORY DO make it a DIFFERENT section, even if the top-level exhibit/addendum identifier is the same. These descriptors indicate distinct sub-sections with different subject matter under a shared parent exhibit.
|
||
* A genuinely NEW section has either a different top-level identifier OR a different content-type/service-category descriptor under the same identifier.
|
||
* If a single header on one page bundles multiple distinct section identifiers together (e.g., lists several addenda, exhibits, or schedules within one header block), it is likely a table of contents or index page - NOT an actual section start. When the individual sections referenced in such a bundled header each appear as their own standalone headers on later pages, DROP the bundled/index header entirely and KEEP the individual section headers from later pages.
|
||
|
||
[CRITICAL RULE]
|
||
* Do NOT modify the header text of the first occurrence of any section. The exact original text MUST be preserved character-for-character because it is used downstream for regex matching to split the document into exhibits. Any modification will break downstream processing.
|
||
|
||
[OUTPUT FORMAT]
|
||
Return a JSON dictionary enclosed in |pipes| with the same structure as the input: keys are page numbers, values are lists of header strings. The output should contain ONLY the first page of each unique section, with the original header text preserved exactly as-is.
|
||
|
||
[INPUT]
|
||
{header_dict_str}
|
||
|
||
Briefly explain which headers you identified as duplicates and why, then return the deduplicated dictionary in |pipes|."""
|
||
|
||
|
||
#####################################################################################
|
||
########################## CHECKS, FIXES, AND VALIDATIONS ###########################
|
||
#####################################################################################
|
||
|
||
|
||
def VALIDATE_REIMBURSEMENTS_INSTRUCTION() -> str:
|
||
"""Validates whether a Service Term and Reimbursement Term pair represents a complete reimbursement methodology."""
|
||
return f"""Validate this SERVICE TERM and REIMBURSEMENT TERM pair from a healthcare contract.
|
||
|
||
## DECISION RULE
|
||
|
||
Default is NO. Return YES only if ALL three conditions pass:
|
||
|
||
---
|
||
|
||
## CONDITION 1: Valid SERVICE TERM
|
||
|
||
Must identify a billable service, procedure, or care category.
|
||
|
||
Valid examples: service names, procedure types, CPT/HCPCS codes, care categories, umbrella terms like "Covered Services" or "All Services"
|
||
|
||
Invalid - return NO if SERVICE TERM is:
|
||
- Clinical definitions or level-of-care descriptions (acuity, intervention frequency, co-morbidities)
|
||
- Authorization or coverage requirements
|
||
- Clean Claims definitions or processing requirements without a payment methodology (e.g., "Clean Claims are claims submitted within 90 days")
|
||
- Contract terms, party information, or administrative language
|
||
- Administrative tasks or non-clinical services (e.g., medical records copying, administrative fees, filing charges)
|
||
- Billing rules or payment logic describing scenarios (e.g., "the procedure for which the Allowed Amount is greatest", "when multiple procedures are performed")
|
||
|
||
---
|
||
|
||
## CONDITION 2: Valid REIMBURSEMENT TERM
|
||
|
||
Must contain a SPECIFIC, CONCRETE payment method you can point to.
|
||
Valid payment methods:
|
||
- Dollar amounts (e.g., "$150 per visit")
|
||
- Percentage rates (e.g., "80% of Medicare allowable")
|
||
- Named fee schedule references with specific percentage (e.g., "100% of DMAP fee schedule"). Note: a term that establishes or develops a fee schedule at a specific percentage of a named schedule IS valid (e.g., "develop a fee schedule at 110% of Medicaid Fee Schedule").
|
||
- Payment formulas or limitations with amounts
|
||
- "Lesser of" or "greater of" statements with specific rates
|
||
- Capitation rates (PMPM, PMPY, per member costs)
|
||
- Flat rates per service or episode
|
||
- Calculation formulas with defined components (e.g., "base rate multiplied by DRG weight plus adjustments")
|
||
- Special types: OUTLIER, STOP_LOSS, SEQUESTRATION, DISCOUNT, PREMIUM, RATE_ESCALATOR, FACILITY_ADJUSTMENT
|
||
|
||
Note: A REIMBURSEMENT TERM is valid as long as it contains a concrete rate or payment method, even if it also includes surrounding descriptive or billing-scenario language. Focus on whether a specific rate is present, not on the framing around it.
|
||
|
||
Invalid - return NO if REIMBURSEMENT TERM is:
|
||
- Empty reimbursements: structure of a payment method but actual rate/percentage is not stated or references another location (e.g., "paid at rates in the next section", "the percentage listed below", "per the attached schedule", "as set forth in Exhibit A"). Note: formulas that define calculation methodology (e.g., "base rate multiplied by DRG weight") ARE valid even if specific dollar amounts aren't stated. Also valid: a percentage tied to a specific CPT/HCPCS code (e.g., "100% of S5125-U3") - the percentage IS the concrete rate.
|
||
- Vague or undefined amounts: uses non-specific language like "appropriate amount", "applicable rate", "as determined", "reasonable charges", "customary charges" without a concrete percentage or dollar figure. A fee schedule reference is only valid if paired with a specific percentage (e.g., "100% of fee schedule" is valid; "appropriate amount under fee schedule" is NOT valid).
|
||
- Service definitions without payment terms
|
||
- Authorization or coverage requirements only
|
||
- Processing instructions, funding arrangements, or administrative statements without specific rates
|
||
- Member cost responsibilities (including Medicare Member Cost Share)
|
||
- Irrelevant information: parties, contract terms, transfer adjustments, non-reimbursement details
|
||
- Billing prohibitions or refund procedures
|
||
|
||
---
|
||
|
||
## CONDITION 3: No Disqualifiers
|
||
|
||
Return NO if EITHER term contains (even if valid payment method exists):
|
||
- Coordination of Benefits (COB) as the primary subject. Note: standard payment reductions (e.g., "less co-payments, deductibles, or amounts paid by third parties") within a reimbursement formula are NOT COB disqualifiers.
|
||
- Risk-sharing, surplus/deficit distributions, settlement payments
|
||
- Performance bonuses, incentives, administrative compensation
|
||
- Audit/timeliness metrics (e.g., "pay 85% of claims within 30 days")
|
||
- Non-reimbursement statements: "not covered", "not payable", "excluded", "shall not bill", "shall not collect"
|
||
- Chargemaster/CDM references
|
||
|
||
---
|
||
|
||
[OUTPUT FORMAT]
|
||
Briefly explain your reasoning following the three conditions above, then return your final answer as a single item in a JSON list.
|
||
Examples: ["YES"] or ["NO"]
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def VALIDATE_REIMBURSEMENTS_PROMPT(
|
||
service_term: str, reimb_term: str
|
||
) -> Tuple[str, Callable[[str], list]]:
|
||
"""Returns ONLY dynamic content for validation.
|
||
Call VALIDATE_REIMBURSEMENTS_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON list output.
|
||
"""
|
||
prompt = f"""## INPUT
|
||
|
||
SERVICE TERM: {service_term}
|
||
REIMBURSEMENT TERM: {reimb_term}
|
||
|
||
## RESPOND
|
||
|
||
1. Is SERVICE TERM a valid service identifier?
|
||
2. Is REIMBURSEMENT TERM a specific payment method?
|
||
3. Any disqualifiers in either term?
|
||
|
||
If 1=yes, 2=yes, 3=no → return ["YES"]
|
||
Otherwise → return ["NO"]"""
|
||
|
||
return (prompt, _json_list_parser)
|
||
|
||
|
||
def DATE_FIX_INSTRUCTION() -> str:
|
||
"""Static instruction for DATE_FIX prompt caching.
|
||
Contains all conversion rules and examples.
|
||
"""
|
||
return f"""[OBJECTIVE]
|
||
Given a date string, convert it to YYYY/MM/DD format.
|
||
Input: Text potentially containing a date
|
||
Output: Date in YYYY/MM/DD format, or input if it's a duration, or "N/A" if ambiguous/incomplete
|
||
|
||
[RULES]
|
||
1. Convert to YYYY/MM/DD (pad with leading zeros, use / separators)
|
||
2. Add 2000 to 2-digit years 0-49, 1900 to 50-99
|
||
3. Return unchanged if it's a duration (e.g., "3 years", "one year from effective date")
|
||
4. Return N/A if: missing/ambiguous month or year components, or invalid dates. If just the day is missing or day is just 0, assume it's the first of the month.
|
||
5. For ambiguous input cases, assume American format (MM/DD/YYYY) and apply YYYY/MM/DD formatting
|
||
6. Any day values with spaces or leading zeros (e.g., '0 1' or '01') are interpreted as single-digit days
|
||
7. Return final answer as a single item in a JSON list
|
||
|
||
[EXAMPLES]
|
||
Input: "7th day of February, 2002"
|
||
Output: ["2002/02/07"]
|
||
|
||
Input: "Sep 20, 2022"
|
||
Output: ["2022/09/20"]
|
||
|
||
Input: "3 years"
|
||
Output: ["3 years"]
|
||
|
||
Input: "FEB 0 1 2020"
|
||
Output: ["2020/02/01"]
|
||
|
||
Input: "8.1.10" # (assumed MM.DD.YYYY)
|
||
Output: ["2010/08/01"]
|
||
|
||
Input: "March 2014" # (missing day, assume 1st)
|
||
Output: ["2014/03/01"]
|
||
|
||
Input: "Feb 26, 2_0_" # (incomplete year)
|
||
Output: ["N/A"]
|
||
|
||
[OUTPUT FORMAT]
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def DATE_FIX_PROMPT(context: str) -> Tuple[str, Callable[[str], list]]:
|
||
"""Returns ONLY dynamic content for date fixing.
|
||
Call DATE_FIX_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON list output.
|
||
"""
|
||
prompt = f"""[CONTEXT]
|
||
Please analyze this date:
|
||
{context}"""
|
||
|
||
return (prompt, _json_list_parser)
|
||
|
||
|
||
def CARVEOUT_CHECK_INSTRUCTION() -> str:
|
||
"""Static instruction for CARVEOUT_CHECK prompt caching.
|
||
Contains objective, case definitions (carveout + special cases), and instructions.
|
||
Carveout definitions are loaded from Constants.VALID_CARVEOUTS.
|
||
Special case definitions are loaded from investment_prompts.json.
|
||
"""
|
||
# Load carveout definitions from Constants
|
||
constants = _get_constants()
|
||
carveout_definitions = constants.VALID_CARVEOUTS
|
||
|
||
# Load special case definitions from investment_prompts.json
|
||
special_case_fields = FieldSet(
|
||
file_path="src/prompts/investment_prompts.json",
|
||
relationship="one_to_n",
|
||
field_type="special_case_check",
|
||
)
|
||
special_case_definitions = {
|
||
field.field_name: field.prompt for field in special_case_fields.fields
|
||
}
|
||
|
||
carveout_text = "\n\n".join(
|
||
[f"{name} : {desc}" for name, desc in carveout_definitions.items()]
|
||
)
|
||
special_case_text = "\n\n".join(
|
||
[f"{name} : {desc}" for name, desc in special_case_definitions.items()]
|
||
)
|
||
|
||
return f"""[OBJECTIVE]
|
||
Examine the Reimbursement Term and identify which case it falls under.
|
||
|
||
[CASE DEFINITIONS]
|
||
Here are the cases, with their definitions:
|
||
Carveout cases: {carveout_text}
|
||
|
||
Special cases: {special_case_text}
|
||
|
||
[INSTRUCTIONS]
|
||
- Determine which case description from the combined list of carveout cases and special cases corresponds to the specific reimbursement method.
|
||
- The Service and Reimbursement will always match one of the listed cases.
|
||
- If either the Service or Reimbursement is an explicit match with any of the cases, then that is the correct case regardless of other information (for example, if the Service says "Outlier", then choose "OUTLIER_TERM" because it is explicit).
|
||
- If multiple cases apply, choose the definition that BEST fits the Reimbursement Term.
|
||
- Always return one value from the combined list of cases above; do not return 'N/A' or 'UNKNOWN'.
|
||
|
||
[OUTPUT FORMAT]
|
||
Briefly explain your answer (including why it was one of carveout cases or special cases), then return your final answer as a single item in a JSON list.
|
||
Example: ["OUTLIER_TERM"] or ["RATE_ESCALATOR"]
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def CARVEOUT_CHECK(
|
||
service_term: str, reimb_term: str
|
||
) -> Tuple[str, Callable[[str], list]]:
|
||
"""Returns ONLY dynamic content for carveout check.
|
||
Call CARVEOUT_CHECK_INSTRUCTION() separately for the cached instruction.
|
||
Note: Case definitions are now included in CARVEOUT_CHECK_INSTRUCTION() for caching.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON list output.
|
||
The parser normalizes the output for CARVEOUT_CD field format (str).
|
||
"""
|
||
prompt = f"""[REIMBURSEMENT TERM]
|
||
Here is the Reimbursement Term to analyze:
|
||
Service: {service_term}
|
||
Reimbursement Term: {reimb_term.replace('"', "'")}"""
|
||
|
||
# Use field-aware parser to normalize CARVEOUT_CD (str format)
|
||
parser = _create_json_list_parser(field_name="CARVEOUT_CD")
|
||
return (prompt, parser)
|
||
|
||
|
||
def EFFECTIVE_DATE_FIX_PROMPT() -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Returns prompt and parser for effective date extraction.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
"""
|
||
prompt = """Extract the effective date of the contract or contract effective date or Effective Date of Agreement or Effective Date of Amendment or the amendment effective date from the given image data. ALSO LOOK FOR dates in phrases that semantically indicate an effective date (may or may not be explictly labelled as effective date), even if they do not exactly match the phrases above.\nExamples include but ARE NOT LIMITED TO:\nPhrases indicating when an contract or amendment begins or takes effect, Phrases specifying the start date of the contract or amendment, Phrases defining when the contract or amendment becomes operational, or Any phrase indicating contract or amendment creation or execution date.\nIt is possible that the contract will specify that the effective date is derived from elsewhere in the contract. Give the date in a JSON FORMAT with AARETE_DERIVED_EFFECTIVE_DATE as the key and effective date value in YYYY/MM/DD format as the value.
|
||
Rules:
|
||
1. Add 2000 to 2-digit years 0-49, 1900 to 50-99
|
||
2. Return N/A if: missing/ambiguous month or year components, or invalid dates. If just the day is missing, assume it's the first of the month.
|
||
3. For ambiguous input cases, assume American format (MM/DD/YYYY) and apply YYYY/MM/DD formatting
|
||
4. Any day values with spaces or leading zeros (e.g., '0 1' or '01') are interpreted as single-digit days
|
||
|
||
Return N/A only for date if NO valid effective date is found. give the answer in the format: {{\"AARETE_DERIVED_EFFECTIVE_DT\": \"YYYY/MM/DD\"}}. nothing else."""
|
||
|
||
return (prompt, _json_dict_parser)
|
||
|
||
|
||
def SPLIT_REIMB_DATES_INSTRUCTION() -> str:
|
||
"""Static instruction for SPLIT_REIMB_DATES prompt caching."""
|
||
return f"""[OBJECTIVE]
|
||
Extract start and end dates from a date range string and return them in YYYY/MM/DD format.
|
||
|
||
[RULES]
|
||
1. If explicit dates are present (e.g., "2023/01/01 - 2023/12/31"):
|
||
- Identify the effective date (start_date) and termination date (end_date)
|
||
- Convert to YYYY/MM/DD format
|
||
|
||
2. If text-based terms are present (e.g., "Initial Term", "Renewal Term", "First Year"):
|
||
- Look for any embedded dates within the text
|
||
- Extract any recognizable date patterns (MM/DD/YYYY, Month DD YYYY, etc.)
|
||
- Convert extracted dates to YYYY/MM/DD format
|
||
|
||
3. If mixed content (e.g., "Initial Term: 01/01/2023 - 12/31/2023"):
|
||
- Extract the date portion and ignore the term labels
|
||
- Convert dates to YYYY/MM/DD format
|
||
|
||
4. If only month and year are available (e.g., "January 2023", "01/2023"):
|
||
- Use the first day of the month (e.g., "January 2023" becomes "2023/01/01")
|
||
- Apply this rule for both start and end dates when applicable
|
||
|
||
5. If only one date is present:
|
||
- Assume it is the start_date and set end_date as "N/A"
|
||
|
||
6. If multiple date ranges are present (separated by commas, e.g., "Effective 5/1/2018 - 4/30/2019, Effective 5/1/2019 - 4/30/2020"):
|
||
- Extract the FIRST (earliest) date range only
|
||
- Use the start_date from the first range and end_date from the first range
|
||
- Ignore all subsequent date ranges
|
||
|
||
7. If no extractable dates are found in the text:
|
||
- Return "N/A" for both start_date and end_date
|
||
|
||
[OUTPUT FORMAT]
|
||
Return ONLY the JSON dictionary with no explanatory text. Use the following format:
|
||
{{
|
||
"start_date": "YYYY/MM/DD",
|
||
"end_date": "YYYY/MM/DD"
|
||
}}
|
||
|
||
Note: Use "N/A" for dates that cannot be determined or extracted from the input text.
|
||
{JSON_DICT_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def SPLIT_REIMB_DATES(date_range: str) -> Tuple[str, Callable[[str], dict]]:
|
||
"""Call SPLIT_REIMB_DATES_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON dict output.
|
||
Note: The output dict contains 'start_date' and 'end_date' keys, which are
|
||
normalized manually in split_reimb_dates() to match REIMB_EFFECTIVE_DT and
|
||
REIMB_TERMINATION_DT field formats (both str).
|
||
"""
|
||
prompt = f"""[DATE RANGE TO EXTRACT]
|
||
{date_range}"""
|
||
|
||
# Use standard parser - normalization happens manually in split_reimb_dates()
|
||
# because the output keys are 'start_date'/'end_date', not the field names
|
||
return (prompt, _json_dict_parser)
|
||
|
||
|
||
def LOB_RELATIONSHIP_INSTRUCTION() -> str:
|
||
"""Static instruction for LOB_RELATIONSHIP prompt caching.
|
||
Contains background, decision rules, and output format.
|
||
"""
|
||
return f"""[OBJECTIVE]
|
||
Analyze an exhibit from a Payer-Provider contract to determine if LOB and Program relationship is Inclusive or Exclusive.
|
||
|
||
[BACKGROUND]
|
||
- In healthcare, there are generally multiple Programs for any given LOB.
|
||
- For example, under the Medicaid LOB, there can be the Programs CHIP, STAR, Medi-Cal, etc.
|
||
- The Exhibit shown contains a number of reimbursement terms, which apply to different LOB and Programs.
|
||
|
||
[DECISION RULES]
|
||
- Inclusive Relationship: When the contract refers to an LOB *AND* the program(s) in the list.
|
||
- Some examples of language indicating an inclusive relationship are: "Medicaid & CHIP", "Medicare and Medicare Advantage", and "Duals, including all special needs programs"
|
||
- Exclusive Relationship: When the contract refers to an LOB and Programs, but the information applies to JUST those specific programs.
|
||
- Some examples of language indicating an exclusive relationship are "Medicaid: CHIP", "Medicare advantage only", and "Duals - only the following SNP programs"
|
||
- If the programs are represented with different sections of the Exhibit under the same higher-level LOB section, this is indicative of an Exclusive relationship.
|
||
- For example, if an exhibit has a header that says "Medicaid & CHIP", but then there are different subsections within the Exhibit for "Medicaid" and "CHIP", this is an Exclusive relationship even though the "&" language is Inclusive.
|
||
|
||
[OUTPUT FORMAT - CRITICAL]
|
||
You MUST format your response as follows:
|
||
1. Briefly explain your reasoning
|
||
2. Then put your final answer as a single item in a JSON list: ["Inclusive"] or ["Exclusive"]
|
||
Example: "The contract shows... ["Inclusive"]"
|
||
Your response MUST include the JSON list with your final answer.
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def LOB_RELATIONSHIP(
|
||
exhibit_text, dynamic_primary_values
|
||
) -> Tuple[str, Callable[[str], list]]:
|
||
"""Returns ONLY dynamic content for LOB relationship analysis.
|
||
Call LOB_RELATIONSHIP_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON list output.
|
||
The parser normalizes the output for LOB_PROGRAM_RELATIONSHIP or
|
||
LOB_PRODUCT_RELATIONSHIP field format (str). Note: This parser is used for
|
||
both fields, so normalization is based on str format.
|
||
"""
|
||
prompt = f"""[LOB AND PROGRAMS IDENTIFIED]
|
||
It was previously identified that the Exhibit contains the following LOB and Programs:
|
||
{dynamic_primary_values}
|
||
|
||
[CONTEXT]
|
||
Here is the Exhibit to be analyzed:
|
||
{exhibit_text.replace('"', "'")}"""
|
||
|
||
# Use field-aware parser to normalize relationship field (str format)
|
||
# Note: This is used for both LOB_PROGRAM_RELATIONSHIP and LOB_PRODUCT_RELATIONSHIP,
|
||
# both of which are str format, so we can use either field name
|
||
parser = _create_json_list_parser(field_name="LOB_PROGRAM_RELATIONSHIP")
|
||
return (prompt, parser)
|
||
|
||
|
||
def DERIVED_TERM_DATE_INSTRUCTION() -> str:
|
||
"""Static instruction for DERIVED_TERM_DATE prompt caching.
|
||
Contains calculation rules and output format.
|
||
"""
|
||
return f"""[OBJECTIVE]
|
||
Determine the initial termination date of the contract from effective date and term duration, excluding any renewals or extensions.
|
||
|
||
[CALCULATION RULES]
|
||
- If termination information comes in as a duration, derive the termination date exclusive of the final day.
|
||
- For example, if effective date is 2023/02/01 and termination information is 'two years', termination date would be 2025/01/31 and not 2025/02/01.
|
||
- If effective date is 2023/02/01 and termination information is 'year-to-year', termination date would be 2024/01/31.
|
||
- If the termination information comes in as a date, return that date.
|
||
- For example, if effective date is 2023/02/01 and termination information is 2024/07/01, return 2024/07/01.
|
||
- Only provide the first termination date, ignoring any automatic or optional renewals.
|
||
|
||
[OUTPUT FORMAT]
|
||
Provide the date in YYYY/MM/DD format only, as a single item in a JSON list.
|
||
Example: ["2024/01/31"] or ["N/A"]
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def DERIVED_TERM_DATE_PROMPT(
|
||
effective_date: str = None, termination_information: str = None
|
||
) -> Tuple[str, Callable[[str], list]]:
|
||
"""Returns ONLY dynamic content for derived term date calculation.
|
||
Call DERIVED_TERM_DATE_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON list output.
|
||
"""
|
||
prompt = f"""[CONTEXT]
|
||
Effective Date: {effective_date}
|
||
Termination Information: {termination_information}"""
|
||
|
||
return (prompt, _json_list_parser)
|
||
|
||
|
||
def SPECIAL_CASE_ASSIGNMENT_INSTRUCTION() -> str:
|
||
"""Static instruction for SPECIAL_CASE_ASSIGNMENT prompt caching.
|
||
Contains objective, rules, and output format.
|
||
"""
|
||
return f"""[OBJECTIVE]
|
||
Analyze a section of a contract to determine which special case term value is most associated with a given reimbursement term.
|
||
|
||
[TASK]
|
||
You will be given:
|
||
1. A section of contract text for context
|
||
2. A reimbursement term (service and reimbursement description)
|
||
3. A numbered list of special case term values
|
||
|
||
Your job is to determine which special case term (by index number) is most closely associated with the reimbursement term based on context clues in the contract text.
|
||
|
||
[RULES]
|
||
- Use context clues from the contract text to match the reimbursement term to the most appropriate special case term
|
||
- If multiple special case terms could apply, choose the one with the strongest contextual connection
|
||
- Return only the integer index of the matching term as a single item in a JSON list
|
||
|
||
[OUTPUT FORMAT]
|
||
Example: ["0"] or ["2"] or ["5"]
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}"""
|
||
|
||
|
||
def SPECIAL_CASE_ASSIGNMENT(
|
||
exhibit_text, reimbursement_dict, special_case_dicts, special_case_term
|
||
) -> Tuple[str, Callable[[str], list]]:
|
||
"""Returns ONLY dynamic content for special case assignment.
|
||
Call SPECIAL_CASE_ASSIGNMENT_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Returns:
|
||
Tuple of (prompt_string, parser_function) where parser expects JSON list output.
|
||
"""
|
||
special_case_list = [
|
||
f"{i} : {special_case_dicts[i][special_case_term]}"
|
||
for i in range(len(special_case_dicts))
|
||
]
|
||
prompt = f"""[CONTEXT]
|
||
Here is the section of the contract:
|
||
{exhibit_text}
|
||
|
||
[REIMBURSEMENT TERM]
|
||
Service: {reimbursement_dict["SERVICE_TERM"]}
|
||
Reimbursement: {reimbursement_dict["REIMB_TERM"]}
|
||
|
||
[{special_case_term} LIST]
|
||
{special_case_list}
|
||
|
||
Return the integer of the {special_case_term} that is most associated with the REIMBURSEMENT TERM."""
|
||
|
||
return (prompt, _json_list_parser)
|
||
|
||
|
||
def AARETE_DERIVED_PAYER_NAME_INSTRUCTION():
|
||
return f"""You are an expert in healthcare entity data normalization.
|
||
Your goal is to take a list of similar payer names and determine the single,
|
||
most accurate canonical legal or brand name.
|
||
|
||
Given variants string contains variations of a single Payer (insurance company) name
|
||
extracted from different contracts. They all refer to the same entity.
|
||
|
||
TASK:
|
||
1. Select or derive the most professional canonical name.
|
||
2. If given STATE_FLAG is FALSE:
|
||
- Collapse regional entities into the national brand (e.g., 'Molina Healthcare').
|
||
- DO NOT remove the word 'State' if it is part of a proper noun (e.g., 'New York State Catholic Health Plan').
|
||
3. If given STATE_FLAG is TRUE:
|
||
- Maintain state distinctions (e.g., 'Molina Healthcare of Texas').
|
||
4. Prefer the 'DBA' (Doing Business As) name if it is present.
|
||
5. Cleaned name should be preferred over original name, but use your judgment to determine if the cleaned name is actually more accurate or if it has removed important context.
|
||
6. Remove excessive noise like "a corporation of...", "an Illinois company", or "d/b/a" prefixes.
|
||
7. Maintain essential legal identifiers only if they are part of the standard brand identity.
|
||
8. Return only the cleaned canonical name without any explanation.
|
||
|
||
Example Variants Input:
|
||
VARIANTS:
|
||
- AvMed Health Plan, Inc
|
||
- AvMED, INC., d/b/a AvMED HEALTH PLANS
|
||
Example Output:
|
||
{{"AARETE_DERIVED_PAYER_NAME":"AvMed Health Plans"}}
|
||
|
||
[OUTPUT FORMAT]
|
||
Return ONLY the JSON dictionary with no explanatory text. Use "AARETE_DERIVED_PAYER_NAME" as the key.
|
||
{JSON_DICT_FORMAT_INSTRUCTIONS}
|
||
"""
|
||
|
||
|
||
def AARETE_DERIVED_PAYER_NAME(
|
||
cluster_rows: list[dict], state_flag: bool
|
||
) -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Constructs the prompt using both original and cleaned names for maximum context.
|
||
"""
|
||
variant_list = []
|
||
for r in cluster_rows:
|
||
# Using your preferred logic to show the LLM both states of the data
|
||
variant = f"- Original: {r['original_name']} (Cleaned: {r['base_name']})"
|
||
variant_list.append(variant)
|
||
|
||
variants_string = "\n".join(variant_list)
|
||
prompt = f"""
|
||
VARIANTS:
|
||
{variants_string}
|
||
STATE_FLAG:
|
||
{state_flag}
|
||
"""
|
||
return (prompt, _json_dict_parser)
|
||
|
||
|
||
def AARETE_DERIVED_PROVIDER_NAME_INSTRUCTION():
|
||
return f"""You are an expert in healthcare provider group name normalization.
|
||
|
||
Your goal is to take a list of similar provider group names and determine
|
||
the single most accurate canonical provider group name.
|
||
|
||
The given variants refer to the same Provider Group entity.
|
||
|
||
TASK:
|
||
1. Select or derive the most professional canonical name.
|
||
2. If given STATE_FLAG is FALSE:
|
||
- Collapse regional entities into the national brand (e.g., 'Molina Healthcare').
|
||
- DO NOT remove the word 'State' if it is part of a proper noun (e.g., 'New York State Catholic Health Plan').
|
||
3. If given STATE_FLAG is TRUE:
|
||
- Maintain state distinctions (e.g., 'Molina Healthcare of Texas').
|
||
4. Prefer the 'DBA' (Doing Business As) name if it is present.
|
||
5. Cleaned name should be preferred over original name, but use your judgment to determine if the cleaned name is actually more accurate or if it has removed important context.
|
||
6. Remove excessive noise like "a corporation of...", "an Illinois company", or "d/b/a" prefixes.
|
||
7. Maintain essential legal identifiers only if they are part of the standard brand identity.
|
||
8. Return only the cleaned canonical name without any explanation.
|
||
|
||
Example Variants Input:
|
||
VARIANTS:
|
||
- Texas Health Physicians Group
|
||
- THPG
|
||
Example Output:
|
||
{{"AARETE_DERIVED_PROVIDER_NAME":"Texas Health Physicians Group"}}
|
||
|
||
[OUTPUT FORMAT]
|
||
Return ONLY the JSON dictionary with key:
|
||
"AARETE_DERIVED_PROVIDER_NAME"
|
||
{JSON_DICT_FORMAT_INSTRUCTIONS}
|
||
"""
|
||
|
||
|
||
def AARETE_DERIVED_PROVIDER_NAME(
|
||
cluster_rows: list[dict], state_flag: bool
|
||
) -> Tuple[str, Callable[[str], dict]]:
|
||
"""
|
||
Construct LLM prompt for provider group canonical selection.
|
||
"""
|
||
|
||
variant_list = []
|
||
|
||
for r in cluster_rows:
|
||
variant = f"- Original: {r['original_name']} (Cleaned: {r['base_name']})"
|
||
variant_list.append(variant)
|
||
|
||
variants_string = "\n".join(variant_list)
|
||
|
||
prompt = f"""
|
||
VARIANTS:
|
||
{variants_string}
|
||
STATE_FLAG:
|
||
{state_flag}
|
||
"""
|
||
|
||
return (prompt, _json_dict_parser)
|
||
|
||
|
||
def SPLIT_SERVICE_TERM(
|
||
service_term: str,
|
||
) -> Tuple[str, Callable[[str], list]]:
|
||
"""Returns prompt for splitting a SERVICE_TERM into components.
|
||
Call SPLIT_SERVICE_TERM_INSTRUCTION() separately for the cached instruction.
|
||
|
||
Args:
|
||
service_term (str): The SERVICE_TERM to be split."""
|
||
|
||
prompt = f"""
|
||
Analyze the following SERVICE_TERM:{service_term}
|
||
"""
|
||
return (prompt, _json_list_parser)
|
||
|
||
|
||
def SPLIT_SERVICE_TERM_INSTRUCTION() -> str:
|
||
return f"""
|
||
[OBJECTIVE]
|
||
Identify and split a SERVICE_TERM into separate Services only when it clearly represents
|
||
multiple independent medical Services.
|
||
|
||
[INPUT]
|
||
A single SERVICE_TERM string.
|
||
|
||
[DEFINITION]
|
||
SERVICE_TERM: Describes the medical service or procedure being reimbursed.
|
||
It can range from very general (e.g., "Covered Services") to very specific
|
||
(e.g., "CPT 98765"), and may include qualifiers such as program names, billing
|
||
codes, reimbursement conditions, visit types, or facility names.
|
||
|
||
[RULES FOR SPLITTING]
|
||
- Only split when there are multiple clearly independent Services present. A Service is
|
||
defined as a medical procedure or category of medical procedures.
|
||
- Each output item must represent a distinct, standalone Service.
|
||
- Preserve original wording exactly (no rephrasing, no expanding abbreviations).
|
||
|
||
[MANDATORY: DO NOT SPLIT THESE CASES]
|
||
|
||
1. **Closely Related or Paired Services**
|
||
These are terms that are grouped because they are near-synonyms, delivery variants,
|
||
or complementary parts of a single clinical service concept. Do not split them.
|
||
|
||
Examples included under this case:
|
||
- Therapy disciplines bundled together as a single offerings block
|
||
(e.g., Physical Therapy, Occupational Therapy, Speech Therapy). Therapeutic program models
|
||
that are grouped in a single SERVICE_TERM listing should generally be kept together, not split, if they
|
||
represent a coherent care bundle.
|
||
- Intensive or program variants of the same foundational model when grouped as one
|
||
phrase (e.g., Residential and Intensive variants under a single program category)
|
||
- Delivery setting pairs (e.g., Inpatient and Outpatient Services)
|
||
- Closely related procedure variants (e.g., Ambulatory Surgical Center and
|
||
Hospital Ambulatory Surgical Center services)
|
||
- Provider role combinations (e.g., Physician Assistant and Nurse Practitioner services)
|
||
- Facility/setting listings (e.g., Behavioral Health Outpatient Clinics/Integrated Clinics, Community Service Agencies)
|
||
|
||
2. **Code Category Descriptions**
|
||
When a SERVICE_TERM begins with a code category prefix (such as a billing code
|
||
family or procedure code set, HCPCS codes), the items listed after it describe what
|
||
falls under that category — not separate services. Do not split.
|
||
|
||
3. **Procedure Groups Tied to Shared Codes or Groupers**
|
||
When a SERVICE_TERM contains multiple procedures tied together by shared
|
||
billing codes, diagnosis-related groupers, or other classification codes,
|
||
do not split. Splitting would sever the reimbursement context.
|
||
|
||
4. **Enumerated Descriptions of a Single Service Concept**
|
||
If a comma-separated list is describing components, sub-items, or examples
|
||
of a single named service or code group, do not split. The list is clarifying
|
||
what is included, not defining separate services.
|
||
|
||
5. **Lines of Business, Programs, or Plan Names**
|
||
Do not split when listed items refer to Lines of Business, Programs, Plan names,
|
||
or Program-specific service variants rather than distinct medical services.
|
||
|
||
6. **Service and Delivery Modifier Combinations**
|
||
If one term modifies how or where another service is delivered (e.g., a setting,
|
||
mode, or method qualifier), treat the full phrase as a single service rather
|
||
than splitting the modifier from the service.
|
||
|
||
7. **Facility or Setting Types Listed Together**
|
||
If the listed items describe types of facilities, clinic settings, or agency
|
||
types rather than distinct medical services, do not split. The list is
|
||
describing where or through whom a service is delivered, not separate services.
|
||
This applies even when a program name or reimbursement prefix precedes the list.
|
||
|
||
[WHEN SPLITTING IS APPROPRIATE]
|
||
Only split when the items are clearly and independently distinct medical services
|
||
that would be separately contracted or reimbursed, and where no shared context,
|
||
code grouping, or qualifier is lost by separating them.
|
||
|
||
[CRITICAL CONTEXT PRESERVATION]
|
||
- DO NOT drop important qualifiers such as:
|
||
• Conditions (e.g., "paid when item is over $500")
|
||
• Billing codes, procedure codes, diagnosis groupers, or numeric ranges
|
||
• Facility names or locations
|
||
• Program or plan names
|
||
• Visit type qualifiers (e.g., "Per Visit", "Inpatient")
|
||
• Payment rules or reimbursement conditions
|
||
- If such context applies to all split Services, APPEND the shared context to
|
||
each split item.
|
||
- If context applies only to part of the string, retain it with the most
|
||
relevant Service.
|
||
- Never return overly reduced terms that lose meaning.
|
||
|
||
[CLEANING]
|
||
- Trim whitespace and unnecessary punctuation only.
|
||
- Do not alter wording beyond splitting.
|
||
|
||
Input: "Home Health, Hospice, Skilled Nursing Facility, Dialysis"
|
||
Output: ["Home Health", "Hospice", "Skilled Nursing Facility", "Dialysis"]
|
||
Reason: Four clearly distinct, independently reimbursable service categories.
|
||
|
||
Input: "Heart and Soul Hospice LLC home hospice Covered Services"
|
||
Output: [
|
||
"Heart and Soul Hospice LLC Home Covered Services",
|
||
"Heart and Soul Hospice LLC Hospice Covered Services"
|
||
]
|
||
Reason: Two distinct services (home and hospice) with shared context (Heart and Soul Hospice LLC, Covered Services) — split and append shared context to each.
|
||
|
||
Input: "TFC, ITFC, and Multi Systemic Therapy"
|
||
Output: ["TFC, ITFC, and Multi Systemic Therapy"]
|
||
Reason: Therapy bundle — do not split.
|
||
|
||
Input: "Billing Code Group A: medical and surgical supplies, diabetic supplies and shoes, breast pumps"
|
||
Output: ["Billing Code Group A: medical and surgical supplies, diabetic supplies and shoes, breast pumps"]
|
||
Reason: Items after a code prefix describe scope of one coded SERVICE_TERM — do not split.
|
||
|
||
Input: "HCPCS E-Codes oxygen and oxygen equipment, ventilators, CPAP/BiPAP, nebulizers/compressors/humidifiers"
|
||
Output: ["HCPCS E-Codes oxygen and oxygen equipment, ventilators, CPAP/BiPAP, nebulizers/compressors/humidifiers"]
|
||
Reason: Items after a code prefix describe scope of one coded SERVICE_TERM — do not split.
|
||
|
||
Input: "Craniotomy and Endovascular Intracranial Procedures Diagnosis-Related Group 025, 026, 027"
|
||
Output: ["Craniotomy and Endovascular Intracranial Procedures Diagnosis-Related Group 025, 026, 027"]
|
||
Reason: Procedures share grouper codes — splitting would sever reimbursement context.
|
||
|
||
Input: "CHIP and STAR Outpatient Services"
|
||
Output: ["CHIP and STAR Outpatient Services"]
|
||
Reason: Program names, not distinct services.
|
||
|
||
Input: "Inpatient and Outpatient Services"
|
||
Output: ["Inpatient and Outpatient Services"]
|
||
Reason: Delivery setting variants of the same service — not independently distinct.
|
||
|
||
Input: "Physician Assistant and Nurse Practitioner services for Health Benefit Exchange"
|
||
Output: ["Physician Assistant and Nurse Practitioner services for Health Benefit Exchange"]
|
||
Reason: Provider types, not service types — do not split.
|
||
|
||
Input: "Ambulatory Surgical Center and Hospital Ambulatory Surgical Center services"
|
||
Output: ["Ambulatory Surgical Center and Hospital Ambulatory Surgical Center services"]
|
||
Reason: Closely related facility type variants of the same service — do not split.
|
||
|
||
Input: "Behavioral Health Outpatient Clinics/Integrated Clinics, Community Service Agencies"
|
||
Output: ["Behavioral Health Outpatient Clinics/Integrated Clinics, Community Service Agencies"]
|
||
Reason: Facility and agency setting types, not distinct reimbursable services.
|
||
|
||
Input: "facility and professional Covered Services rendered to a Covered Person"
|
||
Output: ["facility and professional Covered Services rendered to a Covered Person"]
|
||
Reason: Professional and facility variants of the same service category — do not split.
|
||
|
||
Input: "DDD Reimbursement - Behavioral Health Outpatient Clinics/Integrated Clinics, Community Service Agencies"
|
||
Output: ["DDD Reimbursement - Behavioral Health Outpatient Clinics/Integrated Clinics, Community Service Agencies"]
|
||
Reason: Program-prefixed list of facility settings — not distinct services.
|
||
|
||
Input: "Primary and Specialty Care Services for Medicaid Managed Care, Health And Recovery Plan and Essential Plan Program"
|
||
Output: [
|
||
"Primary Care Services for Medicaid Managed Care, Health And Recovery Plan and Essential Plan Program",
|
||
"Specialty Care Services for Medicaid Managed Care, Health And Recovery Plan and Essential Plan Program"
|
||
]
|
||
Reason: Two distinct clinical service categories sharing trailing program context — split and append shared context to each.
|
||
|
||
[FINAL GUIDANCE]
|
||
If uncertain, do NOT split. The bar for splitting should be high: items must be
|
||
clearly independent medical services that stand alone without losing meaning or context.
|
||
|
||
[OUTPUT FORMAT]
|
||
{JSON_LIST_FORMAT_INSTRUCTIONS}
|
||
"""
|