Skip to content

Commit 86d311d

Browse files
committed
Merge branch 'feature/agent-pentest-fixes' into 'develop'
Agent related pentest fixes See merge request genaiic-reusable-assets/engagement-artifacts/genaiic-idp-accelerator!321
2 parents 5f30f95 + c30597f commit 86d311d

File tree

9 files changed

+121
-19
lines changed

9 files changed

+121
-19
lines changed

lib/idp_common_pkg/idp_common/agents/analytics/agent.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111

1212
import boto3
1313
import strands
14-
from strands.models import BedrockModel
1514

1615
from ..common.config import load_result_format_description
16+
from ..common.strands_bedrock_model import create_strands_bedrock_model
1717
from .config import load_python_plot_generation_examples
1818
from .tools import CodeInterpreterTools, get_database_info, run_athena_query
1919
from .utils import register_code_interpreter_tools
@@ -138,7 +138,9 @@ def run_athena_query_with_config(
138138
# Get model ID from environment variable
139139
model_id = os.environ.get("DOCUMENT_ANALYSIS_AGENT_MODEL_ID")
140140

141-
bedrock_model = BedrockModel(model_id=model_id, boto_session=session)
141+
bedrock_model = create_strands_bedrock_model(
142+
model_id=model_id, boto_session=session
143+
)
142144

143145
# Create the Strands agent with tools and system prompt
144146
strands_agent = strands.Agent(
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: MIT-0
3+
4+
"""
5+
Helper function for creating BedrockModel instances with automatic guardrail support.
6+
"""
7+
8+
import os
9+
10+
from strands.models import BedrockModel
11+
12+
13+
def create_strands_bedrock_model(
14+
model_id: str, boto_session=None, **kwargs
15+
) -> BedrockModel:
16+
"""
17+
Create a BedrockModel with automatic guardrail configuration from environment.
18+
19+
Args:
20+
model_id: The Bedrock model ID to use
21+
boto_session: Optional boto3 session
22+
**kwargs: Additional arguments to pass to BedrockModel
23+
24+
Returns:
25+
BedrockModel instance with guardrails applied if configured
26+
"""
27+
# Get guardrail configuration from environment if available
28+
guardrail_env = os.environ.get("GUARDRAIL_ID_AND_VERSION", "")
29+
if guardrail_env:
30+
try:
31+
guardrail_id, guardrail_version = guardrail_env.split(":")
32+
if guardrail_id and guardrail_version:
33+
kwargs.update(
34+
{
35+
"guardrail_id": guardrail_id,
36+
"guardrail_version": guardrail_version,
37+
"guardrail_trace": "enabled",
38+
}
39+
)
40+
except ValueError:
41+
pass # Invalid format, continue without guardrails
42+
43+
return BedrockModel(model_id=model_id, boto_session=boto_session, **kwargs)

lib/idp_common_pkg/idp_common/agents/external_mcp/agent.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
import boto3
1313
from mcp.client.streamable_http import streamablehttp_client
1414
from strands import Agent
15-
from strands.models import BedrockModel
1615
from strands.tools.mcp import MCPClient
1716

1817
from ..common.oauth_auth import get_cognito_bearer_token
18+
from ..common.strands_bedrock_model import create_strands_bedrock_model
1919

2020
logger = logging.getLogger(__name__)
2121

@@ -130,7 +130,9 @@ def create_external_mcp_agent(
130130
raise Exception(error_msg)
131131

132132
# Create Bedrock model
133-
bedrock_model = BedrockModel(model_id=model_id, boto_session=session)
133+
bedrock_model = create_strands_bedrock_model(
134+
model_id=model_id, boto_session=session
135+
)
134136

135137
# Create system prompt
136138
system_prompt = f"""

lib/idp_common_pkg/idp_common/agents/orchestrator/agent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515

1616
import strands
1717
from strands import tool
18-
from strands.models import BedrockModel
1918

2019
from ..common.config import load_result_format_description
20+
from ..common.strands_bedrock_model import create_strands_bedrock_model
2121

2222
logger = logging.getLogger(__name__)
2323

@@ -161,7 +161,7 @@ def tool_func(query: str) -> str:
161161
)
162162

163163
# Create the orchestrator agent
164-
model = BedrockModel(
164+
model = create_strands_bedrock_model(
165165
model_id=model_id,
166166
session=session,
167167
)

lib/idp_common_pkg/idp_common/agents/sample_calculator/agent.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77

88
import boto3
99
import strands
10-
from strands.models import BedrockModel
1110
from strands_tools import calculator
1211

12+
from ..common.strands_bedrock_model import create_strands_bedrock_model
13+
1314
logger = logging.getLogger(__name__)
1415

1516

@@ -31,7 +32,7 @@ def create_sample_calculator_agent(
3132
model_id = "us.anthropic.claude-3-7-sonnet-20250219-v1:0"
3233

3334
# Create Bedrock model
34-
model = BedrockModel(model_id=model_id, session=session)
35+
model = create_strands_bedrock_model(model_id=model_id, session=session)
3536

3637
# Create and return agent with calculator tool
3738
return strands.Agent(model=model, tools=[calculator])

src/lambda/agent_request_handler/index.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,17 @@ def handler(event, context):
7676
if not query:
7777
error_msg = "Query parameter is required"
7878
logger.error(error_msg)
79-
return {
80-
"statusCode": 400,
81-
"body": error_msg
82-
}
79+
raise Exception(error_msg)
80+
81+
if len(query) > 100000:
82+
error_msg = "Query exceeds maximum length of 100000 characters"
83+
logger.error(f"{error_msg}. Query length: {len(query)}")
84+
raise Exception(error_msg)
8385

8486
if not agent_ids:
8587
error_msg = "At least one agent ID is required"
8688
logger.error(error_msg)
87-
return {
88-
"statusCode": 400,
89-
"body": error_msg
90-
}
89+
raise Exception(error_msg)
9190

9291
# Extract and validate user ID from the identity context
9392
identity = event.get("identity", {})
@@ -153,7 +152,18 @@ def handler(event, context):
153152
"body": error_msg
154153
}
155154
except Exception as e:
156-
error_msg = f"Error processing request: {str(e)}"
155+
# Check if this is a validation error that should propagate as GraphQL error
156+
error_str = str(e)
157+
if any(msg in error_str for msg in [
158+
"Query parameter is required",
159+
"Query exceeds maximum length",
160+
"At least one agent ID is required"
161+
]):
162+
# Re-raise validation errors so they become GraphQL errors
163+
raise e
164+
165+
# Handle other unexpected errors
166+
error_msg = f"Error processing request: {error_str}"
157167
logger.error(error_msg)
158168
return {
159169
"statusCode": 500,

src/ui/src/components/document-agents-layout/AgentJobStatus.jsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ const getStatusIndicator = (status) => {
2020
};
2121

2222
const AgentJobStatus = ({ jobId, status, error }) => {
23+
// Show error even if there's no jobId (for validation errors)
24+
if (error && !jobId) {
25+
return (
26+
<Box padding={{ vertical: 'xs' }}>
27+
<div>
28+
<strong>Error:</strong> {error}
29+
</div>
30+
</Box>
31+
);
32+
}
33+
2334
if (!jobId) {
2435
return null;
2536
}

src/ui/src/components/document-agents-layout/DocumentsAgentsLayout.jsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,23 @@ const DocumentsAgentsLayout = () => {
199199
}, 1000);
200200
} catch (err) {
201201
logger.error('Error submitting query:', err);
202+
logger.error('Error structure:', JSON.stringify(err, null, 2));
203+
204+
let errorMessage = 'Failed to submit query';
205+
206+
// Extract error message from GraphQL error structure
207+
if (err.errors && err.errors.length > 0 && err.errors[0].message) {
208+
errorMessage = err.errors[0].message;
209+
} else if (err.message) {
210+
errorMessage = err.message;
211+
} else if (err.data && err.data.errors && err.data.errors.length > 0 && err.data.errors[0].message) {
212+
errorMessage = err.data.errors[0].message;
213+
} else if (typeof err === 'string') {
214+
errorMessage = err;
215+
}
216+
202217
updateAnalyticsState({
203-
error: err.message || 'Failed to submit query',
218+
error: errorMessage,
204219
jobStatus: 'FAILED',
205220
});
206221
} finally {

template.yaml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3608,6 +3608,7 @@ Resources:
36083608
- !Ref ReportingBucketName
36093609
DOCUMENT_ANALYSIS_AGENT_MODEL_ID: !Ref DocumentAnalysisAgentModelId
36103610
AWS_STACK_NAME: !Ref AWS::StackName
3611+
GUARDRAIL_ID_AND_VERSION: !If [HasGuardrailConfig, !Sub "${BedrockGuardrailId}:${BedrockGuardrailVersion}", ""]
36113612
LoggingConfig:
36123613
LogGroup: !Ref AgentProcessorLogGroup
36133614
Policies:
@@ -3645,6 +3646,14 @@ Resources:
36453646
Resource:
36463647
- !Sub "arn:${AWS::Partition}:bedrock:*::foundation-model/*"
36473648
- !Sub "arn:${AWS::Partition}:bedrock:${AWS::Region}:${AWS::AccountId}:inference-profile/*"
3649+
- !If
3650+
- HasGuardrailConfig
3651+
- Effect: Allow
3652+
Action:
3653+
- "bedrock:ApplyGuardrail"
3654+
Resource:
3655+
- !Sub "arn:${AWS::Partition}:bedrock:${AWS::Region}:${AWS::AccountId}:guardrail/${BedrockGuardrailId}"
3656+
- !Ref AWS::NoValue
36483657
- Effect: Allow
36493658
Action:
36503659
- appsync:GraphQL
@@ -3905,7 +3914,16 @@ Resources:
39053914
}
39063915
}
39073916
ResponseMappingTemplate: |-
3908-
true
3917+
#if($ctx.error)
3918+
$util.error($ctx.error.message, $ctx.error.type)
3919+
#end
3920+
3921+
## Return false if no item was updated (item not found)
3922+
#if(!$ctx.result)
3923+
false
3924+
#else
3925+
true
3926+
#end
39093927
39103928
DeleteAgentJobResolver:
39113929
Type: AWS::AppSync::Resolver

0 commit comments

Comments
 (0)