Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
7a6d4e0
Add feedback functionality with interactive buttons and feedback storage
kris-szlapa Aug 27, 2025
d3a9e4b
Move imports to top of the file
kris-szlapa Aug 27, 2025
0ffe006
Add comments to feedback handlers
kris-szlapa Aug 27, 2025
75aa4f1
Move feedback messange above buttons
kris-szlapa Aug 27, 2025
9b6e659
Add feedback test suite
kris-szlapa Aug 27, 2025
b0efa00
Update test with feedback blocks
kris-szlapa Aug 27, 2025
71773a5
Add slack handlers test suite
kris-szlapa Aug 27, 2025
22c1d16
Add feedback handling to @mentions and fix feedback message processing
kris-szlapa Aug 27, 2025
a2d51ee
Remove app middleware from slack handler
kris-szlapa Aug 27, 2025
c1f0df2
Update feedback message
kris-szlapa Aug 27, 2025
236cb99
Merge branch 'main' of https://github.com/NHSDigital/eps-assist-me in…
kris-szlapa Aug 28, 2025
6544f9e
Add feedback button handlers and unify message handler
kris-szlapa Aug 29, 2025
373ccd0
Update comments
kris-szlapa Aug 29, 2025
7a0b3b8
Optimise function to handle whitespace by stripping once after regex …
kris-szlapa Sep 1, 2025
7cb5c4c
Add button text to BOT_MESSAGES dictionary
kris-szlapa Sep 1, 2025
f46b0c7
Replace hardcoded strings with constants across codebase
kris-szlapa Sep 1, 2025
1788e7d
Add TTL constants to config file
kris-szlapa Sep 1, 2025
003e0d8
Combine feedback handlers into a single unified handler
kris-szlapa Sep 1, 2025
2243a57
Merge branch 'main' of https://github.com/NHSDigital/eps-assist-me in…
kris-szlapa Sep 1, 2025
23b7755
Update feedback pattern
kris-szlapa Sep 2, 2025
5e24f82
Update function names in tests
kris-szlapa Sep 2, 2025
777f204
Ignore inactive button clicks and store feedback with latest_message_ts
kris-szlapa Sep 2, 2025
d39b31f
Update test suits
kris-szlapa Sep 2, 2025
0e2ba42
Remove log_event true from inject_lambda_context
kris-szlapa Sep 2, 2025
f6135c3
Add logic to clean up previous unfeedback Q&A before storing new one
kris-szlapa Sep 2, 2025
e7ceee0
Update test suites to improve coverage
kris-szlapa Sep 2, 2025
45b2c7a
Add more tests to suits
kris-szlapa Sep 2, 2025
e8e3298
Add tests for feedback logic
kris-szlapa Sep 2, 2025
47c7690
Remove the unused function parameter body
kris-szlapa Sep 2, 2025
8018e71
Refactor store_feedback_with_qa function to reduce its cognitive comp…
kris-szlapa Sep 2, 2025
c46c2b1
Delete previous Q&A pair if no feedback received using atomic operation
kris-szlapa Sep 3, 2025
be6f106
Remove unnecessary comment
kris-szlapa Sep 3, 2025
66d5f67
Remove data duplication by storing only references in feedback records
kris-szlapa Sep 4, 2025
afe9dcf
Allow multiple entries per user when text feedback
kris-szlapa Sep 4, 2025
1ef2405
Refactor comments in slack events code
kris-szlapa Sep 4, 2025
5623172
Add SlackBotFunction README
kris-szlapa Sep 4, 2025
4789dd9
Update conversation flow in README
kris-szlapa Sep 4, 2025
0bd7aad
Rename handlers for clarity
kris-szlapa Sep 4, 2025
ddadc9a
Reformat code and add comments
kris-szlapa Sep 4, 2025
c72216b
Post direct messages to channel instead of using threads and refactor…
kris-szlapa Sep 4, 2025
7b8911a
Add pattern to gitallowed
kris-szlapa Sep 4, 2025
63278f9
use different table
anthony-nhs Sep 10, 2025
1d206f3
Merge remote-tracking branch 'origin/main' into AEA-5696-add-feedback
anthony-nhs Sep 10, 2025
62ac7d8
Revert "use different table"
anthony-nhs Sep 10, 2025
73b3947
Merge branch 'main' into AEA-5696-add-feedback
anthony-nhs Sep 10, 2025
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
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[flake8]
max-line-length = 120
exclude = .*,venv,node_modules,cdk.out
max-complexity = 10
max-complexity = 12
ignore = F821,E203,W503
179 changes: 179 additions & 0 deletions packages/slackBotFunction/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# Slack Bot Function

AWS Lambda function that handles Slack interactions for the EPS Assist Me bot. Provides AI-powered responses to user queries about the NHS EPS API using Amazon Bedrock Knowledge Base.

## Architecture

- **Slack Bolt Framework**: Handles Slack events and interactions
- **Amazon Bedrock**: RAG-based AI responses using knowledge base
- **DynamoDB**: Session management and feedback storage
- **Async Processing**: Self-invoking Lambda for long-running AI queries

## User Interaction Patterns

### Starting Conversations

**Public Channels** - Mention the bot:
```
#general channel:
User: "@eps-bot What is EPS API?"
Bot: "EPS API is the Electronic Prescription Service..."
```

**Direct Messages** - Send message directly:
```
DM to @eps-bot:
User: "How do I authenticate with EPS?"
Bot: "Authentication requires..."
```

### Follow-up Questions

**In Channel Threads** - No @mention needed after initial conversation:
```
#general channel thread:
User: "@eps-bot What is EPS API?" ← Initial mention required
Bot: "EPS API is..."
User: "Can you explain more about authentication?" ← No mention needed
Bot: "Authentication works by..."
User: "What about error handling?" ← Still no mention needed
```

**In DMs** - Continue messaging naturally:
```
DM conversation:
User: "How do I authenticate?"
Bot: "Use OAuth 2.0..."
User: "What scopes do I need?" ← Natural follow-up
Bot: "Required scopes are..."
```

### Providing Feedback

**Button Feedback** - Click Yes/No on bot responses:
```
Bot: "EPS API requires OAuth authentication..."
[Yes] [No] ← Click buttons
```

**Text Feedback** - Use "feedback:" prefix anytime (applies to most recent bot response):
```
Bot: "EPS API requires OAuth authentication..."
User: "feedback: This was very helpful, thanks!"
User: "feedback: Could you add more error code examples?"
User: "feedback: The authentication section needs clarification"
```

## Handler Architecture

- **`mention_handler`**: Processes @mentions in public channels
- **`dm_message_handler`**: Handles direct messages to the bot
- **`thread_message_handler`**: Manages follow-up replies in existing threads
- **`feedback_handler`**: Processes Yes/No button clicks

### Conversation Flow
```
Channel:
User: "@eps-bot What is EPS?" ← mention_handler
Bot: "EPS is..." [Yes] [No]

├─ User clicks [Yes] ← feedback_handler
│ Bot: "Thank you for your feedback."
├─ User clicks [No] ← feedback_handler
│ Bot: "Please provide feedback:"
│ User: "feedback: Need more examples" ← thread_message_handler
│ Bot: "Thank you for your feedback."
└─ User: "Tell me more" ← thread_message_handler
Bot: "More details..." [Yes] [No]

DM:
User: "How do I authenticate?" ← dm_message_handler
Bot: "Use OAuth..." [Yes] [No]
User clicks [Yes/No] ← feedback_handler
Bot: "Thank you for your feedback."
User: "feedback: Could be clearer" ← dm_message_handler
Bot: "Thank you for your feedback."
User: "What scopes?" ← dm_message_handler
```

## Conversation Flow Rules

1. **Public channels**: Must @mention bot to start conversation
2. **Threads**: After initial @mention, no further mentions needed
3. **DMs**: No @mention required, direct messaging
4. **Feedback restrictions**:
- Only available on most recent bot response
- Cannot vote twice on same message (Yes/No)
- Cannot rate old messages after conversation continues
5. **Text feedback**: Use "feedback:" prefix anytime in conversation (multiple comments allowed)
- Feedback applies to the most recent bot message in the conversation

## Technical Implementation

### Event Processing Flow
```
Slack Event → Handler (3s timeout) → Async Lambda → Bedrock → Response
```

### Data Storage
- **Sessions**: 30-day TTL for conversation continuity
- **Q&A Pairs**: 90-day TTL for feedback correlation
- **Feedback**: 90-day TTL for analytics
- **Event Dedup**: 1-hour TTL for retry handling

### Privacy Features
- **Automatic cleanup**: Q&A pairs without feedback are deleted when new messages arrive (reduces data retention by 70-90%)
- **Data minimisation**: Configurable TTLs automatically expire old data
- **Secure credentials**: Slack tokens stored in AWS Parameter Store

### Feedback Protection
- **Latest message only**: Users can only rate the most recent bot response in each conversation
- **Duplicate prevention**: Users cannot vote twice on the same message (Yes/No buttons)
- **Multiple text feedback**: Users can provide multiple detailed comments using "feedback:" prefix

## Configuration

### Environment Variables
- `SLACK_BOT_TOKEN_PARAMETER`: Parameter Store path for bot token
- `SLACK_SIGNING_SECRET_PARAMETER`: Parameter Store path for signing secret
- `SLACK_BOT_STATE_TABLE`: DynamoDB table name
- `KNOWLEDGEBASE_ID`: Bedrock Knowledge Base ID
- `RAG_MODEL_ID`: Bedrock model ARN
- `GUARD_RAIL_ID`: Bedrock guardrail ID

### DynamoDB Schema
```
Primary Key: pk (partition key), sk (sort key)

Sessions: pk="thread#C123#1234567890", sk="session"
Q&A Pairs: pk="qa#thread#C123#1234567890#1234567891", sk="turn"
Feedback: pk="feedback#thread#C123#1234567890#1234567891", sk="user#U123"
Text Notes: pk="feedback#thread#C123#1234567890#1234567891", sk="user#U123#note#1234567892"
```

## Development

### Local Testing
```bash
# Install dependencies
npm install

# Run tests
npm test

# Deploy to dev environment
make cdk-deploy STACK_NAME=your-dev-stack
```

### Debugging
- Check CloudWatch logs for Lambda execution details
- Monitor DynamoDB for session and feedback data

## Monitoring

- **CloudWatch Logs**: `/aws/lambda/{stack-name}-SlackBotFunction`
- **DynamoDB Metrics**: Built-in AWS metrics for table operations

**Note**: No automated alerts configured. Uses AWS built-in metrics and manual log review.
28 changes: 27 additions & 1 deletion packages/slackBotFunction/app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from aws_lambda_powertools import Logger
from aws_lambda_powertools.utilities.parameters import get_parameter


# set up logging
logger = Logger(service="slackBotFunction")

Expand Down Expand Up @@ -60,8 +59,35 @@

logger.info("Guardrail configuration loaded", extra={"guardrail_id": GUARD_RAIL_ID, "guardrail_version": GUARD_VERSION})

# Constants
FEEDBACK_PREFIX = "feedback:"
CONTEXT_TYPE_DM = "DM"
CONTEXT_TYPE_THREAD = "thread"
CHANNEL_TYPE_IM = "im"
SESSION_SK = "session"
DEDUP_SK = "dedup"
EVENT_PREFIX = "event#"
FEEDBACK_PREFIX_KEY = "feedback#"
USER_PREFIX = "user#"
DM_PREFIX = "dm#"
THREAD_PREFIX = "thread#"
NOTE_SUFFIX = "#note#"

# TTL constants (in seconds)
TTL_EVENT_DEDUP = 3600 # 1 hour
TTL_FEEDBACK = 7776000 # 90 days
TTL_SESSION = 2592000 # 30 days

# Bot response messages
BOT_MESSAGES = {
"empty_query": "Hi there! Please ask me a question and I'll help you find information from our knowledge base.",
"error_response": "Sorry, an error occurred while processing your request. Please try again later.",
"feedback_positive_thanks": "Thank you for your feedback.",
"feedback_negative_thanks": (
'Please let us know how the answer could be improved. Start your message with "feedback:"'
),
"feedback_thanks": "Thank you for your feedback.",
"feedback_prompt": "Was this helpful?",
"feedback_yes": "Yes",
"feedback_no": "No",
}
1 change: 1 addition & 0 deletions packages/slackBotFunction/app/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
setup_handlers(app)


@logger.inject_lambda_context()
def handler(event: dict, context: LambdaContext) -> dict:
"""
Main Lambda entry point - routes between Slack webhook and async processing
Expand Down
Loading