awslabs.cdk-mcp-server 0.0.10417__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- awslabs/__init__.py +2 -0
- awslabs/cdk_mcp_server/__init__.py +8 -0
- awslabs/cdk_mcp_server/core/__init__.py +1 -0
- awslabs/cdk_mcp_server/core/resources.py +271 -0
- awslabs/cdk_mcp_server/core/search_utils.py +182 -0
- awslabs/cdk_mcp_server/core/server.py +74 -0
- awslabs/cdk_mcp_server/core/tools.py +324 -0
- awslabs/cdk_mcp_server/data/__init__.py +1 -0
- awslabs/cdk_mcp_server/data/cdk_nag_parser.py +331 -0
- awslabs/cdk_mcp_server/data/construct_descriptions.py +32 -0
- awslabs/cdk_mcp_server/data/genai_cdk_loader.py +423 -0
- awslabs/cdk_mcp_server/data/lambda_powertools_loader.py +48 -0
- awslabs/cdk_mcp_server/data/schema_generator.py +666 -0
- awslabs/cdk_mcp_server/data/solutions_constructs_parser.py +782 -0
- awslabs/cdk_mcp_server/server.py +7 -0
- awslabs/cdk_mcp_server/static/CDK_GENERAL_GUIDANCE.md +232 -0
- awslabs/cdk_mcp_server/static/CDK_NAG_GUIDANCE.md +192 -0
- awslabs/cdk_mcp_server/static/__init__.py +5 -0
- awslabs/cdk_mcp_server/static/bedrock/agent/actiongroups.md +137 -0
- awslabs/cdk_mcp_server/static/bedrock/agent/alias.md +39 -0
- awslabs/cdk_mcp_server/static/bedrock/agent/collaboration.md +91 -0
- awslabs/cdk_mcp_server/static/bedrock/agent/creation.md +149 -0
- awslabs/cdk_mcp_server/static/bedrock/agent/custom_orchestration.md +74 -0
- awslabs/cdk_mcp_server/static/bedrock/agent/overview.md +78 -0
- awslabs/cdk_mcp_server/static/bedrock/agent/prompt_override.md +70 -0
- awslabs/cdk_mcp_server/static/bedrock/bedrockguardrails.md +188 -0
- awslabs/cdk_mcp_server/static/bedrock/knowledgebases/chunking.md +137 -0
- awslabs/cdk_mcp_server/static/bedrock/knowledgebases/datasources.md +225 -0
- awslabs/cdk_mcp_server/static/bedrock/knowledgebases/kendra.md +81 -0
- awslabs/cdk_mcp_server/static/bedrock/knowledgebases/overview.md +116 -0
- awslabs/cdk_mcp_server/static/bedrock/knowledgebases/parsing.md +36 -0
- awslabs/cdk_mcp_server/static/bedrock/knowledgebases/transformation.md +30 -0
- awslabs/cdk_mcp_server/static/bedrock/knowledgebases/vector/aurora.md +185 -0
- awslabs/cdk_mcp_server/static/bedrock/knowledgebases/vector/creation.md +80 -0
- awslabs/cdk_mcp_server/static/bedrock/knowledgebases/vector/opensearch.md +56 -0
- awslabs/cdk_mcp_server/static/bedrock/knowledgebases/vector/pinecone.md +66 -0
- awslabs/cdk_mcp_server/static/bedrock/profiles.md +153 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/actiongroups.md +137 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/alias.md +39 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/collaboration.md +91 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/creation.md +149 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/custom_orchestration.md +74 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/overview.md +78 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/prompt_override.md +70 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/bedrockguardrails.md +188 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/chunking.md +137 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/datasources.md +225 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/kendra.md +81 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/overview.md +116 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/parsing.md +36 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/transformation.md +30 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/vector/aurora.md +185 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/vector/creation.md +80 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/vector/opensearch.md +56 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/vector/pinecone.md +66 -0
- awslabs/cdk_mcp_server/static/genai_cdk/bedrock/profiles.md +153 -0
- awslabs/cdk_mcp_server/static/genai_cdk/opensearch-vectorindex/overview.md +135 -0
- awslabs/cdk_mcp_server/static/genai_cdk/opensearchserverless/overview.md +17 -0
- awslabs/cdk_mcp_server/static/lambda_powertools/bedrock.md +127 -0
- awslabs/cdk_mcp_server/static/lambda_powertools/cdk.md +99 -0
- awslabs/cdk_mcp_server/static/lambda_powertools/dependencies.md +45 -0
- awslabs/cdk_mcp_server/static/lambda_powertools/index.md +36 -0
- awslabs/cdk_mcp_server/static/lambda_powertools/insights.md +95 -0
- awslabs/cdk_mcp_server/static/lambda_powertools/logging.md +43 -0
- awslabs/cdk_mcp_server/static/lambda_powertools/metrics.md +93 -0
- awslabs/cdk_mcp_server/static/lambda_powertools/tracing.md +63 -0
- awslabs/cdk_mcp_server/static/opensearch-vectorindex/overview.md +135 -0
- awslabs/cdk_mcp_server/static/opensearchserverless/overview.md +17 -0
- awslabs_cdk_mcp_server-0.0.10417.dist-info/METADATA +14 -0
- awslabs_cdk_mcp_server-0.0.10417.dist-info/RECORD +72 -0
- awslabs_cdk_mcp_server-0.0.10417.dist-info/WHEEL +4 -0
- awslabs_cdk_mcp_server-0.0.10417.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
"""Schema generator for Bedrock Agent Action Groups."""
|
|
2
|
+
|
|
3
|
+
import importlib.util
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def generate_fallback_script(lambda_code_path: str, output_path: str) -> str:
|
|
11
|
+
"""Generate a standalone script for schema generation."""
|
|
12
|
+
return f'''#!/usr/bin/env python3
|
|
13
|
+
"""
|
|
14
|
+
Schema Generator for Bedrock Agent Action Groups
|
|
15
|
+
|
|
16
|
+
This script generates an OpenAPI schema from a Lambda file containing a BedrockAgentResolver app.
|
|
17
|
+
|
|
18
|
+
IMPORTANT: This script requires the following dependencies:
|
|
19
|
+
1. aws-lambda-powertools
|
|
20
|
+
2. pydantic
|
|
21
|
+
|
|
22
|
+
Install them with:
|
|
23
|
+
|
|
24
|
+
pip install aws-lambda-powertools pydantic
|
|
25
|
+
|
|
26
|
+
Then run this script again.
|
|
27
|
+
|
|
28
|
+
This script focuses on extracting the API definition (routes, parameters, responses)
|
|
29
|
+
from the BedrockAgentResolver app, NOT on executing the business logic in the Lambda function.
|
|
30
|
+
If you encounter errors related to missing dependencies or runtime errors in the business logic,
|
|
31
|
+
you can safely modify this script to bypass those errors while preserving the API definition.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
import os
|
|
35
|
+
import sys
|
|
36
|
+
import json
|
|
37
|
+
import importlib.util
|
|
38
|
+
|
|
39
|
+
# Check for required dependencies
|
|
40
|
+
missing_deps = []
|
|
41
|
+
for dep in ['aws_lambda_powertools', 'pydantic']:
|
|
42
|
+
try:
|
|
43
|
+
importlib.import_module(dep)
|
|
44
|
+
except ImportError:
|
|
45
|
+
missing_deps.append(dep)
|
|
46
|
+
|
|
47
|
+
if missing_deps:
|
|
48
|
+
print("ERROR: Missing required dependencies: " + ", ".join(missing_deps))
|
|
49
|
+
print("Please install them with:")
|
|
50
|
+
print("pip install " + " ".join(missing_deps).replace('_', '-'))
|
|
51
|
+
print("Then run this script again.")
|
|
52
|
+
sys.exit(1)
|
|
53
|
+
|
|
54
|
+
# Configuration
|
|
55
|
+
LAMBDA_FILE_PATH = "{lambda_code_path}"
|
|
56
|
+
OUTPUT_PATH = "{output_path}"
|
|
57
|
+
APP_VAR_NAME = "app" # Update this if your BedrockAgentResolver instance has a different name
|
|
58
|
+
|
|
59
|
+
def main():
|
|
60
|
+
print(f"Generating schema from {{LAMBDA_FILE_PATH}}")
|
|
61
|
+
print(f"Output path: {{OUTPUT_PATH}}")
|
|
62
|
+
|
|
63
|
+
# Get the directory and module name
|
|
64
|
+
lambda_dir = os.path.dirname(os.path.abspath(LAMBDA_FILE_PATH))
|
|
65
|
+
module_name = os.path.basename(LAMBDA_FILE_PATH).replace('.py', '')
|
|
66
|
+
|
|
67
|
+
# MODIFICATION GUIDE:
|
|
68
|
+
# If you encounter import errors or runtime errors, you can:
|
|
69
|
+
# 1. Create a simplified version of the Lambda file with problematic imports/code commented out
|
|
70
|
+
# 2. Add try/except blocks around problematic code
|
|
71
|
+
# 3. Create mock implementations for missing functions
|
|
72
|
+
# The key is to preserve the BedrockAgentResolver app definition and routes
|
|
73
|
+
|
|
74
|
+
# Example of creating a simplified version:
|
|
75
|
+
simplified_path = os.path.join(lambda_dir, f"{{module_name}}_simplified.py")
|
|
76
|
+
try:
|
|
77
|
+
with open(LAMBDA_FILE_PATH, 'r') as f:
|
|
78
|
+
content = f.read()
|
|
79
|
+
|
|
80
|
+
# Comment out problematic imports (add more as needed)
|
|
81
|
+
problematic_packages = [
|
|
82
|
+
'matplotlib', 'numpy', 'pandas', 'scipy', 'tensorflow', 'torch', 'sympy',
|
|
83
|
+
'nltk', 'spacy', 'gensim', 'sklearn', 'networkx', 'plotly', 'dash',
|
|
84
|
+
'opencv', 'cv2', 'PIL', 'pillow'
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
lines = content.split('\\n')
|
|
88
|
+
for i, line in enumerate(lines):
|
|
89
|
+
stripped = line.strip()
|
|
90
|
+
if (stripped.startswith('import ') or stripped.startswith('from ')) and \
|
|
91
|
+
any(pkg in stripped for pkg in problematic_packages):
|
|
92
|
+
lines[i] = f"# {{line}} # Commented out for schema generation"
|
|
93
|
+
|
|
94
|
+
simplified_content = '\\n'.join(lines)
|
|
95
|
+
|
|
96
|
+
with open(simplified_path, 'w') as f:
|
|
97
|
+
f.write(simplified_content)
|
|
98
|
+
|
|
99
|
+
print("Created simplified version with problematic imports commented out")
|
|
100
|
+
|
|
101
|
+
# Try with the simplified version
|
|
102
|
+
try:
|
|
103
|
+
# Add directory to Python path
|
|
104
|
+
sys.path.append(os.path.dirname(simplified_path))
|
|
105
|
+
|
|
106
|
+
# Import the simplified module
|
|
107
|
+
print(f"Importing {{simplified_path}}...")
|
|
108
|
+
spec = importlib.util.spec_from_file_location(
|
|
109
|
+
f"{{module_name}}_simplified", simplified_path
|
|
110
|
+
)
|
|
111
|
+
module = importlib.util.module_from_spec(spec)
|
|
112
|
+
spec.loader.exec_module(module)
|
|
113
|
+
|
|
114
|
+
# Get the app object
|
|
115
|
+
if not hasattr(module, APP_VAR_NAME):
|
|
116
|
+
print(f"No '{{APP_VAR_NAME}}' variable found in the module.")
|
|
117
|
+
print("If your BedrockAgentResolver instance has a different name, update APP_VAR_NAME.")
|
|
118
|
+
return False
|
|
119
|
+
|
|
120
|
+
app = getattr(module, APP_VAR_NAME)
|
|
121
|
+
|
|
122
|
+
# Generate the OpenAPI schema
|
|
123
|
+
print("Generating OpenAPI schema...")
|
|
124
|
+
openapi_schema = json.loads(app.get_openapi_json_schema(openapi_version="3.0.0"))
|
|
125
|
+
|
|
126
|
+
# Fix Pydantic v2 issue (forcing OpenAPI 3.0.0)
|
|
127
|
+
if openapi_schema.get("openapi") != "3.0.0":
|
|
128
|
+
openapi_schema["openapi"] = "3.0.0"
|
|
129
|
+
print("Fixed OpenAPI version to 3.0.0 (Pydantic v2 issue)")
|
|
130
|
+
|
|
131
|
+
# Fix operationIds
|
|
132
|
+
for path in openapi_schema['paths']:
|
|
133
|
+
for method in openapi_schema['paths'][path]:
|
|
134
|
+
operation = openapi_schema['paths'][path][method]
|
|
135
|
+
if 'operationId' in operation:
|
|
136
|
+
# Get current operationId
|
|
137
|
+
current_id = operation['operationId']
|
|
138
|
+
# Remove duplication by taking the first part before '_post'
|
|
139
|
+
if '_post' in current_id:
|
|
140
|
+
# Split by underscore and remove duplicates
|
|
141
|
+
parts = current_id.split('_')
|
|
142
|
+
# Keep only unique parts and add '_post' at the end
|
|
143
|
+
unique_parts = []
|
|
144
|
+
seen = set()
|
|
145
|
+
for part in parts[:-1]: # Exclude the last 'post' part
|
|
146
|
+
if part not in seen:
|
|
147
|
+
unique_parts.append(part)
|
|
148
|
+
seen.add(part)
|
|
149
|
+
new_id = '_'.join(unique_parts + ['post'])
|
|
150
|
+
operation['operationId'] = new_id
|
|
151
|
+
print(f"Fixed operationId: {{current_id}} -> {{new_id}}")
|
|
152
|
+
|
|
153
|
+
# Create output directory if it doesn't exist
|
|
154
|
+
os.makedirs(os.path.dirname(os.path.abspath(OUTPUT_PATH)), exist_ok=True)
|
|
155
|
+
|
|
156
|
+
# Save the schema to the output path
|
|
157
|
+
with open(OUTPUT_PATH, 'w') as f:
|
|
158
|
+
json.dump(openapi_schema, f, indent=2)
|
|
159
|
+
|
|
160
|
+
print(f"Schema successfully generated and saved to {{OUTPUT_PATH}}")
|
|
161
|
+
return True
|
|
162
|
+
|
|
163
|
+
except Exception as simplified_error:
|
|
164
|
+
print(f"Error with simplified version: {{str(simplified_error)}}")
|
|
165
|
+
print("You may need to manually modify the script to handle this error.")
|
|
166
|
+
print("Focus on preserving the BedrockAgentResolver app definition and routes.")
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
except Exception as e:
|
|
170
|
+
print(f"Error creating simplified version: {{str(e)}}")
|
|
171
|
+
|
|
172
|
+
# Try direct import as fallback
|
|
173
|
+
try:
|
|
174
|
+
# Add directory to Python path
|
|
175
|
+
sys.path.append(lambda_dir)
|
|
176
|
+
|
|
177
|
+
# Import module directly
|
|
178
|
+
print(f"Trying direct import of {{LAMBDA_FILE_PATH}}...")
|
|
179
|
+
module = __import__(module_name)
|
|
180
|
+
|
|
181
|
+
# Get the app object
|
|
182
|
+
if not hasattr(module, APP_VAR_NAME):
|
|
183
|
+
print(f"No '{{APP_VAR_NAME}}' variable found in the module.")
|
|
184
|
+
print("If your BedrockAgentResolver instance has a different name, update APP_VAR_NAME.")
|
|
185
|
+
return False
|
|
186
|
+
|
|
187
|
+
app = getattr(module, APP_VAR_NAME)
|
|
188
|
+
|
|
189
|
+
# Generate the OpenAPI schema
|
|
190
|
+
print("Generating OpenAPI schema...")
|
|
191
|
+
openapi_schema = json.loads(app.get_openapi_json_schema(openapi_version="3.0.0"))
|
|
192
|
+
|
|
193
|
+
# Fix schema issues
|
|
194
|
+
if openapi_schema.get("openapi") != "3.0.0":
|
|
195
|
+
openapi_schema["openapi"] = "3.0.0"
|
|
196
|
+
print("Fixed OpenAPI version to 3.0.0 (Pydantic v2 issue)")
|
|
197
|
+
|
|
198
|
+
# Create output directory if it doesn't exist
|
|
199
|
+
os.makedirs(os.path.dirname(os.path.abspath(OUTPUT_PATH)), exist_ok=True)
|
|
200
|
+
|
|
201
|
+
# Save the schema to the output path
|
|
202
|
+
with open(OUTPUT_PATH, 'w') as f:
|
|
203
|
+
json.dump(openapi_schema, f, indent=2)
|
|
204
|
+
|
|
205
|
+
print(f"Schema successfully generated and saved to {{OUTPUT_PATH}}")
|
|
206
|
+
return True
|
|
207
|
+
|
|
208
|
+
except Exception as direct_error:
|
|
209
|
+
print(f"Error with direct import: {{str(direct_error)}}")
|
|
210
|
+
print("You may need to manually modify this script to handle the errors.")
|
|
211
|
+
print("Remember that the goal is to extract the API definition, not to run the business logic.")
|
|
212
|
+
return False
|
|
213
|
+
finally:
|
|
214
|
+
# Clean up the simplified file
|
|
215
|
+
if os.path.exists(simplified_path):
|
|
216
|
+
os.remove(simplified_path)
|
|
217
|
+
print("Cleaned up simplified file")
|
|
218
|
+
|
|
219
|
+
if __name__ == '__main__':
|
|
220
|
+
main()
|
|
221
|
+
'''
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def fix_operation_ids(openapi_schema: Dict[str, Any], result: Dict[str, Any]) -> None:
|
|
225
|
+
"""Fix operationIds in the OpenAPI schema.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
openapi_schema: The OpenAPI schema to fix
|
|
229
|
+
result: The result dictionary to update with warnings
|
|
230
|
+
"""
|
|
231
|
+
fixed = False
|
|
232
|
+
for path in openapi_schema['paths']:
|
|
233
|
+
for method in openapi_schema['paths'][path]:
|
|
234
|
+
operation = openapi_schema['paths'][path][method]
|
|
235
|
+
if 'operationId' in operation:
|
|
236
|
+
# Get current operationId
|
|
237
|
+
current_id = operation['operationId']
|
|
238
|
+
# Remove duplication by taking the first part before '_post'
|
|
239
|
+
if '_post' in current_id:
|
|
240
|
+
# Split by underscore and remove duplicates
|
|
241
|
+
parts = current_id.split('_')
|
|
242
|
+
# Keep only unique parts and add '_post' at the end
|
|
243
|
+
unique_parts = []
|
|
244
|
+
seen = set()
|
|
245
|
+
for part in parts[:-1]: # Exclude the last 'post' part
|
|
246
|
+
if part not in seen:
|
|
247
|
+
unique_parts.append(part)
|
|
248
|
+
seen.add(part)
|
|
249
|
+
new_id = '_'.join(unique_parts + ['post'])
|
|
250
|
+
operation['operationId'] = new_id
|
|
251
|
+
fixed = True
|
|
252
|
+
|
|
253
|
+
if fixed:
|
|
254
|
+
result['warnings'].append('Fixed operationIds for Claude 3.5 compatibility')
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def comment_out_problematic_code(
|
|
258
|
+
content: str, problematic_packages: List[str], import_name: Optional[str] = None
|
|
259
|
+
) -> Tuple[str, List[str]]:
|
|
260
|
+
"""Comment out problematic imports and code blocks that use them.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
content: The source code content
|
|
264
|
+
problematic_packages: List of problematic package names
|
|
265
|
+
import_name: Specific import name that failed (optional)
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
Tuple of (modified content, list of modifications made)
|
|
269
|
+
"""
|
|
270
|
+
modifications = []
|
|
271
|
+
|
|
272
|
+
# Add the specific import that failed if not already in the list
|
|
273
|
+
if (
|
|
274
|
+
import_name
|
|
275
|
+
and import_name not in problematic_packages
|
|
276
|
+
and import_name != 'No module named'
|
|
277
|
+
):
|
|
278
|
+
problematic_packages.append(import_name)
|
|
279
|
+
|
|
280
|
+
# Comment out problematic imports and their usage
|
|
281
|
+
lines = content.split('\n')
|
|
282
|
+
i = 0
|
|
283
|
+
while i < len(lines):
|
|
284
|
+
line = lines[i].strip()
|
|
285
|
+
|
|
286
|
+
# Check for import statements (both direct and from-imports)
|
|
287
|
+
if line.startswith('import ') or line.startswith('from '):
|
|
288
|
+
for pkg in problematic_packages:
|
|
289
|
+
# Match both "import pkg" and "from pkg import ..."
|
|
290
|
+
if (
|
|
291
|
+
line.startswith(f'import {pkg}')
|
|
292
|
+
or line.startswith(f'from {pkg} ')
|
|
293
|
+
or f' {pkg}' in line
|
|
294
|
+
or f'.{pkg}' in line
|
|
295
|
+
):
|
|
296
|
+
lines[i] = f'# {lines[i]} # Commented out for schema generation'
|
|
297
|
+
modifications.append(f'Commented out import: {lines[i]}')
|
|
298
|
+
break
|
|
299
|
+
|
|
300
|
+
# Check for try/except blocks that might use problematic packages
|
|
301
|
+
if line.startswith('try:'):
|
|
302
|
+
# Look ahead to see if the next lines use problematic packages
|
|
303
|
+
j = i + 1
|
|
304
|
+
block_level = 1
|
|
305
|
+
contains_problematic_code = False
|
|
306
|
+
|
|
307
|
+
# Check the content of the try block
|
|
308
|
+
while j < len(lines) and block_level > 0:
|
|
309
|
+
next_line = lines[j].strip()
|
|
310
|
+
if next_line.startswith('try:'):
|
|
311
|
+
block_level += 1
|
|
312
|
+
elif next_line.startswith('except'):
|
|
313
|
+
block_level -= 1
|
|
314
|
+
|
|
315
|
+
# Check if this line uses any problematic package
|
|
316
|
+
for pkg in problematic_packages:
|
|
317
|
+
if pkg in lines[j]:
|
|
318
|
+
contains_problematic_code = True
|
|
319
|
+
break
|
|
320
|
+
|
|
321
|
+
j += 1
|
|
322
|
+
|
|
323
|
+
# If the try block contains problematic code, comment out the entire block
|
|
324
|
+
if contains_problematic_code:
|
|
325
|
+
lines[i] = f'# {lines[i]} # Commented out for schema generation'
|
|
326
|
+
modifications.append(f'Commented out try block starting at line {i + 1}')
|
|
327
|
+
|
|
328
|
+
# Comment out the entire try/except block
|
|
329
|
+
j = i + 1
|
|
330
|
+
block_level = 1
|
|
331
|
+
while j < len(lines) and block_level > 0:
|
|
332
|
+
if lines[j].strip().startswith('try:'):
|
|
333
|
+
block_level += 1
|
|
334
|
+
elif lines[j].strip().startswith('except'):
|
|
335
|
+
block_level -= 1
|
|
336
|
+
|
|
337
|
+
lines[j] = f'# {lines[j]} # Commented out for schema generation'
|
|
338
|
+
j += 1
|
|
339
|
+
|
|
340
|
+
# Skip ahead to after the block
|
|
341
|
+
i = j - 1
|
|
342
|
+
|
|
343
|
+
i += 1
|
|
344
|
+
|
|
345
|
+
return '\n'.join(lines), modifications
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def generate_bedrock_schema_from_file(
|
|
349
|
+
lambda_code_path: str,
|
|
350
|
+
output_path: str,
|
|
351
|
+
) -> Dict[str, Any]:
|
|
352
|
+
"""Generate OpenAPI schema from a Lambda file with BedrockAgentResolver.
|
|
353
|
+
|
|
354
|
+
This function implements a progressive fallback approach:
|
|
355
|
+
1. First attempt: Direct import of the Lambda file
|
|
356
|
+
2. Second attempt: Create a simplified version with problematic imports commented out
|
|
357
|
+
3. Last resort: Generate a fallback script for manual execution
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
lambda_code_path: Path to Python file containing BedrockAgentResolver app
|
|
361
|
+
output_path: Where to save the generated schema
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
Dictionary with results of schema generation including:
|
|
365
|
+
- status: "success" or "error"
|
|
366
|
+
- schema_path: Path to the generated schema (if successful)
|
|
367
|
+
- warnings: List of warnings or issues detected
|
|
368
|
+
- process: Details about the approaches attempted
|
|
369
|
+
- error: Error message (if failed)
|
|
370
|
+
- diagnosis: Detailed diagnosis of the issue (if failed)
|
|
371
|
+
- solution: Suggested solution (if failed)
|
|
372
|
+
- fallback_script: Fallback script content (if failed)
|
|
373
|
+
"""
|
|
374
|
+
result = {
|
|
375
|
+
'status': 'success',
|
|
376
|
+
'schema_path': output_path,
|
|
377
|
+
'warnings': [],
|
|
378
|
+
'process': {
|
|
379
|
+
'direct_import': {'attempted': False, 'succeeded': False},
|
|
380
|
+
'simplified_version': {'attempted': False, 'succeeded': False},
|
|
381
|
+
'fallback_script': {'generated': False},
|
|
382
|
+
},
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
try:
|
|
386
|
+
# Check if the file exists
|
|
387
|
+
if not os.path.exists(lambda_code_path):
|
|
388
|
+
raise FileNotFoundError(f'Lambda code file not found: {lambda_code_path}')
|
|
389
|
+
|
|
390
|
+
# Get the directory and module name
|
|
391
|
+
lambda_dir = os.path.dirname(os.path.abspath(lambda_code_path))
|
|
392
|
+
module_name = os.path.basename(lambda_code_path).replace('.py', '')
|
|
393
|
+
|
|
394
|
+
# FIRST ATTEMPT: Direct import
|
|
395
|
+
try:
|
|
396
|
+
result['process']['direct_import']['attempted'] = True
|
|
397
|
+
|
|
398
|
+
# Add the directory to the Python path
|
|
399
|
+
sys.path.append(lambda_dir)
|
|
400
|
+
|
|
401
|
+
# Import the module
|
|
402
|
+
module = __import__(module_name)
|
|
403
|
+
|
|
404
|
+
# Get the app object
|
|
405
|
+
if not hasattr(module, 'app'):
|
|
406
|
+
raise AttributeError("No 'app' variable found in the module")
|
|
407
|
+
|
|
408
|
+
app = module.app
|
|
409
|
+
|
|
410
|
+
# Generate the OpenAPI schema
|
|
411
|
+
openapi_schema = json.loads(app.get_openapi_json_schema(openapi_version='3.0.0'))
|
|
412
|
+
|
|
413
|
+
# Fix Pydantic v2 issue (forcing OpenAPI 3.0.0)
|
|
414
|
+
if openapi_schema.get('openapi') != '3.0.0':
|
|
415
|
+
openapi_schema['openapi'] = '3.0.0'
|
|
416
|
+
result['warnings'].append('Fixed OpenAPI version to 3.0.0 (Pydantic v2 issue)')
|
|
417
|
+
|
|
418
|
+
# Fix operationIds
|
|
419
|
+
fix_operation_ids(openapi_schema, result)
|
|
420
|
+
|
|
421
|
+
# Create output directory if it doesn't exist
|
|
422
|
+
os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
|
|
423
|
+
|
|
424
|
+
# Save the schema to the output path
|
|
425
|
+
with open(output_path, 'w') as f:
|
|
426
|
+
json.dump(openapi_schema, f, indent=2)
|
|
427
|
+
|
|
428
|
+
result['schema'] = openapi_schema
|
|
429
|
+
result['process']['direct_import']['succeeded'] = True
|
|
430
|
+
|
|
431
|
+
except ImportError as e:
|
|
432
|
+
# SECOND ATTEMPT: Simplified version with problematic imports commented out
|
|
433
|
+
result['process']['direct_import']['error'] = str(e)
|
|
434
|
+
result['warnings'].append(f'Direct import failed: {str(e)}')
|
|
435
|
+
|
|
436
|
+
# Extract the import name that failed
|
|
437
|
+
import_name = str(e).split("'")[-2] if "'" in str(e) else str(e)
|
|
438
|
+
|
|
439
|
+
# Try simplified approach
|
|
440
|
+
result['process']['simplified_version']['attempted'] = True
|
|
441
|
+
simplified_path = os.path.join(lambda_dir, f'{module_name}_simplified.py')
|
|
442
|
+
|
|
443
|
+
try:
|
|
444
|
+
with open(lambda_code_path, 'r') as f:
|
|
445
|
+
content = f.read()
|
|
446
|
+
|
|
447
|
+
# Define problematic packages
|
|
448
|
+
problematic_packages = [
|
|
449
|
+
'matplotlib',
|
|
450
|
+
'numpy',
|
|
451
|
+
'pandas',
|
|
452
|
+
'scipy',
|
|
453
|
+
'tensorflow',
|
|
454
|
+
'torch',
|
|
455
|
+
'sympy',
|
|
456
|
+
'nltk',
|
|
457
|
+
'spacy',
|
|
458
|
+
'gensim',
|
|
459
|
+
'sklearn',
|
|
460
|
+
'networkx',
|
|
461
|
+
'plotly',
|
|
462
|
+
'dash',
|
|
463
|
+
'opencv',
|
|
464
|
+
'cv2',
|
|
465
|
+
'PIL',
|
|
466
|
+
'pillow',
|
|
467
|
+
]
|
|
468
|
+
|
|
469
|
+
# Comment out problematic imports and code blocks
|
|
470
|
+
simplified_content, modifications = comment_out_problematic_code(
|
|
471
|
+
content, problematic_packages, import_name
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
# Add modifications to the result
|
|
475
|
+
result['process']['simplified_version']['modifications'] = modifications
|
|
476
|
+
|
|
477
|
+
# Write simplified file
|
|
478
|
+
with open(simplified_path, 'w') as f:
|
|
479
|
+
f.write(simplified_content)
|
|
480
|
+
|
|
481
|
+
try:
|
|
482
|
+
# Import simplified module
|
|
483
|
+
spec = importlib.util.spec_from_file_location(
|
|
484
|
+
f'{module_name}_simplified', simplified_path
|
|
485
|
+
)
|
|
486
|
+
if spec is None:
|
|
487
|
+
raise ImportError(f"Could not find spec for module: {module_name}_simplified")
|
|
488
|
+
|
|
489
|
+
simplified_module = importlib.util.module_from_spec(spec)
|
|
490
|
+
if spec.loader is None:
|
|
491
|
+
raise ImportError(f"Module spec has no loader: {module_name}_simplified")
|
|
492
|
+
|
|
493
|
+
spec.loader.exec_module(simplified_module)
|
|
494
|
+
|
|
495
|
+
# Get app and generate schema
|
|
496
|
+
if not hasattr(simplified_module, 'app'):
|
|
497
|
+
raise AttributeError("No 'app' variable found in the simplified module")
|
|
498
|
+
|
|
499
|
+
app = getattr(simplified_module, 'app')
|
|
500
|
+
openapi_schema = json.loads(
|
|
501
|
+
app.get_openapi_json_schema(openapi_version='3.0.0')
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
# Fix Pydantic v2 issue (forcing OpenAPI 3.0.0)
|
|
505
|
+
if openapi_schema.get('openapi') != '3.0.0':
|
|
506
|
+
openapi_schema['openapi'] = '3.0.0'
|
|
507
|
+
result['warnings'].append(
|
|
508
|
+
'Fixed OpenAPI version to 3.0.0 (Pydantic v2 issue)'
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
# Fix operationIds
|
|
512
|
+
fix_operation_ids(openapi_schema, result)
|
|
513
|
+
|
|
514
|
+
# Create output directory if it doesn't exist
|
|
515
|
+
os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
|
|
516
|
+
|
|
517
|
+
# Save the schema to the output path
|
|
518
|
+
with open(output_path, 'w') as f:
|
|
519
|
+
json.dump(openapi_schema, f, indent=2)
|
|
520
|
+
|
|
521
|
+
result['schema'] = openapi_schema
|
|
522
|
+
result['warnings'].append(
|
|
523
|
+
'Used simplified version with problematic imports and code commented out'
|
|
524
|
+
)
|
|
525
|
+
result['process']['simplified_version']['succeeded'] = True
|
|
526
|
+
|
|
527
|
+
except Exception as simplified_error:
|
|
528
|
+
# Both approaches failed
|
|
529
|
+
result['process']['simplified_version']['error'] = str(simplified_error)
|
|
530
|
+
result['status'] = 'error'
|
|
531
|
+
result['error'] = 'Both direct and simplified approaches failed'
|
|
532
|
+
result['original_error'] = str(e) # Preserve the original error
|
|
533
|
+
result['simplified_error'] = str(
|
|
534
|
+
simplified_error
|
|
535
|
+
) # Add the simplified approach error
|
|
536
|
+
result['diagnosis'] = (
|
|
537
|
+
f'The Lambda function has dependencies that cannot be resolved: {import_name}'
|
|
538
|
+
)
|
|
539
|
+
result['solution'] = (
|
|
540
|
+
'Use the fallback script and manually comment out problematic imports and code'
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
# LAST RESORT: Generate fallback script
|
|
544
|
+
script = generate_fallback_script(lambda_code_path, output_path)
|
|
545
|
+
result['fallback_script'] = script
|
|
546
|
+
result['process']['fallback_script']['generated'] = True
|
|
547
|
+
|
|
548
|
+
# Add instructions
|
|
549
|
+
result['instructions'] = (
|
|
550
|
+
f'Error encountered: {str(simplified_error)}\n\n'
|
|
551
|
+
'To generate the schema manually, save the fallback script to a file '
|
|
552
|
+
'and run it in an environment with all required dependencies installed.'
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
# Add client-agnostic instructions
|
|
556
|
+
result['client_instructions'] = {
|
|
557
|
+
'title': 'Schema Generation Failed',
|
|
558
|
+
'summary': f'The Lambda function has dependencies that cannot be resolved: {import_name}',
|
|
559
|
+
'steps': [
|
|
560
|
+
'Save the fallback script below to a file (e.g., generate_schema.py)',
|
|
561
|
+
'Run the script in your environment where all dependencies are available',
|
|
562
|
+
'The script will generate the schema at the specified output path',
|
|
563
|
+
],
|
|
564
|
+
'script_filename_suggestion': 'generate_schema.py',
|
|
565
|
+
'command_suggestion': 'python generate_schema.py',
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
finally:
|
|
569
|
+
# Clean up simplified file
|
|
570
|
+
if os.path.exists(simplified_path):
|
|
571
|
+
os.remove(simplified_path)
|
|
572
|
+
|
|
573
|
+
except Exception as e:
|
|
574
|
+
# Error creating simplified version
|
|
575
|
+
result['process']['simplified_version']['error'] = str(e)
|
|
576
|
+
result['status'] = 'error'
|
|
577
|
+
result['error'] = f'Failed to create simplified version: {str(e)}'
|
|
578
|
+
result['diagnosis'] = (
|
|
579
|
+
'Could not process the Lambda file to create a simplified version'
|
|
580
|
+
)
|
|
581
|
+
result['solution'] = (
|
|
582
|
+
'Use the fallback script and manually comment out problematic imports and code'
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
# Generate fallback script
|
|
586
|
+
script = generate_fallback_script(lambda_code_path, output_path)
|
|
587
|
+
result['fallback_script'] = script
|
|
588
|
+
result['process']['fallback_script']['generated'] = True
|
|
589
|
+
|
|
590
|
+
# Add instructions
|
|
591
|
+
result['instructions'] = (
|
|
592
|
+
f'Error encountered: {str(e)}\n\n'
|
|
593
|
+
'To generate the schema manually, save the fallback script to a file '
|
|
594
|
+
'and run it in an environment with all required dependencies installed.'
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
except AttributeError as e:
|
|
598
|
+
# App not found
|
|
599
|
+
result['process']['direct_import']['error'] = str(e)
|
|
600
|
+
result['status'] = 'error'
|
|
601
|
+
result['error'] = str(e)
|
|
602
|
+
result['diagnosis'] = 'The BedrockAgentResolver instance was not found in the module'
|
|
603
|
+
result['solution'] = (
|
|
604
|
+
'Edit the APP_VAR_NAME variable in the fallback script to match your BedrockAgentResolver instance name'
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
# Generate fallback script
|
|
608
|
+
script = generate_fallback_script(lambda_code_path, output_path)
|
|
609
|
+
result['fallback_script'] = script
|
|
610
|
+
result['process']['fallback_script']['generated'] = True
|
|
611
|
+
|
|
612
|
+
# Add instructions
|
|
613
|
+
result['instructions'] = (
|
|
614
|
+
f'Error encountered: {str(e)}\n\n'
|
|
615
|
+
'To generate the schema manually, save the fallback script to a file '
|
|
616
|
+
'and run it in an environment with all required dependencies installed. '
|
|
617
|
+
'You may need to update the APP_VAR_NAME variable if your BedrockAgentResolver '
|
|
618
|
+
'instance has a different name than "app".'
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
except Exception as e:
|
|
622
|
+
# Other errors
|
|
623
|
+
result['process']['direct_import']['error'] = str(e)
|
|
624
|
+
result['status'] = 'error'
|
|
625
|
+
result['error'] = str(e)
|
|
626
|
+
result['diagnosis'] = f'An unexpected error occurred: {type(e).__name__}'
|
|
627
|
+
result['solution'] = (
|
|
628
|
+
'Use the fallback script and check for syntax errors or other issues in your Lambda function'
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
# Generate fallback script
|
|
632
|
+
script = generate_fallback_script(lambda_code_path, output_path)
|
|
633
|
+
result['fallback_script'] = script
|
|
634
|
+
result['process']['fallback_script']['generated'] = True
|
|
635
|
+
|
|
636
|
+
# Add instructions
|
|
637
|
+
result['instructions'] = (
|
|
638
|
+
f'Error encountered: {str(e)}\n\n'
|
|
639
|
+
'To generate the schema manually, save the fallback script to a file '
|
|
640
|
+
'and run it in an environment with all required dependencies installed.'
|
|
641
|
+
)
|
|
642
|
+
|
|
643
|
+
finally:
|
|
644
|
+
# Clean up sys.path
|
|
645
|
+
if lambda_dir in sys.path:
|
|
646
|
+
sys.path.remove(lambda_dir)
|
|
647
|
+
|
|
648
|
+
except Exception as e:
|
|
649
|
+
result['status'] = 'error'
|
|
650
|
+
result['error'] = str(e)
|
|
651
|
+
result['diagnosis'] = f'Error accessing or processing the Lambda file: {type(e).__name__}'
|
|
652
|
+
result['solution'] = 'Check file permissions and path correctness'
|
|
653
|
+
|
|
654
|
+
# Generate fallback script
|
|
655
|
+
script = generate_fallback_script(lambda_code_path, output_path)
|
|
656
|
+
result['fallback_script'] = script
|
|
657
|
+
result['process']['fallback_script']['generated'] = True
|
|
658
|
+
|
|
659
|
+
# Add instructions
|
|
660
|
+
result['instructions'] = (
|
|
661
|
+
f'Error encountered: {str(e)}\n\n'
|
|
662
|
+
'To generate the schema manually, save the fallback script to a file '
|
|
663
|
+
'and run it in an environment with all required dependencies installed.'
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
return result
|