Last active
February 27, 2025 13:51
-
-
Save mikegc-aws/584005928bc8493e97c53faf5fd7492c to your computer and use it in GitHub Desktop.
Amzon Bedrock inline agent with return control. Run entire agent from one file. Session state stored in cloud.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import boto3 | |
import json | |
import uuid | |
import datetime | |
from botocore.exceptions import ClientError | |
debug = False | |
def get_current_time(): | |
"""Function to get the current time""" | |
now = datetime.datetime.now() | |
current_time = now.strftime("%H:%M:%S") | |
timezone = datetime.datetime.now().astimezone().tzname() | |
return { | |
"time": current_time, | |
"timezone": timezone | |
} | |
def get_current_date(): | |
"""Function to get the current date""" | |
now = datetime.datetime.now() | |
current_date = now.strftime("%Y-%m-%d") | |
timezone = datetime.datetime.now().astimezone().tzname() | |
return { | |
"date": current_date, | |
"timezone": timezone | |
} | |
def add_two_numbers(a, b): | |
"""Function to add two numbers""" | |
return a + b | |
def process_agent_response(response): | |
"""Process the agent's response and handle any return control events""" | |
invocation_id = None | |
response_text = "" | |
function_info = None | |
for event in response["completion"]: | |
if "returnControl" in event: | |
function_info = event["returnControl"] | |
invocation_id = function_info["invocationId"] | |
print("\nAgent is requesting information...") | |
return invocation_id, response_text, function_info | |
elif "chunk" in event and "bytes" in event["chunk"]: | |
text = event["chunk"]["bytes"].decode('utf-8') | |
response_text += text | |
return invocation_id, response_text, function_info | |
def chat_with_agent(): | |
# Create Bedrock clients | |
bedrock_runtime = boto3.client('bedrock-runtime') | |
bedrock_agent_runtime = boto3.client('bedrock-agent-runtime') | |
# Generate a unique session ID for the chat | |
session_id = str(uuid.uuid4()) | |
print(f"\nStarting new chat session (ID: {session_id})") | |
print("Type 'exit' or 'quit' to end the chat") | |
print("-" * 50) | |
# Define the action group with a time function | |
action_groups = [ | |
{ | |
"actionGroupName": "TimeActions", | |
"description": "Actions related to getting the current time", | |
"actionGroupExecutor": { | |
"customControl": "RETURN_CONTROL" | |
}, | |
"functionSchema": { | |
"functions": [ | |
{ | |
"name": "get_time", | |
"description": "Get the current time and date", | |
"parameters": {}, | |
"requireConfirmation": "DISABLED" | |
}, | |
{ | |
"name": "get_date", | |
"description": "Get the current date", | |
"parameters": {}, | |
"requireConfirmation": "DISABLED" | |
} | |
] | |
} | |
}, | |
{ | |
"actionGroupName": "MathActions", | |
"description": "Actions related to mathematical operations", | |
"actionGroupExecutor": { | |
"customControl": "RETURN_CONTROL" | |
}, | |
"functionSchema": { | |
"functions": [ | |
{ | |
"name": "add_two_numbers", | |
"description": "Add two numbers together", | |
"parameters": { | |
"a": { | |
"description": "The first number to add", | |
"required": True, | |
"type": "number" | |
}, | |
"b": { | |
"description": "The second number to add", | |
"required": True, | |
"type": "number" | |
} | |
} | |
} | |
] | |
} | |
} | |
] | |
# Define the agent instruction | |
instruction = "You are a helpful assistant that can tell the time when asked. You can also engage in general conversation." | |
# Specify the foundation model | |
foundation_model = "us.amazon.nova-pro-v1:0" | |
try: | |
while True: | |
# Get user input | |
user_input = input("\nYou: ").strip() | |
# Check if user wants to exit | |
if user_input.lower() in ['exit', 'quit']: | |
print("\nEnding chat session. Goodbye!") | |
break | |
# Skip empty inputs | |
if not user_input: | |
continue | |
# First invocation - Send user input to agent | |
response = bedrock_agent_runtime.invoke_inline_agent( | |
sessionId=session_id, | |
actionGroups=action_groups, | |
instruction=instruction, | |
foundationModel=foundation_model, | |
inputText=user_input, | |
enableTrace=True | |
) | |
# Process the response and check for return control | |
invocation_id, response_text, function_info = process_agent_response(response) | |
if invocation_id and function_info: | |
# Extract function details from the invocationInputs | |
function_input = function_info.get("invocationInputs", [])[0].get("functionInvocationInput", {}) | |
function_name = function_input.get("function") | |
action_group = function_input.get("actionGroup") | |
parameters = function_input.get("parameters", []) | |
# Convert parameters list to dictionary with proper type conversion | |
param_dict = {} | |
for param in parameters: | |
name = param.get("name") | |
value = param.get("value") | |
param_type = param.get("type") | |
# Convert value based on type | |
if param_type == "number": | |
try: | |
# Handle both integers and floats | |
value = float(value) | |
# Convert to int if it's a whole number | |
if value.is_integer(): | |
value = int(value) | |
except ValueError: | |
print(f"\nWarning: Could not convert {value} to number") | |
continue | |
param_dict[name] = value | |
if debug: | |
print(f"\nRequested function: {function_name}") | |
print(f"Parameters: {param_dict}") | |
# Call the appropriate function | |
result = None | |
if function_name == "get_time": | |
result = get_current_time() | |
elif function_name == "get_date": | |
result = get_current_date() | |
elif function_name == "add_two_numbers": | |
result = add_two_numbers(param_dict["a"], param_dict["b"]) | |
if result: | |
# Format the result for return control | |
return_control_result = { | |
"functionResult": { | |
"actionGroup": action_group, | |
"function": function_name, | |
"responseBody": { | |
"application/json": { | |
"body": json.dumps(result) | |
} | |
} | |
} | |
} | |
# Second invocation - Return the result to the agent | |
response = bedrock_agent_runtime.invoke_inline_agent( | |
sessionId=session_id, | |
actionGroups=action_groups, | |
instruction=instruction, | |
foundationModel=foundation_model, | |
inlineSessionState={ | |
"invocationId": invocation_id, | |
"returnControlInvocationResults": [return_control_result] | |
} | |
) | |
# Process the final response | |
_, response_text, _ = process_agent_response(response) | |
else: | |
print(f"\nWarning: Function {function_name} did not return a result") | |
# Print the agent's response | |
print("\nAssistant:", response_text.strip()) | |
except ClientError as e: | |
print(f"\nError: {e}") | |
except KeyboardInterrupt: | |
print("\n\nChat session interrupted. Goodbye!") | |
except Exception as e: | |
print(f"\nUnexpected error: {e}") | |
if __name__ == "__main__": | |
chat_with_agent() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment