Merged in feature/further-phase-1-and-2-fixes (pull request #624)

Sonnet 4 and model aliasing

* Add EXHIBIT_HEADER_MARKERS for improved document header recognition in table handling

* Refactor EXHIBIT_HEADER_MARKERS definition for improved clarity and organization

* remove unused page span tracking code; add unit tests for new functionality

* remove debugging logs from combine_continuous_tables function

* Refactor logging setup and update test file configuration in investment field testing notebook

* Merge remote-tracking branch 'origin/main' into feature/further-phase-1-and-2-fixes

* Merge remote-tracking branch 'origin/main' into feature/further-phase-1-and-2-fixes

* Refactor model ID handling and add support for model aliases in llm_utils

* Update model IDs to use alias in LLM invocation functions in one_to_n_funcs

* change pre-and-post-processing to new model alias method

* Update model ID references to use 'sonnet_latest' in LLM invocation functions

* Update LLM invocation functions to use 'sonnet_latest' model ID

* Update LLM invocation functions to use 'sonnet_latest' and 'haiku_latest' model IDs

* update invoke_claude function docstring

* Fix caller frame to address mypy error


Approved-by: Katon Minhas
This commit is contained in:
Alex Galarce
2025-07-18 19:00:09 +00:00
parent b79674d6c0
commit e161142ea5
13 changed files with 113 additions and 61 deletions
+15 -1
View File
@@ -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
@@ -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
)
+2 -2
View File
@@ -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 = ""
@@ -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(",")])
@@ -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)
@@ -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"
@@ -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)
@@ -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:
+2 -2
View File
@@ -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:
+3 -3
View File
@@ -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
@@ -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)
+71 -32
View File
@@ -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 <ec2>")
@@ -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
+1 -1
View File
@@ -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: