Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding inline hr assist application code #475

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Dynamic AI Assistant Demo using Amazon Bedrock Inline Agents

This demo showcases Amazon Bedrock's Inline Agents capability through a simple HR scenario with a Flask-based web interface. It demonstrates how a single AI assistant can dynamically adapt its capabilities, tools, and knowledge access based on user roles (Employee/Manager) at runtime.

![HR Assistant UI](./images/hr-assistant-user-interface.png)

## Demo Overview

The demo implements an HR Assistant that provides:
- **For Employees**: Access to vacation management, expense reports, and basic policy information
- **For Managers**: Additional access to compensation review and confidential HR policies
- **For All Users**: Code interpreter for data analysis

This demonstrates how Inline Agents can:
- Dynamically modify available tools during runtime
- Filter knowledge base access based on user roles
- Adapt to different user personas without redeploying the agent

## Architecture

![Architecture Diagram](./images/hr-assist-inline-agents-bedrock.png)

## Prerequisites

1. **AWS Account** with access to Amazon Bedrock
2. **AWS CLI** configured with appropriate permissions
3. **Python 3.12+**
4. **Amazon Bedrock Model Access to**:
- Anthropic Claude Models
- Amazon Nova Models

## Resource Setup

Run `prerequisite.py` to create required AWS resources:

```bash
python prerequisite.py
```

This creates:

**1. Lambda Functions:**

* Compensation Management: Handles salary and bonus requests
* Vacation Management: Processes time-off requests
* Budget Management: Handles expense approvals

**2. Knowledge Base:**
* Creates a Knowledge Base with sample HR policies
* Uploads policy documents with role-based metadata
* Configures access filtering based on user roles

## Run flask application
Run `flask-application.py` and open the url in the browser to view and interact with application:

```bash
python flask-application.py
```

## Delete resource
```bash
python delete_resources.py
```

## License
This project is licensed under the MIT License - see the LICENSE file for details.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import json
import logging
import traceback

# Set up logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
try:
# Log the entire incoming event
logger.info(f"Received event: \n{json.dumps(event, indent=2)}")

# Log the type and structure of parameters
logger.info(f"Parameters type: {type(event.get('parameters'))}")
logger.info(f"Parameters content: {json.dumps(event.get('parameters', []), indent=2)}")

# Extract amount with detailed logging
amount = None
parameters = event.get('parameters', [])

logger.info(f"Processing parameters list of length: {len(parameters)}")

for param in parameters:
logger.info(f"Processing parameter: {param}")
if param.get('name') == 'amount':
try:
amount = float(param.get('value', 0))
logger.info(f"Successfully extracted amount: {amount}")
except ValueError as ve:
logger.error(f"Error converting amount to float: {str(ve)}")
raise
break

if amount is None:
logger.error("Amount parameter not found in the request")
raise ValueError("Amount parameter not found in the request")

# Make decision based on threshold
status = "not approved" if amount > 300 else "approved"
logger.info(f"Decision made: {status} for amount {amount}")

body = {
'status': status,
'amount': amount
}

# Build response
response_body = {
'application/json': {
'body': json.dumps(body)
}
}

action_response = {
'actionGroup': event.get('actionGroup', 'FetchDetails'),
'apiPath': event.get('apiPath', '/fetch'),
'httpMethod': event.get('httpMethod', 'GET'),
'httpStatusCode': 200,
'responseBody': response_body
}

response = {'response': action_response}

# Log the final response
logger.info(f"Returning successful response: \n{json.dumps(response, indent=2)}")
return response

except Exception as e:
# Log the full stack trace
logger.error(f"Exception occurred: {str(e)}")
logger.error(f"Stack trace: {traceback.format_exc()}")

error_body = {
'status': 'error',
'amount': 0,
'message': str(e)
}

response_body = {
'application/json': {
'body': json.dumps(error_body)
}
}

action_response = {
'actionGroup': event.get('actionGroup', 'FetchDetails'),
'apiPath': event.get('apiPath', '/fetch'),
'httpMethod': event.get('httpMethod', 'GET'),
'httpStatusCode': 400,
'responseBody': response_body
}

error_response = {'response': action_response}
logger.info(f"Returning error response: \n{json.dumps(error_response, indent=2)}")
return error_response
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import json
import logging
import random
import string

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def generate_ticket_number():
"""Generate a random ticket number in the format: 8 random characters + 3 numbers"""
# Generate 8 random letters
letters = ''.join(random.choices(string.ascii_uppercase + string.ascii_lowercase, k=8))
# Generate 3 random numbers
numbers = ''.join(random.choices(string.digits, k=3))
return f"{letters}{numbers}"

def lambda_handler(event, context):
try:
logger.info(f"Received event: \n{json.dumps(event, indent=2)}")

# Generate dynamic ticket number
ticket_number = generate_ticket_number()

# Message with dynamic ticket number
message = f"Your request has been submitted and a ticket has been created for the HR compensation team. You can visit the ticket or ask me for an update on the ticket. Ticket number: {ticket_number}"

body = {
'status': 'success',
'message': message,
'ticket_number': ticket_number # Including ticket number separately if needed
}

# Build response
response_body = {
'application/json': {
'body': json.dumps(body)
}
}

action_response = {
'actionGroup': event.get('actionGroup', 'CompensationManagement'),
'apiPath': event.get('apiPath', '/compensation'),
'httpMethod': event.get('httpMethod', 'POST'),
'httpStatusCode': 200,
'responseBody': response_body
}

final_response = {'response': action_response}
logger.info(f"Returning successful response: \n{json.dumps(final_response, indent=2)}")
return final_response

except Exception as e:
logger.error(f"Exception occurred: {str(e)}")

error_body = {
'status': 'error',
'message': str(e)
}

response_body = {
'application/json': {
'body': json.dumps(error_body)
}
}

action_response = {
'actionGroup': event.get('actionGroup', 'CompensationManagement'),
'apiPath': event.get('apiPath', '/compensation'),
'httpMethod': event.get('httpMethod', 'POST'),
'httpStatusCode': 400,
'responseBody': response_body
}

error_response = {'response': action_response}
logger.info(f"Returning error response: \n{json.dumps(error_response, indent=2)}")
return error_response
Loading