gofannon 0.1.0__py3-none-any.whl → 0.25.11__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.
- gofannon/arxiv/__init__.py +0 -2
- gofannon/base/__init__.py +29 -167
- gofannon/base/bedrock.py +666 -0
- gofannon/base/langchain.py +85 -0
- gofannon/base/smol_agents.py +51 -0
- gofannon/basic_math/__init__.py +1 -5
- gofannon/config.py +3 -1
- gofannon/github/__init__.py +0 -7
- gofannon/github/pr_review_tool.py +104 -0
- gofannon/google_search/__init__.py +0 -0
- gofannon/google_search/google_search.py +55 -0
- gofannon/headless_browser/__init__.py +0 -0
- gofannon/headless_browser/base.py +42 -0
- gofannon/headless_browser/headless_browser_get.py +35 -0
- gofannon/open_notify_space/__init__.py +1 -0
- gofannon/open_notify_space/iss_locator.py +126 -0
- gofannon/reasoning/__init__.py +0 -3
- gofannon-0.25.11.dist-info/METADATA +148 -0
- {gofannon-0.1.0.dist-info → gofannon-0.25.11.dist-info}/RECORD +21 -10
- {gofannon-0.1.0.dist-info → gofannon-0.25.11.dist-info}/WHEEL +1 -1
- gofannon-0.1.0.dist-info/METADATA +0 -84
- {gofannon-0.1.0.dist-info → gofannon-0.25.11.dist-info}/LICENSE +0 -0
gofannon/base/bedrock.py
ADDED
@@ -0,0 +1,666 @@
|
|
1
|
+
from typing import Type, Callable
|
2
|
+
import subprocess
|
3
|
+
import shutil
|
4
|
+
import zipfile
|
5
|
+
import json
|
6
|
+
import time
|
7
|
+
|
8
|
+
try:
|
9
|
+
import boto3
|
10
|
+
from botocore.exceptions import ClientError
|
11
|
+
from botocore.client import BaseClient
|
12
|
+
|
13
|
+
_HAS_BOTO3 = True
|
14
|
+
except ImportError:
|
15
|
+
_HAS_BOTO3 = False
|
16
|
+
|
17
|
+
try:
|
18
|
+
from jsonschema import validate
|
19
|
+
import jsonschema.exceptions
|
20
|
+
|
21
|
+
_HAS_JSONSCHEMA = True
|
22
|
+
except ImportError:
|
23
|
+
_HAS_JSONSCHEMA = False
|
24
|
+
|
25
|
+
|
26
|
+
class BedrockMixin:
|
27
|
+
|
28
|
+
def export_to_bedrock(self, agent_app_config: dict = None) -> dict:
|
29
|
+
"""
|
30
|
+
Export tool as Bedrock Agent tool configuration
|
31
|
+
"""
|
32
|
+
if not _HAS_BOTO3:
|
33
|
+
error = "boto3 not installed. Install with `pip install boto3`"
|
34
|
+
self.logger.error(error)
|
35
|
+
raise RuntimeError(error)
|
36
|
+
|
37
|
+
if not _HAS_JSONSCHEMA:
|
38
|
+
error = "jasonschema not installed. Install with `pip install jsonschema`"
|
39
|
+
self.logger.error(error)
|
40
|
+
raise RuntimeError(error)
|
41
|
+
|
42
|
+
valid_agent_app_config_schema = {
|
43
|
+
"type": "object",
|
44
|
+
"properties": {
|
45
|
+
"app_id": {"type": "string"},
|
46
|
+
"agent_session_timeout": {"type": "integer"},
|
47
|
+
"instruction": {"type": "string"},
|
48
|
+
"agent_description": {"type": "string"},
|
49
|
+
"target_model": {"type": "string"},
|
50
|
+
"python_runtime_version": {"type": "string"},
|
51
|
+
"temp_build_root": {"type": "string"},
|
52
|
+
},
|
53
|
+
"required": [
|
54
|
+
"app_id",
|
55
|
+
"agent_session_timeout",
|
56
|
+
"instruction",
|
57
|
+
"agent_description",
|
58
|
+
"target_model",
|
59
|
+
"python_runtime_version",
|
60
|
+
"temp_build_root",
|
61
|
+
],
|
62
|
+
}
|
63
|
+
try:
|
64
|
+
validate(agent_app_config, valid_agent_app_config_schema)
|
65
|
+
except jsonschema.exceptions.ValidationError as err:
|
66
|
+
error = f"JSON validation failure on input parameters: {err}"
|
67
|
+
self.logger.error(error)
|
68
|
+
raise RuntimeError(error)
|
69
|
+
|
70
|
+
self.bedrock_agent_client = boto3.client("bedrock-agent")
|
71
|
+
self.app_id = agent_app_config["app_id"]
|
72
|
+
self.agent_name = agent_app_config["agent_name"]
|
73
|
+
self.agent_session_timeout = agent_app_config["agent_session_timeout"]
|
74
|
+
self.agent_instruction = agent_app_config["instruction"]
|
75
|
+
self.agent_description = agent_app_config["agent_description"]
|
76
|
+
self.agent_target_model = agent_app_config["target_model"]
|
77
|
+
self.python_runtime_version = agent_app_config["python_runtime_version"]
|
78
|
+
self.temp_build_root = agent_app_config["temp_build_root"]
|
79
|
+
try:
|
80
|
+
self.aws_account_id = (
|
81
|
+
boto3.client("sts").get_caller_identity().get("Account")
|
82
|
+
)
|
83
|
+
except ClientError as e:
|
84
|
+
error = f"Error getting AWS account number. Client error: {e}"
|
85
|
+
self.logger.error(error)
|
86
|
+
raise RuntimeError(error)
|
87
|
+
except Exception as e:
|
88
|
+
error = f"Error getting AWS account number. Unexpected error: {e}"
|
89
|
+
self.logger.error(error)
|
90
|
+
raise RuntimeError(error)
|
91
|
+
|
92
|
+
self.openapi_schema_dict = self._generate_openapi_schema()
|
93
|
+
|
94
|
+
self.logger.info(
|
95
|
+
f"Starting build of app {self.app_id}...", self.__class__.__name__
|
96
|
+
)
|
97
|
+
# Creates:
|
98
|
+
# the lambda - a .zip archive that contains all the dependencies and the lambda source.
|
99
|
+
# This lambda will have a resource-based policy attached giving it
|
100
|
+
# permission to be invoked by the agent attached.
|
101
|
+
# an IAM role - the lambda execution role with managed attached policy
|
102
|
+
# AWSLambdaBasicExecutionRole allowing the lambda to use CloudWatch
|
103
|
+
|
104
|
+
self.logger.info("\tCreating lambda...", self.__class__.__name__)
|
105
|
+
|
106
|
+
self.lambda_arn = self._create_bedrock_lambda()
|
107
|
+
|
108
|
+
# Creates:
|
109
|
+
# the Bedrock agent - The bedrock agent itself
|
110
|
+
# an IAM role - The agent execution role
|
111
|
+
# An IAM policy - a policy to attach to the role allowing the agent to invoke the
|
112
|
+
# - foundational model
|
113
|
+
self.logger.info("\tCreating agent...", self.__class__.__name__)
|
114
|
+
self.agent_id = self._create_bedrock_agent()
|
115
|
+
|
116
|
+
# Creates:
|
117
|
+
# the Bedrock agent action group - Defined with an API schema
|
118
|
+
self.logger.info("\tCreating agent action group...", self.__class__.__name__)
|
119
|
+
action_group = self._create_agent_action_group()
|
120
|
+
|
121
|
+
# Finally, prepare the agent
|
122
|
+
self.logger.info("\tPreparing agent...", self.__class__.__name__)
|
123
|
+
try:
|
124
|
+
response = self.bedrock_agent_client.prepare_agent(agentId=self.agent_id)
|
125
|
+
except ClientError as e:
|
126
|
+
error = f"Error preparing agent. Client error: {e}"
|
127
|
+
self.logger.error(error)
|
128
|
+
raise RuntimeError(error)
|
129
|
+
except Exception as e:
|
130
|
+
error = f"Error preparing agent. Unexpected error: {e}"
|
131
|
+
self.logger.error(error)
|
132
|
+
raise RuntimeError(error)
|
133
|
+
|
134
|
+
output_manifest = {
|
135
|
+
"lambdaARN": self.lambda_arn,
|
136
|
+
"lambdaRoleName": self.lambda_role_name,
|
137
|
+
"agentId": self.agent_id,
|
138
|
+
"agentRoleName": self.agent_role_name,
|
139
|
+
"agentPolicyARN": self.agent_role_policy_arn,
|
140
|
+
"agentActionGroup": self.agent_action_group_id,
|
141
|
+
}
|
142
|
+
self.logger.info("Done!", self.__class__.__name__)
|
143
|
+
return output_manifest
|
144
|
+
|
145
|
+
def _generate_openapi_schema(self) -> dict:
|
146
|
+
"""Convert Gofannon definition to OpenAPI schema"""
|
147
|
+
params = self.definition["function"]["parameters"]
|
148
|
+
if not "properties" in params:
|
149
|
+
params["properties"] = {}
|
150
|
+
openapi_schema = {
|
151
|
+
"openapi": "3.0.0",
|
152
|
+
"info": {"title": self.name, "version": "1.0.0"},
|
153
|
+
"paths": {
|
154
|
+
f"/{self.name}": {
|
155
|
+
"get": {
|
156
|
+
"description": self.definition["function"]["description"],
|
157
|
+
"requestBody": {
|
158
|
+
"content": {
|
159
|
+
"application/json": {
|
160
|
+
"schema": {
|
161
|
+
"type": "object",
|
162
|
+
"properties": {
|
163
|
+
param: {
|
164
|
+
"type": props["type"],
|
165
|
+
"description": props["description"],
|
166
|
+
}
|
167
|
+
for param, props in params[
|
168
|
+
"properties"
|
169
|
+
].items()
|
170
|
+
},
|
171
|
+
"required": params.get("required", []),
|
172
|
+
}
|
173
|
+
}
|
174
|
+
}
|
175
|
+
},
|
176
|
+
"responses": {
|
177
|
+
"200": {
|
178
|
+
"description": "Successful operation",
|
179
|
+
"content": {
|
180
|
+
"application/json": {
|
181
|
+
"schema": {
|
182
|
+
"type": "object",
|
183
|
+
"properties": {
|
184
|
+
"result": {"type": "string"}
|
185
|
+
},
|
186
|
+
}
|
187
|
+
}
|
188
|
+
},
|
189
|
+
}
|
190
|
+
},
|
191
|
+
}
|
192
|
+
}
|
193
|
+
},
|
194
|
+
}
|
195
|
+
|
196
|
+
return openapi_schema
|
197
|
+
|
198
|
+
def _create_bedrock_lambda(self) -> str:
|
199
|
+
"""Create Lambda function for Bedrock integration"""
|
200
|
+
|
201
|
+
lambda_client = boto3.client("lambda")
|
202
|
+
self.lambda_role_arn = self._create_bedrock_lambda_role()
|
203
|
+
|
204
|
+
# Build the lambda .zip deployment package here. Package consits of:
|
205
|
+
# 1. All the Python dependencies, including Gofannon itself
|
206
|
+
# 2. The lambda source code.
|
207
|
+
# bash code as comments at the bottom of this file.
|
208
|
+
# See: https://docs.aws.amazon.com/lambda/latest/dg/python-package.html
|
209
|
+
|
210
|
+
# TODO: verify the path here
|
211
|
+
command = f"""../../scripts/build_lambda.sh {self.temp_build_root} {self.python_runtime_version.replace("python","")} {self.app_id}"""
|
212
|
+
try:
|
213
|
+
process = subprocess.Popen(
|
214
|
+
command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
215
|
+
)
|
216
|
+
stdout, stderr = process.communicate()
|
217
|
+
|
218
|
+
if process.returncode == 0:
|
219
|
+
self.built_archive = stdout.decode("utf-8").replace("\n", "")
|
220
|
+
else:
|
221
|
+
raise RuntimeError(stderr.decode("utf-8"))
|
222
|
+
|
223
|
+
except Exception as e:
|
224
|
+
error = f"Error creating .zip archive. {e}"
|
225
|
+
self.logger.error(error)
|
226
|
+
raise RuntimeError(error)
|
227
|
+
|
228
|
+
# TODO: Skeleton code in the function directory or here?
|
229
|
+
lambda_src_string = self._get_lambda_source(
|
230
|
+
module_name=self.__class__.__module__, class_name=self.__class__.__name__
|
231
|
+
)
|
232
|
+
|
233
|
+
try:
|
234
|
+
with open(f"{self.temp_build_root}/lambda_function.py", "w") as text_file:
|
235
|
+
text_file.write(lambda_src_string)
|
236
|
+
except Exception as e:
|
237
|
+
error = f"Error writing lambda Python source file: {e}"
|
238
|
+
self.logger.error(error)
|
239
|
+
raise RuntimeError(error)
|
240
|
+
|
241
|
+
try:
|
242
|
+
with zipfile.ZipFile(
|
243
|
+
self.built_archive,
|
244
|
+
"a",
|
245
|
+
compression=zipfile.ZIP_DEFLATED,
|
246
|
+
) as zipf:
|
247
|
+
zipf.write(
|
248
|
+
f"{self.temp_build_root}/lambda_function.py", "lambda_function.py"
|
249
|
+
)
|
250
|
+
except Exception as e:
|
251
|
+
error = f"Error inserting lambda Python source file into zip: {e}"
|
252
|
+
self.logger.error(error)
|
253
|
+
raise RuntimeError(error)
|
254
|
+
|
255
|
+
with open(self.built_archive, "rb") as f:
|
256
|
+
zipped_code = f.read()
|
257
|
+
|
258
|
+
try:
|
259
|
+
response = lambda_client.create_function(
|
260
|
+
FunctionName=f"{self.app_id}_{self.name}",
|
261
|
+
Runtime=self.python_runtime_version,
|
262
|
+
Role=self.lambda_role_arn,
|
263
|
+
Handler="lambda_function.lambda_handler",
|
264
|
+
Code={"ZipFile": zipped_code},
|
265
|
+
Description=f"{self.app_id} tool: {self.name}",
|
266
|
+
Timeout=30,
|
267
|
+
MemorySize=256,
|
268
|
+
)
|
269
|
+
except ClientError as e:
|
270
|
+
error = f"Error creating lambda. Client error: {e}"
|
271
|
+
self.logger.error(error)
|
272
|
+
raise RuntimeError(error)
|
273
|
+
|
274
|
+
except Exception as e:
|
275
|
+
error = f"Error creating lambda. Unexpected error: {e}"
|
276
|
+
self.logger.error(error)
|
277
|
+
raise RuntimeError(error)
|
278
|
+
|
279
|
+
# Clean up the .zip archive build
|
280
|
+
try:
|
281
|
+
shutil.rmtree(self.temp_build_root)
|
282
|
+
except OSError as e:
|
283
|
+
error = (
|
284
|
+
f"Error deleting temporary build directory {self.temp_build_root}: {e}"
|
285
|
+
)
|
286
|
+
self.logger.error(error)
|
287
|
+
pass
|
288
|
+
|
289
|
+
# Add the resourced-base policy allowing the lambda to be invoked
|
290
|
+
# by the agent.
|
291
|
+
try:
|
292
|
+
add_permission_response = lambda_client.add_permission(
|
293
|
+
StatementId=f"{self.app_id}-bedrock-invoke",
|
294
|
+
FunctionName=response["FunctionArn"],
|
295
|
+
Action="lambda:InvokeFunction",
|
296
|
+
Principal="bedrock.amazonaws.com",
|
297
|
+
# TODO: fix this. Remove wildcard
|
298
|
+
SourceArn=f"arn:aws:bedrock:us-east-1:{self.aws_account_id}:agent/*",
|
299
|
+
)
|
300
|
+
except ClientError as e:
|
301
|
+
error = f"Error attaching resource policy to lambda execution role. Client error: {e}"
|
302
|
+
self.logger.error(error)
|
303
|
+
raise RuntimeError(error)
|
304
|
+
except Exception as e:
|
305
|
+
error = f"Error attaching resource policy to lambda execution role. Unexpected error: {e}"
|
306
|
+
self.logger.error(error)
|
307
|
+
raise RuntimeError(error)
|
308
|
+
|
309
|
+
return response["FunctionArn"]
|
310
|
+
|
311
|
+
def _create_bedrock_lambda_role(self) -> str:
|
312
|
+
"""Get or create IAM role for Bedrock integration"""
|
313
|
+
iam = boto3.client("iam")
|
314
|
+
|
315
|
+
self.lambda_role_name = f"{self.app_id}_bedrock_lambda_execution_role"
|
316
|
+
self.assume_role = self._get_assumed_role(service="lambda.amazonaws.com")
|
317
|
+
|
318
|
+
try:
|
319
|
+
iam.create_role(
|
320
|
+
RoleName=self.lambda_role_name,
|
321
|
+
AssumeRolePolicyDocument=json.dumps(self.assume_role),
|
322
|
+
)
|
323
|
+
except ClientError as e:
|
324
|
+
error = f"Error creating lambda execution role. Client error: {e}"
|
325
|
+
self.logger.error(error)
|
326
|
+
raise RuntimeError(error)
|
327
|
+
except Exception as e:
|
328
|
+
error = f"Error creating lambda execution role. Unexpected error: {e}"
|
329
|
+
self.logger.error(error)
|
330
|
+
raise RuntimeError(error)
|
331
|
+
|
332
|
+
try:
|
333
|
+
iam.attach_role_policy(
|
334
|
+
RoleName=self.lambda_role_name,
|
335
|
+
PolicyArn="arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
|
336
|
+
)
|
337
|
+
except ClientError as e:
|
338
|
+
error = f"Error attaching managed policy to lambda execution role. Client error: {e}"
|
339
|
+
self.logger.error(error)
|
340
|
+
raise RuntimeError(error)
|
341
|
+
|
342
|
+
except Exception as e:
|
343
|
+
error = f"Error attaching managed policy to lambda execution role. Unexpected error: {e}"
|
344
|
+
self.logger.error(error)
|
345
|
+
raise RuntimeError(error)
|
346
|
+
|
347
|
+
return iam.get_role(RoleName=self.lambda_role_name)["Role"]["Arn"]
|
348
|
+
|
349
|
+
def _create_bedrock_agent(self) -> str:
|
350
|
+
"""Create Bedrock agent"""
|
351
|
+
|
352
|
+
self.agent_role_arn = self._create_agent_role()
|
353
|
+
|
354
|
+
args = {
|
355
|
+
"agentName": f"{self.app_id}_{self.agent_name}Agent",
|
356
|
+
"agentResourceRoleArn": self.agent_role_arn,
|
357
|
+
"foundationModel": self.agent_target_model,
|
358
|
+
"idleSessionTTLInSeconds": self.agent_session_timeout,
|
359
|
+
"instruction": self.agent_instruction,
|
360
|
+
"description": self.agent_description,
|
361
|
+
}
|
362
|
+
|
363
|
+
try:
|
364
|
+
response = self.bedrock_agent_client.create_agent(**args)
|
365
|
+
except ClientError as e:
|
366
|
+
error = f"Error creating agent. Client error: {e}"
|
367
|
+
self.logger.error(error)
|
368
|
+
raise RuntimeError(error)
|
369
|
+
|
370
|
+
except Exception as e:
|
371
|
+
error = f"Error creating agent. Unexpected error: {e}"
|
372
|
+
self.logger.error(error)
|
373
|
+
raise RuntimeError(error)
|
374
|
+
|
375
|
+
return response["agent"]["agentId"]
|
376
|
+
|
377
|
+
def _create_agent_action_group(self):
|
378
|
+
"""Create Bedrock action group and attach to Bedrock agent"""
|
379
|
+
|
380
|
+
args = {
|
381
|
+
"agentId": self.agent_id,
|
382
|
+
"actionGroupExecutor": {
|
383
|
+
"lambda": self.lambda_arn,
|
384
|
+
},
|
385
|
+
"actionGroupName": f"{self.app_id}_{self.agent_name}_ag",
|
386
|
+
"agentVersion": "DRAFT",
|
387
|
+
"apiSchema": {"payload": json.dumps(self.openapi_schema_dict)},
|
388
|
+
"description": self.agent_description,
|
389
|
+
}
|
390
|
+
|
391
|
+
# Wait until agent status transitions to CREATING before creating the action group.
|
392
|
+
counter = 0
|
393
|
+
agent_details = self.bedrock_agent_client.get_agent(agentId=self.agent_id)
|
394
|
+
agent_status = agent_details["agent"]["agentStatus"]
|
395
|
+
while (agent_status == "CREATING") and (counter <= 9):
|
396
|
+
counter = counter + 1
|
397
|
+
time.sleep(1)
|
398
|
+
agent_details = self.bedrock_agent_client.get_agent(agentId=self.agent_id)
|
399
|
+
agent_status = agent_details["agent"]["agentStatus"]
|
400
|
+
|
401
|
+
if counter == 10:
|
402
|
+
error = f"Error creating agent timeout. Agent created, but status never transitioned out of CREATING"
|
403
|
+
self.logger.error(error)
|
404
|
+
raise RuntimeError(error)
|
405
|
+
|
406
|
+
try:
|
407
|
+
agent_action_group = self.bedrock_agent_client.create_agent_action_group(
|
408
|
+
**args
|
409
|
+
)
|
410
|
+
self.agent_action_group_id = agent_action_group["agentActionGroup"][
|
411
|
+
"actionGroupId"
|
412
|
+
]
|
413
|
+
except ClientError as e:
|
414
|
+
error = f"Error creating agent action group. Client error: {e}"
|
415
|
+
self.logger.error(error)
|
416
|
+
raise RuntimeError(error)
|
417
|
+
except Exception as e:
|
418
|
+
error = f"Error creating agent action group. Unexpected error: {e}"
|
419
|
+
self.logger.error(error)
|
420
|
+
raise RuntimeError(error)
|
421
|
+
|
422
|
+
return agent_action_group
|
423
|
+
|
424
|
+
def _create_agent_role(self) -> str:
|
425
|
+
"""Create IAM agent role for Bedrock integration"""
|
426
|
+
|
427
|
+
iam = boto3.client("iam")
|
428
|
+
|
429
|
+
self.agent_role_name = f"{self.app_id}_bedrock_agent_execution_role"
|
430
|
+
self.agent_policy_name = f"{self.app_id}_bedrock_agent_allow_model"
|
431
|
+
|
432
|
+
assume_role = self._get_assumed_role(service="bedrock.amazonaws.com")
|
433
|
+
|
434
|
+
try:
|
435
|
+
iam.create_role(
|
436
|
+
RoleName=self.agent_role_name,
|
437
|
+
AssumeRolePolicyDocument=json.dumps(assume_role),
|
438
|
+
)
|
439
|
+
except ClientError as e:
|
440
|
+
error = f"Error creating agent role. Client error: {e}"
|
441
|
+
self.logger.error(error)
|
442
|
+
raise RuntimeError(error)
|
443
|
+
except Exception as e:
|
444
|
+
error = f"Error creating agent role. Unexpected error: {e}"
|
445
|
+
self.logger.error(error)
|
446
|
+
raise RuntimeError(error)
|
447
|
+
|
448
|
+
managed_policy_txt = self._get_managed_policy(
|
449
|
+
resource=f"foundation-model/{self.agent_target_model}"
|
450
|
+
)
|
451
|
+
|
452
|
+
try:
|
453
|
+
managed_policy = iam.create_policy(
|
454
|
+
PolicyName=self.agent_policy_name,
|
455
|
+
PolicyDocument=json.dumps(managed_policy_txt),
|
456
|
+
)
|
457
|
+
except ClientError as e:
|
458
|
+
error = f"Error creating agent policy. Client error: {e}"
|
459
|
+
self.logger.error(error)
|
460
|
+
raise RuntimeError(error)
|
461
|
+
except Exception as e:
|
462
|
+
error = f"Error creating agent policy. Unexpected error: {e}"
|
463
|
+
self.logger.error(error)
|
464
|
+
raise RuntimeError(error)
|
465
|
+
|
466
|
+
self.agent_role_policy_arn = (
|
467
|
+
f"arn:aws:iam::{self.aws_account_id}:policy/{self.agent_policy_name}"
|
468
|
+
)
|
469
|
+
try:
|
470
|
+
iam.attach_role_policy(
|
471
|
+
RoleName=self.agent_role_name,
|
472
|
+
PolicyArn=self.agent_role_policy_arn,
|
473
|
+
)
|
474
|
+
except ClientError as e:
|
475
|
+
error = f"Error creating attaching policy to agent role. Client error: {e}"
|
476
|
+
self.logger.error(error)
|
477
|
+
raise RuntimeError(error)
|
478
|
+
except Exception as e:
|
479
|
+
error = (
|
480
|
+
f"Error creating attaching policy to agent role. Unexpected error: {e}"
|
481
|
+
)
|
482
|
+
self.logger.error(error)
|
483
|
+
raise RuntimeError(error)
|
484
|
+
|
485
|
+
return iam.get_role(RoleName=self.agent_role_name)["Role"]["Arn"]
|
486
|
+
|
487
|
+
def _get_assumed_role(self, service: str = None) -> str:
|
488
|
+
"""Skeleton code for creating a role"""
|
489
|
+
role = {
|
490
|
+
"Version": "2012-10-17",
|
491
|
+
"Statement": [
|
492
|
+
{
|
493
|
+
"Effect": "Allow",
|
494
|
+
"Principal": {"Service": f"{service}"},
|
495
|
+
"Action": "sts:AssumeRole",
|
496
|
+
}
|
497
|
+
],
|
498
|
+
}
|
499
|
+
return role
|
500
|
+
|
501
|
+
def _get_managed_policy(self, resource: str = None) -> str:
|
502
|
+
"""Skeleton code for creating a policy"""
|
503
|
+
managed_policy_txt = {
|
504
|
+
"Version": "2012-10-17",
|
505
|
+
"Statement": [
|
506
|
+
{
|
507
|
+
"Action": "bedrock:InvokeModel",
|
508
|
+
"Resource": f"arn:aws:bedrock:us-east-1::{resource}",
|
509
|
+
"Effect": "Allow",
|
510
|
+
}
|
511
|
+
],
|
512
|
+
}
|
513
|
+
|
514
|
+
return managed_policy_txt
|
515
|
+
|
516
|
+
def _get_lambda_source(
|
517
|
+
self, module_name: str = None, class_name: str = None
|
518
|
+
) -> str:
|
519
|
+
"""Bedrock wrapped lambda used by the agent"""
|
520
|
+
|
521
|
+
# funky spacing below is required...
|
522
|
+
lambda_src = f"""
|
523
|
+
import json
|
524
|
+
from {module_name} import {class_name}
|
525
|
+
|
526
|
+
|
527
|
+
def lambda_handler(event, context):
|
528
|
+
tool = {class_name}()
|
529
|
+
result = tool.fn()
|
530
|
+
action_response = {{
|
531
|
+
"actionGroup": event["actionGroup"],
|
532
|
+
"apiPath": event["apiPath"],
|
533
|
+
"httpMethod": event["httpMethod"],
|
534
|
+
"httpStatusCode": 200,
|
535
|
+
"responseBody": {{"application/json": {{"body": json.dumps(result)}}}},
|
536
|
+
}}
|
537
|
+
api_response = {{"messageVersion": "1.0", "response": action_response}}
|
538
|
+
|
539
|
+
return api_response"""
|
540
|
+
return lambda_src
|
541
|
+
|
542
|
+
def delete_app(self, bedrock_config: dict = None) -> bool:
|
543
|
+
"""Tear down the boto3 stack created by .export_to_bedrock()"""
|
544
|
+
|
545
|
+
valid_bedrock_config_schema = {
|
546
|
+
"type": "object",
|
547
|
+
"properties": {
|
548
|
+
"lambdaARN": {"type": "string"},
|
549
|
+
"lambdaRoleName": {"type": "string"},
|
550
|
+
"agentId": {"type": "string"},
|
551
|
+
"agentRoleName": {"type": "string"},
|
552
|
+
"agentPolicyARN": {"type": "string"},
|
553
|
+
"agentActionGroup": {"type": "string"},
|
554
|
+
},
|
555
|
+
"required": [
|
556
|
+
"lambdaARN",
|
557
|
+
"lambdaRoleName",
|
558
|
+
"agentId",
|
559
|
+
"agentRoleName",
|
560
|
+
"agentPolicyARN",
|
561
|
+
"agentActionGroup",
|
562
|
+
],
|
563
|
+
}
|
564
|
+
try:
|
565
|
+
validate(bedrock_config, valid_bedrock_config_schema)
|
566
|
+
except jsonschema.exceptions.ValidationError as err:
|
567
|
+
error = f"JSON validation failure on input bedrock_config: {err}"
|
568
|
+
self.logger.error(error)
|
569
|
+
raise RuntimeError(error)
|
570
|
+
|
571
|
+
self.lambda_client = boto3.client("lambda")
|
572
|
+
self.iam_client = boto3.client("iam")
|
573
|
+
self.bedrock_agent_client = boto3.client("bedrock-agent")
|
574
|
+
|
575
|
+
# Order is important here...
|
576
|
+
try:
|
577
|
+
detach_agent_policy = self.iam_client.detach_role_policy(
|
578
|
+
RoleName=bedrock_config["agentRoleName"],
|
579
|
+
PolicyArn=bedrock_config["agentPolicyARN"],
|
580
|
+
)
|
581
|
+
except ClientError as e:
|
582
|
+
error = f"Error detaching agent policy. Client error: {e}"
|
583
|
+
self.logger.error(error)
|
584
|
+
pass
|
585
|
+
except Exception as e:
|
586
|
+
error = f"Error detaching agent policy. Unexpected error: {e}"
|
587
|
+
self.logger.error(error)
|
588
|
+
pass
|
589
|
+
try:
|
590
|
+
delete_agent_role = self.iam_client.delete_role(
|
591
|
+
RoleName=bedrock_config["agentRoleName"]
|
592
|
+
)
|
593
|
+
except ClientError as e:
|
594
|
+
error = f"Error deleting agent role. Client error: {e}"
|
595
|
+
self.logger.error(error)
|
596
|
+
pass
|
597
|
+
except Exception as e:
|
598
|
+
error = f"Error deleting agent role. Unexpected error: {e}"
|
599
|
+
self.logger.error(error)
|
600
|
+
pass
|
601
|
+
|
602
|
+
try:
|
603
|
+
delete_agent_policy = self.iam_client.delete_policy(
|
604
|
+
PolicyArn=bedrock_config["agentPolicyARN"]
|
605
|
+
)
|
606
|
+
except ClientError as e:
|
607
|
+
error = f"Error deleting agent policy. Client error: {e}"
|
608
|
+
self.logger.error(error)
|
609
|
+
pass
|
610
|
+
except Exception as e:
|
611
|
+
error = f"Error deleting agent policy. Unexpected error: {e}"
|
612
|
+
self.logger.error(error)
|
613
|
+
pass
|
614
|
+
|
615
|
+
try:
|
616
|
+
delete_agent = self.bedrock_agent_client.delete_agent(
|
617
|
+
agentId=bedrock_config["agentId"], skipResourceInUseCheck=True
|
618
|
+
)
|
619
|
+
except ClientError as e:
|
620
|
+
error = f"Error deleting agent. Client error: {e}"
|
621
|
+
self.logger.error(error)
|
622
|
+
pass
|
623
|
+
except Exception as e:
|
624
|
+
error = f"Error deleting agent. Unexpected error: {e}"
|
625
|
+
self.logger.error(error)
|
626
|
+
pass
|
627
|
+
|
628
|
+
try:
|
629
|
+
detach_lambda_policy = self.iam_client.detach_role_policy(
|
630
|
+
RoleName=bedrock_config["lambdaRoleName"],
|
631
|
+
PolicyArn="arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
|
632
|
+
)
|
633
|
+
except ClientError as e:
|
634
|
+
error = f"Error detaching lambda policy. Client error: {e}"
|
635
|
+
self.logger.error(error)
|
636
|
+
pass
|
637
|
+
except Exception as e:
|
638
|
+
error = f"Error detaching lambda policy. Unexpected error: {e}"
|
639
|
+
self.logger.error(error)
|
640
|
+
pass
|
641
|
+
|
642
|
+
try:
|
643
|
+
delete_lambda_role = self.iam_client.delete_role(
|
644
|
+
RoleName=bedrock_config["lambdaRoleName"]
|
645
|
+
)
|
646
|
+
except ClientError as e:
|
647
|
+
error = f"Error deleting lambda role. Client error: {e}"
|
648
|
+
self.logger.error(error)
|
649
|
+
pass
|
650
|
+
except Exception as e:
|
651
|
+
error = f"Error deleting lambda role. Unexpected error: {e}"
|
652
|
+
self.logger.error(error)
|
653
|
+
pass
|
654
|
+
|
655
|
+
try:
|
656
|
+
delete_lambda = self.lambda_client.delete_function(
|
657
|
+
FunctionName=bedrock_config["lambdaARN"]
|
658
|
+
)
|
659
|
+
except ClientError as e:
|
660
|
+
error = f"Error deleting lambda. Client error: {e}"
|
661
|
+
self.logger.error(error)
|
662
|
+
pass
|
663
|
+
except Exception as e:
|
664
|
+
error = f"Error deleting lambda. Unexpected error: {e}"
|
665
|
+
self.logger.error(error)
|
666
|
+
pass
|