diff --git a/fieldExtraction/src/config.py b/fieldExtraction/src/config.py index 0c5291b..c24dc06 100644 --- a/fieldExtraction/src/config.py +++ b/fieldExtraction/src/config.py @@ -236,7 +236,21 @@ MODEL_ID_CLAUDE35_SONNET = "anthropic.claude-3-5-sonnet-20240620-v1:0" MODEL_ID_CLAUDE2 = "anthropic.claude-instant-v1" MODEL_ID_CLAUDE35_SONNET_V2 = "anthropic.claude-3-5-sonnet-20241022-v2:0" MODEL_ID_CLAUDE37_SONNET = "anthropic.claude-3-7-sonnet-20250219-v1:0" -MODEL_ID_CLAUDE4_SONNET = "anthropic.claude-sonnet-4-20250514-v1:0" +MODEL_ID_CLAUDE4_SONNET = "us.anthropic.claude-sonnet-4-20250514-v1:0" + +# Model aliases for easy upgrades +MODEL_ALIASES = { + "sonnet_latest": MODEL_ID_CLAUDE4_SONNET, + "haiku_latest": MODEL_ID_CLAUDE3_HAIKU, + "legacy_sonnet": MODEL_ID_CLAUDE35_SONNET, +} + +def resolve_model_id(model_identifier: str) -> str: + """ + Resolve a model identifier to its actual model ID. + Supports both direct model IDs and aliases. + """ + return MODEL_ALIASES.get(model_identifier, model_identifier) ######################################## CLAUDE AND PROMPT TRACKING ######################################## ENABLE_ANSWER_TRACKING = False diff --git a/fieldExtraction/src/investment/aarete_derived.py b/fieldExtraction/src/investment/aarete_derived.py index 6bc5477..28d3473 100644 --- a/fieldExtraction/src/investment/aarete_derived.py +++ b/fieldExtraction/src/investment/aarete_derived.py @@ -126,7 +126,7 @@ def get_aarete_derived(aarete_derived_fields: FieldSet, # Invoke Claude and extract answer field_answer_raw = llm_utils.invoke_claude( field_prompt, - config.MODEL_ID_CLAUDE35_SONNET, + "sonnet_latest", filename, 8192 ) diff --git a/fieldExtraction/src/investment/code_funcs.py b/fieldExtraction/src/investment/code_funcs.py index 130ae98..9a5a34e 100644 --- a/fieldExtraction/src/investment/code_funcs.py +++ b/fieldExtraction/src/investment/code_funcs.py @@ -147,7 +147,7 @@ def code_primary(service, code_primary_questions, filename): if not string_utils.is_empty(service): claude_answer_raw = llm_utils.invoke_claude( investment_prompts.CODE_PRIMARY_BREAKOUT(str(service), code_primary_questions), - config.MODEL_ID_CLAUDE35_SONNET, + "sonnet_latest", filename ) code_answer_dict = string_utils.universal_json_load(claude_answer_raw) @@ -290,7 +290,7 @@ def code_implicit(service, filename): list: Updated list of dictionaries with mapped CPT4 procedure codes and descriptions. """ if not string_utils.is_empty(service): - claude_answer_raw = llm_utils.invoke_claude(investment_prompts.CODE_IMPLICIT(service), config.MODEL_ID_CLAUDE35_SONNET, filename) + claude_answer_raw = llm_utils.invoke_claude(investment_prompts.CODE_IMPLICIT(service), "sonnet_latest", filename) code_answer_final = string_utils.universal_json_load(claude_answer_raw) else: code_answer_final = "" diff --git a/fieldExtraction/src/investment/dynamic_funcs.py b/fieldExtraction/src/investment/dynamic_funcs.py index 48ef0ae..fb76ab7 100644 --- a/fieldExtraction/src/investment/dynamic_funcs.py +++ b/fieldExtraction/src/investment/dynamic_funcs.py @@ -20,7 +20,7 @@ def prompt_dynamic(text, field_prompts, filename): dict: A dictionary containing field names as keys and extracted answers as values. """ prompt = investment_prompts.EXHIBIT_LEVEL(text, field_prompts) - llm_answer_raw = llm_utils.invoke_claude(prompt, config.MODEL_ID_CLAUDE35_SONNET, filename) # Returns dictionary of lists + llm_answer_raw = llm_utils.invoke_claude(prompt, "sonnet_latest", filename) # Returns dictionary of lists llm_answer_final = string_utils.universal_json_load(llm_answer_raw) return llm_answer_final @@ -45,7 +45,7 @@ def get_dynamic_one_to_one_fields(answer_dicts): def prompt_dynamic_primary(exhibit_text, field, filename): # Prompt Exhibit Header prompt = investment_prompts.DYNAMIC_PRIMARY(exhibit_text, field.field_name, field.get_prompt()) - claude_answer_raw = llm_utils.invoke_claude(prompt, config.MODEL_ID_CLAUDE35_SONNET, filename) + claude_answer_raw = llm_utils.invoke_claude(prompt, "sonnet_latest", filename) claude_answer_final = string_utils.extract_text_from_delimiters(claude_answer_raw, string_utils.Delimiter.PIPE) if "," in claude_answer_final: claude_answer_final = "|".join([item.strip() for item in claude_answer_final.split(",")]) diff --git a/fieldExtraction/src/investment/investment_postprocessing_funcs.py b/fieldExtraction/src/investment/investment_postprocessing_funcs.py index 7e69d3b..3b3cc78 100644 --- a/fieldExtraction/src/investment/investment_postprocessing_funcs.py +++ b/fieldExtraction/src/investment/investment_postprocessing_funcs.py @@ -474,7 +474,7 @@ def update_lob_for_duals(answer_dicts): # Check if the SERVICE_TERM contains "Medicare" and "Medicaid" - case insensitive if 'medicare' in service_term and 'medicaid' in service_term: prompt = investment_prompts.DUAL_LOB_CHECK(service_term) - claude_answer_raw = llm_utils.invoke_claude(prompt, model_id=config.MODEL_ID_CLAUDE35_SONNET, filename="", max_tokens=200) + claude_answer_raw = llm_utils.invoke_claude(prompt, model_id="sonnet_latest", filename="", max_tokens=200) claude_answer_extracted = string_utils.extract_text_from_delimiters(claude_answer_raw, Delimiter.PIPE) answer_dict["AARETE_DERIVED_LOB"] = claude_answer_extracted final_answer_dicts.append(answer_dict) diff --git a/fieldExtraction/src/investment/one_to_n_funcs.py b/fieldExtraction/src/investment/one_to_n_funcs.py index 24809d1..db10072 100644 --- a/fieldExtraction/src/investment/one_to_n_funcs.py +++ b/fieldExtraction/src/investment/one_to_n_funcs.py @@ -34,7 +34,7 @@ def get_exhibit_level_answers(exhibit_chunk, filename): prompt = EXHIBIT_LEVEL(exhibit_chunk, exhibit_level_fields.print_prompt_dict()) llm_answer_raw = llm_utils.invoke_claude( - prompt, config.MODEL_ID_CLAUDE35_SONNET, filename, max_tokens=8192 + prompt, "sonnet_latest", filename, max_tokens=8192 ) logging.debug(f"LLM raw output for {filename}: {llm_answer_raw}") llm_answer_final = string_utils.universal_json_load(llm_answer_raw) @@ -50,7 +50,7 @@ def get_reimbursement_primary(reimbursement_level_fields, exhibit_text, filename {prompt}""" ) llm_answer_raw = llm_utils.invoke_claude( - prompt, config.MODEL_ID_CLAUDE35_SONNET, filename, max_tokens=25000 + prompt, "sonnet_latest", filename, max_tokens=25000 ) logging.debug( f"""LLM raw output for {filename}: @@ -95,7 +95,7 @@ def get_special_cases( investment_prompts.SPECIAL_CASE_CHECK( contract_service_cd, contract_reimbursement_method ), - config.MODEL_ID_CLAUDE35_SONNET, + "sonnet_latest", filename, ) special_case_answer = string_utils.extract_text_from_delimiters( @@ -106,7 +106,7 @@ def get_special_cases( investment_prompts.CARVEOUT_CHECK( contract_service_cd, contract_reimbursement_method ), - config.MODEL_ID_CLAUDE35_SONNET, + "sonnet_latest", filename, ) carveout_answer = string_utils.extract_text_from_delimiters( @@ -289,7 +289,7 @@ def methodology_breakout_single_row( logging.debug(f"Methodology Breakout Prompt for {filename}: {prompt}") llm_response = llm_utils.invoke_claude( prompt, - config.MODEL_ID_CLAUDE35_SONNET, + "sonnet_latest", filename, ) logging.debug(f"LLM Response for {filename}: {llm_response}") @@ -327,7 +327,7 @@ def methodology_breakout_single_row( methodology_breakout_dict.get("FEE_SCHEDULE"), fs_breakout_questions, ), - config.MODEL_ID_CLAUDE35_SONNET, + "sonnet_latest", filename, ) ) @@ -619,7 +619,7 @@ def validate_reimbursements_for_llm(answer_dict: dict, filename: str) -> bool: prompt = investment_prompts.VALIDATE_REIMBURSEMENTS_PROMPT(service_term, reimb_term) llm_response = llm_utils.invoke_claude( - prompt, config.MODEL_ID_CLAUDE3_HAIKU, filename # try haiku for speed/cost + prompt, "haiku_latest", filename # try haiku for speed/cost ) final_answer = string_utils.extract_text_from_delimiters( @@ -689,7 +689,7 @@ def split_compound_reimbursement_llm(answer_dict: dict, filename: str) -> list[d service_term, reimb_term) try: llm_response = llm_utils.invoke_claude( - prompt, config.MODEL_ID_CLAUDE3_HAIKU, filename, max_tokens=4096 + prompt, "haiku_latest", filename, max_tokens=4096 ) split_data = string_utils.universal_json_load(llm_response) @@ -904,6 +904,6 @@ def is_exhibit_lesser_of_redundant(reimb_term: str, exhibit_lesser_of: str, file prompt = investment_prompts.CHECK_REDUNDANT_LESSER(reimb_term, exhibit_lesser_of) - response = llm_utils.invoke_claude(prompt, config.MODEL_ID_CLAUDE3_HAIKU, filename=filename) + response = llm_utils.invoke_claude(prompt, "haiku_latest", filename=filename) # logging.debug(f"LLM response for redundancy check in {filename}: {response}") return string_utils.extract_text_from_delimiters(response, Delimiter.PIPE).strip().upper() == "YES" \ No newline at end of file diff --git a/fieldExtraction/src/investment/one_to_one_funcs.py b/fieldExtraction/src/investment/one_to_one_funcs.py index 3c6ad5c..5cb1ae5 100644 --- a/fieldExtraction/src/investment/one_to_one_funcs.py +++ b/fieldExtraction/src/investment/one_to_one_funcs.py @@ -38,7 +38,7 @@ def extract_global_lesser_of(contract_text: str, filename:str) -> dict: logging.debug(f"Extracting global lesser of statement for {filename} with prompt: {prompt}") - response = llm_utils.invoke_claude(prompt, config.MODEL_ID_CLAUDE35_SONNET, filename) + response = llm_utils.invoke_claude(prompt, "sonnet_latest", filename) logging.debug(f"Response for global lesser of statement extraction: {response}") return {"GLOBAL_LESSER_OF_STATEMENT": string_utils.extract_text_from_delimiters(response, Delimiter.PIPE) or "N/A"} @@ -142,7 +142,7 @@ def run_smart_chunked_fields(one_to_one_fields: FieldSet, try: field_group_answer_raw = llm_utils.invoke_claude( - prompt, config.MODEL_ID_CLAUDE35_SONNET, filename, 8192 + prompt, "sonnet_latest", filename, 8192 ) if len(fields) == 1: field_group_answer_parsed = string_utils.extract_text_from_delimiters(field_group_answer_raw, Delimiter.PIPE) @@ -306,7 +306,7 @@ def run_global_lesser_of_breakout(global_lesser_of_stmt: str, filename: str) -> prompt = REIMB_TERM_BREAKOUT(mock_service, global_lesser_of_stmt, METHODOLOGY_BREAKOUT_QUESTIONS) logging.debug(f"Running global lesser of breakout for {filename} with prompt: {prompt}") - llm_response = llm_utils.invoke_claude(prompt, config.MODEL_ID_CLAUDE35_SONNET, filename) + llm_response = llm_utils.invoke_claude(prompt, "sonnet_latest", filename) try: breakout_results = string_utils.universal_json_load(llm_response) diff --git a/fieldExtraction/src/investment/tin_npi_funcs.py b/fieldExtraction/src/investment/tin_npi_funcs.py index 6d3c510..35e3b88 100644 --- a/fieldExtraction/src/investment/tin_npi_funcs.py +++ b/fieldExtraction/src/investment/tin_npi_funcs.py @@ -241,7 +241,7 @@ def identify_group_provider(text_dict: dict, providers: list[dict], filename: st prompt = investment_prompts.GROUP_TIN_NPI_TEMPLATE(key_sections=key_sections, provider_list=provider_list) # Get response and parse - response = llm_utils.invoke_claude(prompt, config.MODEL_ID_CLAUDE35_SONNET, filename) + response = llm_utils.invoke_claude(prompt, "sonnet_latest", filename) try: group_info = json.loads(string_utils.extract_text_from_delimiters(response, Delimiter.PIPE)) @@ -336,7 +336,7 @@ def get_provider_info(text_dict: dict, page_num : str, filename: str) -> list: provider_fields = FieldSet(file_path=config.FIELD_JSON_PATH, field_type="provider_info") prompt = investment_prompts.TIN_NPI_TEMPLATE(chunk, provider_fields.print_prompt_dict()) - claude_answer_raw = llm_utils.invoke_claude(prompt, config.MODEL_ID_CLAUDE35_SONNET, filename, max_tokens=8192) # Sometimes rosters can have 50+ provider + claude_answer_raw = llm_utils.invoke_claude(prompt, "sonnet_latest", filename, max_tokens=8192) # Sometimes rosters can have 50+ provider # Defensive programming for any json parsing errors try: diff --git a/fieldExtraction/src/postprocessing_funcs.py b/fieldExtraction/src/postprocessing_funcs.py index 68d538f..1c97720 100644 --- a/fieldExtraction/src/postprocessing_funcs.py +++ b/fieldExtraction/src/postprocessing_funcs.py @@ -943,7 +943,7 @@ def clean_lob(df, filename): ): prompt = postprocessing_prompts.LOB_SWEEPER(row["EXHIBIT"]) answer = llm_utils.invoke_claude( - prompt, config.MODEL_ID_CLAUDE35_SONNET, filename, 256 + prompt, "sonnet_latest", filename, 256 ) return answer # Return the value from 'EXHIBIT' if condition is met return row["CONTRACT_LOB"] @@ -1184,7 +1184,7 @@ def date_postprocessing(date: str) -> str: return "N/A" prompt = investment_prompts.DATE_FIX_PROMPT(date) - response = llm_utils.invoke_claude(prompt, config.MODEL_ID_CLAUDE35_SONNET, "date_fix") + response = llm_utils.invoke_claude(prompt, "sonnet_latest", "date_fix") return extract_text_from_delimiters(response, Delimiter.PIPE) def generate_reimb_ids(df: pd.DataFrame) -> pd.DataFrame: diff --git a/fieldExtraction/src/preprocessing_funcs.py b/fieldExtraction/src/preprocessing_funcs.py index 13b9fa0..f9d49a6 100644 --- a/fieldExtraction/src/preprocessing_funcs.py +++ b/fieldExtraction/src/preprocessing_funcs.py @@ -243,7 +243,7 @@ def get_exhibit_pages(text_dict: dict[str, str], filename: str) -> tuple[list[st if "." not in page_num or ".0" in page_num: prompt = investment_prompts.EXHIBIT_HEADER(page_content[0:400]) claude_answer_raw = llm_utils.invoke_claude( - prompt, config.MODEL_ID_CLAUDE35_SONNET, filename, max_tokens=300 + prompt, "sonnet_latest", filename, max_tokens=300 ) claude_answer_extracted = string_utils.extract_text_from_delimiters( claude_answer_raw, Delimiter.PIPE @@ -304,7 +304,7 @@ def link_exhibit_pages(all_exhibit_headers, filename): previous_header = page_header else: prompt = investment_prompts.EXHIBIT_LINKAGE(page_header, previous_header) - claude_answer_raw = llm_utils.invoke_claude(prompt, config.MODEL_ID_CLAUDE35_SONNET, filename) + claude_answer_raw = llm_utils.invoke_claude(prompt, "sonnet_latest", filename) exhibit_is_different = string_utils.extract_text_from_delimiters(claude_answer_raw, Delimiter.PIPE) previous_header = page_header @@ -502,7 +502,7 @@ def get_exhibit_headers(exhibit_dict: dict[str, str], filename: str) -> dict[str else: prompt = investment_prompts.EXHIBIT_HEADER(exhibit_chunk[0:400]) llm_answer_raw = llm_utils.invoke_claude( - prompt, config.MODEL_ID_CLAUDE35_SONNET, filename + prompt, "sonnet_latest", filename ) llm_answer_final = string_utils.extract_text_from_delimiters( llm_answer_raw, Delimiter.PIPE diff --git a/fieldExtraction/src/prompts/investment_prompts.py b/fieldExtraction/src/prompts/investment_prompts.py index 9fcd51e..a6f0b59 100644 --- a/fieldExtraction/src/prompts/investment_prompts.py +++ b/fieldExtraction/src/prompts/investment_prompts.py @@ -6,7 +6,6 @@ import pandas as pd import src.constants.valid as valid import src.utils.llm_utils as llm_utils import src.utils.string_utils as string_utils -from src.config import MODEL_ID_CLAUDE35_SONNET from src.constants.investment_values import ( EXHIBIT_HEADER_MARKERS, VALID_AARETE_DERIVED_FEE_SCHEDULE, @@ -782,7 +781,7 @@ def DERIVED_TERM_DATE_PROMPT(effective_date:str=None, termination_information:st @cache def invoke_derived_term_date(effective_date:str=None, termination_information:str=None) -> str: prompt = DERIVED_TERM_DATE_PROMPT(effective_date, termination_information) - response = llm_utils.invoke_claude(prompt, MODEL_ID_CLAUDE35_SONNET, "derive_term_date") + response = llm_utils.invoke_claude(prompt, "sonnet_latest", "derive_term_date") return extract_text_from_delimiters(response, Delimiter.PIPE) diff --git a/fieldExtraction/src/utils/llm_utils.py b/fieldExtraction/src/utils/llm_utils.py index ed591ed..29a9f1f 100644 --- a/fieldExtraction/src/utils/llm_utils.py +++ b/fieldExtraction/src/utils/llm_utils.py @@ -16,6 +16,7 @@ import hashlib import inspect import csv import base64 +import logging def update_stats(filename, tokens_sent, tokens_received, elapsed_time, model_type): # Update per-file statistics @@ -110,7 +111,29 @@ def get_cache_key(prompt, model_id): """Generates a unique hash key for caching.""" return hashlib.sha256(f"{model_id}:{prompt}".encode()).hexdigest() -def invoke_claude(prompt, model_id, filename, max_tokens=4096): +def invoke_claude(prompt: str, model_id: str, filename: str, max_tokens: int = 4096) -> str: + """Invokes the Claude model with the given parameters. + + Args: + prompt (str): The input prompt to send to the model. + model_id (str): The ID of the model to use. Supports both direct model IDs + (e.g. 'anthropic.claude-3-5-sonnet-20240620-v1:0') as well as aliases (e.g. + 'sonnet_latest', 'haiku_latest', etc.). + filename (str): The name of the file being processed, for cost logging purposes. + max_tokens (int, optional): The maximum number of tokens to generate. Defaults to 4096. + + Raises: + ValueError: If the model_id is not supported after alias resolution + + Returns: + str: The model's response. + + Note: + - Responses are cached using LRU eviction (limit: 20,000 entries) + - Includes automatic retry logic with exponential backoff for throttling + - Cost logging is performed automatically for all successful requests + - Model aliases are resolved via config.resolve_model_id() + """ cache_key = get_cache_key(prompt, model_id) # Check cache first - use get() method from LRUCache @@ -118,40 +141,43 @@ def invoke_claude(prompt, model_id, filename, max_tokens=4096): claude_cache.move_to_end(cache_key) # Mark as recently used return claude_cache[cache_key] + # Resolve alias to actual model ID + resolved_model_id = config.resolve_model_id(model_id) + # Select execution mode if config.RUN_MODE == "local": - if model_id == config.MODEL_ID_CLAUDE2: - response = local_claude_2(prompt, model_id, filename, max_tokens) + if resolved_model_id == config.MODEL_ID_CLAUDE2: + response = local_claude_2(prompt, resolved_model_id, filename, max_tokens) input_cost_per_1k, output_cost_per_1k = 0.008, 0.024 - elif model_id == config.MODEL_ID_CLAUDE3_HAIKU: - response = local_claude_3_and_up(prompt, model_id, filename, max_tokens) + elif resolved_model_id == config.MODEL_ID_CLAUDE3_HAIKU: + response = local_claude_3_and_up(prompt, resolved_model_id, filename, max_tokens) input_cost_per_1k, output_cost_per_1k = 0.00025, 0.00125 - elif model_id == config.MODEL_ID_CLAUDE35_SONNET: - response = local_claude_3_and_up(prompt, model_id, filename, max_tokens) + elif resolved_model_id == config.MODEL_ID_CLAUDE35_SONNET: + response = local_claude_3_and_up(prompt, resolved_model_id, filename, max_tokens) input_cost_per_1k, output_cost_per_1k = 0.003, 0.015 - elif model_id == config.MODEL_ID_CLAUDE37_SONNET: - response = local_claude_3_and_up(prompt, model_id, filename, max_tokens) + elif resolved_model_id == config.MODEL_ID_CLAUDE37_SONNET: + response = local_claude_3_and_up(prompt, resolved_model_id, filename, max_tokens) input_cost_per_1k, output_cost_per_1k = 0.003, 0.015 - elif model_id == config.MODEL_ID_CLAUDE4_SONNET: - response = local_claude_3_and_up(prompt, model_id, filename, max_tokens) + elif resolved_model_id == config.MODEL_ID_CLAUDE4_SONNET: + response = local_claude_3_and_up(prompt, resolved_model_id, filename, max_tokens) input_cost_per_1k, output_cost_per_1k = 0.003, 0.015 else: raise ValueError(f"Unsupported model_id: {model_id}") elif config.RUN_MODE == "ec2": - if model_id == config.MODEL_ID_CLAUDE2: - response = ec2_claude2(prompt, model_id, filename, max_tokens) + if resolved_model_id == config.MODEL_ID_CLAUDE2: + response = ec2_claude2(prompt, resolved_model_id, filename, max_tokens) input_cost_per_1k, output_cost_per_1k = 0.008, 0.024 - elif model_id == config.MODEL_ID_CLAUDE3_HAIKU: - response = ec2_claude_3_and_up(prompt, model_id, filename, max_tokens) + elif resolved_model_id == config.MODEL_ID_CLAUDE3_HAIKU: + response = ec2_claude_3_and_up(prompt, resolved_model_id, filename, max_tokens) input_cost_per_1k, output_cost_per_1k = 0.00025, 0.00125 - elif model_id == config.MODEL_ID_CLAUDE35_SONNET: - response = ec2_claude_3_and_up(prompt, model_id, filename, max_tokens) + elif resolved_model_id == config.MODEL_ID_CLAUDE35_SONNET: + response = ec2_claude_3_and_up(prompt, resolved_model_id, filename, max_tokens) input_cost_per_1k, output_cost_per_1k = 0.003, 0.015 - elif model_id == config.MODEL_ID_CLAUDE37_SONNET: - response = ec2_claude_3_and_up(prompt, model_id, filename, max_tokens) + elif resolved_model_id == config.MODEL_ID_CLAUDE37_SONNET: + response = ec2_claude_3_and_up(prompt, resolved_model_id, filename, max_tokens) input_cost_per_1k, output_cost_per_1k = 0.003, 0.015 - elif model_id == config.MODEL_ID_CLAUDE4_SONNET: - response = ec2_claude_3_and_up(prompt, model_id, filename, max_tokens) + elif resolved_model_id == config.MODEL_ID_CLAUDE4_SONNET: + response = ec2_claude_3_and_up(prompt, resolved_model_id, filename, max_tokens) input_cost_per_1k, output_cost_per_1k = 0.003, 0.015 else: raise ValueError(f"Unsupported model_id: {model_id}") @@ -167,9 +193,12 @@ def invoke_claude(prompt, model_id, filename, max_tokens=4096): # Cost logging current_frame = inspect.currentframe() - caller_frame = current_frame.f_back - caller_name = caller_frame.f_code.co_name - del current_frame, caller_frame # Avoid reference cycles + if current_frame is not None and current_frame.f_back is not None: + caller_name = current_frame.f_back.f_code.co_name + else: + caller_name = "unknown" + + del current_frame # Avoid reference cycles cost_log_entry = create_cost_log_entry( filename, caller_name, prompt, response, input_cost_per_1k, output_cost_per_1k @@ -193,16 +222,23 @@ def invoke_claude_with_image_array(prompt, model_id, filename, base64_images,max claude_cache.move_to_end(cache_key) # Mark as recently used return claude_cache[cache_key] + # Resolve alias to actual model ID + resolved_model_id = config.resolve_model_id(model_id) + # Select execution mode if config.RUN_MODE == "local": - if model_id == config.MODEL_ID_CLAUDE35_SONNET: - response = local_claude_3_and_up_with_image_array(prompt, model_id, filename, max_tokens,base64_images) - print(f"Eff Dt Response from local Claude 3 for {filename}", response) + if resolved_model_id == config.MODEL_ID_CLAUDE35_SONNET: + response = local_claude_3_and_up_with_image_array(prompt, resolved_model_id, filename, max_tokens,base64_images) + logging.info(f"Effective Date Response from local Claude 3 for {filename}: {response}") input_cost_per_1k, output_cost_per_1k = 0.003, 0.015 + else: + raise ValueError(f"Unsupported model_id for image array: {resolved_model_id}") elif config.RUN_MODE == "ec2": - if model_id == config.MODEL_ID_CLAUDE2: - response = ec2_claude2(prompt, model_id, filename, max_tokens) + if resolved_model_id == config.MODEL_ID_CLAUDE2: + response = ec2_claude2(prompt, resolved_model_id, filename, max_tokens) input_cost_per_1k, output_cost_per_1k = 0.008, 0.024 + else: + raise ValueError(f"Unsupported model_id for image array: {resolved_model_id}") else: print("Usage: python local_main.py <-local> OR python main.py ") @@ -216,9 +252,12 @@ def invoke_claude_with_image_array(prompt, model_id, filename, base64_images,max # Cost logging current_frame = inspect.currentframe() - caller_frame = current_frame.f_back - caller_name = caller_frame.f_code.co_name - del current_frame, caller_frame # Avoid reference cycles + if current_frame is not None and current_frame.f_back is not None: + caller_name = current_frame.f_back.f_code.co_name + else: + caller_name = "unknown" + + del current_frame # Avoid reference cycles cost_log_entry = create_cost_log_entry( filename, caller_name, prompt, response, input_cost_per_1k, output_cost_per_1k diff --git a/fieldExtraction/src/utils/string_utils.py b/fieldExtraction/src/utils/string_utils.py index 4759ecc..0ada330 100644 --- a/fieldExtraction/src/utils/string_utils.py +++ b/fieldExtraction/src/utils/string_utils.py @@ -214,7 +214,7 @@ def secondary_string_to_dict(dict_string: str, filename: str) -> dict: try: prompt = preprocessing_prompts.FIX_JSON(dict_substring) dict_substring = llm_utils.invoke_claude( - prompt, config.MODEL_ID_CLAUDE3_HAIKU, filename + prompt, "haiku_latest", filename ) result_dict = json.loads(dict_substring) except Exception as e: