7ffd62fd8e
Bugfix/highmark methodology breakout * fix reimb_term rows for dates * reimb_primary prompt update * prompt changes * Merge remote-tracking branch 'origin/main' into bugfix/highmark-methodology-breakout * prompt changes * prompt updates * Merge branch 'main' into bugfix/highmark-methodology-breakout * prompt updates * Prompt updates Approved-by: Katon Minhas
1436 lines
72 KiB
Python
1436 lines
72 KiB
Python
|
|
import src.config as config
|
|
from src.prompts.fieldset import FieldSet
|
|
|
|
PIPE_FORMAT_INSTRUCTIONS = "Briefly explain your answer, then enclose your final answer in |pipes|. If the requested information doesn't apply to this context, return |N/A|. If the information should exist but cannot be clearly identified, return |UNKNOWN|."
|
|
|
|
def get_cacheable_instructions(constants=None):
|
|
"""
|
|
Returns a dictionary of all cacheable instructions for cache warming.
|
|
This should be called once before parallel processing starts.
|
|
|
|
Args:
|
|
constants: Constants object (optional, needed for field prompts)
|
|
|
|
Returns:
|
|
dict: Dictionary mapping cache keys to instruction text
|
|
"""
|
|
|
|
cacheable_instructions = {}
|
|
|
|
# REIMBURSEMENT_PRIMARY instruction
|
|
if config.FIELDS in ['all', 'one_to_n']:
|
|
try:
|
|
reimbursement_fields = FieldSet(
|
|
config.FIELD_JSON_PATH, field_type="reimbursement_primary"
|
|
)
|
|
if constants:
|
|
field_prompts = reimbursement_fields.print_prompt_dict(constants)
|
|
else:
|
|
field_prompts = reimbursement_fields.print_prompt_dict()
|
|
|
|
instruction, _ = REIMBURSEMENT_PRIMARY("", field_prompts)
|
|
cacheable_instructions["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:
|
|
methodology_fields = FieldSet(
|
|
config.FIELD_JSON_PATH, field_type="methodology_breakout"
|
|
)
|
|
if constants:
|
|
field_prompts = methodology_fields.print_prompt_dict(constants)
|
|
else:
|
|
field_prompts = methodology_fields.print_prompt_dict()
|
|
|
|
instruction, _ = METHODOLOGY_BREAKOUT("", field_prompts)
|
|
cacheable_instructions["METHODOLOGY_BREAKOUT"] = instruction
|
|
except Exception as e:
|
|
pass
|
|
|
|
# OUTLIER_BREAKOUT instruction
|
|
if config.FIELDS in ['all', 'one_to_n']:
|
|
try:
|
|
instruction, _ = OUTLIER_BREAKOUT("")
|
|
cacheable_instructions["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()
|
|
cacheable_instructions["EXHIBIT_LEVEL_LESSER_OF"] = EXHIBIT_LEVEL_LESSER_OF_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["SPLIT_REIMBURSEMENTS"] = SPLIT_REIMBURSEMENTS_INSTRUCTION()
|
|
cacheable_instructions["CHECK_REDUNDANT_LESSER"] = CHECK_REDUNDANT_LESSER_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()
|
|
except Exception:
|
|
pass
|
|
|
|
# TIN_NPI_TEMPLATE instruction
|
|
if config.FIELDS in ['all', 'one_to_one']:
|
|
try:
|
|
provider_fields = FieldSet(config.FIELD_JSON_PATH, field_type="provider_info")
|
|
field_prompts = provider_fields.print_prompt_dict()
|
|
instruction, _ = TIN_NPI_TEMPLATE("", field_prompts)
|
|
cacheable_instructions["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()
|
|
cacheable_instructions["ONE_TO_ONE_MULTI_FIELD"] = ONE_TO_ONE_MULTI_FIELD_INSTRUCTION()
|
|
except Exception:
|
|
pass
|
|
|
|
return cacheable_instructions
|
|
|
|
|
|
#####################################################################################
|
|
################################## PRIMARY PROMPTS ##################################
|
|
#####################################################################################
|
|
|
|
|
|
def EXHIBIT_LEVEL(context, fields):
|
|
return f"""Extract attributes from a section of a contract.
|
|
|
|
[FORMATTING INSTRUCTIONS AND DESIRED OUTPUT]
|
|
Return a json dictionary with the following attributes. It is possible that the page will not have some of the attributes. When this is the case, return N/A.
|
|
|
|
Here are the attributes to be included in the dictionary, and instructions on how to correctly answer:
|
|
{fields}
|
|
|
|
[GENERAL INSTRUCTIONS]
|
|
For each field, return ALL correct answers found. There may be more than one correct answer. If there are multiple, return them in a pipe-separated list ("|").
|
|
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.
|
|
|
|
Here is the text to analyze:
|
|
|
|
{context.replace('"', "'")}
|
|
|
|
Briefly explain your answer, then put the final answer in the properly-formatted JSON dictionary.
|
|
"""
|
|
|
|
|
|
def EXHIBIT_LEVEL_INSTRUCTION() -> str:
|
|
return (
|
|
"[OBJECTIVE]\n"
|
|
"Extract structured attributes from contract text. Follow formatting and output rules in the prompt.\n"
|
|
"Be consistent and deterministic; avoid creative paraphrasing."
|
|
)
|
|
|
|
|
|
def EXHIBIT_LEVEL_LESSER_OF(context):
|
|
return f"""Analyze a section of a contract.
|
|
|
|
Look for exhibit-wide payment constraints that apply universally to ALL services in this exhibit, regardless of service category or line of business.
|
|
|
|
Valid exhibit-wide constraints have language like:
|
|
- 'All services under this exhibit are subject to the lesser of...'
|
|
- 'Except as otherwise provided in this Exhibit, the Allowed Amount for Covered Services shall be the lesser of...'
|
|
- 'Payment for any service in this exhibit shall not exceed...'
|
|
- 'At no time shall Plan pay an amount that exceeds...'
|
|
|
|
DO NOT extract constraints that:
|
|
- Apply only to specific lines of business or service categories
|
|
- Are part of individual service methodologies
|
|
|
|
If you find a true exhibit-wide constraint, return the component that represents the actual payment limitation, excluding administrative processing language such as:
|
|
- 'less any applicable co-payments, deductibles and coinsurance'
|
|
- 'minus patient responsibility amounts'
|
|
|
|
**GUIDANCE FOR ABBREVIATED TERMS:**
|
|
- 'billed' can be expanded to 'Provider's billed charges'
|
|
- 'allowable' typically refers to amounts defined elsewhere in the contract and is not an acceptable exhibit-wide constraint due to its nonspecificity
|
|
- Other generic terms like 'contracted rate' or 'negotiated amount' should be avoided unless they represent specific, quantifiable constraints
|
|
- Specific percentages of external fee schedules (e.g., '105% of Medicare rates') are acceptable constraints
|
|
|
|
Examples:
|
|
- 'all rates will be reimbursed at the lesser of billed or allowable' → return 'Provider's billed charges'
|
|
- 'Lesser of 105% of Medicare rates or allowable charges' → return '105% of Medicare rates'
|
|
- 'Lesser of Provider's charges or contracted rates' → return 'Provider's billed charges'
|
|
- 'At no time shall payment exceed Provider's billed charges' → return 'Provider's billed charges'
|
|
|
|
If no exhibit-wide constraint exists, return 'N/A'.
|
|
|
|
Here is the Exhibit to analyze:
|
|
{context}
|
|
|
|
Briefly explain your answer, then put your final answer in |pipes|
|
|
"""
|
|
|
|
|
|
def EXHIBIT_LEVEL_LESSER_OF_INSTRUCTION() -> str:
|
|
return (
|
|
"[OBJECTIVE]\n"
|
|
"Identify presence of exhibit-level 'lesser of' or 'not to exceed' statements. Return answer in pipes."
|
|
)
|
|
|
|
|
|
def DYNAMIC_PRIMARY_HEADER(context, field_name, field_prompt):
|
|
return f"""Extract attributes from the provided text, paying close attention to detail.
|
|
|
|
[ATTRIBUTE TO EXTRACT]
|
|
Here is the attribute, and instructions on how to correctly answer:
|
|
{field_name} : {field_prompt}
|
|
|
|
[GENERAL INSTRUCTIONS]
|
|
- Return all valid, explicitly stated values. If multiple values are found, list them separated by commas.
|
|
- If none of the valid values appear explicitly in the text, return N/A.
|
|
- Do not make assumptions. Only write an answer if it is clearly and explicitly stated.
|
|
|
|
Here is the text to analyze:
|
|
|
|
{context.replace('"', "'")}
|
|
|
|
Briefly explain your answer before putting the final answer in |pipes|.
|
|
"""
|
|
|
|
|
|
def DYNAMIC_PRIMARY_TEXT(context, field_name, field_prompt):
|
|
return f"""Extract attributes from the provided text, paying close attention to detail.
|
|
|
|
[ATTRIBUTE TO EXTRACT]
|
|
Here is the attribute, and instructions on how to correctly answer:
|
|
{field_name} : {field_prompt}
|
|
|
|
[GENERAL INSTRUCTIONS]
|
|
- Return all valid, explicitly stated values. If multiple values are found, list them separated by commas.
|
|
- 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".
|
|
|
|
Here is the text to analyze:
|
|
|
|
{context.replace('"', "'")}
|
|
|
|
Briefly explain your answer before putting the final answer in |pipes|.
|
|
"""
|
|
|
|
def REIMB_DATES_ASSIGNMENT(service_term: str, reimb_term: str, field_prompt: str, exhibit_text_simplified: str, page_num: str):
|
|
"""
|
|
Specialized prompt for assigning REIMB_DATES to specific reimbursement terms.
|
|
|
|
This is MORE SPECIFIC than the discovery prompt and focuses on assigning the
|
|
correct date range to each individual reimbursement term based on context.
|
|
"""
|
|
|
|
prompt = f"""[OBJECTIVE]
|
|
Identify which date range applies to a specific Reimbursement Term.
|
|
|
|
[INSTRUCTIONS]
|
|
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"
|
|
|
|
[REIMB_DATES FIELD DEFINITION]
|
|
{field_prompt}
|
|
|
|
[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]
|
|
|
|
[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}"
|
|
|
|
[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 object 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".
|
|
"""
|
|
|
|
return prompt
|
|
|
|
|
|
def DYNAMIC_ASSIGNMENT(service_term: str, reimb_term: str, field_name: str, field_prompt: str, exhibit_text_simplified: str, page_num: str):
|
|
|
|
# Set up as prompt, instruction to allow for Page-level caching later
|
|
prompt = 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.
|
|
|
|
CRITICAL CONSIDERATIONS:
|
|
1. Look for what {field_name} 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 as a basis for their calculation. The presence of 'Medicaid' as a rate does not on it's own guarantee that the Reimbursement Term is 'Medicaid'.
|
|
|
|
2. USE THE SERVICE_TERM AS PRIMARY 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.
|
|
|
|
3. 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...").
|
|
|
|
4. 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.
|
|
|
|
5. "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.
|
|
|
|
[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]
|
|
"""
|
|
instruction = 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}
|
|
|
|
[OUTPUT FORMAT]
|
|
Briefly explain your answer, return a properly-formatted JSON object where the key is the field name `{field_name}` and the value is the correct answer. If there is more than one correct answer, return them in a comma-separated list. If there is no correct answer, return 'N/A'
|
|
"""
|
|
|
|
return prompt + instruction
|
|
|
|
|
|
def REIMBURSEMENT_PRIMARY(context):
|
|
instruction = 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!
|
|
- 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 provide ANY relevant information (like code type, provider type, hospital name, etc), include that in the SERVICE_TERM or REIMB_TERM as appropriate.
|
|
- 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.
|
|
- ALWAYS include all relevant percentages and dollar values. If there's even a slight chance that the number is relevant, include it.
|
|
- For each REIMB_TERM, include any additional information mentioned that will be useful in determining reimbursement for the service, e.g., "payment amount will be determined by multiplying it by the DRG weight". It should be appended to relevant REIMB_TERM and not added as a separate entry.
|
|
- If different reimbursement percentages or dollar values are mentioned for different effective dates, create separate entries for each effective date.
|
|
|
|
[EXCLUSIONS]
|
|
- NEVER return reimbursement terms that are labeled as Examples or appear in an EXAMPLE section. 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
|
|
|
|
[ANALYSIS CONTEXT]"""
|
|
|
|
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.
|
|
"""
|
|
|
|
return instruction, prompt
|
|
|
|
def LESSER_OF_DISTRIBUTION(service_term: str, reimb_term: str, exhibit_text_simplified: str, page_num: str):
|
|
return f"""[OBJECTIVE]
|
|
Examine an Exhibit of a Payer-Provider contract. Find any overarching 'Lesser of' or 'not to exceed' statements that apply to the given Reimbursement Term.
|
|
|
|
['LESSER OF' OR 'NOT TO EXCEED' LANGUAGE]
|
|
They specify that a given medical Service should be paid at the lesser of two or more values or should not exceed a value (e.g. billed charges). Sometimes they are written clearly, other times there will be one sentence that applies to the entire Exhibit, or a portion of the Exhibit. Your job is to find such a statement (if it exists) and determine if it applies to the given Service.
|
|
|
|
This involves 3 steps:
|
|
[1. IDENTIFY OVERARCHING 'LESSER OF' OR 'NOT TO EXCEED' STATEMENTS]
|
|
There may be one or more overarching 'Lesser of' statements. Such statements will include language like:
|
|
- '...lesser of the rates listed below and ...'
|
|
- 'All services under this exhibit are subject to the lesser of...'
|
|
- 'Except as otherwise provided in this Exhibit, the Allowed Amount for Covered Services shall be the lesser of...'
|
|
- 'Payment for any service in this exhibit shall not exceed...'
|
|
- 'At no time shall Plan pay an amount that exceeds...'
|
|
|
|
[2. DETERMINE IF THE STATEMENT APPLIES TO THE SERVICE]
|
|
If there are multiple overarching lesser of/ not to exceed statements, it is crucial to NOT assign the one that doesn't apply to this particular service.
|
|
Look carefully to ensure that the intended meaning of the text is to apply a Lesser of/ not to exceed to the given Service.
|
|
|
|
[3. DISTRIBUTE THE LESSER OF/ NOT TO EXCEED STATEMENT to THE SERVICE AND METHODOLOGY PAIR]
|
|
If you find that an overarching 'Lesser of' or 'not to exceed' statement applies to the given Service, you'll need to perform a concatenation (or substitution if required) to create one meaningful statement. This is the 'Lesser of'/ 'not to exceed' distribution.
|
|
|
|
For example, if you see "The Services in this exhibit will be paid at the lesser of the rates listed below and 100% of Billed Charges"
|
|
and you determine that the above statement applies to the terms below:
|
|
SERVICE: "Radiology Services"
|
|
METHODOLOGY:"$100"
|
|
then you need to concatenate the SERVICE and METHODOLOGY values appropriately, for a final answer of:
|
|
"Radiology Services will be paid at the lesser of $100 and 100% of Billed Charges"
|
|
|
|
[CONTEXT]
|
|
Here is the Service and Methodology to evaluate:
|
|
SERVICE: {service_term}
|
|
METHODOLOGY: {reimb_term}
|
|
|
|
Here is the Exhibit Text (the Service can be found on page {page_num}):
|
|
[START TEXT]
|
|
{exhibit_text_simplified}
|
|
[END TEXT]
|
|
|
|
[OUTPUT FORMAT]
|
|
If there is no overarching lesser of Statement, return 'N/A'
|
|
If there is an overarching lesser of Statement, but it doesn't apply to the Service, return 'N/A'.
|
|
If there is an overarching lesser of Statement that applies, return the full Statement with the Service and Methodology appropriately concatenated.
|
|
|
|
Briefly explain your answer, then put your final answer in pipes.
|
|
"""
|
|
|
|
def LESSER_OF_CHECK(service_term: str, reimb_term: str):
|
|
return f"""[OBJECTIVE]
|
|
Examine the following 'lesser of' statement, taken from the Compensation Schedule of a Payer-Provider contract.
|
|
Determine if the statement is an overarching 'lesser of'
|
|
|
|
[INSTRUCTIONS]
|
|
Overarching 'Lesser of' statements are those which do not stand alone as a meaningful statement and instead refer to Services elsewhere in the document.
|
|
They may include language like "for all Services in this Exhibit...", "for the rates listed below...", and other language that refers specifically to Services located in other part of the Exhibit.
|
|
This is in contrast to a Stand-Alone 'lesser of' statement, which makes sense as a complete statement by itself.
|
|
|
|
The distinction between the two options can be subtle. Here are two examples to illustrate:
|
|
[Y Example]
|
|
Service: "Covered Services"
|
|
Reimbursement: "Covered Services in this Exhibit will be paid at the lesser of a) the rates listed below or b) 100% of the Medicare fee schedule.
|
|
Answer: "Y"
|
|
This is an overarching lesser of because it makes specific references to rates elsewhere in the document.
|
|
|
|
[N Example]
|
|
Service: "Covered Services"
|
|
Reimbursement: "Except as otherwise mentioned, Covered Services in this Exhibit will be paid at the lesser of a) Allowable Charges or b) 100% of the Medicare fee schedule"
|
|
Answer: "N"
|
|
This is not an overarching lesser of because both rates presented are present in the Reimbursement itself. There are no references to rates elsewhere (the 'except as otherwise mentioned' clause is an exclusion, which is different)
|
|
|
|
|
|
[CONTEXT]
|
|
Service: {service_term}
|
|
Reimbursement: {reimb_term}
|
|
|
|
[OUTPUT FORMAT]
|
|
- If the Service and Reimbursement Term represent an overarching 'lesser of', return 'Y'
|
|
- If the Service and Reimbursement Term are a stand-alone 'lesser of', return 'N'
|
|
|
|
Briefly explain your answer, then put your final answer in |pipes|.
|
|
"""
|
|
|
|
|
|
#####################################################################################
|
|
################################# BREAKOUT PROMPTS ##################################
|
|
#####################################################################################
|
|
|
|
def METHODOLOGY_BREAKOUT(service_term: str, reimb_term: str, questions: str):
|
|
"""
|
|
This prompt is used for the fields with field_type="methodology_breakout" + any other fields we need to run based on the result of special case.
|
|
|
|
All fields run by this prompt will end up becoming their own row, rather than being distributed across the rows of the exhibit
|
|
"""
|
|
|
|
instruction = 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", or similar comparative logic must be represented as its own dictionary in the output list.
|
|
- **IMPORTANT**: For each component, classify the `AARETE_DERIVED_REIMB_METHOD` based on the INDIVIDUAL component, not the overall structure:
|
|
- Dollar amounts (e.g., "$1,180.92") → "Flat Rate"
|
|
- Percentages of charges (e.g., "100% of billed charges") → "Billed Charges"
|
|
- Fee schedule references (e.g., "Medicare rate") → "Fee Schedule"
|
|
- If the methodology includes **additional reimbursement terms** beyond the "lesser of" structure (e.g., administrative fees, 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.
|
|
- If the methodology includes two reimbursement methodologies that are comparative or equal, such as "70% of Billed Charges equal to Provider's Acquisition Cost plus five percent (5%)", each should be represented as a separate dictionary in the output list.
|
|
- 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.
|
|
|
|
In the case of 'RBRVS', 'RVU', or American Society of Anesthesiology ('ASA'), any dollar amount mentioned refers to a conversion factor, and not to a flat fee rate.
|
|
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.
|
|
|
|
[OUTPUT FORMAT]
|
|
The output should always be a JSON list/array of dictionaries, even when there is only one methodology.
|
|
For any fields that don't apply to a particular methodology, use "N/A" as the value.
|
|
|
|
[CONTEXT]"""
|
|
|
|
prompt = f"""Here is the text to analyze and respond to:
|
|
Service Term: {service_term}
|
|
Reimbursement Term: {reimb_term}
|
|
|
|
[OUTPUT FORMAT]
|
|
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."""
|
|
|
|
# Include dynamic fields in the prompt (not instruction) for cache reuse
|
|
prompt += f"\n\n[FIELDS]\nPopulate a list of JSON dictionaries with the following fields:\n{questions}\n"
|
|
|
|
return instruction, prompt
|
|
|
|
|
|
def FEE_SCHEDULE_BREAKOUT(methodology, fee_schedule, questions):
|
|
return f"""[OBJECTIVE]
|
|
Analyze a given reimbursement methodology from a Payer-Provider contract:
|
|
|
|
[FIELDS]
|
|
Populate a JSON dictionary with the following fields:
|
|
{questions}
|
|
Use the text in the methodology to populate a JSON dictionary with the following fields, specifically for {fee_schedule} Fee Schedule:
|
|
|
|
[CONTEXT]
|
|
Here is the text to analyze and respond to:
|
|
Methodology: {methodology.replace('"', "'")}
|
|
|
|
[OUTPUT FORMAT]
|
|
Write your JSON dictionary below:
|
|
"""
|
|
|
|
|
|
def GROUPER_BREAKOUT(service, term, questions):
|
|
return f"""[OBJECTIVE]
|
|
Analyze the given service and reimbursement term to identify grouper-based reimbursement information, and answer the following question(s):
|
|
|
|
[FIELDS]
|
|
Populate a JSON dictionary with the following fields:
|
|
{questions}
|
|
|
|
[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
|
|
|
|
[CONTEXT]
|
|
Here are the service and reimbursement terms to analyze:
|
|
SERVICE: {service}
|
|
REIMBURSEMENT TERM: {term}
|
|
|
|
[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 SPECIAL_CASE_BREAKOUT(term, questions):
|
|
"""
|
|
Generic prompt for special case breakout fields when no additional descriptions are needed.
|
|
"""
|
|
return 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.
|
|
"""
|
|
|
|
|
|
def RATE_ESCALATOR_BREAKOUT(term):
|
|
"""
|
|
Creates a prompt to extract rate escalator field information from a rate escalator statement.
|
|
|
|
Args:
|
|
rate_escalator_statement: The text containing rate escalator information
|
|
rate_escalator_questions: Dictionary or list of questions with instructions for each field
|
|
|
|
Returns:
|
|
Formatted prompt string for rate escalator field extraction
|
|
"""
|
|
questions = FieldSet(
|
|
config.FIELD_JSON_PATH, field_type="rate_escalator_breakout"
|
|
).print_prompt_dict()
|
|
return SPECIAL_CASE_BREAKOUT(term, questions)
|
|
|
|
|
|
def TRIGGER_CAP_BREAKOUT(term):
|
|
questions = FieldSet(
|
|
config.FIELD_JSON_PATH, field_type="trigger_cap_breakout"
|
|
).print_prompt_dict()
|
|
return SPECIAL_CASE_BREAKOUT(term, questions)
|
|
|
|
|
|
def OUTLIER_BREAKOUT(term):
|
|
questions = FieldSet(
|
|
config.FIELD_JSON_PATH, field_type="outlier_breakout"
|
|
).print_prompt_dict()
|
|
|
|
instruction = 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
|
|
"""
|
|
|
|
prompt = f"""
|
|
[FIELDS]
|
|
{questions}
|
|
|
|
[CONTEXT]
|
|
Here is the text to analyze and respond to:
|
|
{term}
|
|
|
|
[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'"""
|
|
|
|
return instruction, prompt
|
|
|
|
def FACILITY_ADJUSTMENT_BREAKOUT(term):
|
|
questions = FieldSet(
|
|
config.FIELD_JSON_PATH, field_type="facility_adjustment_breakout"
|
|
).print_prompt_dict()
|
|
return SPECIAL_CASE_BREAKOUT(term, questions)
|
|
|
|
|
|
def SEQUESTRATION_BREAKOUT(term):
|
|
questions = FieldSet(
|
|
config.FIELD_JSON_PATH, field_type="sequestration_breakout"
|
|
).print_prompt_dict()
|
|
return SPECIAL_CASE_BREAKOUT(term, questions)
|
|
|
|
|
|
def DISCOUNT_BREAKOUT(term):
|
|
questions = FieldSet(
|
|
config.FIELD_JSON_PATH, field_type="discount_breakout"
|
|
).print_prompt_dict()
|
|
return SPECIAL_CASE_BREAKOUT(term, questions)
|
|
|
|
|
|
def PREMIUM_BREAKOUT(term):
|
|
questions = FieldSet(
|
|
config.FIELD_JSON_PATH, field_type="premium_breakout"
|
|
).print_prompt_dict()
|
|
return SPECIAL_CASE_BREAKOUT(term, questions)
|
|
|
|
|
|
def ADDITION_BREAKOUT(term):
|
|
questions = FieldSet(
|
|
config.FIELD_JSON_PATH, field_type="addition_breakout"
|
|
).print_prompt_dict()
|
|
return SPECIAL_CASE_BREAKOUT(term, questions)
|
|
|
|
|
|
def STOP_LOSS_BREAKOUT(term):
|
|
questions = FieldSet(
|
|
config.FIELD_JSON_PATH, field_type="stop_loss_breakout"
|
|
).print_prompt_dict()
|
|
return SPECIAL_CASE_BREAKOUT(term, questions)
|
|
|
|
|
|
#####################################################################################
|
|
################################### CODE PROMPTS ####################################
|
|
#####################################################################################
|
|
|
|
|
|
def CODE_EXPLICIT(service, methodology, questions):
|
|
return f"""Analyze a given medical service:
|
|
|
|
Use the text in the Service and Methodology to populate a JSON dictionary with the following fields:
|
|
|
|
{questions}.
|
|
|
|
- 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.
|
|
- 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 gives a range of codes, without listing each code in the range 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.
|
|
|
|
Here is the Service and Methodology to analyze:
|
|
Service: {service.replace('"', "'")}
|
|
Methodology: {methodology.replace('"', "'")}
|
|
|
|
Briefly explain your answer before putting the final answer in JSON dictionary format.
|
|
"""
|
|
|
|
|
|
def CODE_CATEGORY(service, choices):
|
|
return f"""Analyze a given medical Service Term.
|
|
|
|
What Procedure Code description does the Service Term describe?
|
|
|
|
[VALID DESCRIPTIONS]
|
|
Here are the descriptions to choose from:
|
|
{choices}
|
|
|
|
[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 in a 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 (e.g. A0000-A9999, etc.)
|
|
|
|
[CONTEXT]
|
|
Here is the Service Term to analyze:
|
|
{service.replace('"', "'")}
|
|
|
|
[OUTPUT FORMAT]
|
|
Explain your answer in 1-2 sentences. Write your final answer in JSON list format.
|
|
"""
|
|
|
|
|
|
def CODE_IMPLICIT_SPECIAL(service):
|
|
return f"""Analyze a given medical Service Term.
|
|
|
|
[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".
|
|
|
|
Here is the Service to analyze:
|
|
{service}
|
|
|
|
Briefly explain your answer, but put your final answer in |pipes|
|
|
"""
|
|
|
|
|
|
def CODE_IMPLICIT(service, choices):
|
|
|
|
return f"""Analyze a given medical Service Term.
|
|
|
|
What Procedure Code description does the Service Term describe?
|
|
|
|
[VALID DESCRIPTIONS]
|
|
Here are the descriptions to choose from:
|
|
{choices}
|
|
|
|
[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 in a 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 "[]".
|
|
|
|
[CONTEXT]
|
|
Here is the Service Term to analyze:
|
|
{service.replace('"', "'")}
|
|
|
|
[OUTPUT FORMAT]
|
|
Explain your answer, ensuring each point in the instructions is addressed. Then return your final answer in JSON list format.
|
|
"""
|
|
|
|
|
|
def CODE_LAST_CHECK(service):
|
|
return f"""Analyze the given healthcare-related term.
|
|
|
|
Determine if the term is a specific medical service or category of services. If so, return "Specific".
|
|
If the term is a generic medical service, not a service at all, the name of a hospital or other provider, or other nonsensical or non-service phrase, return "Generic".
|
|
|
|
Here is the service to analyze: {service}
|
|
|
|
Briefly explain your answer, then put your final answer in |pipes|.
|
|
"""
|
|
|
|
|
|
def FILL_BILL_TYPE(service, choices):
|
|
return f"""Analyze a given medical Service Term.
|
|
|
|
What Bill Type Code Description does the Service Term fall under?
|
|
|
|
[VALID DESCRIPTIONS]
|
|
Here are the descriptions to choose from:
|
|
{choices}
|
|
|
|
[INSTRUCTIONS]
|
|
- 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"]
|
|
|
|
[CONTEXT]
|
|
Here is the Service Term to analyze:
|
|
{service.replace('"', "'")}
|
|
|
|
[OUTPUT FORMAT]
|
|
Briefly explain your answer, then return your final answer in JSON list format.
|
|
"""
|
|
|
|
|
|
#####################################################################################
|
|
################################ ONE-TO-ONE-PROMPTS #################################
|
|
#####################################################################################
|
|
|
|
|
|
def ONE_TO_ONE_SINGLE_FIELD_TEMPLATE(context: str, field_prompt: dict[str, str]):
|
|
return 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".
|
|
"""
|
|
|
|
|
|
def ONE_TO_ONE_SINGLE_FIELD_INSTRUCTION() -> str:
|
|
return (
|
|
"[OBJECTIVE]\n"
|
|
"Answer a single, deterministic question about the contract text. Return result in pipes as instructed."
|
|
)
|
|
|
|
|
|
def TIN_NPI_TEMPLATE(context, questions):
|
|
instruction = f"""
|
|
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
|
|
- Payers: Insurance companies, health plans who PAY for healthcare services
|
|
|
|
CRITICAL: If a provider has multiple TINs or NPIs, include ALL of them separated by pipes (|) in the same provider record. Do NOT create separate records for the same provider entity.
|
|
|
|
Examples:
|
|
- If you see "NPI: 1083087266 and 1396117412", return "NPI": "1083087266|1396117412"
|
|
- If you see "Tax ID: 12-3456789, also 98-7654321", return "TIN": "123456789|987654321"
|
|
|
|
Briefly explain your reasoning. After your explanation, include the heading "FINAL PROVIDER INFO:" followed by a properly formatted JSON array with your final output.
|
|
|
|
[EXTRACTION AND FORMATTING INSTRUCTIONS]
|
|
- First, analyze the text thoroughly and identify all provider entities
|
|
- Return TIN and NPIs that are found on the page even if they aren't explicitly labeled as such.
|
|
- IMPORTANT VALIDATION REQUIREMENTS:
|
|
* TIN must be exactly 9 digits (no more, no less)
|
|
* NPI must be exactly 10 digits (no more, no less)
|
|
* Double-check the digit count for each TIN and NPI before including them in the response
|
|
- In your response, include ONLY ONE SINGLE properly formatted JSON array, after your explanation
|
|
- Each provider must be an object within this array
|
|
- All provider objects must have these exact keys: "TIN", "NPI", "NAME"
|
|
- Use null (not strings like "null" or "N/A") for missing values
|
|
- 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": "987654321",
|
|
"NPI": null,
|
|
"NAME": "Dr. John Smith",
|
|
}}
|
|
]
|
|
|
|
Contract text:"""
|
|
|
|
prompt = f"""
|
|
[FIELDS TO EXTRACT]
|
|
{questions}
|
|
|
|
Contract text:
|
|
{context.replace('"', "'" )}
|
|
"""
|
|
|
|
return instruction, prompt
|
|
|
|
|
|
def GROUP_TIN_NPI_TEMPLATE(key_sections: str, provider_list: str) -> str:
|
|
return f"""
|
|
Identify which provider is the MAIN CONTRACTING GROUP in this contract.
|
|
|
|
Below is a list of all providers found in the contract:
|
|
{provider_list}
|
|
|
|
Based on the key sections of the contract (first and last parts where party definitions and signatures are usually located):
|
|
|
|
{key_sections.replace('"', "'")}
|
|
|
|
Look for these indicators to identify the main contracting group:
|
|
1. Provider explicitly referred to as "Provider", "Group", or "Group Practice" in party definitions
|
|
2. Provider appearing in signature blocks or with authorized signatories
|
|
3. Provider referenced with phrases like "hereby agrees", "enters into agreement with", or "party of the first/second part"
|
|
4. Entity described as representing multiple practitioners or locations
|
|
5. Entity mentioned most frequently throughout the key sections
|
|
|
|
In case of multiple potential matches, prioritize:
|
|
- Entities with both TIN and NPI over those with only one identifier
|
|
- Entities described as medical groups over individual practitioners
|
|
- Entities appearing in formal contract party definitions over those mentioned elsewhere
|
|
|
|
Determine which provider is the main contracting entity/group.
|
|
Return the name, TIN, and NPI of the main group provider in JSON format like this:
|
|
|{{"NAME": "Provider Name", "TIN": "123456789", "NPI": "1234567890"}}|
|
|
|
|
If you cannot confidently determine the main group, return null values:
|
|
|{{"NAME": null, "TIN": null, "NPI": null}}|
|
|
|
|
Feel free to justify your answer, but enclose your final answer in |pipes|.
|
|
"""
|
|
def FULL_CONTEXT_CLAIM_TYPES_ADDITIONAL_INSTRUCTION():
|
|
return "Additionally Check the contract text for the provider name from the preamble or signature section to help determine the claim type."
|
|
|
|
def FULL_CONTEXT_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 in a pipe (|) separated list."
|
|
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 in a pipe (|) separated list."
|
|
|
|
|
|
def CHECK_PROVIDER_NAME_MATCH_PROMPT(provider_name: str, hybrid_smart_chunking_provider_group_name: str) -> str:
|
|
"""Create prompt for LLM-based provider name matching."""
|
|
return f"""Determine if the provider name matches any of the healthcare organizations listed.
|
|
|
|
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 separated by ' | '. Determine if PROVIDER NAME A matches PROVIDER NAME B (or ANY name if multiple are present).
|
|
|
|
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 (separated by ' | '), 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.
|
|
|
|
Briefly explain your reasoning. Then return ONLY 'Y' (match found) or 'N' (no match found). Enclose your final answer in |pipes|"""
|
|
|
|
def ONE_TO_ONE_MULTI_FIELD_TEMPLATE(context, questions: dict[str, str]):
|
|
return f"""The following is a contract (or excerpt thereof). Answer the following questions: {questions}
|
|
|
|
## START CONTRACT TEXT ##
|
|
{context.replace('"', "'")}
|
|
## END CONTRACT TEXT ##
|
|
|
|
For each question, explain your reasoning. Then provide your final answers in a JSON dictionary format. 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 'UNKNOWN' for any question where the requested information should exist but cannot be clearly identified
|
|
- Contain only the final answers, not explanations
|
|
- NOT contain any curly brackets '{{' or '}}' inside.
|
|
- If curly bracket still exists inside JSON, replace them with '()' instead.
|
|
|
|
Example format:
|
|
I found the effective date in section 2.1 which states...
|
|
The term length appears in two places but I'm selecting...
|
|
|
|
{{
|
|
"effective_date": "January 1, 2024",
|
|
"term_length": "36 months"
|
|
}}
|
|
"""
|
|
|
|
|
|
def ONE_TO_ONE_MULTI_FIELD_INSTRUCTION() -> str:
|
|
return (
|
|
"[OBJECTIVE]\n"
|
|
"Answer multiple deterministic questions about contract text. Follow JSON-only output rules in the prompt."
|
|
)
|
|
|
|
|
|
#####################################################################################
|
|
############################### PREPROCESSING PROMPTS ###############################
|
|
#####################################################################################
|
|
|
|
|
|
def EXHIBIT_HEADER(context, EXHIBIT_HEADER_MARKERS):
|
|
return f"""Analyze the following contract excerpt and extract the full header found.
|
|
|
|
Capture the complete hierarchy of document identifiers present in the text. The following keywords and phrases should be considered as exhibit/attachment headers:
|
|
* {'\n* '.join(EXHIBIT_HEADER_MARKERS)}
|
|
|
|
Rules for Inclusion:
|
|
* Include ALL text that appears to be part of the header
|
|
* Headers MUST appear at the top of the page - do not mistakenly extract Footers, which will be found at the bottom of the page with other text before them.
|
|
* Full header names and numbers/letters (e.g., "Attachment C: Commercial-Exchange")
|
|
* Any other relevant subtitles and identifiers
|
|
* Any words in the header that may 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
|
|
* Include any information referring to the current exhibit's relationship with another exhibit (e.g. "Exhibit 1: Provider Roster (see Exhibit 2 for Compensation Schedule)")
|
|
|
|
- Rules for Exclusion:
|
|
* Do NOT abbreviate or summarize any part of the exhibit names
|
|
* Do NOT include page numbers
|
|
* Do NOT cherry-pick only certain parts - capture the complete hierarchy
|
|
* Do NOT include docusign IDs and other metadata
|
|
* DO NOT include unrelated text written in lowercase or title case.
|
|
|
|
If there is no Exhibit Header present, return |N/A|. Do not fill in values from the examples above, these are only examples.
|
|
|
|
Here is the text to analyze:
|
|
{context.replace('"', "'")}
|
|
|
|
Before returning any output, ensure that all instructions for inclusion were followed.
|
|
|
|
Enclose your final answer in |pipes|, followed by a brief explanation.
|
|
"""
|
|
|
|
|
|
def EXHIBIT_LINKAGE(header1, header2):
|
|
return f"""
|
|
|
|
[INSTRUCTIONS]
|
|
* Determine whether the two Headers are meaningfully different from each other.
|
|
* 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.
|
|
* Return |Y| if the two Headers are meaningfully different. Return |N| if they are not.
|
|
|
|
[CONTEXT]
|
|
Header 1:
|
|
"{header1}"
|
|
|
|
Header 2:
|
|
"{header2}"
|
|
|
|
Briefly explain your answer, then enclose your final answer in |pipes|.
|
|
"""
|
|
|
|
|
|
#####################################################################################
|
|
########################## CHECKS, FIXES, AND VALIDATIONS ###########################
|
|
#####################################################################################
|
|
|
|
|
|
def VALIDATE_REIMBURSEMENTS_PROMPT(service_term: str, reimb_term: str) -> str:
|
|
"""Validates whether a Reimbursement Term represents a complete reimbursement methodology.
|
|
Args:
|
|
reimb_term (str): The reimbursement term to validate.
|
|
service_term (str): The associated service term.
|
|
Returns:
|
|
str: A prompt for the AI to determine if the pair is a complete reimbursement methodology.
|
|
"""
|
|
return f"""Analyze a REIMBURSEMENT TERM and SERVICE TERM pair from a healthcare contract:
|
|
|
|
Determine if the pair of SERVICE TERM and REIMBURSEMENT TERM represent a COMPLETE reimbursement methodology that specifies how payment will be calculated.
|
|
|
|
VALID entries - return YES:
|
|
- Specific payment rates (percentages, dollar amounts)
|
|
- Fee schedule references with rates
|
|
- Calculation formulas
|
|
- Payment limitations with amounts
|
|
- "Lesser of" or "greater of" statements with clear and specific rates
|
|
- Capitation rates (PMPM, PMPY, per member costs)
|
|
- Flat rates per service or episode
|
|
- These Special cases: OUTLIER, STOP_LOSS, SEQUESTRATION, DISCOUNT, PREMIUM, RATE_ESCALATOR, FACILITY_ADJUSTMENT
|
|
|
|
INVALID entries - return NO unless there is VALID language as well:
|
|
- 'Empty' Reimbursements - have the same structure as a valid reimbursement but doesn't actually reference any specific rate or fee schedule (e.g. "Covered Services will be paid at the rates stated in the next section")
|
|
- Service definitions without payment terms
|
|
- Authorization/coverage requirements only
|
|
- Processing instructions, funding arrangements, and administrative statements without specific rates
|
|
- Member cost responsibilities (including Medicare Member Cost Share)
|
|
|
|
HARD INVALID entries - return NO even if there is VALID reimbursement language present as well:
|
|
- Any reference to Coordination of Benefits or general benefit descriptions
|
|
- Any reference to Clean Claims in SERVICE TERM only
|
|
- Audit and Time-based language describing how many claims must be paid in a certain timeframe (e.g. "... pay 85% of claims within a 30-day window")
|
|
- Risk-sharing arrangements, surplus/deficit distributions, settlement payments
|
|
- Performance bonuses and incentive payments, or administrative compensation
|
|
- Non-reimbursement statements ("not covered", "excluded", "not payable")
|
|
- Charge Description Master (CDM) or "chargemaster" language.
|
|
|
|
Examples:
|
|
- "reimbursed at 100% of DMAP fee schedule" → YES (clear payment method)
|
|
- "flat rate of $20.00 per frame" → YES (specific rate)
|
|
- "$5.58 PMPM" → YES (capitation rate)
|
|
- "80% of Medicare allowable" → YES (percentage with reference)
|
|
- "Standard eyeglass lenses as defined by DMAP are CR39 lenses..." → NO (definition only)
|
|
- "Contact lenses must be prior authorized" → NO (authorization only)
|
|
- "Not payable per contract" → NO (no reimbursement)
|
|
- "payment to IPA equal to fifty percent (50%) of the Surplus" → NO (risk-sharing distribution)
|
|
|
|
Here are the SERVICE TERM and REIMBURSEMENT TERM to analyze:
|
|
SERVICE TERM: {service_term}
|
|
REIMBURSEMENT TERM: {reimb_term}
|
|
|
|
Briefly explain your reasoning, then return YES or NO in |pipes|."""
|
|
|
|
|
|
def VALIDATE_REIMBURSEMENTS_INSTRUCTION() -> str:
|
|
return (
|
|
"[OBJECTIVE]\n"
|
|
"Validate whether a Reimbursement Term constitutes a complete reimbursement methodology."
|
|
)
|
|
|
|
|
|
def DATE_FIX_PROMPT(context: str) -> str:
|
|
return f"""Given a date string, convert it to YYYY/MM/DD format following these rules:
|
|
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, 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 in |pipes|
|
|
|
|
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|
|
|
|
|
Please analyze this date:
|
|
|
|
## START CONTEXT ##
|
|
{context}
|
|
## END CONTEXT ##
|
|
|
|
{PIPE_FORMAT_INSTRUCTIONS}
|
|
"""
|
|
|
|
|
|
def CHECK_REDUNDANT_LESSER(reimb_term, exhibit_lesser_of):
|
|
return f"""You are analyzing whether an exhibit-level lesser-of constraint is semantically redundant with a reimbursement methodology.
|
|
|
|
REIMBURSEMENT METHODOLOGY: {reimb_term}
|
|
|
|
EXHIBIT CONSTRAINT: {exhibit_lesser_of}
|
|
|
|
Determine if the exhibit constraint is already covered by the reimbursement methodology.
|
|
|
|
CRITICAL: Pay careful attention to NUMERICAL DIFFERENCES. Different percentages (e.g., 90% vs 100%) are NOT redundant even if they both reference billed charges.
|
|
|
|
Examples of REDUNDANT language:
|
|
- Reimb: "lesser of fee schedule or billed charges" + Exhibit: "not to exceed billed charges" → REDUNDANT
|
|
- Reimb: "payment shall not exceed provider's charges" + Exhibit: "lesser of rate or charges" → REDUNDANT
|
|
- Reimb: "100% of billed charges not to exceed usual and customary" + Exhibit: "not to exceed billed charges" → REDUNDANT
|
|
|
|
Examples of NON-REDUNDANT language:
|
|
- Reimb: "100% of billed charges" + Exhibit: "90% of billed charges" → NOT REDUNDANT (90% is more restrictive than 100%)
|
|
- Reimb: "billed charges not to exceed usual and customary" + Exhibit: "90% of billed charges" → NOT REDUNDANT (90% is a specific cap vs general UCR limit)
|
|
- Reimb: "fee schedule rate" + Exhibit: "90% of billed charges" → NOT REDUNDANT (different constraint types)
|
|
|
|
IMPORTANT: If the exhibit constraint introduces ANY new numerical limit, percentage, or dollar amount not present in the reimbursement methodology, it is NOT redundant.
|
|
|
|
Answer YES only if the exhibit constraint adds NO additional limitation beyond what's already in the reimbursement methodology.
|
|
|
|
Briefly explain your reasoning, then return YES or NO in |pipes|."""
|
|
|
|
|
|
def CARVEOUT_CHECK(service_term: str, reimb_term: str, carveout_definitions, special_case_definitions):
|
|
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: {"\n\n".join([name + " : " + description for name, description in carveout_definitions.items()])}
|
|
Special cases: {"\n\n".join([name + " : " + description for name, description in special_case_definitions.items()])}
|
|
|
|
[REIMBURSEMENT TERM]
|
|
Here is the Reimbursement Term to analyze:
|
|
Service: {service_term}
|
|
Reimbursement Term: {reimb_term.replace('"', "'")}
|
|
|
|
[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 enclose your final answer in |pipes|.
|
|
"""
|
|
|
|
|
|
def CHECK_REDUNDANT_LESSER_INSTRUCTION() -> str:
|
|
return (
|
|
"[OBJECTIVE]\n"
|
|
"Decide if two texts are semantically redundant with respect to 'lesser of' meaning. Return YES/NO in pipes."
|
|
)
|
|
|
|
|
|
def DUAL_LOB_CHECK(service_term):
|
|
return f"""Examine the given Service and determine if the Service applies to Medicare, Medicaid, or Both.
|
|
|
|
If the Service is only for Medicaid, return 'Medicaid'
|
|
If the Service is only for Medicare, return 'Medicare'
|
|
If the Service is for both Medicare and Medicaid, return 'Duals'
|
|
If the service is for neither Medicare nor Medicaid, return 'neither'
|
|
If it is unclear, write 'N/A'.
|
|
|
|
Here is the Service:
|
|
{service_term}
|
|
|
|
Explain your answer in one or two sentences, but put your final answer in |pipes|.
|
|
"""
|
|
|
|
|
|
def EFFECTIVE_DATE_FIX_PROMPT() -> str:
|
|
return """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."""
|
|
|
|
|
|
def SPLIT_REIMB_DATES(date_range: str) -> str:
|
|
"""
|
|
Splits a date range into start and end dates, returning them in YYYY/MM/DD format.
|
|
Handles both explicit dates and text-based duration language.
|
|
|
|
Args:
|
|
date_range (str): The date range to split, e.g., "2023/01/01 - 2023/12/31" or "Initial Term - Renewal Term"
|
|
|
|
Returns:
|
|
str: A JSON string with "start_date" and "end_date" keys
|
|
"""
|
|
return f"""Extract the start and end dates from the following date range context: {date_range}
|
|
|
|
Based on the date range context:
|
|
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 no extractable dates are found in the text:
|
|
- Return "N/A" for both start_date and end_date
|
|
|
|
Return the dates in the following JSON 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."""
|
|
|
|
|
|
def SPLIT_REIMBURSEMENTS_INSTRUCTION() -> str:
|
|
return (
|
|
"[OBJECTIVE]\n"
|
|
"Split a compound reimbursement term into distinct components using only explicit contract text."
|
|
)
|
|
|
|
|
|
def DYNAMIC_PRIMARY_INSTRUCTION() -> str:
|
|
return (
|
|
"[OBJECTIVE]\n"
|
|
"Extract attribute values for dynamic fields from contract text. Use exact values and return in pipes where instructed."
|
|
)
|
|
|
|
|
|
def CARVEOUT_CHECK_INSTRUCTION() -> str:
|
|
return (
|
|
"[OBJECTIVE]\n"
|
|
"Determine if a carve-out applies for a service given a base reimbursement. Return result in pipes as instructed."
|
|
)
|
|
|
|
|
|
def LOB_RELATIONSHIP(exhibit_text, dynamic_primary_values):
|
|
return f"""Analyze the exhibit from a Payer-Provider contract.
|
|
|
|
[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.
|
|
|
|
It was previously identified that the Exhibit contains the following LOB and Programs:
|
|
{dynamic_primary_values}
|
|
|
|
[INSTRUCTIONS]
|
|
- Determine if the Exhibit presents an "Inclusive" relationship or "Exclusive" relationship between LOB and Program.
|
|
- 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 exlusive 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.
|
|
|
|
[CONTEXT]
|
|
Here is the Exhibit to be analyzed:
|
|
{exhibit_text.replace('"', "'")}
|
|
|
|
[OUTPUT FORMAT - REQUIRED]
|
|
You MUST format your response with pipe delimiters. Example format:
|
|
"Based on the analysis... |Inclusive|" or "The relationship is... |Exclusive|"
|
|
Your final answer MUST be enclosed in pipe delimiters: |Inclusive| or |Exclusive|
|
|
"""
|
|
|
|
|
|
def DERIVED_TERM_DATE_PROMPT(
|
|
effective_date: str = None, termination_information: str = None
|
|
) -> str:
|
|
"""
|
|
Generates a prompt to extract the termination date from a legal contract.
|
|
|
|
Args:
|
|
effective_date (str, optional): The effective date of the contract. Defaults to None.
|
|
termination_information (str, optional): Information regarding the termination of the contract. Defaults to None.
|
|
|
|
Returns:
|
|
str: A formatted string prompt requesting the termination date of the contract.
|
|
"""
|
|
return f"The following are excerpts from a legal contract related to effective and termination dates. Determine the initial termination date of the contract, excluding any renewals or extensions.\nIf 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.\nIf 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.\nEffective Date: {effective_date}\nTermination Information: {termination_information}\nOnly provide the first termination date, ignoring any automatic or optional renewals. Provide the date in YYYY/MM/DD format only. \n{PIPE_FORMAT_INSTRUCTIONS}"
|
|
|
|
|
|
def LOB_RELATIONSHIP_INSTRUCTION() -> str:
|
|
return (
|
|
"[OBJECTIVE]\n"
|
|
"Determine if LOB and Program relationship in the exhibit is Inclusive or Exclusive.\n\n"
|
|
"[OUTPUT FORMAT - CRITICAL]\n"
|
|
"You MUST format your response as follows:\n"
|
|
"1. Briefly explain your reasoning\n"
|
|
"2. Then put your final answer in pipe delimiters: |Inclusive| or |Exclusive|\n"
|
|
"Example: 'The contract shows... |Inclusive|'\n"
|
|
"Your response MUST include the pipe delimiters (|) around your final answer."
|
|
)
|
|
|
|
|
|
def SPECIAL_CASE_ASSIGNMENT(
|
|
exhibit_text, reimbursement_dict, special_case_dicts, special_case_term
|
|
):
|
|
return f"""[OBJECTIVE]
|
|
Analyze a section of a contract. Determine which of {special_case_term} values are associated with the REIMBURSEMENT TERM shown.
|
|
|
|
[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]
|
|
{[f"{i} : {special_case_dicts[i][special_case_term]}" for i in range(len(special_case_dicts))]}
|
|
|
|
Return the integer of the {special_case_term} that is most associated with the REIMBURSEMENT TERM. Base your answer on context clues found in the text.
|
|
|
|
{PIPE_FORMAT_INSTRUCTIONS}
|
|
"""
|