DEV * Apply formatting fix * Merged in DAIP2-1898-one-to-one-issues-filename-tin-auto-renewal-and-contract-title (pull request #888) DAIP2-1898 one to one issues filename tin auto renewal and contract title * contract title fixes * Add instruction to strip hyphens from TIN and NPI * prompt update * Merge branch 'bugfix/auto-renewal-term' into bugfix/DAIP2-1699-issue-tracker-fixes-one-to-one-dates-prov-info * removed print statements * pipeline error fixed * signed_ind logic changed * signatory_ind logic updated * prompt update * prompt update * pipeline error fixed * Merge branch 'DEV' into bugfix/DAIP2-1699-issue-tracker-fixes-one-to-one-dates-prov-info * Merge branch 'DEV' into bugfix/DAIP2-1699-issue-tracker-fixes-one-to-one-dates-prov-info * requested changes applied * test case added * prompt update * contract title prompt update * prompt update * Merge branch 'DEV' into DAIP2-1898-one-to-one-issues-filename-tin-auto-renewal-and-contract-title Approved-by: Siddhant Medar * Merged in feature/DAIP2-1803-add-aarete-derived-provider-name (pull request #884) Feature/DAIP2-1803 add aarete derived provider name * Merged DEV into feature/DAIP2-1562-add-aarete_derived_payer_name * state logic added in clustering * removed print statements * pull request updates * black format fix * Merged DEV into feature/DAIP2-1562-add-aarete_derived_payer_name * added scalability feature and optimization * black format * Pull Request Changes * generalised funcs from aarete derived payer name * updated remove states and updated docstring for build similarity matrix * derived payer name functionality added * config and main file changes * sync dev into ADD AARETE DERIVED PROVIDER GROUP NAME FULL * updated derived_provider name * pipeline fixes * Merged DEV into feature/DAIP2-1803-add-aarete-derived-provider-name * config changes * field name updated to AARETE_DERIVED_PROVIDER_NAME * Merged DEV into feature/DAIP2-1803-add-aarete-derived-provider-name * black format fix * Fix docstr… * Merged in improve-logging (pull request #886) Improve logging * Add timing blocks for comprehensive pipeline logging - Add timing_utils.timed_block() to key extraction functions in one_to_n_funcs.py: - exhibit_level sub-functions (prompt_exhibit_level, dynamic_primary, etc.) - reimbursement_level extraction and cleaning - breakout functions (methodology_breakout, special_case_breakout) - carveout_and_special_case parallel processing - one_to_n_cleaning sub-steps (crosswalk, lob_relationship, split_reimb_dates) - Add timing to dynamic_funcs.py: - dynamic_assignment parallel processing - Add timing to postprocess.py: - standard_postprocess, contract_config_postprocess, dashboard_postprocess - Add timing to preprocess.py: - exhibit_chunking sub-steps (get_exhibit_pages, link_exhibit_pages, chunk_by_exhibit) - Add info-level logging statements for completed operations with row/item counts * Standardize logging levels and remove deprecated code DAIP2-1798: Ensure logging statements are at … * Merged in bugfix/DAIP2-1913-lesser-of-indicator-issues (pull request #889) Bugfix/DAIP2-1913 lesser of indicator issues * contract title fixes * Add instruction to strip hyphens from TIN and NPI * prompt update * Merge branch 'bugfix/auto-renewal-term' into bugfix/DAIP2-1699-issue-tracker-fixes-one-to-one-dates-prov-info * removed print statements * pipeline error fixed * signed_ind logic changed * signatory_ind logic updated * prompt update * prompt update * pipeline error fixed * Merge branch 'DEV' into bugfix/DAIP2-1699-issue-tracker-fixes-one-to-one-dates-prov-info * Merge branch 'DEV' into bugfix/DAIP2-1699-issue-tracker-fixes-one-to-one-dates-prov-info * requested changes applied * test case added * prompt update * contract title prompt update * prompt update * Merge branch 'DEV' into DAIP2-1898-one-to-one-issues-filename-tin-auto-renewal-and-contract-title * handle nested cases of lesser_of and greater_of * Merge branch 'DEV' into bugfix/DAIP2-1913-lesser-of-indicator-issues * pi… * Merged in bugfix/reimb_primary (pull request #890) Update reimbursement prompts for consistent PMPM handling, time-unit dedup, and validation improvements * Update reimbursement prompts for consistent PMPM handling, time-unit dedup, and validation improvements - REIMBURSEMENT_PRIMARY: append unit of measure (PMPM, per visit, etc.) from table headers into REIMB_TERM - METHODOLOGY_BREAKOUT: collapse mathematically equivalent time-unit rates into single entry - VALIDATE_REIMBURSEMENTS: add umbrella terms as valid service terms, accept CPT/HCPCS percentage as concrete rate, narrow COB disqualifier to primary subject only Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Approved-by: Siddhant Medar * Merged in feature/update-postprocessing-columns (pull request #893) move column order to final step * move column order to final step * Black Approved-by: Siddhant Medar * Merged in bugfix/update-postprocessing-paths (pull request #896) Update postprocessing paths * Update postprocessing paths Approved-by: Siddhant Medar * Merged in feature/TIN_stats_update (pull request #898) Feature/TIN stats update * Tin_stats_report * black formatting * minor changes * black format applied * Addressed the katons comments * Merged DEV into feature/TIN_stats_update * black reformat Approved-by: Katon Minhas * Merged main into DEV * Merged in feature/exhibit-smart-chunking (pull request #883) Feature/exhibit smart chunking * exhibit processing per page * header dict deduplication * dedup prompt refinment * refinment for header extraction proecess * Merge remote-tracking branch 'origin/DEV' into feature/exhibit-smart-chunking * merge updates * minor fix * prompt fix for reimb type * COB defenition for clear understanding * black formatting * remove quit statement * pipiline test * pipeline test * black formatting * Merge remote-tracking branch 'origin/DEV' into feature/exhibit-smart-chunking * black formating * Merge remote-tracking branch 'origin/DEV' into feature/exhibit-smart-chunking * typo * PR comment fixes * exhibit funcs refactored * black formatting * Refactor exhibit chunking config into dedicated class Created ExhibitChunkingConfig class to centralize exhibit smart chunking configuration parameters (DEFAULT_SUBCHUNK_SIZE, MIN_PARENT_CHUNK_SIZE, CHUNK_RELEVANCE_THRESHOLD). This improves code organization … * Merged in bugfix/DAIP2-1870-dynamic-issues (pull request #892) Bugfix/DAIP2-1870 dynamic issues * updated list format appending * fill bill type from claim type cd * Fixed remaining format fixes * Merged DEV into bugfix/DAIP2-1870-dynamic-issues * changed crosswalk mapping from PRODUCT to AARETE_DERIVED_PRODUCT * Merged DEV into bugfix/DAIP2-1870-dynamic-issues * updating LOB field values as well from PRODUCT AND PROGRAM * updated apply crosswalk for reverse mapping * Merged DEV into bugfix/DAIP2-1870-dynamic-issues * added QHP and BHP * removed lob reverse mapping * Merged DEV into bugfix/DAIP2-1870-dynamic-issues * Remove Bill Type/Claim Type fill * Merged DEV into bugfix/DAIP2-1870-dynamic-issues * Black format * Merged DEV into bugfix/DAIP2-1870-dynamic-issues Approved-by: Katon Minhas * Merged in bugfix/prov_info_json_fixes (pull request #899) Bugfix/prov info json fixes * fix: robust PROV_INFO_JSON sanitization and TIN backfill logic json_utils: - Add sanitize_prov_info_json with layered parsing (JSON, literal_eval, empty-value-after-colon fix, best-effort dict extraction). - Add _normalize_prov_entries and _prov_value_to_str for uniform str-valued output; flatten list values, strip TIN hyphens. - format_prov_info_json now delegates to sanitize_prov_info_json. postprocessing_funcs: - Add fill_prov_info_tin_from_filename_tin for TIN backfill. - Add validate_and_reformat_date (pipe-wrapped, datetime strings). - Add format_as_json_list (pipe-delimited, comma-separated, quote stripping). postprocess: - Integrate new postprocessing helpers into pipeline flow. postprocess_existing_output: - Support CSV and Excel input, configurable paths, fillna for CSV. tests: - Add test_json_parsers.py for PROV_INFO_JSON parsing coverage. - Add test_postprocess.py for date/list formatting and defau… * Merged in bugfix/reimb_primary_issues (pull request #900) Bugfix/reimb primary issues * Update lesser of distribution prompt to preserve reimb term langauge * Added prompt change to extract correct service when there are multiple sub-rates; Updated validate reimb term to prevent filtering out erm with valid rate * Make reimb primary prompt change * Revert "Make reimb primary prompt change" This reverts commit 55f7d73b9cb523097c96ea50b6614c1db53b46d2. Approved-by: Katon Minhas * Merged in dtc_report (pull request #902) Dtc report * dtc_report_added * lint format fixed * Changed regex location call Approved-by: Katon Minhas * Merged in feature/historical-cost-analysis (pull request #901) Feature/historical cost analysis * Initial commit * Finalize cost_analysis * Black format * Merged DEV into feature/historical-cost-analysis * Refactor * Black * Merged DEV into feature/historical-cost-analysis Approved-by: Siddhant Medar * Merged in feature/ai-pr-review-agent (pull request #906) Add AI code review agent to pull request pipeline * Add AI code review agent to pull request pipeline Adds a pull-requests pipeline that runs CI checks (lint, type check, unit tests) followed by an AI code review step. The AI review uses OIDC to authenticate with AWS Bedrock - no hardcoded AWS keys. It clones the code-review-agent repo and posts a review comment on the PR. * Fix AI code review step: install git in python:3.12-slim image * Trigger pipeline * Trigger pipeline * Trigger pipeline * Trigger pipeline * Trigger pipeline * Trigger pipeline * Trigger pipeline * Remove debug token decode line * Update guide: all checklist items complete, agent working * Remove guide file from repo * Trigger pipeline Approved-by: Katon Minhas * Merged in DAIP2-1980-aarete-derived-effective-dt-issues (pull request #904) DAIP2-1980 aarete derived effective dt issues * fixed earlier of a and b * prompt update * prompt shortened * test changes reverted * Merge branch 'DEV' into DAIP2-1980-aarete-derived-effective-dt-issues * test changes reverted * Merged DEV into DAIP2-1980-aarete-derived-effective-dt-issues * Fix effective date prompt * Replace em-dashes with readable version Approved-by: Katon Minhas * Merged in feature/DAIP2-2023-eliminate-full-context-processing (pull request #905) Feature/DAIP2-2023 eliminate full context processing * testing full context fields * remove full context processing * merge Dev with DAIP2-2-23 * full context removal in client codes * AARETE_DERIVED_PROVIDER_NAME field changes * Merged DEV into feature/DAIP2-2023-eliminate-full-context-processing * optimized provider name * black format fix * contract title fixes * PAYER NAME AUTO RENEWAL IND fixes * Merged DEV into feature/DAIP2-2023-eliminate-full-context-processing * Merge branch 'DEV' into feature/DAIP2-2023-eliminate-full-context-processing * Remove prints Approved-by: Katon Minhas * Merged in feature/code_optimization (pull request #903) Feature/code optimization * Ran Black * made a small change in code_last_check, fixed so it returns string and not single char * Made changes to make sure that default_ind postprocess only happens to the cc output and not dashboard * Merge bugfix/default_ind_postprocess into feature/code_optimization - Parent-child: child_rank column init and cols_to_keep filter - Postprocess: default_ind only on cc output - code_last_check: parser str/list return handling + exception logging - Prompt: FIELD ASSIGNMENT for code extraction (no CRITICAL prefix) * More prompt changes and debugging print statements * Added system to group same service term + bill type cd + claim type cd and then parallelize the code extraction (will improve consistency) * Revert one-time PROV_INFO_JSON ad hoc logic; retain json_utils and output format - Remove temporary postprocess step that filled empty TIN in PROV_INFO_JSON from FILENAME_TIN (fill_prov_info_tin_from_filename_ti… * Merged in DAIP2-2121-prov-info-fields-issue-fixes (pull request #908) empty prov_info_json fixed * empty prov_info_json fixed * Merged DEV into DAIP2-2121-prov-info-fields-issue-fixes * format of PROV_OTHER_TIN fixed * Revert "format of PROV_OTHER_TIN fixed" This reverts commit 580847de03265520b1b4fad473bbf38ef1573753. * formatting and deduplication * pipeline error fixed * test cases added * pipeline error fixed Approved-by: Katon Minhas * Merged in feature/context-caching (pull request #909) Feature/context caching * Initial commit - context caching for DYNAMIC_PRIMARY * implement context caching for all relevant prompts * Remove option to not context cache * IndentationError fixed * Merge branch 'DEV' into feature/context-caching * Merge and format * Move documentation * Merged DEV into feature/context-caching * Update unit tests * Merged DEV into feature/context-caching * Update signatures * Fix test coverage gap Approved-by: Praneel Panchigar Approved-by: Karan Desai * Merged in bugfix/DAIP2-2138-reimbursement-primary-testing (pull request #910) Bugfix/DAIP2-2138 reimbursement primary testing * updated table instructions for reimb term extraction * updated split tables to include header rows * issue fix * Merged DEV into bugfix/DAIP2-2138-reimbursement-primary-testing * black format fix Approved-by: Katon Minhas * Merged in feature/doczy_reports (pull request #911) Feature/doczy reports * Added post_doczy_reports * black format fix * Merged DEV into feature/doczy_reports * fixed code review Approved-by: Katon Minhas * Merged in feature/median-cost-analysis (pull request #912) Add median * Add median Approved-by: Siddhant Medar Approved-by: Siddhant Medar
10 KiB
Context Caching Implementation - Complete
Summary
Successfully implemented context caching for 6 high-value prompts across all 3 client pipelines. This enables Anthropic's prompt caching at the exhibit/context level, where the same context is cached and reused across multiple field extractions, reducing token costs by ~84% for repeated context processing.
Prompts with Context Caching
- DYNAMIC_PRIMARY (pilot) - Primary term field extraction
- EXHIBIT_LEVEL - Exhibit-level metadata extraction
- DYNAMIC_ASSIGNMENT - Dynamic term assignment to exhibit rows
- REIMB_DATES_ASSIGNMENT - Reimbursement date assignment (specialized)
- LESSER_OF_DISTRIBUTION - Lesser-of logic distribution across codes
- LESSER_OF_CHECK - Lesser-of presence validation
Pipelines Updated
- ✅ bcbs_promise - All 5 applicable functions updated
- ✅ clover - All 5 applicable functions updated
- ✅ saas - All 5 applicable functions updated
What Changed
1. Extended LLM API (llm_utils.py)
Added context_for_caching parameter throughout the call chain:
invoke_claude()- New optional parameter_build_claude_3_request_body()- Structures multi-block messages with cache controlget_cache_key()- Includes context in cache key generationlocal_claude_3_and_up()- Passes parameter throughec2_claude_3_and_up()- Passes parameter through
Key Innovation: Messages now support multiple content blocks where specific blocks can be marked for caching:
"messages": [{
"role": "user",
"content": [
{
"type": "text",
"text": "Large exhibit text (40k tokens)",
"cache_control": {"type": "ephemeral"} # CACHED
},
{
"type": "text",
"text": "Field-specific question (200 tokens)" # NOT CACHED
}
]
}]
2. Updated Existing Prompt Templates (prompt_templates.py)
Updated 6 existing functions to always split prompts into cacheable and fresh components:
- DYNAMIC_PRIMARY() - Caches exhibit text, field question stays fresh
- EXHIBIT_LEVEL() - Caches exhibit text, field questions stay fresh
- DYNAMIC_ASSIGNMENT() - Caches exhibit simplified text, term questions stay fresh
- REIMB_DATES_ASSIGNMENT() - Specialized for REIMB_DATES assignment
- LESSER_OF_DISTRIBUTION() - Caches exhibit text and cross-exhibit context
- LESSER_OF_CHECK() - Caches exhibit title context
Each returns (context_text, prompt, parser) instead of (prompt, parser).
These functions now always return (context_text, prompt, parser) for context caching.
3. Updated All Client Prompt Calls
Updated functions across all 3 pipelines:
bcbs_promise/prompts/prompt_calls.py:
prompt_exhibit_level()prompt_dynamic_primary()prompt_dynamic_assignment()prompt_lesser_of_distribution()prompt_lesser_of_check()
clover/prompts/prompt_calls.py:
prompt_exhibit_level()prompt_dynamic_primary()prompt_dynamic_assignment()prompt_lesser_of_distribution()prompt_lesser_of_check()
saas/prompts/prompt_calls.py:
prompt_exhibit_level()prompt_dynamic_primary()prompt_dynamic_assignment()prompt_lesser_of_distribution()prompt_lesser_of_check()
Each function now:
- Uses the original template function (always split for caching)
- Receives
(context_text, prompt, parser)tuple - Passes
context_for_caching=context_texttoinvoke_claude() - Logs context length for monitoring
Cost Impact Analysis
Current Structure (Before)
- System message (cached): Field extraction instruction (~2k tokens)
- User message (NOT cached): Combined exhibit + field question (~40k tokens)
For 20 fields on same exhibit:
- Instruction: 2k × 1 creation = cached once ✓
- Content: 40k × 20 calls = 800k tokens at $0.003/1k = $2.40
New Structure (After)
- System message (cached): Field extraction instruction (~2k tokens)
- User message block 1 (cached): Exhibit context (~40k tokens)
- User message block 2 (not cached): Field question (~200 tokens)
For 20 fields on same exhibit:
- Instruction: 2k × 1 creation = cached once ✓
- Context: 40k × 1 creation at $0.00375/1k = $0.15
- Context: 40k × 19 reads at $0.0003/1k = $0.228
- Field questions: 20 × 200 tokens at $0.003/1k = $0.012
- Total: $0.39 (84% cost reduction)
Break-Even Analysis
- 1st field: Pay 25% premium for cache creation
- 2nd field: Start saving with 90% cheaper cache reads
- 3+ fields: Massive savings accumulate
How It Works
Caching Layers (Claude API)
Layer 1: System Instruction (cached) ← Already implemented
↓
Layer 2: Exhibit Context (cached) ← NEW - This implementation
↓
Layer 3: Field Question (fresh) ← Changes per call
Flow Example
# Processing LOB field for exhibit
context_text, prompt, parser = DYNAMIC_PRIMARY(
exhibit_text="[40k token exhibit]",
field_name="LOB",
field_prompt="Line of Business definition",
)
llm_utils.invoke_claude(
prompt=prompt, # Just the field question
context_for_caching=context_text, # Exhibit text (cached)
instruction=DYNAMIC_PRIMARY_INSTRUCTION(), # Rules (already cached)
cache=True
)
# First call: Cache creation for exhibit
# Cost: (2k instruction + 40k context) × cache multiplier + 200 tokens fresh
# Processing PROGRAM field for SAME exhibit
context_text, prompt, parser = DYNAMIC_PRIMARY(
exhibit_text="[SAME 40k token exhibit]", # Same content
field_name="PROGRAM",
field_prompt="Program definition",
)
llm_utils.invoke_claude(
prompt=prompt, # Different field question
context_for_caching=context_text, # SAME exhibit (cache hit!)
instruction=DYNAMIC_PRIMARY_INSTRUCTION(),
cache=True
)
# Second call: Cache read for exhibit
# Cost: (2k + 40k) × cache read rate (90% cheaper) + 200 tokens fresh
Testing
Created comprehensive test suite in src/tests/test_context_caching.py:
✅ test_dynamic_primary_returns_three_values() - Validates always-split signature
✅ test_dynamic_primary_original_still_works() - Backward compatibility
✅ test_build_request_body_with_context_caching() - Message structure verification
✅ test_build_request_body_without_context_caching() - Fallback behavior
✅ test_cache_key_includes_context() - Cache key uniqueness
Monitoring & Validation
To verify the implementation is working:
-
Check usage logs for cache metrics:
# In usage_tracking.py logs, look for: cache_creation_tokens: 40000 # First call cache_read_tokens: 40000 # Subsequent calls -
Monitor cost per file in usage reports:
- Should see dramatic cost reduction for files with many dynamic fields
- Exhibits with 10+ fields should show 80%+ savings on exhibit processing
-
Log analysis:
DEBUG: Context length for caching: 42567 charsThis confirms context is being passed to caching layer.
Implementation Status
✅ Completed
All high-value prompts have been migrated to context caching across all 3 client pipelines:
- DYNAMIC_PRIMARY ✅ - Primary term field extraction (pilot implementation)
- EXHIBIT_LEVEL ✅ - Exhibit-level metadata extraction
- DYNAMIC_ASSIGNMENT ✅ - Dynamic term assignment to exhibit rows
- REIMB_DATES_ASSIGNMENT ✅ - Reimbursement date assignment (specialized)
- LESSER_OF_DISTRIBUTION ✅ - Lesser-of logic distribution across codes
- LESSER_OF_CHECK ✅ - Lesser-of presence validation
Cost Savings: Estimated 80-85% reduction in token costs for repeated exhibit/context processing across these 6 prompts.
Future Considerations
Lower Priority Candidates (evaluate after monitoring current implementation):
- METHODOLOGY_BREAKOUT - Could cache reimbursement terms for multiple breakout operations
- Other exhibit-level prompts - If processing changes to single-field-at-a-time pattern
Monitoring Required:
- Track cache hit rates and actual cost savings in production
- Validate that 5-minute cache TTL aligns with typical processing patterns
- Identify any additional prompts with repeated context usage patterns
Implementation Pattern (For Future Extensions)
For any new prompt to extend:
- Update
[PROMPT_NAME]()to return(context, prompt, parser) - Update corresponding
prompt_[name]()function to use caching version - Pass context via
context_for_cachingparameter - Monitor cache metrics to validate savings
Backward Compatibility
✅ Original DYNAMIC_PRIMARY() function remains unchanged
✅ Other templates continue to work without modification
✅ context_for_caching parameter is optional (defaults to None)
✅ When None, behavior is identical to previous implementation
✅ All tests should pass without modification
Files Modified
Core Infrastructure:
- src/utils/llm_utils.py - Extended API with
context_for_cachingparameter - src/prompts/prompt_templates.py - Added 6 context-caching template variants
Pipeline Updates (All 3 Clients):
- src/pipelines/clients/bcbs_promise/prompts/prompt_calls.py - Updated 5 functions
- src/pipelines/clients/clover/prompts/prompt_calls.py - Updated 5 functions
- src/pipelines/saas/prompts/prompt_calls.py - Updated 5 functions
Testing & Documentation:
- src/tests/test_context_caching.py - Comprehensive test suite
- CONTEXT_CACHING_IMPLEMENTATION.md - This documentation
Technical Notes
- Anthropic prompt caching requires minimum 1024 tokens for cache block
- Cache TTL is 5 minutes for
ephemeraltype - Only works with Claude 3.5+ Sonnet v2 models (checked via
_supports_prompt_cache()) - Cache keys include both instruction and context to ensure uniqueness
- Multiple content blocks in user messages is supported by Bedrock Messages API