diff --git a/README.md b/README.md index 9000a8657..782f3d5f1 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,61 @@ # EPS Assist Me -![Build](https://github.com/NHSDigital/eps-assist-me/workflows/release/badge.svg?branch=main) -This is a Slack-based AI assistant that helps people query and understand documents relating to onboarding to the FHIR NHS EPS API (used for prescriptions and dispensing). The assistant uses Amazon Bedrock Knowledge Base with OpenSearch Serverless to provide intelligent responses to user queries through Slack slash commands. +![Build](https://github.com/NHSDigital/eps-assist-me/actions/workflows/ci.yml/badge.svg?branch=main) -## Architecture +EPS Assist Me is a Slack-based AI assistant that answers questions about onboarding to and implementation of the NHS Electronic Prescription Service (EPS) APIs. +It uses Amazon Bedrock Knowledge Base for RAG to search relevant documentation and generate responses based off it. -The solution consists of: +## What This Is -- **Slack Bot Function**: AWS Lambda function that handles Slack slash commands and integrates with Amazon Bedrock Knowledge Base -- **Create Index Function**: AWS Lambda function that creates and manages OpenSearch vector indices for the knowledge base -- **Sync Knowledge Base Function**: AWS Lambda function that automatically triggers knowledge base ingestion when documents are uploaded to S3 -- **OpenSearch Serverless**: Vector database for storing and searching document embeddings -- **Amazon Bedrock Knowledge Base**: RAG (Retrieval-Augmented Generation) service with guardrails -- **S3 Storage**: Document storage for the knowledge base with automatic sync triggers -- **AWS CDK**: Infrastructure as Code for deployment +An AI assistant deployed as a set of AWS Lambda functions behind a Slack integration. +Users ask questions in Slack. The bot retrieves relevant content from a knowledge base and responds. + +The knowledge base is populated by uploading documents to S3. +Documents are automatically converted, ingested, and made searchable. + +## Architecture Overview + +![EPS Assist Me Flowchart](images/eps-assist-me-flowchart.png) + +Four Lambda functions deployed via a single CDK stack. Responsibilities are strictly separated between Slack event handling, document conversion, automated ingestion, and Bedrock logging which is a standalone function, created to toggle logging on and off as needed. ## Project Structure -This is a monorepo with the following structure: +- `packages/cdk/` CDK infrastructure, as a single stack containing all resources required for the secure functioning of the bot. Includes Bedrock prompt templates in `prompts/` directory. +- `packages/slackBotFunction/` Handles Slack events - mentions, DMs, threads, and user feedback. It queries Bedrock to retrieve relevant content and generate responses. +- `packages/preprocessingFunction/` Converts uploaded documents (PDF, DOCX, etc.) to Markdown for ingestion. +- `packages/syncKnowledgeBaseFunction/` Triggers knowledge base ingestion when processed documents land in S3. Notifies Slack when ingestion starts. +- `packages/bedrockLoggingConfigFunction/` CloudFormation custom resource for Bedrock model invocation logging. +- `packages/sample_docs/` Test documents. Not for real usage. +- `scripts/` Developer utilities (doc sync, regression tests). +- `.devcontainer` Dockerfile and VS Code devcontainer definition. +- `.github` CI/CD workflows, actions, and scripts. +- `.vscode` Workspace file. -``` -packages/ -├── cdk/ # AWS CDK infrastructure code -│ ├── bin/ # CDK app entry point -│ │ └── utils/ # CDK utility functions -│ ├── constructs/ # Reusable CDK constructs -│ │ └── RestApiGateway/ # API Gateway specific constructs -│ ├── resources/ # AWS resource definitions -│ └── stacks/ # CDK stack definitions -├── sample_docs/ # Contains sample docs for testing purposes. These should not be used for real usage -├── slackBotFunction/ # Lambda function for Slack bot integration -│ ├── app/ # Application code -│ │ ├── config/ # Configuration and environment variables -│ │ ├── services/ # Business logic services -│ │ ├── slack/ # Slack-specific logic -│ │ └── handler.py # Lambda handler -│ └── tests/ # Unit tests -└── syncKnowledgeBaseFunction/ # Lambda function for automatic knowledge base sync - ├── app/ # Application code - │ ├── config/ # Configuration and environment variables - │ └── handler.py # Lambda handler - └── tests/ # Unit tests -``` +## Running Locally -## Contributing +Use VS Code with a devcontainer. It installs all required tools and correct versions. +See [devcontainer docs](https://code.visualstudio.com/docs/devcontainers/containers) for host setup. -Contributions to this project are welcome from anyone, providing that they conform to the [guidelines for contribution](https://github.com/NHSDigital/eps-assist-me/blob/main/CONTRIBUTING.md) and the [community code of conduct](https://github.com/NHSDigital/eps-assist-me/blob/main/CODE_OF_CONDUCT.md). +```bash +# install everything +make install -### Licensing +# configure AWS SSO (first time only) +make aws-configure +# region: eu-west-2, use hscic credentials, select dev account -This code is dual licensed under the MIT license and the OGL (Open Government License). Any new work added to this repository must conform to the conditions of these licenses. In particular this means that this project may not depend on GPL-licensed or AGPL-licensed libraries, as these would violate the terms of those libraries' licenses. +# verify CDK compiles +make cdk-synth -The contents of this repository are protected by Crown Copyright (C). +# run all tests +make test -## Development +# deploy +STACK_NAME=your-stack-name make cdk-deploy +``` -It is recommended that you use visual studio code and a devcontainer as this will install all necessary components and correct versions of tools and languages. -See https://code.visualstudio.com/docs/devcontainers/containers for details on how to set this up on your host machine. -There is also a workspace file in .vscode that should be opened once you have started the devcontainer. The workspace file can also be opened outside of a devcontainer if you wish. +Token expired? `make aws-login` All commits must be made using [signed commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits) @@ -82,146 +80,85 @@ This will ensure that your VSCode bash terminal prompts you for your GPG key pas You can cache the gpg key passphrase by following instructions at https://superuser.com/questions/624343/keep-gnupg-credentials-cached-for-entire-user-session -### Setup - -Ensure you have the following lines in the file .envrc - -```bash -export AWS_DEFAULT_PROFILE=prescription-dev -``` - -Once you have saved .envrc, start a new terminal in vscode and run this command to authenticate against AWS - -```bash -make aws-configure -``` - -Put the following values in: - -```text -SSO session name (Recommended): sso-session -SSO start URL [None]: -SSO region [None]: eu-west-2 -SSO registration scopes [sso:account:access]: -``` - -This will then open a browser window and you should authenticate with your hscic credentials -You should then select the development account and set default region to be eu-west-2. - -You will now be able to use AWS and SAM CLI commands to access the dev account. You can also use the AWS extension to view resources. - -When the token expires, you may need to reauthorise using `make aws-login` - -### Environment Variables - -For deployment, the following environment variables are required: +## Environment Variables + +Required for deployment: + +| Variable | Purpose | +|---|---| +| `STACK_NAME` | CloudFormation stack name | +| `ACCOUNT_ID` | AWS Account ID | +| `VERSION_NUMBER` | Deployment version | +| `COMMIT_ID` | Git commit ID | +| `LOG_RETENTION_IN_DAYS` | CloudWatch log retention period | +| `SLACK_BOT_TOKEN` | Slack bot OAuth token | +| `SLACK_SIGNING_SECRET` | Slack app signing secret | + +## Make Commands + +| Command | What it does | +|---|---| +| `make install` | Install all dependencies (Node, Python, pre-commit hooks) | +| `make test` | Run unit tests for all Lambda functions | +| `make lint` | Run all linters (Black, Flake8, ESLint, actionlint, ShellCheck) | +| `make cdk-synth` | Synthesise CDK to CloudFormation templates | +| `make cdk-deploy` | Build and deploy to AWS (requires `STACK_NAME`) | +| `make cdk-diff` | Compare deployed stack with local CDK code | +| `make cdk-watch` | Live-sync code and CDK changes to AWS | +| `make sync-docs` | Sync sample docs to S3 for a PR stack | +| `make convert-docs` | Convert all documents in `raw_docs/` to Markdown locally | +| `make convert-docs-file` | Convert a single file. Usage: `FILE=doc.pdf make convert-docs-file` | +| `make clean` | Remove build artifacts and test coverage | +| `make deep-clean` | Clean + remove `node_modules` and `.venv` | + +## CI Setup + +GitHub Actions require a repo secret called `SONAR_TOKEN`. +Get it from [SonarCloud](https://sonarcloud.io/) - you need "Execute Analysis" permission on `NHSDigital_eps-assist-me`. + +Pre-commit hooks are installed via `make install-hooks` and configured in `.pre-commit-config.yaml`. +Same checks run in CI. + +## GitHub Folder + +- `dependabot.yml` Dependabot definition. +- `pull_request_template.md` PR template. + +Actions: + +- `mark_jira_released` Marks Jira issues as released. +- `sync_documents` Syncs documents to S3 for knowledge base ingestion. +- `update_confluence_jira` Updates Confluence with Jira issues. + +Scripts: + +- `call_mark_jira_released.sh` Calls Lambda to mark Jira issues released. +- `check-sbom-issues-against-ignores.sh` Validates SBOM scan against ignore list. +- `create_env_release_notes.sh` Generates environment release notes. +- `create_int_rc_release_notes.sh` Creates integration release candidate notes. +- `delete_stacks.sh` Deletes CloudFormation stacks for closed PRs. +- `find_s3_bucket.sh` Finds S3 bucket for a CloudFormation stack. +- `fix_cdk_json.sh` Updates `cdk.json` context values before deployment. +- `get_current_dev_tag.sh` Gets current dev tag. +- `get_target_deployed_tag.sh` Gets currently deployed tag. + +Workflows: + +- `ci.yml` Merge to main. Deploys to DEV and QA after quality checks. +- `pull_request.yml` PR opened/updated. Packages and deploys to dev account. +- `release.yml` On-demand release to INT and PROD with manual approval. +- `release_all_stacks.yml` Reusable deployment workflow with environment-specific config. +- `cdk_package_code.yml` Packages code into Docker image as GitHub artifact. +- `create_release_notes.yml` Generates deployment release notes. +- `delete_old_cloudformation_stacks.yml` Daily cleanup of old stacks. +- `run_regression_tests.yml` Runs regression tests against a deployed stack. -- `ACCOUNT_ID`: AWS Account ID -- `STACK_NAME`: Name of the CloudFormation stack -- `VERSION_NUMBER`: Version number for the deployment -- `COMMIT_ID`: Git commit ID -- `LOG_RETENTION_IN_DAYS`: CloudWatch log retention period -- `SLACK_BOT_TOKEN`: Slack bot OAuth token -- `SLACK_SIGNING_SECRET`: Slack app signing secret - -### CI Setup - -The GitHub Actions require a secret to exist on the repo called "SONAR_TOKEN". -This can be obtained from [SonarCloud](https://sonarcloud.io/) -as described [here](https://docs.sonarsource.com/sonarqube/latest/user-guide/user-account/generating-and-using-tokens/). -You will need the "Execute Analysis" permission for the project (NHSDigital_eps-assist-me) in order for the token to work. - -### Pre-commit hooks - -Some pre-commit hooks are installed as part of the install above, to run basic lint checks and ensure you can't accidentally commit invalid changes. -The pre-commit hook uses python package pre-commit and is configured in the file .pre-commit-config.yaml. -A combination of these checks are also run in CI. - -### Make commands - -There are `make` commands that are run as part of the CI pipeline and help alias some functionality during development. - -#### Install targets - -- `install-node` Installs node dependencies. -- `install-python` Installs python dependencies. -- `install-hooks` Installs git pre commit hooks. -- `install` Runs all install targets. - -#### CDK targets -These are used to do common commands related to cdk - -- `cdk-deploy` Builds and deploys the code to AWS. Requires `STACK_NAME` environment variable. -- `cdk-synth` Converts the CDK code to cloudformation templates. -- `cdk-diff` Runs cdk diff, comparing the deployed stack with the local CDK code to identify differences. -- `cdk-watch` Syncs the code and CDK templates to AWS. This keeps running and automatically uploads changes to AWS. Requires `STACK_NAME` environment variable. - -#### Clean and deep-clean targets - -- `clean` Clears up any files that have been generated by building or testing locally. -- `deep-clean` Runs clean target and also removes any node_modules and python libraries installed locally. - -#### Linting and testing - -- `lint` Runs all linting checks -- `lint-black` Runs black formatter on Python code. -- `lint-flake8` Runs flake8 linter on Python code. -- `lint-githubactions` Lints the repository's GitHub Actions workflows. -- `lint-githubaction-scripts` Lints all shell scripts in `.github/scripts` using ShellCheck. -- `cfn-guard` Runs cfn-guard against CDK resources. -- `git-secrets-docker-setup` Sets up git-secrets Docker container. -- `pre-commit` Runs pre-commit hooks on all files. -- `test` Runs unit tests for Lambda functions. -- `sync-docs` Runs a script to sync sample docs to s3 bucket for a pull request. Useful for setting up a stack for testing - -#### Compiling - -- `compile-node` Runs TypeScript compiler (tsc) for the project. - -#### Check licenses - -- `check-licenses` Checks licenses for all packages. This command calls both check-licenses-node and check-licenses-python. -- `check-licenses-node` Checks licenses for all node code. -- `check-licenses-python` Checks licenses for all python code. - -#### CLI Login to AWS - -- `aws-configure` Configures a connection to AWS. -- `aws-login` Reconnects to AWS using a previously configured connection. - -### GitHub folder - -This .github folder contains workflows and templates related to GitHub, along with actions and scripts pertaining to Jira. - -- `dependabot.yml` Dependabot definition file. -- `pull_request_template.md` Template for pull requests. - -Actions are in the `.github/actions` folder: - -- `mark_jira_released` Action to mark Jira issues as released. -- `update_confluence_jira` Action to update Confluence with Jira issues. +## Contributing -Scripts are in the `.github/scripts` folder: +Contributions to this project are welcome from anyone, providing that they conform to the [contribution guidelines](https://github.com/NHSDigital/eps-assist-me/blob/main/CONTRIBUTING.md) and [code of conduct](https://github.com/NHSDigital/eps-assist-me/blob/main/CODE_OF_CONDUCT.md). -- `call_mark_jira_released.sh` Calls a Lambda function to mark Jira issues as released. -- `check-sbom-issues-against-ignores.sh` Validates SBOM scan against ignore list and reports unignored critical issues. -- `create_env_release_notes.sh` Generates release notes for a specific environment using a Lambda function. -- `create_int_rc_release_notes.sh` Creates release candidate notes for integration environment using a Lambda function. -- `delete_stacks.sh` Checks and deletes active CloudFormation stacks associated with closed pull requests. -- `fix_cdk_json.sh` Updates context values in `cdk.json` using environment variables before deployment. -- `get_current_dev_tag.sh` Retrieves the current development tag and sets it as an environment variable. -- `get_target_deployed_tag.sh` Retrieves the currently deployed tag and sets it as an environment variable. +### Licensing -Workflows are in the `.github/workflows` folder: +This code is dual licensed under the MIT license and the OGL (Open Government License).Any new work added to this repository must conform to the conditions of these licenses. In particular this means that this project may not depend on GPL-licensed or AGPL-licensed libraries, as these would violate the terms of those libraries' licenses. -- `combine-dependabot-prs.yml` Workflow for combining dependabot pull requests. Runs on demand. -- `create_release_notes.yml` Generates release notes for deployments and environment updates. -- `delete_old_cloudformation_stacks.yml` Workflow for deleting old cloud formation stacks. Runs daily. -- `dependabot_auto_approve_and_merge.yml` Workflow to auto merge dependabot updates. -- `pr_title_check.yml` Checks PR titles for required prefix and ticket or dependabot reference. -- `pr-link.yml` This workflow template links Pull Requests to Jira tickets and runs when a pull request is opened. -- `pull_request.yml` Called when pull request is opened or updated. Packages and deploys the code to dev AWS account for testing. -- `release_all_stacks.yml` Reusable workflow for deploying to any environment with environment-specific approvals and configurations. -- `release.yml` Runs on demand to create a release and deploy to INT and PROD environments with manual approval. -- `cdk_package_code.yml` Packages code into a docker image and uploads to a github artifact for later deployment. -- `ci.yml` Merge to main workflow that automatically deploys to DEV and QA environments after quality checks. +The contents of this repository are protected by Crown Copyright (C). diff --git a/images/eps-assist-me-flowchart.png b/images/eps-assist-me-flowchart.png new file mode 100644 index 000000000..f81e586d1 Binary files /dev/null and b/images/eps-assist-me-flowchart.png differ diff --git a/packages/bedrockLoggingConfigFunction/README.md b/packages/bedrockLoggingConfigFunction/README.md new file mode 100644 index 000000000..83655930d --- /dev/null +++ b/packages/bedrockLoggingConfigFunction/README.md @@ -0,0 +1,35 @@ +# Bedrock Logging Config Function + +CloudFormation custom resource Lambda that configures Bedrock model invocation logging to CloudWatch. + +## What This Is + +A bridge resource. +AWS CloudFormation currently has no native resource type for configuring Bedrock logging. This Lambda bridges that gap by using the Bedrock API directly during stack deployment/update/deletion. + +## What This Is Not + +- Not a runtime dependency - it only executes during CDK/CloudFormation deployments +- Not the log consumer - it only tells Bedrock *where* to send logs + +## Architecture Overview + +```mermaid +flowchart LR + CloudFormation -->|Create/Update/Delete| ConfigLambda[bedrockLoggingConfigFunction] + ConfigLambda -->|Put/Delete Logging Config| BedrockAPI[Bedrock API] +``` + +## Environment Variables + +Configured by CDK based on stack parameters. + +| Variable | Purpose | +|---|---| +| `ENABLE_LOGGING` | Toggle for enabling/disabling logs (`true` or `false`) | +| `CLOUDWATCH_LOG_GROUP_NAME` | Destination CloudWatch Log Group | +| `CLOUDWATCH_ROLE_ARN` | IAM Role allowing Bedrock to write to CloudWatch | + +## Known Constraints + +- It affects the Bedrock logging configuration for the *entire AWS region/account* where deployed. If another stack tries to modify Bedrock logging in the same account/region, they will overwrite each other. diff --git a/packages/cdk/README.md b/packages/cdk/README.md new file mode 100644 index 000000000..2f34ffcde --- /dev/null +++ b/packages/cdk/README.md @@ -0,0 +1,49 @@ +# CDK Infrastructure + +AWS Cloud Development Kit (CDK) application defining the EPS Assist Me infrastructure. + +## What This Is + +The single source of truth for the project's cloud resources. +Provisions the entire bot ecosystem in one deployable stack. + +## Architecture + +Provisions: + +- **API Gateway** - Receives Slack events +- **Lambda Functions** - `slackBotFunction`, `preprocessingFunction`, `syncKnowledgeBaseFunction`, `notifyS3UploadFunction`, `bedrockLoggingConfigFunction` +- **Amazon Bedrock** - Knowledge Base and Data Source configuration +- **OpenSearch Serverless** - Vector database for RAG document embeddings +- **S3 Buckets** - Raw and processed document storage with event notifications +- **DynamoDB** - Bot session state and feedback storage +- **SQS** - Queue for asynchronous processing of document events +- **IAM Roles** - Least-privilege access across services + +## Project Structure + +- `bin/` CDK app entry point (`EpsAssistMeApp.ts`) +- `constructs/` Reusable Layer 3 (L3) components (e.g. `RestApiGateway`, `LambdaFunction`, `DynamoDbTable`) +- `resources/` L2/L1 definitions grouped by domain (e.g. `VectorKnowledgeBaseResources`, `OpenSearchResources`) +- `stacks/` The actual CloudFormation stack definition (`EpsAssistMeStack`) +- `prompts/` Text templates used to construct Bedrock prompts (System, User, Reformulation) + +## Environment Variables + +Configured in the stack context (`cdk.json` or via CLI). + +| Variable | Purpose | +|---|---| +| `accountId` | Target AWS Account ID | +| `stackName` | CloudFormation stack name | +| `versionNumber` | Stack version | +| `commitId` | Hash for tagging | +| `logRetentionInDays` | CloudWatch retention policy | +| `slackBotToken` | The OAuth token from Slack | +| `slackSigningSecret` | The signing secret from Slack | + +## Deployment Notes + +- Deployment uses context variables passed during synthesis (`cdk synth --context...`) +- OpenSearch Serverless collections can take around 5-10 minutes to provision +- The Bedrock data source ingestion relies on IAM permissions that might occasionally have propagation delays on first deploy diff --git a/packages/preprocessingFunction/README.md b/packages/preprocessingFunction/README.md new file mode 100644 index 000000000..7c9475b77 --- /dev/null +++ b/packages/preprocessingFunction/README.md @@ -0,0 +1,59 @@ +# Preprocessing Function + +Lambda that converts raw uploaded documents into Markdown format for Bedrock Knowledge Base ingestion. +Runs sequentially when new documents land in the raw S3 bucket prefix. + +## What This Is + +A document standardisation step. +Converts `.pdf`, `.doc`, `.docx`, `.xls`, `.xlsx`, and `.csv` files into `.md`. +Passes through `.txt`, `.md`, `.html`, and `.json` files untouched. + +Output is written to the processed S3 bucket prefix, ready for ingestion. + +## Architecture Overview + +```mermaid +flowchart LR + S3Raw["S3 (raw/)"] -->|event| Preprocessing[preprocessingFunction] + Preprocessing -->|convert/copy| S3Processed["S3 (processed/)"] +``` + +Downloads from `raw/`, converts to Markdown locally (in a secure temp directory), and uploads to `processed/`. + +## Project Structure + +- `app/handler.py` Lambda entry point. Processes S3 records. +- `app/config/config.py` Configuration and environment variables. +- `app/services/` Conversion logic (`converter.py`) and S3 helpers (`s3_client.py`). +- `app/cli.py` Local CLI wrapper for convert-docs. +- `tests/` Unit tests. + +## Environment Variables + +Set by CDK. + +| Variable | Purpose | +|---|---| +| `DOCS_BUCKET_NAME` | S3 bucket containing the documents | +| `RAW_PREFIX` | Prefix where raw uploads land (e.g. `raw/`) | +| `PROCESSED_PREFIX` | Prefix where Markdown output goes (e.g. `processed/`) | +| `AWS_ACCOUNT_ID` | AWS Account ID | + +## Running Tests + +```bash +cd packages/preprocessingFunction +PYTHONPATH=. poetry run python -m pytest +``` + +Or from the repo root: + +```bash +make test +``` + +## Known Constraints + +- Complex PDFs (heavy formatting, multi-column layouts) may produce imperfect Markdown +- Runs sequentially per uploaded file - large batch uploads may take time to process diff --git a/packages/slackBotFunction/README.md b/packages/slackBotFunction/README.md index 052800e8e..c845d6e22 100644 --- a/packages/slackBotFunction/README.md +++ b/packages/slackBotFunction/README.md @@ -1,179 +1,77 @@ # 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. +Lambda that handles all Slack interactions for the EPS Assist Me bot. +Receives events from Slack, queries Bedrock Knowledge Base, returns AI-generated responses. -## Architecture +## What This Is -- **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 +The core bot logic. Handles: -## User Interaction Patterns +- `@mentions` in public channels +- direct messages +- thread follow-ups (no re-mention needed) +- feedback (Yes/No buttons and `feedback:` text prefix) -### Starting Conversations +One Lambda. Uses a self-invoking async pattern to handle heavy processing while still acknowledging Slack's 3-second response timeout. -**Public Channels** - Mention the bot: -``` -#general channel: -User: "@eps-bot What is EPS API?" -Bot: "EPS API is the Electronic Prescription Service..." -``` +## What This Is Not -**Direct Messages** - Send message directly: -``` -DM to @eps-bot: -User: "How do I authenticate with EPS?" -Bot: "Authentication requires..." -``` +- Not the infrastructure - that's in `packages/cdk/` +- Not the document ingestion pipeline - that's `preprocessingFunction` and `syncKnowledgeBaseFunction` +- Not the upload notifier - that's `notifyS3UploadFunction` -### 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 -``` +## Architecture Overview -**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" +```mermaid +flowchart LR + SlackEvent[Slack Event] -->|3s timeout| Handler + Handler -->|async| SelfInvoke[Self-invoke] + SelfInvoke --> Bedrock[Bedrock KB] + Bedrock --> Response[Slack Response] + Handler --> DynamoDB[DynamoDB] ``` -## Handler Architecture +- **Slack Bolt** for event handling +- **Bedrock Knowledge Base** for RAG responses with guardrails +- **DynamoDB** for session state and feedback storage -- **`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 +## Project Structure -### 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 -``` +- `app/handler.py` Lambda entry point. +- `app/core/` Configuration and environment variables. +- `app/services/` Business logic - Bedrock client, DynamoDB, Slack client, prompt loading, AI processing. +- `app/slack/` Event handlers - mentions, DMs, threads, feedback. +- `app/utils/` Shared utilities. +- `tests/` Unit tests. -## Conversation Flow Rules +## Environment Variables -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 +Set by CDK. Don't hardcode these. -## Technical Implementation +| Variable | Purpose | +|---|---| +| `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 | -### Event Processing Flow -``` -Slack Event → Handler (3s timeout) → Async Lambda → Bedrock → Response -``` +## Running Tests -### 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" +```bash +cd packages/slackBotFunction +PYTHONPATH=. poetry run python -m pytest ``` -## Development +Or from the repo root: -### Local Testing ```bash -# Install dependencies -npm install - -# Run tests -npm test - -# Deploy to dev environment -make cdk-deploy STACK_NAME=your-dev-stack +make test ``` -### 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 +## Known Constraints -**Note**: No automated alerts configured. Uses AWS built-in metrics and manual log review. +- Slack enforces a 3-second response window. A quick acknowledgement is required, but how the subsequent background processing is handled (like the async self-invoke pattern) is an architectural choice. +- Bedrock guardrails can block legitimate queries if they hit content filters - check CloudWatch logs +- Session state lives in DynamoDB with TTLs - conversations expire after 30 days diff --git a/packages/syncKnowledgeBaseFunction/README.md b/packages/syncKnowledgeBaseFunction/README.md new file mode 100644 index 000000000..96e49259b --- /dev/null +++ b/packages/syncKnowledgeBaseFunction/README.md @@ -0,0 +1,42 @@ +# Sync Knowledge Base Function + +Lambda that automatically triggers Bedrock Knowledge Base ingestion when new documents are ready. +Ensures the AI assistant has access to the latest documentation. + +## What This Is + +The ingestion trigger. +Listens for S3 events from the `processed/` prefix (via SQS). Once a batch of converted documents lands, it starts a Bedrock Data Source ingestion job. + +It also notifies Slack that ingestion has started. + +## Architecture Overview + +```mermaid +flowchart LR + S3Processed["S3 (processed/)"] -->|event| SQS + SQS --> Sync[syncKnowledgeBaseFunction] + Sync -->|start ingestion job| Bedrock[Bedrock Data Source] + Sync -->|notify| Slack +``` + +Batch processing via SQS prevents overwhelming the Bedrock API with multiple parallel ingestion jobs. + +## Project Structure + +- `app/handler.py` Lambda entry point. Processes SSQS/S3 records. +- `app/config/` Configuration and environment variables. + +## Environment Variables + +Set by CDK. + +| Variable | Purpose | +|---|---| +| `KNOWLEDGEBASE_ID` | Target Bedrock Knowledge Base ID | +| `DATA_SOURCE_ID` | Target Bedrock Data Source ID | + +## Known Constraints + +- If an ingestion job is already running, Bedrock returns a `ConflictException`. The lambda catches this and handles it safely, usually letting subsequent retries succeed when the first job finishes. +- Only file types supported by Bedrock (`.pdf`, `.txt`, `.md`, `.csv`, `.docx`, etc.) trigger ingestion.