Skip to content

Commit 15c8bbd

Browse files
committed
fixes
1 parent 9ae0feb commit 15c8bbd

File tree

6 files changed

+55
-27
lines changed

6 files changed

+55
-27
lines changed

lib/idp_common_pkg/idp_common/config/models.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,14 @@
2020

2121
from typing import Any, Dict, List, Optional, Union, Literal, Annotated
2222
from typing_extensions import Self
23-
from pydantic import BaseModel, ConfigDict, Field, field_validator, Discriminator, model_validator
23+
from pydantic import (
24+
BaseModel,
25+
ConfigDict,
26+
Field,
27+
field_validator,
28+
Discriminator,
29+
model_validator,
30+
)
2431

2532

2633
class ImageConfig(BaseModel):
@@ -79,7 +86,10 @@ class AgenticConfig(BaseModel):
7986

8087
enabled: bool = Field(default=False, description="Enable agentic extraction")
8188
review_agent: bool = Field(default=False, description="Enable review agent")
82-
review_agent_model: str | None= Field(default=None, description="Model used for reviewing and correcting extraction work")
89+
review_agent_model: str | None = Field(
90+
default=None,
91+
description="Model used for reviewing and correcting extraction work",
92+
)
8393

8494

8595
class ExtractionConfig(BaseModel):
@@ -121,17 +131,16 @@ def parse_int(cls, v: Any) -> int:
121131
if isinstance(v, str):
122132
return int(v) if v else 0
123133
return int(v)
124-
125-
@model_validator(mode="after")
126-
def model_validator(self) -> Self:
127134

135+
@model_validator(mode="after")
136+
def set_default_review_agent_model(self) -> Self:
137+
"""Set review_agent_model to extraction model if not specified."""
128138
if not self.agentic.review_agent_model:
129139
self.agentic.review_agent_model = self.model
130140

131141
return self
132142

133143

134-
135144
class ClassificationConfig(BaseModel):
136145
"""Document classification configuration"""
137146

@@ -434,7 +443,7 @@ class ErrorAnalyzerConfig(BaseModel):
434443
"AccessDenied",
435444
"ThrottlingException",
436445
],
437-
description="Error patterns to search for in logs"
446+
description="Error patterns to search for in logs",
438447
)
439448
system_prompt: str = Field(
440449
default="""
@@ -522,11 +531,10 @@ class ErrorAnalyzerConfig(BaseModel):
522531
- No time specified: 24 hours (default)
523532
524533
IMPORTANT: Do not include any search quality reflections, search quality scores, or meta-analysis sections in your response. Only provide the three required sections: Root Cause, Recommendations, and Evidence.""",
525-
description="System prompt for error analyzer"
534+
description="System prompt for error analyzer",
526535
)
527536
parameters: ErrorAnalyzerParameters = Field(
528-
default_factory=ErrorAnalyzerParameters,
529-
description="Error analyzer parameters"
537+
default_factory=ErrorAnalyzerParameters, description="Error analyzer parameters"
530538
)
531539

532540

@@ -646,12 +654,10 @@ class AgentsConfig(BaseModel):
646654
"""Agents configuration"""
647655

648656
error_analyzer: Optional[ErrorAnalyzerConfig] = Field(
649-
default_factory=ErrorAnalyzerConfig,
650-
description="Error analyzer configuration"
657+
default_factory=ErrorAnalyzerConfig, description="Error analyzer configuration"
651658
)
652659
chat_companion: Optional[ChatCompanionConfig] = Field(
653-
default_factory=ChatCompanionConfig,
654-
description="Chat companion configuration"
660+
default_factory=ChatCompanionConfig, description="Chat companion configuration"
655661
)
656662

657663

lib/idp_common_pkg/idp_common/extraction/agentic_idp.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@
5252
# In Lambda: Full JSON structured logs
5353
# Outside Lambda: Human-readable format for local development
5454
logger = Logger(service="agentic_idp", level=os.getenv("LOG_LEVEL", "INFO"))
55-
logging.getLogger("strands.models.bedrock").setLevel(logging.DEBUG)
55+
# Configure strands bedrock logger based on environment variable
56+
logging.getLogger("strands.models.bedrock").setLevel(
57+
os.getenv("STRANDS_LOG_LEVEL", os.getenv("LOG_LEVEL", "INFO"))
58+
)
5659
TargetModel = TypeVar("TargetModel", bound=BaseModel)
5760

5861

@@ -193,6 +196,8 @@ def view_image(image_index: int, agent: Agent) -> dict:
193196
"""
194197

195198
# Validate image index exists
199+
if not page_images:
200+
raise ValueError("No images available to view.")
196201
if image_index >= len(page_images):
197202
raise ValueError(
198203
f"Invalid image_index {image_index}. "
@@ -729,27 +734,27 @@ def _prepare_prompt_content(
729734
else:
730735
prompt_content = [ContentBlock(text=str(prompt))]
731736

732-
# Add page images if provided
737+
# Add page images if provided (limit to 20 as per Bedrock constraints)
733738
if page_images:
734739
if len(page_images) > 20:
735740
prompt_content.append(
736741
ContentBlock(
737-
text=f"There are {len(page_images)} images, initially you'll see 20 of them, use the tools to see the rest."
742+
text=f"There are {len(page_images)} images, initially you'll see 20 of them, use the view_image tool to see the rest."
738743
)
739744
)
740745

741746
prompt_content += [
742747
ContentBlock(
743748
image=ImageContent(format="png", source=ImageSource(bytes=img_bytes))
744749
)
745-
for img_bytes in page_images
750+
for img_bytes in page_images[:20]
746751
]
747752

748753
# Add existing data context if provided
749754
if existing_data:
750755
prompt_content.append(
751756
ContentBlock(
752-
text=f"Please update the existing data using the extraction tool or patches. Existing data: {existing_data.model_dump()}"
757+
text=f"Please update the existing data using the extraction tool or patches. Existing data: {existing_data.model_dump(mode='json')}"
753758
)
754759
)
755760

@@ -1014,7 +1019,7 @@ async def structured_output_async(
10141019
),
10151020
)
10161021
if existing_data:
1017-
agent.state.set("current_extraction", existing_data.model_dump())
1022+
agent.state.set("current_extraction", existing_data.model_dump(mode="json"))
10181023

10191024
response, result = await _invoke_agent_for_extraction(
10201025
agent=agent,
@@ -1075,9 +1080,11 @@ async def structured_output_async(
10751080
tools=tools,
10761081
system_prompt=f"{final_system_prompt}",
10771082
state={
1078-
"current_extraction": None,
1083+
"current_extraction": result.model_dump(mode="json"),
10791084
"images": {},
1080-
"existing_data": existing_data.model_dump() if existing_data else None,
1085+
"existing_data": existing_data.model_dump(mode="json")
1086+
if existing_data
1087+
else None,
10811088
"extraction_schema_json": schema_json, # Store for schema reminder tool
10821089
},
10831090
conversation_manager=SummarizingConversationManager(
@@ -1095,7 +1102,7 @@ async def structured_output_async(
10951102

10961103
# Check if patches were applied during review
10971104
updated_extraction = agent.state.get("current_extraction")
1098-
if updated_extraction != result.model_dump():
1105+
if updated_extraction != result.model_dump(mode="json"):
10991106
# Patches were applied, validate the new extraction
11001107
try:
11011108
result = data_format(**updated_extraction)

lib/idp_common_pkg/idp_common/extraction/service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def __init__(
111111
self._class_label: str = ""
112112
self._attribute_descriptions: str = ""
113113
self._class_schema: dict[str, Any] = {}
114-
self._page_images: list[Any] = []
114+
self._page_images: list[bytes] = []
115115
self._image_uris: list[str] = []
116116

117117
# Get model_id from config for logging (type-safe access with fallback)

lib/idp_common_pkg/idp_common/utils/bedrock_utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
InvokeModelRequestTypeDef,
1919
InvokeModelResponseTypeDef,
2020
)
21-
from pydantic_core import ArgsKwargs
2221

2322
# Configure logger
2423
logger = logging.getLogger(__name__)
@@ -48,14 +47,14 @@ async def wrapper(*args, **kwargs) -> T:
4847

4948
def log_bedrock_invocation_error(error: Exception, attempt_num: int):
5049
"""Log bedrock invocation details when an error occurs"""
51-
# Fallback logging if extraction fails
50+
# Fallback logging if extraction fails
5251
logger.error(
5352
"Bedrock invocation error",
5453
extra={
5554
"function_name": func.__name__,
5655
"original_error": str(error),
5756
"max_attempts": max_retries,
58-
"attempt_num":attempt_num
57+
"attempt_num": attempt_num,
5958
},
6059
)
6160

@@ -203,6 +202,7 @@ def log_bedrock_invocation_error(error: Exception, attempt_num: int):
203202
error_code
204203
not in [
205204
"ThrottlingException",
205+
"throttlingException",
206206
"ModelErrorException",
207207
"ValidationException",
208208
]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: MIT-0
3+
4+
"""
5+
Strands agent tools for IDP common library.
6+
"""
7+
8+
from idp_common.utils.strands_agent_tools.todo_list import (
9+
create_todo_list,
10+
update_todo,
11+
view_todo_list,
12+
)
13+
14+
__all__ = ["create_todo_list", "update_todo", "view_todo_list"]

lib/idp_common_pkg/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ agentic-extraction = [
177177
"tabulate>=0.9.0",
178178
"aws-lambda-powertools>=3.2.0", # Structured logging and observability
179179
"datamodel-code-generator>=0.25.0", # Generate Pydantic models from JSON Schema
180+
"mypy-boto3-bedrock-runtime>=1.39.0", # Type stubs for bedrock_utils.py
180181
]
181182

182183
[project.urls]

0 commit comments

Comments
 (0)