Merged in feature/rework-reimb-tin-npi (pull request #502)
Feature/rework reimb tin npi * use LLM for reimb_[tin/npi/name] * Merge remote-tracking branch 'origin/main' into feature/rework-reimb-tin-npi * Merge remote-tracking branch 'origin/main' into feature/rework-reimb-tin-npi * deduplicate tin/name/npi before adding to prompt * Pull out prompt and llm_response in methodology_breakout_single_row for easier debugging * Refactor reimbursement_tin_npi to handle multiple providers and update valid TIN, NPI, and NAME fields accordingly * Reorder REIMB_PROV_NAME field in investment_prompts.json * Update REIMB_PROV_NAME prompt to specify valid values for provider names * Remove valid_values constraint from prov_name to raise accuracy * Remove debugging print statements from reimbursement_tin_npi and related functions * Remove debugging print statements from run_provider_info_fields function * Merge remote-tracking branch 'origin/main' into feature/rework-reimb-tin-npi * Remove debugging print statements from get_reimbursement_primary function * Merge remote-tracking branch 'origin/main' into feature/rework-reimb-tin-npi * Refactor reimbursement_tin_npi to always add providers (when we have any) to reimbursement_level_fields Approved-by: Katon Minhas
This commit is contained in:
@@ -139,7 +139,7 @@ def run_one_to_n_prompts(filename, text_dict, exhibit_pages, exhibit_chunk_mappi
|
||||
exhibit_header = one_to_n_funcs.get_exhibit_header(exhibit_chunk, filename)
|
||||
|
||||
################## GET REIMBURSEMENT TIN/NPI ##################
|
||||
tin_npi_answers, reimbursement_level_fields = reimbursement_tin_npi(exhibit_chunk, reimbursement_level_fields)
|
||||
tin_npi_answers, reimbursement_level_fields = reimbursement_tin_npi(exhibit_chunk, reimbursement_level_fields, filename)
|
||||
|
||||
################## GET EXHIBIT-LEVEL ANSWERS ##################
|
||||
exhibit_level_answers = one_to_n_funcs.get_exhibit_level_answers(exhibit_chunk, filename)
|
||||
|
||||
@@ -254,10 +254,11 @@ def methodology_breakout_single_row(
|
||||
contract_reimbursement_method = answer_dict.get("REIMB_TERM")
|
||||
# Methodology Breakout: AARETE_DERIVED_REIMB_METHOD, REIMB_FEE_RATE, REIMB_PCT_RATE, LESSER_OF_IND, GREATER_OF_IND, AARETE_DERIVED_PROV_TYPE
|
||||
if contract_reimbursement_method:
|
||||
llm_response = llm_utils.invoke_claude(
|
||||
methodology_prompt_template(
|
||||
prompt = methodology_prompt_template(
|
||||
service, contract_reimbursement_method, methodology_breakout_questions
|
||||
),
|
||||
)
|
||||
llm_response = llm_utils.invoke_claude(
|
||||
prompt,
|
||||
config.MODEL_ID_CLAUDE35_SONNET,
|
||||
filename,
|
||||
)
|
||||
@@ -461,6 +462,7 @@ def reimbursement_level(
|
||||
Returns:
|
||||
dict: The parsed LLM response as a list of dictionaries with unique identifiers
|
||||
"""
|
||||
print("Inside reimbursement_level function, reimbusement_level_fields:", reimbursement_level_fields.get_prompt_dict())
|
||||
reimbursement_primary_answers = get_reimbursement_primary(
|
||||
reimbursement_level_fields, exhibit_text, filename
|
||||
) # Returns list of dicts
|
||||
|
||||
@@ -52,15 +52,11 @@ def run_provider_info_fields(contract_text: str,
|
||||
page_provider_info = get_provider_info(page_text, filename)
|
||||
combined_provider_info.extend(page_provider_info)
|
||||
|
||||
# print(f"Combined provider info for {filename}: {combined_provider_info}") # Debugging line
|
||||
|
||||
# Clean and standardize results
|
||||
cleaned_provider_info = clean_provider_info(combined_provider_info)
|
||||
# print(f"Cleaned provider info for {filename}: {cleaned_provider_info}") # Debugging line
|
||||
|
||||
# Deduplicate providers based on TIN and NPI
|
||||
deduplicated_provider_info = deduplicate_providers(cleaned_provider_info)
|
||||
# print(f"Deduplicated provider info for {filename} [{len(deduplicated_provider_info)} entries]: {deduplicated_provider_info}") # Debugging line
|
||||
|
||||
# if payer_name:
|
||||
# # Filter out providers that are too similar to the payer name
|
||||
@@ -83,7 +79,6 @@ def run_provider_info_fields(contract_text: str,
|
||||
elif len(deduplicated_provider_info) == 1:
|
||||
# If only one provider is found, mark it as the group provider
|
||||
deduplicated_provider_info[0]["IS_GROUP"] = True
|
||||
# print(f"After group assignment for {filename}: {deduplicated_provider_info}") # Debugging line
|
||||
|
||||
return deduplicated_provider_info
|
||||
|
||||
|
||||
@@ -161,39 +161,55 @@ def merge_provider_info(one_to_one_results: dict, provider_info: list[dict]) ->
|
||||
one_to_one_results["PROV_OTHER_NAME_FULL"] = "|".join(other_names) if other_names else ""
|
||||
return one_to_one_results
|
||||
|
||||
def reimbursement_tin_npi(exhibit_chunk: str, reimbursement_level_fields: FieldSet) -> tuple[dict, FieldSet]:
|
||||
def reimbursement_tin_npi(exhibit_chunk: str, reimbursement_level_fields: FieldSet, filename: str) -> tuple[dict, FieldSet]:
|
||||
"""
|
||||
Extracts Reimbursement-Level TIN and NPI values from the given exhibit chunk using regex patterns. For multiple TIN/NPIs in Exhibit, assigns the field to be run as part of Reimbursement-Level Primary
|
||||
Extracts Reimbursement-Level TIN and NPI values from the given exhibit chunk using an LLM.
|
||||
When multiple providers are found, they're added as valid values for LLM selection
|
||||
of these fields during reimbursement primary processing.
|
||||
|
||||
Args:
|
||||
exhibit_chunk (str): The text chunk containing reimbursement details.
|
||||
reimbursement_level_fields (FieldSet): The FieldSet object to store extracted field values.
|
||||
filename (str): The name of the file being processed.
|
||||
|
||||
Returns:
|
||||
tuple[dict, FieldSet]: A dictionary containing extracted TIN and NPI values, and an
|
||||
updated FieldSet with additional fields if multiple values are found.
|
||||
Fields Added: REIMB_PROV_TIN,
|
||||
Fields Added: REIMB_PROV_NAME,
|
||||
REIMB_PROV_TIN,
|
||||
REIMB_PROV_NPI
|
||||
"""
|
||||
|
||||
answer_dict = {}
|
||||
|
||||
# Extract all providers from the exhibit chunk
|
||||
providers = get_provider_info(exhibit_chunk, filename)
|
||||
|
||||
# Clean, standardize, and deduplicate extracted providers
|
||||
filtered_providers = deduplicate_providers(clean_provider_info(providers))
|
||||
|
||||
fields = [("REIMB_PROV_TIN", r"\b\d{2}-\d{7}\b|\b\d{9}\b"),
|
||||
("REIMB_PROV_NPI", r"\b\d{10}\b")]
|
||||
|
||||
for field_tuple in fields:
|
||||
field_name, field_pattern = field_tuple[0], field_tuple[1]
|
||||
field_matches, page_list = regex_utils.find_regex_matches(field_pattern, {'1' : exhibit_chunk})
|
||||
|
||||
if not field_matches:
|
||||
answer_dict[field_name] = "N/A"
|
||||
elif len(field_matches) == 1:
|
||||
answer_dict['REIMB_PROV_TIN'] = field_matches[0]
|
||||
else: # Multiple tins
|
||||
reimbursement_field = Field.load_from_file(config.FIELD_JSON_PATH, field_name)
|
||||
reimbursement_field.update_valid_values(field_matches)
|
||||
reimbursement_level_fields.add_field(reimbursement_field)
|
||||
# Always add providers to reimbursement_level fields for proper term attribution
|
||||
if len(filtered_providers) > 0:
|
||||
tin_field = Field.load_from_file(config.FIELD_JSON_PATH, "REIMB_PROV_TIN")
|
||||
tin_list = list(set([provider.get("TIN", "N/A") for provider in filtered_providers])) # Remove duplicates
|
||||
tin_field.update_valid_values(tin_list)
|
||||
reimbursement_level_fields.add_field(tin_field)
|
||||
|
||||
npi_field = Field.load_from_file(config.FIELD_JSON_PATH, "REIMB_PROV_NPI")
|
||||
npi_list = list(set([provider.get("NPI", "N/A") for provider in filtered_providers])) # Remove duplicates
|
||||
npi_field.update_valid_values(npi_list)
|
||||
reimbursement_level_fields.add_field(npi_field)
|
||||
|
||||
name_field = Field.load_from_file(config.FIELD_JSON_PATH, "REIMB_PROV_NAME")
|
||||
name_list = list(set([provider.get("NAME", "N/A") for provider in filtered_providers])) # Remove duplicates
|
||||
name_field.update_valid_values(name_list)
|
||||
reimbursement_level_fields.add_field(name_field)
|
||||
# If no providers are found, set default values
|
||||
else:
|
||||
answer_dict['REIMB_PROV_TIN'] = "N/A"
|
||||
answer_dict['REIMB_PROV_NPI'] = "N/A"
|
||||
answer_dict['REIMB_PROV_NAME'] = "N/A"
|
||||
|
||||
return answer_dict, reimbursement_level_fields
|
||||
|
||||
|
||||
@@ -233,11 +249,8 @@ def identify_group_provider(text_dict: dict, providers: list[dict], filename: st
|
||||
# Construct prompt
|
||||
prompt = investment_prompts.GROUP_TIN_NPI_TEMPLATE(key_sections=key_sections, provider_list=provider_list)
|
||||
|
||||
# print(f"Prompt for group provider identification: {prompt}") # Debugging line
|
||||
|
||||
# Get response and parse
|
||||
response = llm_utils.invoke_claude(prompt, config.MODEL_ID_CLAUDE35_SONNET, filename)
|
||||
# print(f"Response from LLM for group provider identification: {response}") # Debugging line
|
||||
try:
|
||||
group_info = json.loads(string_utils.extract_text_from_delimiters(response, Delimiter.PIPE))
|
||||
|
||||
@@ -380,7 +393,6 @@ def get_provider_info(chunk: str, filename: str) -> list:
|
||||
prompt = investment_prompts.TIN_NPI_TEMPLATE(chunk, provider_fields.get_prompt_dict())
|
||||
# Get response from LLM
|
||||
claude_answer_raw = llm_utils.invoke_claude(prompt, config.MODEL_ID_CLAUDE35_SONNET, filename, max_tokens=8192) # Sometimes rosters can have 50+ providers
|
||||
print(f"Raw response from LLM: {claude_answer_raw}")
|
||||
|
||||
# Defensive programming for any json parsing errors
|
||||
try:
|
||||
|
||||
@@ -62,12 +62,6 @@
|
||||
"prompt": "Identify the termination date for reimbursement rates:\n- Look for dates associated with:\n • 'Termination' or 'termination date'\n • 'Expires' or 'expiration date'\n • 'Through' or 'until'\n- Dates may appear as:\n • Full dates (12/1/17, December 1, 2017)\n • Month and year (December 2017)\n • Year only (2017)",
|
||||
"format": "date"
|
||||
},
|
||||
{
|
||||
"field_name": "REIMB_PROV_NAME",
|
||||
"relationship": "one_to_n",
|
||||
"field_type": "dynamic",
|
||||
"prompt": "Identify any Provider Names present that apply to the text. Do NOT return the name of a payer or insurance plan, only the names of specific providers (doctors, hospitals, clinics, etc) listed. Do not return the word 'Provider'."
|
||||
},
|
||||
{
|
||||
"field_name": "NOT_TO_EXCEED_IND",
|
||||
"relationship": "one_to_n",
|
||||
@@ -236,13 +230,19 @@
|
||||
"field_name": "REIMB_PROV_TIN",
|
||||
"relationship": "one_to_n",
|
||||
"field_type": "TBD",
|
||||
"prompt": "What TIN (Tax ID Number) does the reimbursement term apply to? Choose only from the following: {valid_values}."
|
||||
"prompt": "What TIN (Tax ID Number) does the reimbursement term apply to? When the term applies to multiple providers simultaneously, list ALL applicable TINs separated by commas. Look for language patterns indicating multiple providers (phrases like 'following providers', 'these facilities', 'listed providers', etc.). Choose only from the following: {valid_values}."
|
||||
},
|
||||
{
|
||||
"field_name": "REIMB_PROV_NPI",
|
||||
"relationship": "one_to_n",
|
||||
"field_type": "TBD",
|
||||
"prompt": "What NPI (National Provider Identifier) does the reimbursement term apply to? Choose only from the following: {valid_values}."
|
||||
"prompt": "What NPI (National Provider Identifier) does the reimbursement term apply to? When the term applies to multiple providers simultaneously, list ALL applicable NPIs separated by commas. Look for language patterns indicating multiple providers (phrases like 'following providers', 'these facilities', 'listed providers', etc.). Choose only from the following: {valid_values}."
|
||||
},
|
||||
{
|
||||
"field_name": "REIMB_PROV_NAME",
|
||||
"relationship": "one_to_n",
|
||||
"field_type": "dynamic",
|
||||
"prompt": "Identify any Provider Names that this reimbursement term applies to. When the term applies to multiple providers simultaneously, list ALL applicable names separated by commas. Look for language patterns indicating multiple providers. Extract only the official names of healthcare providers (doctors, hospitals, clinics, etc.) - not payers or insurance plans. If unable to determine applicable provider names, respond with 'UNKNOWN'."
|
||||
},
|
||||
{
|
||||
"field_name": "AARETE_DERIVED_EFFECTIVE_DT",
|
||||
|
||||
Reference in New Issue
Block a user