2424from aws_lambda_powertools import Logger
2525from botocore .config import Config
2626from botocore .exceptions import ClientError
27+ from idp_common_pkg .idp_common import IDPConfig
28+ from idp_common_pkg .idp_common .utils .bedrock_utils import (
29+ async_exponential_backoff_retry ,
30+ )
2731from PIL import Image
2832from pydantic import BaseModel , Field
2933from strands import Agent , tool
4246 update_todo ,
4347 view_todo_list ,
4448)
45- from lib .idp_common_pkg .idp_common .utils .bedrock_utils import (
46- async_exponential_backoff_retry ,
47- )
4849
4950# Use AWS Lambda Powertools Logger for structured logging
5051# Automatically logs as JSON with Lambda context, request_id, timestamp, etc.
@@ -478,7 +479,7 @@ async def structured_output_async(
478479 existing_data : BaseModel | None = None ,
479480 system_prompt : str | None = None ,
480481 custom_instruction : str | None = None ,
481- review_agent : bool = False ,
482+ config : IDPConfig = IDPConfig () ,
482483 context : str = "Extraction" ,
483484 max_retries : int = 7 ,
484485 connect_timeout : float = 10.0 ,
@@ -593,9 +594,6 @@ async def structured_output_async(
593594 },
594595 )
595596
596- # Build final system prompt without modifying the original
597- final_system_prompt = system_prompt
598-
599597 # Configure retry behavior and timeouts using boto3 Config
600598 boto_config = Config (
601599 retries = {
@@ -606,13 +604,6 @@ async def structured_output_async(
606604 read_timeout = read_timeout ,
607605 )
608606
609- model_config = dict (model_id = model_id , boto_client_config = boto_config )
610- # Set max_tokens based on actual model limits
611- # Reference: https://docs.aws.amazon.com/bedrock/latest/userguide/
612-
613- # Determine model's maximum
614- # Use regex for more flexible matching (e.g., claude-sonnet-4-5 should match claude-sonnet-4)
615-
616607 model_max = 4_096 # Default fallback
617608 model_id_lower = model_id .lower ()
618609 # Check Claude 4 patterns first (more specific)
@@ -681,7 +672,7 @@ async def structured_output_async(
681672 else :
682673 logger .debug ("Caching not supported for model" , extra = {"model_id" : model_id })
683674
684- final_system_prompt = SYSTEM_PROMPT
675+ final_system_prompt = system_prompt if system_prompt else SYSTEM_PROMPT
685676
686677 if custom_instruction :
687678 final_system_prompt = f"{ final_system_prompt } \n \n Custom Instructions for this specific task: { custom_instruction } "
@@ -763,6 +754,7 @@ async def structured_output_async(
763754 ContentBlock (
764755 text = f"Please update the existing data using the extraction tool or patches. Existing data: { existing_data .model_dump ()} "
765756 ),
757+ ContentBlock (cachePoint = CachePoint (type = "default" )),
766758 ],
767759 )
768760 )
@@ -875,7 +867,7 @@ async def structured_output_async(
875867 )
876868
877869 # Add explicit review step (Option 2)
878- if review_agent :
870+ if config . extraction . agentic . enabled and config . extraction . agentic . review_agent :
879871 logger .debug (
880872 "Initiating final review of extracted data" ,
881873 extra = {"review_enabled" : True },
@@ -899,10 +891,32 @@ async def structured_output_async(
899891 If everything is correct, respond with "Data verified and accurate."
900892 If corrections are needed, use the apply_json_patches tool to fix any issues you find.
901893 """
902- )
894+ ),
895+ ContentBlock (cachePoint = CachePoint (type = "default" )),
903896 ],
904897 )
905898 )
899+ model_config = dict (
900+ model_id = config .extraction .agentic .review_agent_model ,
901+ boto_client_config = boto_config ,
902+ max_tokens = max_output_tokens ,
903+ )
904+ agent = Agent (
905+ model = BedrockModel (** model_config ), # pyright: ignore[reportArgumentType]
906+ tools = tools ,
907+ system_prompt = f"{ final_system_prompt } " ,
908+ state = {
909+ "current_extraction" : None ,
910+ "images" : {},
911+ "existing_data" : existing_data .model_dump ()
912+ if existing_data
913+ else None ,
914+ "extraction_schema_json" : schema_json , # Store for schema reminder tool
915+ },
916+ conversation_manager = SummarizingConversationManager (
917+ summary_ratio = 0.8 , preserve_recent_messages = 2
918+ ),
919+ )
906920
907921 review_response = await invoke_agent_with_retry (
908922 agent = agent , input = review_prompt
@@ -960,8 +974,8 @@ def structured_output(
960974 existing_data : BaseModel | None = None ,
961975 system_prompt : str | None = None ,
962976 custom_instruction : str | None = None ,
963- review_agent : bool = False ,
964977 context : str = "Extraction" ,
978+ config : IDPConfig = IDPConfig (),
965979 max_retries : int = 7 ,
966980 connect_timeout : float = 10.0 ,
967981 read_timeout : float = 300.0 ,
@@ -1045,7 +1059,7 @@ def run_in_new_loop():
10451059 existing_data = existing_data ,
10461060 system_prompt = system_prompt ,
10471061 custom_instruction = custom_instruction ,
1048- review_agent = review_agent ,
1062+ config = config ,
10491063 context = context ,
10501064 max_retries = max_retries ,
10511065 connect_timeout = connect_timeout ,
@@ -1076,7 +1090,7 @@ def run_in_new_loop():
10761090 existing_data = existing_data ,
10771091 system_prompt = system_prompt ,
10781092 custom_instruction = custom_instruction ,
1079- review_agent = review_agent ,
1093+ config = config ,
10801094 context = context ,
10811095 max_retries = max_retries ,
10821096 connect_timeout = connect_timeout ,
0 commit comments