Merged in bugfix/prov-info-issues (pull request #816)

Bugfix/prov info issues

* Initial commit

* Passed payer name to tin npi llm call to avoid assigning tin npi to payer

* Removed record that contains NO_IDENTIFIERS_FOUND from PROV_INFO_JSON

* Updated unit test for test_tin_npi_funcs

* Changed name check code back to original to be consistent

* Updated test_tin_npi_funcs.py to pass in payer_name in functions

* Add fix to unit test

* Updated output in unit tests

* Merged main into bugfix/prov-info-issues


Approved-by: Katon Minhas
This commit is contained in:
Sha Brown
2025-12-22 19:03:09 +00:00
committed by Katon Minhas
parent 7ffd62fd8e
commit 77c845a82e
4 changed files with 28 additions and 24 deletions
@@ -121,18 +121,17 @@ def run_one_to_one_prompts(
relationship="one_to_one", file_path=config.FIELD_JSON_PATH
).combine(dynamic_one_to_one_fields)
################## RUN PROVIDER INFO ##################
one_to_one_results, one_to_one_fields = tin_npi_funcs.run_provider_info_fields(
contract_text, one_to_one_fields, text_dict, filename
)
################## RUN HYBRID SMART CHUNKED PROMPTS ##################
# RAG function loads retrieval questions internally and matches with investment_prompts.json
hybrid_smart_chunked_answers_dict = hybrid_smart_chunking_funcs.run_hybrid_smart_chunked_fields(
one_to_one_fields, constants, contract_text, filename, text_dict
)
################## RUN PROVIDER INFO ##################
payer_name = hybrid_smart_chunked_answers_dict.get('PAYER_NAME')
one_to_one_results = tin_npi_funcs.run_provider_info_fields(
contract_text, text_dict, filename, payer_name
)
################## RUN FULL CONTEXT PROMPTS ##################
full_context_answers_dict = one_to_one_funcs.run_full_context_fields(
@@ -273,6 +273,9 @@ def merge_provider_info_with_hybrid_smart_chunking(one_to_one_results, filename)
name for name in other_names if name not in group_names and name != "UNKNOWN"
] # remove any group Names from other Names
# Filter out records with NO_IDENTIFIERS_FOUND
provider_list = [p for p in provider_list if p.get('NAME') != 'NO_IDENTIFIERS_FOUND']
# Reconstruct PROV_INFO_JSON and PROV_INFO_JSON_FORMATTED with IS_GROUP flags
one_to_one_results["PROV_INFO_JSON"] = json.dumps(provider_list)
one_to_one_results["PROV_INFO_JSON_FORMATTED"] = "\n".join(
@@ -412,7 +415,8 @@ def deduplicate_providers(
def get_provider_info(
text_dict: dict,
page_num: str,
filename: str
filename: str,
payer_name:str
) -> list:
"""
Extracts provider information from a specified page in a document using a language model.
@@ -436,7 +440,7 @@ def get_provider_info(
file_path=config.FIELD_JSON_PATH, field_type="provider_info"
)
instruction, prompt = prompt_templates.TIN_NPI_TEMPLATE(
chunk, provider_fields.print_prompt_dict()
chunk, provider_fields.print_prompt_dict(), payer_name
)
claude_answer_raw = llm_utils.invoke_claude(
prompt, "sonnet_latest", filename, max_tokens=8192, cache=True, instruction=instruction
@@ -534,10 +538,9 @@ def get_provider_info(
def run_provider_info_fields(
contract_text: str,
one_to_one_fields: FieldSet,
text_dict: dict,
filename: str,
# payer_name: str # This is not being used right now, but it might be useful for filtering providers later
payer_name: str
):
"""
Extracts provider information from a contract text using regex-based identifier extraction,
@@ -632,7 +635,7 @@ def run_provider_info_fields(
# Process each page separately and combine results
combined_provider_info = []
for page_num in relevant_pages:
page_provider_info = get_provider_info(text_dict, page_num, filename)
page_provider_info = get_provider_info(text_dict, page_num, filename, payer_name)
combined_provider_info.extend(page_provider_info)
# Clean and standardize results
@@ -657,4 +660,4 @@ def run_provider_info_fields(
filename_tin = get_all_matches(filename, regex_patterns.FILE_NAME_TIN_PATTERN)
one_to_one_results["FILENAME_TIN"] = filename_tin[0] if filename_tin else "N/A"
return one_to_one_results, one_to_one_fields
return one_to_one_results
@@ -1,6 +1,7 @@
import src.config as config
from src.prompts.fieldset import FieldSet
from src.utils import string_utils
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|."
@@ -862,13 +863,17 @@ def ONE_TO_ONE_SINGLE_FIELD_INSTRUCTION() -> str:
)
def TIN_NPI_TEMPLATE(context, questions):
def TIN_NPI_TEMPLATE(context, questions, payer_name):
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.'
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
- Payers: Insurance companies, health plans who PAY for healthcare services{payer_instruction}
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.
+7 -10
View File
@@ -118,7 +118,7 @@ class TestTinNpiFuncs:
]
)
result = tin_npi_funcs.get_provider_info(sample_text_dict, "1", "test.pdf")
result = tin_npi_funcs.get_provider_info(sample_text_dict, "1", "test.pdf", payer_name="Test Payer")
assert len(result) == 1
@patch("src.utils.llm_utils.invoke_claude")
@@ -134,11 +134,10 @@ class TestTinNpiFuncs:
]
)
one_to_one_fields = FieldSet(relationship="one_to_one")
contract_text = "Contract with TIN 12-3456789"
results, fields = tin_npi_funcs.run_provider_info_fields(
contract_text, one_to_one_fields, sample_text_dict, "test.pdf"
results = tin_npi_funcs.run_provider_info_fields(
contract_text, sample_text_dict, "test.pdf", "Test Payer"
)
assert "PROV_INFO_JSON" in results
@@ -274,12 +273,11 @@ class TestTinNpiFuncs:
]
)
one_to_one_fields = FieldSet(relationship="one_to_one")
# Include both exact and OCR-correctable TINs
contract_text = "Contract with TIN 12-3456789 and corrupted TIN 98765432O"
results, fields = tin_npi_funcs.run_provider_info_fields(
contract_text, one_to_one_fields, sample_text_dict, "test.pdf"
results = tin_npi_funcs.run_provider_info_fields(
contract_text, sample_text_dict, "test.pdf", "Test Pyer"
)
# Check that quality tracking fields are present
@@ -333,12 +331,11 @@ class TestTinNpiFuncs:
]
)
one_to_one_fields = FieldSet(relationship="one_to_one")
# Only OCR-corrupted identifiers, no clean ones
contract_text = "Contract with corrupted TIN 12345678O and NPI 123456789O"
results, fields = tin_npi_funcs.run_provider_info_fields(
contract_text, one_to_one_fields, sample_text_dict, "test.pdf"
results = tin_npi_funcs.run_provider_info_fields(
contract_text, sample_text_dict, "test.pdf", "Test Payer"
)
# Should find OCR-corrected values