├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── aws-creds.py
├── frontend
├── .gitignore
├── README.md
├── babel.config.js
├── package.json
├── parameters.json
├── public
│ └── index.html
└── src
│ ├── App.vue
│ ├── app.css
│ ├── aws-exports.js
│ ├── components
│ └── NotFoundPage.vue
│ ├── main.js
│ ├── router.js
│ ├── utils
│ ├── auth.js
│ └── core.js
│ └── views
│ ├── AboutView.vue
│ ├── Home.vue
│ ├── KB.vue
│ ├── LLMs.vue
│ ├── LoginView.vue
│ ├── PromptView.vue
│ └── RAG.vue
├── lambdas
├── bedrockFunc
│ ├── cognitouser.py
│ └── requirements.txt
├── llmFunctions
│ ├── kbfunction.py
│ └── llmfunction.py
└── ragFunctions
│ ├── promptfunction.py
│ ├── ragfunction.py
│ └── requirements.txt
├── prompt-engineering
├── claude-prompt-template.txt
├── llama-prompt-template.txt
└── mistral-prompt-template.txt
├── startup.sh
├── template.yaml
└── tools
├── account-setup.sh
├── gen-ai-builder-init v1.1.yml
├── gen-ai-builder-init-v1.2.yaml
├── resize.sh
├── samconf.toml
├── solutions
├── kbfunction.py
├── llmfunction.py
└── ragfunction.py
└── troubleshoot.sh
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .DS_Store
3 | *.toml
4 | .aws-sam/
5 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *main* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT No Attribution
2 |
3 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so.
10 |
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
13 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
15 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 |
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Amazon Bedrock Serverless Workshop
2 |
3 | This project contains source code and supporting files for a serverless application that you can deploy Amazon Bedrock Serverless solution using the SAM CLI. In this, you use the Retrieval-Augmented Generation (RAG) technique to build a generative AI-powered chatbot. A foundation model (FM) in Amazon Bedrock is used to answer your chatbot questions through pre-indexed content. Amazon Bedrock is a fully managed service that makes FMs (from Amazon and leading AI startups) available through an API, so you can choose from various FMs to find the model that's best suited to your use case. For storing (indexing) and retrieving relevant content, you use Amazon Kendra, a fully managed service that provides intelligent enterprise searches powered by machine learning. You use AWS Lambda as the serverless compute for running the application code in an event-driven manner.
4 |
5 | The project includes the following files and folders.
6 | - **Bedrock Function:** Code for the application's RAG Lambda function, and also a function for authenticating the app.
7 | - **UI code:** The code is developed using Amazon Amplify with Vue JavaScript framework.
8 | - **Sam Template:** A template (template.yaml) that defines the application's AWS backend resources. Once you run this using SAM, it creates Amazon API Gateway endpoint, AWS Lambda function (RAG), and a Amazon Cognito user pool.
9 |
10 | ## Deploy and test Amazon Bedrock serverless application
11 |
12 | To deploy and test this code, visit the following Amazon Bedrock workshop.
13 |
14 | See the [Creating a Serverless Chatbot using Amazon Bedrock](https://catalog.us-east-1.prod.workshops.aws/workshops/27eb3134-4f33-4689-bb73-269e4273947a)
15 |
16 | ## Resources
17 |
18 | - [AWS SAM developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html)
19 | - [Amazon Kendra developer guide](https://docs.aws.amazon.com/kendra/latest/dg/what-is-kendra.html)
20 | - [Amazon Bedrock user guide](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html)
21 |
22 | ## Security
23 |
24 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.
25 |
26 | ## License
27 |
28 | This library is licensed under the MIT-0 License. See the LICENSE file.
29 |
30 |
--------------------------------------------------------------------------------
/aws-creds.py:
--------------------------------------------------------------------------------
1 | import os
2 | import boto3
3 |
4 | # Get the current AWS credentials
5 | credentials = boto3.Session().get_credentials()
6 |
7 | # Create the AWS credentials directory if it doesn't exist
8 | aws_dir = os.path.expanduser('~/.aws')
9 | if not os.path.exists(aws_dir):
10 | os.makedirs(aws_dir)
11 |
12 | # Write the credentials to the AWS credentials file
13 | credential_file = os.path.join(aws_dir, 'credentials')
14 | with open(credential_file, 'w') as f:
15 | f.write('[default]\n')
16 | f.write('region=us-west-2\n')
17 | f.write(f'accessKeyId={credentials.access_key}\n')
18 | f.write(f'secretAccessKey={credentials.secret_key}\n')
19 | if credentials.token:
20 | f.write(f'sessionToken={credentials.token}\n')
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 |
2 |
3 | #amplify-do-not-edit-begin
4 | amplify/\#current-cloud-backend
5 | amplify/.config/local-*
6 | amplify/logs
7 | amplify/mock-data
8 | amplify/mock-api-resources
9 | amplify/backend/amplify-meta.json
10 | amplify/backend/.temp
11 | build/
12 | dist/
13 | node_modules/
14 | aws-exports.js
15 | awsconfiguration.json
16 | amplifyconfiguration.json
17 | amplifyconfiguration.dart
18 | amplify-build-config.json
19 | amplify-gradle-config.json
20 | amplifytools.xcconfig
21 | .secret-*
22 | **.sample
23 | #amplify-do-not-edit-end
24 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # Tasks webapp
2 |
3 | ## Project setup
4 |
5 | ```sh
6 | npm install
7 | ```
8 |
9 | ## Configure API Gateway endpoint URL
10 |
11 | Edit `src/main.js` and set the value for `axios.defaults.baseURL` to your API Gateway endpoint. This value is output from the SAM template in the sibling project to this one.
12 |
13 | ### Compiles and hot-reloads for development
14 |
15 | ```sh
16 | npm run serve
17 | ```
18 |
19 | ### Compiles and minifies for production
20 |
21 | ```sh
22 | npm run build
23 | ```
24 |
25 | ### Install latest version of Amplify CLI
26 |
27 | ```sh
28 | npm i -g @aws-amplify/cli
29 | ```
30 |
31 | ### Initialize Amplify
32 |
33 | ```sh
34 | amplify init
35 | ```
36 |
37 | ### Add hosting
38 |
39 | ```sh
40 | amplify add hosting
41 | ```
42 |
43 | ### Publish app
44 |
45 | ```sh
46 | amplify publish
47 | ```
--------------------------------------------------------------------------------
/frontend/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "getting-started-serverless",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "@aws-sdk/client-cognito-identity-provider": "^3.418.0",
12 | "amazon-cognito-identity-js": "^6.3.5",
13 | "axios": "^0.27.2",
14 | "babel-preset-expo": "^9.5.2",
15 | "core-js": "^3.19.1",
16 | "electron": "^26.2.0",
17 | "jwt-decode": "^3.1.2",
18 | "linkify-html": "^4.1.1",
19 | "linkifyjs": "^4.1.1",
20 | "util": "^0.12.5",
21 | "vue": "^2.6.14",
22 | "vue-axios": "^3.5.2",
23 | "vue-router": "^3.5.3",
24 | "webpack": "^4.46.0"
25 | },
26 | "devDependencies": {
27 | "@expo/webpack-config": "^18.1.3",
28 | "@vue/cli-plugin-babel": "~5.0.8",
29 | "@vue/cli-plugin-eslint": "~5.0.8",
30 | "@vue/cli-service": "~5.0.8",
31 | "babel-eslint": "^10.1.0",
32 | "eslint": "^7.32.0",
33 | "eslint-plugin-vue": "^8.0.3",
34 | "vue-template-compiler": "^2.6.14"
35 | },
36 | "eslintConfig": {
37 | "root": true,
38 | "env": {
39 | "node": true
40 | },
41 | "extends": [
42 | "plugin:vue/essential",
43 | "eslint:recommended"
44 | ],
45 | "parserOptions": {
46 | "parser": "babel-eslint"
47 | },
48 | "rules": {}
49 | },
50 | "browserslist": [
51 | "> 1%",
52 | "last 2 versions",
53 | "not dead"
54 | ]
55 | }
56 |
--------------------------------------------------------------------------------
/frontend/parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Hosting with Amplify Console (Managed hosting with custom domains, Continuous deployment)",
3 | "type": "Manual deployment"
4 | }
--------------------------------------------------------------------------------
/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
11 |
12 | Bedrock Workshop
13 |
14 |
15 |
16 |
17 | We're sorry but this application doesn't work properly without JavaScript enabled.
18 | Please enable it to continue.
19 |
20 |
21 |
22 |
23 |
26 |
29 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Serverless Chatbot using Amazon Bedrock
6 |
15 |
16 |
17 |
18 |
21 |
22 |
23 | General chat with LLMs
24 |
25 |
26 | |
27 |
28 |
29 | RAG chat with LLMs
30 |
31 |
32 | |
33 |
34 |
35 | Prompt Engineering
36 |
37 |
38 | |
39 |
40 |
41 | RAG with Knowledge Bases
42 |
43 |
44 | |
45 |
46 |
47 | Sign out
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
79 |
80 |
--------------------------------------------------------------------------------
/frontend/src/app.css:
--------------------------------------------------------------------------------
1 | *,
2 | *::before,
3 | *::after {
4 | box-sizing: border-box;
5 | }
6 |
7 | :root {
8 | --select-border: #777;
9 | --select-focus: blue;
10 | --select-arrow: var(--select-border);
11 | }
12 |
13 | .output pre {
14 | white-space: pre-wrap;
15 | word-wrap: break-word;
16 | font-family: inherit;
17 | }
18 |
19 | select {
20 | -webkit-appearance: none;
21 | -moz-appearance: none;
22 | appearance: none;
23 | background-color: transparent;
24 | border: none;
25 | padding: 0 1em 0 0;
26 | margin: 0;
27 | width: 100%;
28 | font-family: inherit;
29 | font-size: inherit;
30 | cursor: inherit;
31 | line-height: inherit;
32 | z-index: 1;
33 | outline: none;
34 | }
35 | select::-ms-expand {
36 | display: none;
37 | }
38 |
39 | .select {
40 | display: grid;
41 | grid-template-areas: "select";
42 | align-items: center;
43 | position: relative;
44 | min-width: 15ch;
45 | max-width: 30ch;
46 | border: 1px solid var(--select-border);
47 | border-radius: 0.25em;
48 | padding: 0.25em 0.5em;
49 | font-size: 1rem;
50 | cursor: pointer;
51 | line-height: 1.1;
52 | background-color: #fff;
53 | background-image: linear-gradient(to top, #f9f9f9, #fff 33%);
54 | }
55 | .select select, .select::after {
56 | grid-area: select;
57 | }
58 | .select:not(.select--multiple)::after {
59 | content: "";
60 | justify-self: end;
61 | width: 0.8em;
62 | height: 0.5em;
63 | background-color: var(--select-arrow);
64 | -webkit-clip-path: polygon(100% 0%, 0 0%, 50% 100%);
65 | clip-path: polygon(100% 0%, 0 0%, 50% 100%);
66 | }
67 |
68 | select:focus + .focus {
69 | position: absolute;
70 | top: -1px;
71 | left: -1px;
72 | right: -1px;
73 | bottom: -1px;
74 | border: 2px solid var(--select-focus);
75 | border-radius: inherit;
76 | }
77 |
78 | select[multiple] {
79 | padding-right: 0;
80 | /*
81 | * Safari will not reveal an option
82 | * unless the select height has room to
83 | * show all of it
84 | * Firefox and Chrome allow showing
85 | * a partial option
86 | */
87 | height: 6rem;
88 | /*
89 | * Experimental - styling of selected options
90 | * in the multiselect
91 | * Not supported crossbrowser
92 | */
93 | }
94 | select[multiple] option {
95 | white-space: normal;
96 | outline-color: var(--select-focus);
97 | }
98 |
99 | .select--disabled {
100 | cursor: not-allowed;
101 | background-color: #eee;
102 | background-image: linear-gradient(to top, #ddd, #eee 33%);
103 | }
104 |
105 | label {
106 | font-size: 1rem;
107 | font-weight: 500;
108 | }
109 |
110 | .select + label {
111 | margin-top: 1rem;
112 | }
113 |
114 | body {
115 | min-height: 100vh;
116 | display: grid;
117 |
118 | grid-gap: 0.5rem;
119 | font-family: "Baloo 2", sans-serif;
120 | background-color: #e9f2fd;
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/frontend/src/aws-exports.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.
3 |
4 | const awsmobile = {
5 | "aws_project_region": "us-west-2"
6 | };
7 |
8 |
9 | export default awsmobile;
10 |
--------------------------------------------------------------------------------
/frontend/src/components/NotFoundPage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 404 not found
5 | it seems you're in the wrong page
6 |
7 |
8 |
--------------------------------------------------------------------------------
/frontend/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 | import axios from 'axios'
4 | import router from './router'
5 | import VueAxios from 'vue-axios'
6 |
7 | Vue.config.productionTip = false
8 |
9 | //These values are replaces during amplify setup
10 | axios.defaults.baseURL = ""
11 | Vue.prototype.$UserPoolId = ''
12 | Vue.prototype.$ClientId = ''
13 |
14 | Vue.use(VueAxios, axios)
15 |
16 | Vue.config.productionTip = false
17 |
18 | new Vue({
19 | router,
20 | render: h => h(App),
21 |
22 | }).$mount('#app')
23 |
--------------------------------------------------------------------------------
/frontend/src/router.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import PromptView from './views/PromptView.vue'
4 | import RAG from './views/RAG.vue'
5 | import LLMs from './views/LLMs.vue'
6 | import LoginView from './views/LoginView.vue'
7 | import AboutView from './views/AboutView.vue'
8 | import NotFoundPage from './components/NotFoundPage.vue'
9 | import KB from './views/KB.vue'
10 | import { clearAuthToken, isLoggedIn } from './utils/auth'
11 |
12 | Vue.use(Router)
13 |
14 | const router = new Router({
15 | mode: 'history',
16 | base: process.env.BASE_URL,
17 | routes: [
18 | {
19 | path: '/',
20 | name: 'llms',
21 | component: LLMs
22 | },
23 | {
24 | path: '/rag',
25 | name: 'rag',
26 | component: RAG
27 | },
28 | {
29 | path: '/kb',
30 | name: 'kb',
31 | component: KB
32 | },
33 | {
34 | path: '/prompt',
35 | name: 'prompt',
36 | component: PromptView
37 | },
38 | {
39 | path: '/login',
40 | name: 'login',
41 | component: LoginView,
42 | meta: {
43 | allowAnonymous: true
44 | }
45 | },
46 | {
47 | path: '/logout',
48 | name: 'Logout',
49 | beforeEnter(to, from, next) {
50 | clearAuthToken()
51 | next('/login')
52 | },
53 | },
54 | {
55 | path: '/about',
56 | name: 'about',
57 | component: AboutView
58 | },
59 | {
60 | path: '*',
61 | component: NotFoundPage
62 | }
63 | ]
64 | })
65 |
66 |
67 | router.beforeEach((to, from, next) => {
68 |
69 |
70 | if (to.name == 'login' && isLoggedIn()) {
71 | console.log('redirecting to /')
72 | next({ path: '/' })
73 | }
74 | else if (!to.meta.allowAnonymous && !isLoggedIn()) {
75 |
76 | next({
77 | path: '/login',
78 | query: { redirect: to.fullPath }
79 | })
80 | }
81 | else {
82 | console.log('router next()')
83 | next()
84 | }
85 | })
86 |
87 | export default router
--------------------------------------------------------------------------------
/frontend/src/utils/auth.js:
--------------------------------------------------------------------------------
1 | import decode from 'jwt-decode'
2 | import axios from 'axios'
3 | import * as AmazonCognitoIdentity from 'amazon-cognito-identity-js';
4 | const AUTH_TOKEN_KEY = 'authToken'
5 |
6 |
7 | export function loginUser(username, password, UserPoolId, ClientId) {
8 | console.log("started login user");
9 | var authenticationData = {
10 | Username: username,
11 | Password: password
12 | };
13 | var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
14 | var poolData = {
15 | UserPoolId: UserPoolId,
16 | ClientId: ClientId
17 | };
18 | var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
19 |
20 | var userData = {
21 | Username: username,
22 | Pool: userPool
23 | };
24 | var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
25 | cognitoUser.authenticateUser(authenticationDetails, {
26 | onSuccess: function (result) {
27 |
28 | var accessToken = result.getIdToken().getJwtToken();
29 |
30 | setAuthToken(accessToken)
31 | console.log(getAuthToken)
32 | if (isLoggedIn()) {
33 | window.location.href = '/';
34 | }
35 |
36 | },
37 | onFailure: function (err) {
38 | //isSubmitted =false;
39 | window.alert("Incorrect Username/Password");
40 | console.log(err)
41 | }
42 | });
43 | console.log("completed user login");
44 |
45 | }
46 |
47 |
48 |
49 |
50 | export function logoutUser() {
51 | clearAuthToken()
52 | }
53 |
54 | export function setAuthToken(token) {
55 | console.log('Setting token: ' + token)
56 | axios.defaults.headers.common['Authorization'] = `Bearer ${token}`
57 | localStorage.setItem(AUTH_TOKEN_KEY, token)
58 | let sub = getTokenSub(token)
59 | console.log(`sub: ${sub}`)
60 | }
61 |
62 | export function getAuthToken() {
63 | return localStorage.getItem(AUTH_TOKEN_KEY)
64 | }
65 |
66 | export function clearAuthToken() {
67 | console.log('Clearing auth token')
68 | axios.defaults.headers.common['Authorization'] = ''
69 | localStorage.removeItem(AUTH_TOKEN_KEY)
70 | }
71 |
72 | export function isLoggedIn() {
73 |
74 | let authToken = getAuthToken()
75 | let isLoggedIn = !!authToken && !isTokenExpired(authToken)
76 | console.log(`isLoggedIn: ${isLoggedIn}`)
77 | return isLoggedIn
78 | }
79 |
80 |
81 | export function getUserInfo() {
82 | if (isLoggedIn()) {
83 | return decode(getAuthToken())
84 | }
85 | }
86 |
87 | export function getUsername() {
88 | if (isLoggedIn()) {
89 | let token = decode(getAuthToken())
90 | if (!token.username) {
91 | return null
92 | }
93 |
94 | return token.username
95 | }
96 | }
97 |
98 | function getTokenSub(encodedToken) {
99 | let token = decode(encodedToken)
100 | if (!token.sub) {
101 | return null
102 | }
103 |
104 | return token.sub
105 | }
106 |
107 | function getTokenExpirationDate(encodedToken) {
108 | let token = decode(encodedToken)
109 | if (!token.exp) {
110 | return null
111 | }
112 |
113 | let date = new Date(0)
114 | date.setUTCSeconds(token.exp)
115 |
116 | return date
117 | }
118 |
119 | function isTokenExpired(token) {
120 | let expirationDate = getTokenExpirationDate(token)
121 | return expirationDate < new Date()
122 | }
123 |
--------------------------------------------------------------------------------
/frontend/src/utils/core.js:
--------------------------------------------------------------------------------
1 | export function sleep(ms) {
2 | return new Promise(
3 | resolve => setTimeout(resolve, ms)
4 | );
5 | }
--------------------------------------------------------------------------------
/frontend/src/views/AboutView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
About
4 | This is an about page.
5 |
6 |
--------------------------------------------------------------------------------
/frontend/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Model:
19 |
20 |
21 |
22 |
23 | Claude 2
24 | Llama 2 Chat 13B
25 | Jurassic 2 Ultra
26 |
27 |
28 |
29 |
30 | Temperature:
31 |
32 |
33 |
34 |
35 | 0
36 | 0.5
37 | 1
38 |
39 |
40 |
41 |
42 |
43 | Tokens:
44 |
45 |
46 |
47 |
48 | 250
49 | 500
50 | 1000
51 | 2000
52 | 5000
53 |
54 |
55 |
56 |
57 |
58 |
59 |
65 |
66 |
67 | Loading...
68 |
69 |
70 | Response:
71 | {{output.answer}}
72 |
73 | References:
74 | {{output.source_documents }}
75 |
76 |
77 |
78 |
79 |
80 |
83 |
84 |
85 |
86 |
87 | What is federal funds rate as of September 2023?
88 |
89 | What is federal funds rate as of September 2024?
90 |
91 | What are the demographic trends in dental space?
92 |
93 | What are Amazon sustainability goals by year 2040?
94 |
95 |
96 |
97 |
98 |
99 |
100 |
160 |
--------------------------------------------------------------------------------
/frontend/src/views/KB.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Model:
19 |
20 |
21 |
22 |
23 | Claude 3 Haiku
24 | Claude 3.5 Sonnet
25 | Claude 3 Opus
26 | Llama 3.1 Instruct 8B
27 |
28 |
29 |
30 |
31 |
32 |
33 |
39 |
40 |
41 | Loading...
42 |
43 |
44 | Response:
45 | {{output.answer}}
46 |
47 | References:
48 | {{output.source_documents}}
49 |
50 | Error:
51 | {{output.error}}
52 |
53 |
54 |
55 |
56 |
57 |
60 |
61 |
62 |
63 |
64 | What is federal funds rate as of April 2024?
65 |
66 | What is federal funds rate as of September 2025?
67 |
68 | What are the demographic trends in dental space?
69 |
70 | What are Amazon sustainability goals by year 2040?
71 |
72 |
73 |
74 |
75 |
76 |
77 |
129 |
--------------------------------------------------------------------------------
/frontend/src/views/LLMs.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Model:
19 |
20 |
21 |
22 |
23 | Claude 3 Haiku
24 | Claude 3.5 Sonnet
25 | Claude 3 Opus
26 | Mistral 7B
27 | Llama 3.1 Instruct 8B
28 |
29 |
30 |
31 |
32 | Temperature:
33 |
34 |
35 |
36 |
37 | 0
38 | 0.5
39 | 1
40 |
41 |
42 |
43 |
44 |
45 | Tokens:
46 |
47 |
48 |
49 |
50 | 250
51 | 500
52 | 1000
53 | 2000
54 | 5000
55 |
56 |
57 |
58 |
59 |
60 |
61 |
67 |
68 |
69 | Loading...
70 |
71 |
72 |
73 |
74 | Response:
75 | {{output.answer}}
76 |
77 |
78 |
79 |
References:
80 |
{{output.source_documents }}
81 |
82 |
Error:
83 |
{{output.error}}
84 |
85 |
86 |
87 |
88 |
89 |
92 |
93 |
94 |
95 |
96 | List top 10 cities of USA by population?
97 |
98 | What is the distance between Earth and Moon?
99 |
100 | List out all USA presidents, sort by most recent.
101 |
102 | How many beers are need for a 2 hour Happy Hour party with a guest count of 50?
103 |
104 | Tell me a story around a fox and tiger.
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/frontend/src/views/LoginView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
15 |
18 |
19 |
Please sign in
20 |
21 | {{ error }}
22 |
23 |
53 |
54 |
85 |
86 |
91 | Sign in
92 |
93 |
94 |
95 |
96 |
97 |
120 |
121 |
149 |
150 |
--------------------------------------------------------------------------------
/frontend/src/views/PromptView.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Model:
19 |
20 |
21 |
22 |
23 | Claude 3.5 Sonnet
24 |
25 |
26 |
27 |
28 | Temperature:
29 |
30 |
31 |
32 |
33 | 0
34 | 0.5
35 | 1
36 |
37 |
38 |
39 |
40 |
41 | Tokens:
42 |
43 |
44 |
45 |
46 | 250
47 | 500
48 | 1000
49 | 2000
50 |
51 |
52 |
53 |
54 |
55 |
56 |
65 |
66 |
67 | Loading...
68 |
69 |
70 | Response:
71 | {{output.answer}}
72 |
73 | Error:
74 | {{output.error}}
75 |
76 |
77 |
78 |
79 |
80 |
83 |
84 |
85 |
86 |
87 | What is federal funds rate as of April 2024?
88 |
89 | What is federal funds rate as of September 2025?
90 |
91 | What are the demographic trends in dental space?
92 |
93 | What are Amazon sustainability goals by year 2040?
94 |
95 | Tell me a story about a fox and tiger, the story must be for a 5 year old and under 100 words.
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
165 |
--------------------------------------------------------------------------------
/frontend/src/views/RAG.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Model:
19 |
20 |
21 |
22 |
23 | Claude 3 Haiku
24 | Claude 3.5 Sonnet
25 | Claude 3 Opus
26 | Mistral 7B
27 | Llama 3.1 Instruct 8B
28 |
29 |
30 |
31 |
32 | Temperature:
33 |
34 |
35 |
36 |
37 | 0
38 | 0.5
39 | 1
40 |
41 |
42 |
43 |
44 |
45 | Tokens:
46 |
47 |
48 |
49 |
50 | 250
51 | 500
52 | 1000
53 | 2000
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | Query
62 |
63 |
64 | Ask Question
65 |
66 |
67 |
68 | Loading...
69 |
70 |
71 | Response:
72 | {{output.answer}}
73 |
74 | References:
75 | {{output.source_documents}}
76 |
77 | Error:
78 | {{output.error}}
79 |
80 |
81 |
82 |
83 |
84 |
87 |
88 |
89 |
90 |
91 | What is federal funds rate as of April 2024?
92 |
93 | What is federal funds rate as of September 2025?
94 |
95 | What are the demographic trends in dental space?
96 |
97 | What are Amazon sustainability goals by year 2040?
98 |
99 |
100 |
101 |
102 |
103 |
104 |
166 |
--------------------------------------------------------------------------------
/lambdas/bedrockFunc/cognitouser.py:
--------------------------------------------------------------------------------
1 | import boto3
2 | import json
3 | import secrets
4 | import string
5 | import os
6 | import requests
7 |
8 | REGION = os.environ['AWS_REGION']
9 | ChatbotUserPool = os.getenv("Cognito_UserPool", None)
10 | ChatbotUserPoolClient = os.getenv("Cognito_ClientID", None)
11 | secrets_name = os.getenv("SECRET_ID", 'ui-credentials')
12 | user_id = os.getenv("USER_ID", 'bedrock')
13 |
14 |
15 | cognitoidentityserviceprovider = boto3.client('cognito-idp', region_name=REGION)
16 |
17 | def generate_random_password(length=12):
18 | # Define character sets
19 | lowercase_letters = string.ascii_lowercase
20 | uppercase_letters = string.ascii_uppercase
21 | digits = string.digits
22 | special_characters = '!$@'
23 |
24 | # Ensure that the password includes at least one special character
25 | special_char = secrets.choice(special_characters)
26 |
27 | # Calculate the remaining length of the password
28 | remaining_length = length - 1
29 |
30 | # Generate the rest of the password with a mix of characters
31 | password_characters = (
32 | secrets.choice(lowercase_letters) +
33 | secrets.choice(uppercase_letters) +
34 | secrets.choice(digits) +
35 | ''.join(secrets.choice(lowercase_letters + uppercase_letters + digits + special_characters) for _ in range(remaining_length - 3))
36 | )
37 |
38 | # Shuffle the characters to make the password random
39 | password_list = list(password_characters)
40 | secrets.SystemRandom().shuffle(password_list)
41 | shuffled_password = ''.join(password_list)
42 |
43 | # Add the special character back at a random position
44 | position = secrets.randbelow(length)
45 | password = shuffled_password[:position] + special_char + shuffled_password[position:]
46 |
47 | return password
48 |
49 | def create_secret(password):
50 |
51 | # Initialize the AWS Secrets Manager client
52 | client = boto3.client('secretsmanager')
53 |
54 | # Store the user ID and password in a dictionary
55 | secret_data = {
56 | 'user_id': user_id,
57 | 'password': password
58 | }
59 |
60 | # Create or update the secret in Secrets Manager
61 | try:
62 | response = client.create_secret(
63 | Name=secrets_name,
64 | SecretString=str(secret_data)
65 | )
66 | print("Secret created successfully!")
67 | except client.exceptions.ResourceExistsException:
68 | # If the secret already exists, update it
69 | response = client.update_secret(
70 | SecretId=secrets_name,
71 | SecretString=str(secret_data)
72 | )
73 | print("Secret updated successfully!")
74 |
75 |
76 | def lambda_handler(event, context):
77 | request_type = event['RequestType']
78 | print('request_type:', request_type)
79 | status_msg = 'SUCCESS'
80 | print('response_url:', event['ResponseURL'])
81 | try:
82 |
83 | if request_type == 'Delete' :
84 | #Delete the secrete
85 | client = boto3.client('secretsmanager')
86 | client.delete_secret(SecretId=secrets_name, ForceDeleteWithoutRecovery=True)
87 | print('workshop secret deleted successful')
88 | status_msg = 'SUCCESS'
89 | else:
90 | password = generate_random_password()
91 | print('password:', password)
92 | create_secret(password)
93 |
94 | try:
95 | cognitoidentityserviceprovider.admin_create_user(
96 | UserPoolId = ChatbotUserPool,
97 | Username = user_id,
98 | UserAttributes = [
99 | {"Name": "name", "Value": user_id}
100 | ]
101 | )
102 | except Exception as e1:
103 | print('cognito user exist already')
104 |
105 | cognitoidentityserviceprovider.admin_set_user_password(
106 | UserPoolId = ChatbotUserPool,
107 | Username = user_id,
108 | Password = password,
109 | Permanent=True
110 | )
111 | status_msg = 'SUCCESS'
112 | except Exception as e:
113 | print('Error: ' + str(e))
114 | status_msg = 'FAILED'
115 |
116 |
117 | # Respond to CloudFormation to let it know we are done
118 | response_data = build_response(event, status_msg)
119 | response_url = event['ResponseURL']
120 | requests.put(response_url, data=json.dumps(response_data))
121 |
122 | print('Cfn signal has been sent back successfully')
123 |
124 | def build_response(event, status):
125 | """A utility function used to build a response to CloudFormation"""
126 |
127 | response_data = {
128 | 'Status': status,
129 | 'Reason': 'Success',
130 | 'PhysicalResourceId': 'myapp::{}'.format(event['LogicalResourceId']),
131 | 'Data': {},
132 | 'RequestId': event['RequestId'],
133 | "LogicalResourceId": event["LogicalResourceId"],
134 | "StackId": event["StackId"],
135 | }
136 | return response_data
137 |
138 |
--------------------------------------------------------------------------------
/lambdas/bedrockFunc/requirements.txt:
--------------------------------------------------------------------------------
1 | boto3>=1.28.57
2 | langchain>=0.0.331
3 | coloredlogs==15.0.1
4 | jq==1.6.0
5 |
--------------------------------------------------------------------------------
/lambdas/llmFunctions/kbfunction.py:
--------------------------------------------------------------------------------
1 | import boto3
2 | import json
3 |
4 |
5 | region = boto3.session.Session().region_name
6 |
7 | def lambda_handler(event, context):
8 | print(f"Event is: {event}")
9 |
10 | response = 'This function is not yet available. Please proceed to Task 8, where you will implement it.'
11 | status_code = 200
12 |
13 |
14 | return {
15 | 'statusCode': status_code,
16 | 'headers': {
17 | 'Access-Control-Allow-Origin': '*',
18 | 'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
19 | 'Access-Control-Allow-Methods': 'OPTIONS,POST'
20 | },
21 | 'body': json.dumps({'answer': response})
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/lambdas/llmFunctions/llmfunction.py:
--------------------------------------------------------------------------------
1 | import boto3
2 | import json
3 | import traceback
4 |
5 |
6 | region = boto3.session.Session().region_name
7 |
8 | def lambda_handler(event, context):
9 | print(f"Event is: {event}")
10 |
11 | response = 'This function is not ready, please complete the next two tasks and test again.'
12 | status_code = 200
13 |
14 |
15 | return {
16 | 'statusCode': status_code,
17 | 'headers': {
18 | 'Access-Control-Allow-Origin': '*',
19 | 'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
20 | 'Access-Control-Allow-Methods': 'OPTIONS,POST'
21 | },
22 | 'body': json.dumps({'answer': response})
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/lambdas/ragFunctions/promptfunction.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 | import boto3
4 | from langchain_community.retrievers import AmazonKendraRetriever
5 | from langchain_aws import ChatBedrock
6 | from langchain.memory import ConversationBufferWindowMemory
7 | from langchain.prompts import PromptTemplate
8 | from langchain.chains import RetrievalQA
9 | import traceback
10 |
11 | # Set up the Kendra client
12 | kendra = boto3.client('kendra')
13 |
14 | KENDRA_INDEX_ID = os.getenv('KENDRA_INDEX_ID')
15 |
16 | def lambda_handler(event, context):
17 | print(f"Event is: {event}")
18 |
19 | event_body = json.loads(event["body"])
20 | question = event_body["query"]
21 | prompt_template = event_body["prompt"]
22 | print(f"Query: {question}")
23 | print(f"Prompt: {prompt_template}")
24 |
25 | model_id = event_body["model_id"]
26 | temperature = event_body["temperature"]
27 | max_tokens = event_body["max_tokens"]
28 |
29 | response = ''
30 | status_code = 200
31 |
32 | try:
33 | llm = get_claude_llm(model_id, temperature, max_tokens)
34 |
35 | # Initialize the Kendra loader
36 | retriever = AmazonKendraRetriever(
37 | kendra_client=kendra,
38 | index_id=KENDRA_INDEX_ID
39 | )
40 | claude_prompt = PromptTemplate(
41 | template=prompt_template, input_variables=["context","question"]
42 | )
43 |
44 | qa = RetrievalQA.from_chain_type(
45 | llm=llm,
46 | chain_type="stuff",
47 | retriever=retriever,
48 | return_source_documents=False,
49 | chain_type_kwargs={"prompt": claude_prompt}
50 | )
51 |
52 | response = qa(question, return_only_outputs=True)
53 | print(response)
54 |
55 | response_with_metadata = {
56 | "answer": response['result']
57 | }
58 |
59 |
60 | return {
61 | 'statusCode': status_code,
62 | 'headers': {
63 | 'Access-Control-Allow-Origin': '*',
64 | 'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
65 | 'Access-Control-Allow-Methods': 'OPTIONS,POST'
66 | },
67 | 'body': json.dumps(response_with_metadata)
68 | }
69 |
70 | except Exception as e:
71 | print(f"An unexpected error occurred: {str(e)}")
72 | stack_trace = traceback.format_exc()
73 | print(f"stack trace: {stack_trace}")
74 | print(f"error: {str(e)}")
75 |
76 | response = str(e)
77 | return {
78 | 'statusCode': status_code,
79 | 'headers': {
80 | 'Access-Control-Allow-Origin': '*',
81 | 'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
82 | 'Access-Control-Allow-Methods': 'OPTIONS,POST'
83 | },
84 | 'body': json.dumps({'error': response})
85 | }
86 |
87 | def get_claude_llm(model_id, temperature, max_tokens):
88 | model_kwargs = {
89 | "max_tokens": max_tokens,
90 | "temperature": temperature,
91 | "top_k": 50,
92 | "top_p": 1
93 | }
94 | llm = ChatBedrock(model_id=model_id, model_kwargs=model_kwargs)
95 | return llm
96 |
97 | #This is a TODO item, presently the history is not retained between the calls
98 | def get_memory():
99 | memory = ConversationBufferWindowMemory(
100 | memory_key="chat_history",
101 | k=5,
102 | input_key="question",
103 | output_key="answer",
104 | return_messages=True)
105 | return memory
106 |
--------------------------------------------------------------------------------
/lambdas/ragFunctions/ragfunction.py:
--------------------------------------------------------------------------------
1 | import boto3
2 | import json
3 | import json
4 | import traceback
5 |
6 |
7 | region = boto3.session.Session().region_name
8 |
9 | def lambda_handler(event, context):
10 | print(f"Event is: {event}")
11 |
12 | response = 'This function is not ready, please complete the next two tasks and test again.'
13 | status_code = 200
14 |
15 |
16 | return {
17 | 'statusCode': status_code,
18 | 'headers': {
19 | 'Access-Control-Allow-Origin': '*',
20 | 'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
21 | 'Access-Control-Allow-Methods': 'OPTIONS,POST'
22 | },
23 | 'body': json.dumps({'answer': response})
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/lambdas/ragFunctions/requirements.txt:
--------------------------------------------------------------------------------
1 | #This project will require modification to support langchain 0.3.0, till then use this version 0.2.16
2 | langchain==0.2.16
3 | langchain-community
4 | langchain-aws
5 |
--------------------------------------------------------------------------------
/prompt-engineering/claude-prompt-template.txt:
--------------------------------------------------------------------------------
1 | Human: You are an AI assistant specialized Healthcare and Life Sciences area, and providing information and advices in that area. We expect you to provide answers to questions using fact-based information available from the context only. If the provided context is insufficient to answer the question, politely indicate that and refrain from speculating or making up information.
2 |
3 | The response should be specific, use facts only, and when possible. When providing factual information, cite the relevant sources from the context.
4 |
5 | If the question is unclear or ambiguous, feel free to ask for clarification before providing an answer.
6 |
7 | If the context contains sensitive or confidential information, refrain from disclosing or discussing it in your response.
8 |
9 | If you don't know the answer, just say that you don't know, don't try to make up an answer.
10 |
11 | Use the following information to provide a concise answer to user query in tags. Look for the contextual information enclosed in tags.
12 |
13 | {context}
14 |
15 | {question}
16 |
17 | Assistant:
--------------------------------------------------------------------------------
/prompt-engineering/llama-prompt-template.txt:
--------------------------------------------------------------------------------
1 | [INST]
2 | You are an AI assistant specialized Healthcare and Life Sciences area, and providing information and advices in that area. We expect you to provide answers to questions using fact-based information available from the context only. If the provided context is insufficient to answer the question, politely indicate that and refrain from speculating or making up information.
3 |
4 | The response should be specific, use facts only, and when possible. When providing factual information, cite the relevant sources from the context.
5 |
6 | If the question is unclear or ambiguous, feel free to ask for clarification before providing an answer.
7 |
8 | If the context contains sensitive or confidential information, refrain from disclosing or discussing it in your response.
9 |
10 | If you don't know the answer, just say that you don't know, don't try to make up an answer.
11 |
12 | Use the following information to provide a concise answer to user query in tags. Look for the contextual information enclosed in tags.
13 | You do not have to include the original question in your response.
14 |
15 | {context}
16 |
17 | {question}
18 |
19 | [/INST]
20 |
21 |
--------------------------------------------------------------------------------
/prompt-engineering/mistral-prompt-template.txt:
--------------------------------------------------------------------------------
1 | [INST]
2 | You are an AI assistant specialized Healthcare and Life Sciences area, and providing information and advices in that area. We expect you to provide answers to questions using fact-based information available from the context only. If the provided context is insufficient to answer the question, politely indicate that and refrain from speculating or making up information.
3 |
4 | The response should be specific, use facts only, and when possible. When providing factual information, cite the relevant sources from the context.
5 |
6 | If the question is unclear or ambiguous, feel free to ask for clarification before providing an answer.
7 |
8 | If the context contains sensitive or confidential information, refrain from disclosing or discussing it in your response.
9 |
10 | If you don't know the answer, just say that you don't know, don't try to make up an answer.
11 |
12 | Use the following information to provide a concise answer to user query in tags. Look for the contextual information enclosed in tags.
13 |
14 | {context}
15 |
16 | {question}
17 |
18 | [/INST]
19 |
20 |
--------------------------------------------------------------------------------
/startup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo "Preparing for project deploymnet"
3 | # Check if 'CFNStackName' is set in the environment variables
4 | if [ -z "$CFNStackName" ]; then
5 | echo "Error: 'CFNStackName' environment variable is not set. Please set it and run the script."
6 | exit 1
7 | fi
8 | echo "CFN Start up stack name: $CFNStackName"
9 |
10 |
11 | echo "Set region"
12 | export AWS_REGION=$(aws configure get region)
13 | echo "AWS Region is $AWS_REGION"
14 |
15 | echo "Build the backend code using sam build"
16 | cd ~/environment/bedrock-serverless-workshop
17 | sam build
18 |
19 | echo "Export S3 bucket name and Kendra index which are created as part of Startup CFN stack"
20 | export S3BucketName=$(aws cloudformation describe-stacks --stack-name ${CFNStackName} --query "Stacks[0].Outputs[?OutputKey=='S3BucketName'].OutputValue" --output text)
21 | export KendraIndexID=$(aws cloudformation describe-stacks --stack-name ${CFNStackName} --query "Stacks[0].Outputs[?OutputKey=='KendraIndexID'].OutputValue" --output text)
22 |
23 | export SAMStackName="sam-$CFNStackName"
24 | echo $SAMStackName
25 |
26 | echo "Copy toml file and replace the parameters"
27 |
28 | cp tools/samconf.toml samconfig.toml
29 | # Replace values in .//samconfig.toml
30 | sed -Ei "s||${KendraIndexID}|g" ./samconfig.toml
31 | sed -Ei "s||${S3BucketName}|g" ./samconfig.toml
32 | sed -Ei "s||${SAMStackName}|g" ./samconfig.toml
33 | sed -Ei "s||${AWS_REGION}|g" ./samconfig.toml
34 |
35 | echo "Deploy app with sam deploy"
36 | sam deploy
37 |
38 | echo "Export few more parameters from the sam stack output"
39 | export BedrockApiUrl=$(aws cloudformation describe-stacks --stack-name ${SAMStackName} --query "Stacks[0].Outputs[?OutputKey=='BedrockApiUrl'].OutputValue" --output text)
40 | export UserPoolId=$(aws cloudformation describe-stacks --stack-name ${SAMStackName} --query "Stacks[0].Outputs[?OutputKey=='CognitoUserPool'].OutputValue" --output text)
41 | export UserPoolClientId=$(aws cloudformation describe-stacks --stack-name ${SAMStackName} --query "Stacks[0].Outputs[?OutputKey=='CongnitoUserPoolClientID'].OutputValue" --output text)
42 | export SecretName=$(aws cloudformation describe-stacks --stack-name ${SAMStackName} --query "Stacks[0].Outputs[?OutputKey=='SecretsName'].OutputValue" --output text)
43 | echo "API Gateway endpoint: $BedrockApiUrl"
44 | echo "Cognito user pool id: $UserPoolId"
45 | echo "Cognito client id: $UserPoolClientId"
46 | echo "Secret name: $SecretName"
47 |
48 | # Replace values in ./backend/samconfig.toml
49 | sed -Ei "s||${BedrockApiUrl}|g" ./frontend/src/main.js
50 | sed -Ei "s||${UserPoolId}|g" ./frontend/src/main.js
51 | sed -Ei "s||${UserPoolClientId}|g" ./frontend/src/main.js
52 |
53 | #Pre req for frontend setup
54 | mkdir ~/.npm-global
55 | npm config set prefix '~/.npm-global'
56 | export PATH=~/.npm-global/bin:$PATH
57 |
58 | #Install Ampliyfy and build frontend
59 | #this export to fix the npm installation issue
60 | export UV_USE_IO_URING=0
61 | cd /home/ec2-user/environment/bedrock-serverless-workshop/frontend
62 | npm i -S @vue/cli-service
63 | npm i @vue/cli-plugin-babel -D
64 | npm i -g @aws-amplify/cli
65 | npm install
66 | npm run build
67 | cp ~/.aws/credentials ~/.aws/config
68 |
69 | #Amplify initialization
70 | echo "Amplify initialization"
71 | mv dist build
72 | amplify init --yes
73 |
74 | echo "Add hosting, hit enter key if it prompts for action, use default"
75 | amplify add hosting --parameters parameters.json
76 |
77 | echo "Publish the amplify project"
78 | amplify publish --yes
79 |
80 | echo "Save the user_id and password to login to UI"
81 | aws secretsmanager get-secret-value --secret-id $SecretName | jq -r .SecretString
82 |
--------------------------------------------------------------------------------
/template.yaml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: '2010-09-09'
2 | Transform: AWS::Serverless-2016-10-31
3 | Description:
4 | SAM template for serverless bedrock chatbot workshop.
5 |
6 | Parameters:
7 | KendraIndexId:
8 | Description: ID of the Kendra Index
9 | Type: String
10 |
11 | BedrockWSS3Bucket:
12 | Description: Workshop bucket name
13 | Type: String
14 |
15 |
16 | ApiGatewayStageName:
17 | Default: prod
18 | Description : Stage name for the API Gateway
19 | Type: String
20 |
21 | # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
22 | Globals:
23 | Function:
24 | Timeout: 60
25 | MemorySize: 3000
26 | Tracing: Active
27 | Api:
28 | TracingEnabled: true
29 | Cors:
30 | AllowMethods: "'GET,POST,OPTIONS'"
31 | AllowHeaders: "'Content-Type', 'Authorization', 'X-Forwarded-For', 'X-Api-Key', 'X-Amz-Date', 'X-Amz-Security-Token'"
32 | AllowOrigin: "'*'"
33 |
34 | Resources:
35 | # REST API
36 | BedrockLambdaApi:
37 | Type: AWS::ApiGateway::RestApi
38 | Properties:
39 | Name: bedrock-workshop
40 | Description: Mock Integration REST API demo
41 |
42 |
43 |
44 | RagResource:
45 | Type: AWS::ApiGateway::Resource
46 | Properties:
47 | ParentId: !GetAtt BedrockLambdaApi.RootResourceId
48 | PathPart: 'rag'
49 | RestApiId: !Ref BedrockLambdaApi
50 |
51 | LLMResource:
52 | Type: AWS::ApiGateway::Resource
53 | Properties:
54 | ParentId: !GetAtt BedrockLambdaApi.RootResourceId
55 | PathPart: 'llms'
56 | RestApiId: !Ref BedrockLambdaApi
57 |
58 | PromptResource:
59 | Type: AWS::ApiGateway::Resource
60 | Properties:
61 | ParentId: !GetAtt BedrockLambdaApi.RootResourceId
62 | PathPart: 'prompt'
63 | RestApiId: !Ref BedrockLambdaApi
64 |
65 | KBResource:
66 | Type: AWS::ApiGateway::Resource
67 | Properties:
68 | ParentId: !GetAtt BedrockLambdaApi.RootResourceId
69 | PathPart: 'kb'
70 | RestApiId: !Ref BedrockLambdaApi
71 |
72 | # KB Method Config
73 | KBOptionsMethod:
74 | Type: AWS::ApiGateway::Method
75 | Properties:
76 | HttpMethod: OPTIONS
77 | ResourceId: !Ref KBResource
78 | RestApiId: !Ref BedrockLambdaApi
79 | AuthorizationType: NONE
80 | Integration:
81 | IntegrationResponses:
82 | - StatusCode: 200
83 | ResponseParameters:
84 | method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
85 | method.response.header.Access-Control-Allow-Methods: "'POST,OPTIONS'"
86 | method.response.header.Access-Control-Allow-Origin: "'*'"
87 | PassthroughBehavior: WHEN_NO_MATCH
88 | RequestTemplates:
89 | application/json: '{"statusCode": 200}'
90 | Type: MOCK
91 | MethodResponses:
92 | - StatusCode: 200
93 | ResponseModels:
94 | application/json: 'Empty'
95 | ResponseParameters:
96 | method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
97 | method.response.header.Access-Control-Allow-Methods: "'POST,OPTIONS'"
98 | method.response.header.Access-Control-Allow-Origin: "'*'"
99 |
100 | KBPostMethod:
101 | Type: AWS::ApiGateway::Method
102 | Properties:
103 | HttpMethod: POST
104 | ResourceId: !Ref KBResource
105 | RestApiId: !Ref BedrockLambdaApi
106 | AuthorizationType: NONE
107 | Integration:
108 | IntegrationHttpMethod: POST
109 | Type: AWS_PROXY
110 | Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${KBFunction.Arn}/invocations"
111 | MethodResponses:
112 | - StatusCode: 200
113 | ResponseParameters:
114 | method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
115 | method.response.header.Access-Control-Allow-Methods: "'POST,OPTIONS'"
116 | method.response.header.Access-Control-Allow-Origin: "'*'"
117 |
118 | KBFunctionPermission:
119 | Type: AWS::Lambda::Permission
120 | Properties:
121 | FunctionName: !Ref KBFunction
122 | Action: lambda:InvokeFunction
123 | Principal: apigateway.amazonaws.com
124 | SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${BedrockLambdaApi}/*/*/*"
125 |
126 | KBApiDeployment:
127 | Type: AWS::ApiGateway::Deployment
128 | DependsOn: KBPostMethod
129 | Properties:
130 | Description: KB API Deployment
131 | RestApiId: !Ref BedrockLambdaApi
132 |
133 | # RAG Method Config
134 | RagOptionsMethod:
135 | Type: AWS::ApiGateway::Method
136 | Properties:
137 | HttpMethod: OPTIONS
138 | ResourceId: !Ref RagResource
139 | RestApiId: !Ref BedrockLambdaApi
140 | AuthorizationType: NONE
141 | Integration:
142 | IntegrationResponses:
143 | - StatusCode: 200
144 | ResponseParameters:
145 | method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
146 | method.response.header.Access-Control-Allow-Methods: "'POST,OPTIONS'"
147 | method.response.header.Access-Control-Allow-Origin: "'*'"
148 | PassthroughBehavior: WHEN_NO_MATCH
149 | RequestTemplates:
150 | application/json: '{"statusCode": 200}'
151 | Type: MOCK
152 | MethodResponses:
153 | - StatusCode: 200
154 | ResponseModels:
155 | application/json: 'Empty'
156 | ResponseParameters:
157 | method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
158 | method.response.header.Access-Control-Allow-Methods: "'POST,OPTIONS'"
159 | method.response.header.Access-Control-Allow-Origin: "'*'"
160 |
161 | RagPostMethod:
162 | Type: AWS::ApiGateway::Method
163 | Properties:
164 | HttpMethod: POST
165 | ResourceId: !Ref RagResource
166 | RestApiId: !Ref BedrockLambdaApi
167 | AuthorizationType: NONE
168 | Integration:
169 | IntegrationHttpMethod: POST
170 | Type: AWS_PROXY
171 | Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RagFunction.Arn}/invocations"
172 | MethodResponses:
173 | - StatusCode: 200
174 | ResponseParameters:
175 | method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
176 | method.response.header.Access-Control-Allow-Methods: "'POST,OPTIONS'"
177 | method.response.header.Access-Control-Allow-Origin: "'*'"
178 |
179 | RagFunctionPermission:
180 | Type: AWS::Lambda::Permission
181 | Properties:
182 | FunctionName: !Ref RagFunction
183 | Action: lambda:InvokeFunction
184 | Principal: apigateway.amazonaws.com
185 | SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${BedrockLambdaApi}/*/*/*"
186 |
187 | RagApiDeployment:
188 | Type: AWS::ApiGateway::Deployment
189 | DependsOn: RagPostMethod
190 | Properties:
191 | Description: RAG API Deployment
192 | RestApiId: !Ref BedrockLambdaApi
193 |
194 | # Prompt Method Config
195 | PromptOptionsMethod:
196 | Type: AWS::ApiGateway::Method
197 | Properties:
198 | HttpMethod: OPTIONS
199 | ResourceId: !Ref PromptResource
200 | RestApiId: !Ref BedrockLambdaApi
201 | AuthorizationType: NONE
202 | Integration:
203 | IntegrationResponses:
204 | - StatusCode: 200
205 | ResponseParameters:
206 | method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
207 | method.response.header.Access-Control-Allow-Methods: "'POST,OPTIONS'"
208 | method.response.header.Access-Control-Allow-Origin: "'*'"
209 | PassthroughBehavior: WHEN_NO_MATCH
210 | RequestTemplates:
211 | application/json: '{"statusCode": 200}'
212 | Type: MOCK
213 | MethodResponses:
214 | - StatusCode: 200
215 | ResponseModels:
216 | application/json: 'Empty'
217 | ResponseParameters:
218 | method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
219 | method.response.header.Access-Control-Allow-Methods: "'POST,OPTIONS'"
220 | method.response.header.Access-Control-Allow-Origin: "'*'"
221 |
222 | PromptPostMethod:
223 | Type: AWS::ApiGateway::Method
224 | Properties:
225 | HttpMethod: POST
226 | ResourceId: !Ref PromptResource
227 | RestApiId: !Ref BedrockLambdaApi
228 | AuthorizationType: NONE
229 | Integration:
230 | IntegrationHttpMethod: POST
231 | Type: AWS_PROXY
232 | Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PromptFunction.Arn}/invocations"
233 | MethodResponses:
234 | - StatusCode: 200
235 | ResponseParameters:
236 | method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
237 | method.response.header.Access-Control-Allow-Methods: "'POST,OPTIONS'"
238 | method.response.header.Access-Control-Allow-Origin: "'*'"
239 |
240 | PromptFunctionPermission:
241 | Type: AWS::Lambda::Permission
242 | Properties:
243 | FunctionName: !Ref PromptFunction
244 | Action: lambda:InvokeFunction
245 | Principal: apigateway.amazonaws.com
246 | SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${BedrockLambdaApi}/*/*/*"
247 |
248 | PromptApiDeployment:
249 | Type: AWS::ApiGateway::Deployment
250 | DependsOn: PromptPostMethod
251 | Properties:
252 | Description: RAG API Deployment
253 | RestApiId: !Ref BedrockLambdaApi
254 |
255 | # LLM Method Config
256 | LLMOptionsMethod:
257 | Type: AWS::ApiGateway::Method
258 | Properties:
259 | HttpMethod: OPTIONS
260 | ResourceId: !Ref LLMResource
261 | RestApiId: !Ref BedrockLambdaApi
262 | AuthorizationType: NONE
263 | Integration:
264 | IntegrationResponses:
265 | - StatusCode: 200
266 | ResponseParameters:
267 | method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
268 | method.response.header.Access-Control-Allow-Methods: "'POST,OPTIONS'"
269 | method.response.header.Access-Control-Allow-Origin: "'*'"
270 | PassthroughBehavior: WHEN_NO_MATCH
271 | RequestTemplates:
272 | application/json: '{"statusCode": 200}'
273 | Type: MOCK
274 | MethodResponses:
275 | - StatusCode: 200
276 | ResponseModels:
277 | application/json: 'Empty'
278 | ResponseParameters:
279 | method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
280 | method.response.header.Access-Control-Allow-Methods: "'POST,OPTIONS'"
281 | method.response.header.Access-Control-Allow-Origin: "'*'"
282 |
283 | LLMPostMethod:
284 | Type: AWS::ApiGateway::Method
285 | Properties:
286 | HttpMethod: POST
287 | ResourceId: !Ref LLMResource
288 | RestApiId: !Ref BedrockLambdaApi
289 | AuthorizationType: NONE
290 | Integration:
291 | IntegrationHttpMethod: POST
292 | Type: AWS_PROXY
293 | Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LLMFunction.Arn}/invocations"
294 | MethodResponses:
295 | - StatusCode: 200
296 | ResponseParameters:
297 | method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
298 | method.response.header.Access-Control-Allow-Methods: "'POST,OPTIONS'"
299 | method.response.header.Access-Control-Allow-Origin: "'*'"
300 |
301 | LLMFunctionPermission:
302 | Type: AWS::Lambda::Permission
303 | Properties:
304 | FunctionName: !Ref LLMFunction
305 | Action: lambda:InvokeFunction
306 | Principal: apigateway.amazonaws.com
307 | SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${BedrockLambdaApi}/*/*/*"
308 |
309 | LLMApiDeployment:
310 | Type: AWS::ApiGateway::Deployment
311 | DependsOn: LLMPostMethod
312 | Properties:
313 | Description: LLM API Deployment
314 | RestApiId: !Ref BedrockLambdaApi
315 |
316 | #### Cognito Auth Config
317 | ApiCognitoAuthorizer:
318 | Type: AWS::ApiGateway::Authorizer
319 | Properties:
320 | IdentitySource: 'method.request.header.Authorization'
321 | Name: ApiCognitoAuthorizer
322 | ProviderARNs:
323 | - !GetAtt ChatbotUserPool.Arn
324 | RestApiId: !Ref BedrockLambdaApi
325 | Type: COGNITO_USER_POOLS
326 |
327 | ChatbotUserPool:
328 | Type: AWS::Cognito::UserPool
329 | Properties:
330 | UsernameConfiguration:
331 | CaseSensitive: false
332 | AutoVerifiedAttributes:
333 | - email
334 | Schema:
335 | - Name: email
336 | AttributeDataType: String
337 | Mutable: false
338 | Required: false
339 | - Name: name
340 | AttributeDataType: String
341 | Mutable: true
342 | Required: true
343 | ChatbotUserPoolClient:
344 | Type: AWS::Cognito::UserPoolClient
345 | Properties:
346 | UserPoolId: !Ref ChatbotUserPool
347 | ExplicitAuthFlows:
348 | - ALLOW_USER_PASSWORD_AUTH
349 | - ALLOW_REFRESH_TOKEN_AUTH
350 | - ALLOW_USER_SRP_AUTH
351 | - ALLOW_CUSTOM_AUTH
352 | AllowedOAuthFlowsUserPoolClient: true
353 | CallbackURLs:
354 | - http://localhost:3000
355 | AllowedOAuthFlows:
356 | - code
357 | - implicit
358 | AllowedOAuthScopes:
359 | - phone
360 | - email
361 | - openid
362 | - profile
363 | SupportedIdentityProviders:
364 | - COGNITO
365 |
366 | CognitoUserCreateFunction:
367 | Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
368 | Properties:
369 | CodeUri: lambdas/bedrockFunc
370 | Handler: cognitouser.lambda_handler
371 | Runtime: python3.11
372 | MemorySize: 2048
373 | Architectures:
374 | - x86_64
375 | Environment:
376 | Variables:
377 | Cognito_UserPool: !Ref ChatbotUserPool
378 | Cognito_ClientID: !Ref ChatbotUserPoolClient
379 | SECRET_ID:
380 | Fn::Sub: "ui-credentials-${BedrockLambdaApi}"
381 | Policies:
382 | - Version: '2012-10-17'
383 | Statement:
384 | - Effect: Allow
385 | Action:
386 | - secretsmanager:Get*
387 | - secretsmanager:Describe*
388 | - secretsmanager:CreateSecret
389 | - secretsmanager:DeleteSecret
390 | - secretsmanager:UpdateSecret
391 | Resource:
392 | - !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:*"
393 | - Effect: Allow
394 | Action:
395 | - cognito-idp:Describe*
396 | - cognito-idp:CreateUserPool
397 | - cognito-idp:CreateUserPoolClient
398 | - cognito-idp:DeleteUserPool
399 | - cognito-idp:UpdateUserPool
400 | - cognito-idp:Admin*
401 | Resource:
402 | - !Sub "arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/*"
403 | - !Sub "arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/*/*"
404 |
405 | DeploymentCustomResource:
406 | Type: Custom::AppConfiguration
407 | Properties:
408 | ServiceToken: !GetAtt CognitoUserCreateFunction.Arn
409 |
410 | ApiGatewayStage:
411 | Type: AWS::ApiGateway::Stage
412 | Properties:
413 | DeploymentId: !Ref LLMApiDeployment
414 | Description: Lambda API Stage
415 | RestApiId: !Ref BedrockLambdaApi
416 | StageName: !Ref ApiGatewayStageName
417 |
418 |
419 | LLMFunction:
420 | Type: AWS::Serverless::Function
421 | Properties:
422 | Handler: llmfunction.lambda_handler
423 | CodeUri: lambdas/llmFunctions
424 | Runtime: python3.11
425 | Architectures:
426 | - x86_64
427 | Role: !GetAtt LLMExecutionRole.Arn
428 |
429 | RagFunction:
430 | Type: AWS::Serverless::Function
431 | Properties:
432 | Handler: ragfunction.lambda_handler
433 | CodeUri: lambdas/ragFunctions
434 | Runtime: python3.11
435 | Architectures:
436 | - x86_64
437 | Environment:
438 | Variables:
439 | KENDRA_INDEX_ID: !Ref KendraIndexId
440 | S3_BUCKET_NAME: !Ref BedrockWSS3Bucket
441 | Role: !GetAtt LLMExecutionRole.Arn
442 |
443 | PromptFunction:
444 | Type: AWS::Serverless::Function
445 | Properties:
446 | Handler: promptfunction.lambda_handler
447 | CodeUri: lambdas/ragFunctions
448 | Runtime: python3.11
449 | Architectures:
450 | - x86_64
451 | Environment:
452 | Variables:
453 | KENDRA_INDEX_ID: !Ref KendraIndexId
454 | S3_BUCKET_NAME: !Ref BedrockWSS3Bucket
455 | Role: !GetAtt LLMExecutionRole.Arn
456 |
457 | KBFunction:
458 | Type: AWS::Serverless::Function
459 | Properties:
460 | Handler: kbfunction.lambda_handler
461 | CodeUri: lambdas/llmFunctions
462 | Runtime: python3.11
463 | Architectures:
464 | - x86_64
465 | Environment:
466 | Variables:
467 | KB_ID: copy_kb_id
468 | Role: !GetAtt LLMExecutionRole.Arn
469 |
470 | LLMExecutionRole:
471 | Type: AWS::IAM::Role
472 | Properties:
473 | AssumeRolePolicyDocument:
474 | Version: '2012-10-17'
475 | Statement:
476 | - Effect: Allow
477 | Principal:
478 | Service: lambda.amazonaws.com
479 | Action: sts:AssumeRole
480 | Path: /
481 |
482 | LLMExecutionPolicy:
483 | Type: AWS::IAM::Policy
484 | Properties:
485 | PolicyName: LLMExecutionPolicy
486 | Roles:
487 | - !Ref LLMExecutionRole
488 | PolicyDocument:
489 | Version: '2012-10-17'
490 | Statement:
491 | - Effect: Allow
492 | Action:
493 | - kendra:Query
494 | - kendra:GetQuerySuggestions
495 | - kendra:Retrieve
496 | - s3:GetObject
497 | - s3:ListBucket
498 | - s3:ListBucketVersions
499 | - s3:DescribeBucket
500 | - bedrock:ListFoundationModels
501 | - bedrock:GetFoundationModel
502 | - bedrock:InvokeModel
503 | - bedrock:InvokeModelWithResponseStream
504 | - bedrock:ListTagsForResource
505 | - bedrock:UntagResource
506 | - bedrock:TagResource
507 | - bedrock:AcceptAcknowledgement
508 | - bedrock:GetModelPermission
509 | - bedrock:GetModelInvocationLogging
510 | - bedrock:PutModelInvocationLogging
511 | - bedrock:GetKnowledgeBase
512 | - bedrock:RetrieveAndGenerate
513 | - bedrock:Retrieve
514 | - logs:CreateLogGroup
515 | - logs:CreateLogStream
516 | - logs:PutLogEvents
517 | Resource:
518 | - !Sub "arn:aws:kendra:${AWS::Region}:${AWS::AccountId}:index/${KendraIndexId}"
519 | - !Sub "arn:aws:s3:::${BedrockWSS3Bucket}/*"
520 | - !Sub "arn:aws:s3:::${BedrockWSS3Bucket}"
521 | - "*"
522 | BedrockExecutionRoleForKBs:
523 | Type: 'AWS::IAM::Role'
524 | Properties:
525 | AssumeRolePolicyDocument:
526 | Version: 2012-10-17
527 | Statement:
528 | - Sid: ''
529 | Effect: Allow
530 | Principal:
531 | Service: bedrock.amazonaws.com
532 | Action: 'sts:AssumeRole'
533 | Policies:
534 | - PolicyName: BedrockOpenSearchS3AccessPolicy
535 | PolicyDocument:
536 | Version: '2012-10-17'
537 | Statement:
538 | - Effect: Allow
539 | Action:
540 | - 'bedrock:InvokeModel'
541 | Resource:
542 | - !Sub "arn:aws:bedrock:${AWS::Region}::foundation-model/amazon.titan-embed-text-v2:0"
543 | - Effect: Allow
544 | Action:
545 | - 'aoss:APIAccessAll'
546 | Resource:
547 | - !Sub "arn:aws:aoss:${AWS::Region}:${AWS::AccountId}:collection/*"
548 | - Effect: Allow
549 | Action:
550 | - 's3:ListBucket'
551 | - 's3:GetObject'
552 | Resource:
553 | - !Sub "arn:aws:s3:::${BedrockWSS3Bucket}/sample-documents/*"
554 | - !Sub "arn:aws:s3:::${BedrockWSS3Bucket}"
555 |
556 | Outputs:
557 | CognitoUserPool:
558 | Description: Cognito User Pool
559 | Value:
560 | Fn::Sub: "${ChatbotUserPool}"
561 |
562 | CongnitoUserPoolClientID:
563 | Description: Cognito User Pool Client ID
564 | Value:
565 | Fn::Sub: "${ChatbotUserPoolClient}"
566 |
567 | BedrockApiUrl:
568 | Description: API Gateway endpoint URL for the Bedrock Lambda Function
569 | Value:
570 | Fn::Sub: "https://${BedrockLambdaApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/prod"
571 |
572 | SecretsName:
573 | Description: Secrets name to retrieve ui credentials
574 | Value:
575 | Fn::Sub: "ui-credentials-${BedrockLambdaApi}"
576 |
--------------------------------------------------------------------------------
/tools/account-setup.sh:
--------------------------------------------------------------------------------
1 | # Create AWS accounts
2 | for i in {18..20}; do
3 | aws organizations create-account --email prallam+acnt$i@amazon.com --account-name aws-events-acnt$i
4 | done
5 |
6 | # Move the accounts into the 'bedrockevent' organizational unit
7 | OU_ID=$(aws organizations list-organizational-units | jq -r '.OrganizationalUnits[] | select(.Name == "bedrockevent").Id')
8 | for i in {18..20}; do
9 | ACCOUNT_ID=$(aws organizations list-accounts | jq -r '.Accounts[] | select(.Name == "aws-events-acnt'$i'").Id')
10 | aws organizations move-account --account-id $ACCOUNT_ID --source-parent-id ou-7439 --destination-parent-id $OU_ID
11 | done
12 |
13 | aws organizations move-account --account-id 525840005564 --source-parent-id ou-7439 --destination-parent-id ou-7439-16nv0zlq
14 |
15 |
16 | #Create 5 IAM users CFN call
17 | #aws cloudformation create-stack --stack-name user-setup-DONOT-DELETE --template-body file://gen-ai-builder-init-v1.2.yaml --capabilities CAPABILITY_NAMED_IAM
18 |
--------------------------------------------------------------------------------
/tools/gen-ai-builder-init v1.1.yml:
--------------------------------------------------------------------------------
1 | Metadata:
2 | AWS::CloudFormation::Interface:
3 | ParameterGroups: []
4 | ParameterLabels: {}
5 | Comments: Prepares Account User Access for GenAI Builder Event
6 | CreatedBy: Carter Meyers (AWS)
7 | Description: GenAI Builder Event Prep - v1.0
8 | LastUpdated: October 18, 2023
9 | Version: v1.0
10 | Outputs:
11 | User01:
12 | Value: !Join
13 | - ''
14 | - - 'Sign-in URL: https://'
15 | - !Ref 'AWS::AccountId'
16 | - '.signin.aws.amazon.com/console | Username: '
17 | - !Ref 'User01'
18 | - ' | Password: '
19 | - GenAIBuilder!
20 | User02:
21 | Value: !Join
22 | - ''
23 | - - 'Sign-in URL: https://'
24 | - !Ref 'AWS::AccountId'
25 | - '.signin.aws.amazon.com/console | Username: '
26 | - !Ref 'User02'
27 | - ' | Password: '
28 | - GenAIBuilder!
29 | User03:
30 | Value: !Join
31 | - ''
32 | - - 'Sign-in URL: https://'
33 | - !Ref 'AWS::AccountId'
34 | - '.signin.aws.amazon.com/console | Username: '
35 | - !Ref 'User03'
36 | - ' | Password: '
37 | - GenAIBuilder!
38 | User04:
39 | Value: !Join
40 | - ''
41 | - - 'Sign-in URL: https://'
42 | - !Ref 'AWS::AccountId'
43 | - '.signin.aws.amazon.com/console | Username: '
44 | - !Ref 'User04'
45 | - ' | Password: '
46 | - GenAIBuilder!
47 | User05:
48 | Value: !Join
49 | - ''
50 | - - 'Sign-in URL: https://'
51 | - !Ref 'AWS::AccountId'
52 | - '.signin.aws.amazon.com/console | Username: '
53 | - !Ref 'User05'
54 | - ' | Password: '
55 | - GenAIBuilder!
56 | User06:
57 | Value: !Join
58 | - ''
59 | - - 'Sign-in URL: https://'
60 | - !Ref 'AWS::AccountId'
61 | - '.signin.aws.amazon.com/console | Username: '
62 | - !Ref 'User06'
63 | - ' | Password: '
64 | - GenAIBuilder!
65 | User07:
66 | Value: !Join
67 | - ''
68 | - - 'Sign-in URL: https://'
69 | - !Ref 'AWS::AccountId'
70 | - '.signin.aws.amazon.com/console | Username: '
71 | - !Ref 'User07'
72 | - ' | Password: '
73 | - GenAIBuilder!
74 | User08:
75 | Value: !Join
76 | - ''
77 | - - 'Sign-in URL: https://'
78 | - !Ref 'AWS::AccountId'
79 | - '.signin.aws.amazon.com/console | Username: '
80 | - !Ref 'User08'
81 | - ' | Password: '
82 | - GenAIBuilder!
83 | User09:
84 | Value: !Join
85 | - ''
86 | - - 'Sign-in URL: https://'
87 | - !Ref 'AWS::AccountId'
88 | - '.signin.aws.amazon.com/console | Username: '
89 | - !Ref 'User09'
90 | - ' | Password: '
91 | - GenAIBuilder!
92 | User10:
93 | Value: !Join
94 | - ''
95 | - - 'Sign-in URL: https://'
96 | - !Ref 'AWS::AccountId'
97 | - '.signin.aws.amazon.com/console | Username: '
98 | - !Ref 'User10'
99 | - ' | Password: '
100 | - GenAIBuilder!
101 | Resources:
102 | IAMGroup:
103 | Properties:
104 | ManagedPolicyArns: []
105 | Policies:
106 | - PolicyDocument:
107 | Statement:
108 | - Action:
109 | - sns:*
110 | - cognito-identity:*
111 | - s3:*
112 | - apigateway:*
113 | - cloudformation:*
114 | - cloud9:*
115 | - amplify:*
116 | - iam:*
117 | - kendra:*
118 | - serverlessrepo:*
119 | - lambda:*
120 | - logs:*
121 | - cloudwatch:*
122 | - ec2:*
123 | - cognito-idp:*
124 | - secretsmanager:*
125 | - aws-marketplace:*
126 | - bedrock:*
127 | Effect: Allow
128 | Resource:
129 | - '*'
130 | - Action:
131 | - bedrock:UpdateProvisionedModelThroughput
132 | - bedrock:DeleteProvisionedModelThroughput
133 | - bedrock:CreateProvisionedModelThroughput
134 | Effect: Deny
135 | Resource:
136 | - '*'
137 | PolicyName: gen-ai-builder-access
138 | Type: AWS::IAM::Group
139 |
140 | User01:
141 | Properties:
142 | UserName: User01
143 | Groups:
144 | - !Ref 'IAMGroup'
145 | LoginProfile:
146 | Password: GenAIBuilder!
147 | PasswordResetRequired: false
148 | Type: AWS::IAM::User
149 | User02:
150 | Properties:
151 | UserName: User02
152 | Groups:
153 | - !Ref 'IAMGroup'
154 | LoginProfile:
155 | Password: GenAIBuilder!
156 | PasswordResetRequired: false
157 | Type: AWS::IAM::User
158 | User03:
159 | Properties:
160 | UserName: User03
161 | Groups:
162 | - !Ref 'IAMGroup'
163 | LoginProfile:
164 | Password: GenAIBuilder!
165 | PasswordResetRequired: false
166 | Type: AWS::IAM::User
167 | User04:
168 | Properties:
169 | UserName: User04
170 | Groups:
171 | - !Ref 'IAMGroup'
172 | LoginProfile:
173 | Password: GenAIBuilder!
174 | PasswordResetRequired: false
175 | Type: AWS::IAM::User
176 | User05:
177 | Properties:
178 | UserName: User05
179 | Groups:
180 | - !Ref 'IAMGroup'
181 | LoginProfile:
182 | Password: GenAIBuilder!
183 | PasswordResetRequired: false
184 | Type: AWS::IAM::User
185 | User06:
186 | Properties:
187 | UserName: User06
188 | Groups:
189 | - !Ref 'IAMGroup'
190 | LoginProfile:
191 | Password: GenAIBuilder!
192 | PasswordResetRequired: false
193 | Type: AWS::IAM::User
194 | User07:
195 | Properties:
196 | UserName: User07
197 | Groups:
198 | - !Ref 'IAMGroup'
199 | LoginProfile:
200 | Password: GenAIBuilder!
201 | PasswordResetRequired: false
202 | Type: AWS::IAM::User
203 | User08:
204 | Properties:
205 | UserName: User08
206 | Groups:
207 | - !Ref 'IAMGroup'
208 | LoginProfile:
209 | Password: GenAIBuilder!
210 | PasswordResetRequired: false
211 | Type: AWS::IAM::User
212 | User09:
213 | Properties:
214 | UserName: User09
215 | Groups:
216 | - !Ref 'IAMGroup'
217 | LoginProfile:
218 | Password: GenAIBuilder!
219 | PasswordResetRequired: false
220 | Type: AWS::IAM::User
221 | User10:
222 | Properties:
223 | UserName: User10
224 | Groups:
225 | - !Ref 'IAMGroup'
226 | LoginProfile:
227 | Password: GenAIBuilder!
228 | PasswordResetRequired: false
229 | Type: AWS::IAM::User
230 |
--------------------------------------------------------------------------------
/tools/gen-ai-builder-init-v1.2.yaml:
--------------------------------------------------------------------------------
1 | Metadata:
2 | AWS::CloudFormation::Interface:
3 | ParameterGroups: []
4 | ParameterLabels: {}
5 | Comments: Prepares Account User Access for GenAI Builder Event
6 | CreatedBy: Carter Meyers (AWS)
7 | Description: GenAI Builder Event Prep - v1.2
8 | LastUpdated: October 21, 2023
9 | Version: v1.0
10 | Outputs:
11 | User01:
12 | Value: !Join
13 | - ''
14 | - - 'Sign-in URL: https://'
15 | - !Ref 'AWS::AccountId'
16 | - '.signin.aws.amazon.com/console | Username: '
17 | - !Ref 'User01'
18 | - ' | Password: '
19 | - GenAIBuilder!
20 | User02:
21 | Value: !Join
22 | - ''
23 | - - 'Sign-in URL: https://'
24 | - !Ref 'AWS::AccountId'
25 | - '.signin.aws.amazon.com/console | Username: '
26 | - !Ref 'User02'
27 | - ' | Password: '
28 | - GenAIBuilder!
29 | User03:
30 | Value: !Join
31 | - ''
32 | - - 'Sign-in URL: https://'
33 | - !Ref 'AWS::AccountId'
34 | - '.signin.aws.amazon.com/console | Username: '
35 | - !Ref 'User03'
36 | - ' | Password: '
37 | - GenAIBuilder!
38 | User04:
39 | Value: !Join
40 | - ''
41 | - - 'Sign-in URL: https://'
42 | - !Ref 'AWS::AccountId'
43 | - '.signin.aws.amazon.com/console | Username: '
44 | - !Ref 'User04'
45 | - ' | Password: '
46 | - GenAIBuilder!
47 | User05:
48 | Value: !Join
49 | - ''
50 | - - 'Sign-in URL: https://'
51 | - !Ref 'AWS::AccountId'
52 | - '.signin.aws.amazon.com/console | Username: '
53 | - !Ref 'User05'
54 | - ' | Password: '
55 | - GenAIBuilder!
56 | Resources:
57 | IAMGroup:
58 | Properties:
59 | ManagedPolicyArns: []
60 | Policies:
61 | - PolicyDocument:
62 | Statement:
63 | - Action:
64 | - sns:*
65 | - cognito-identity:*
66 | - s3:*
67 | - apigateway:*
68 | - cloudformation:*
69 | - cloud9:*
70 | - amplify:*
71 | - iam:*
72 | - kendra:*
73 | - serverlessrepo:*
74 | - lambda:*
75 | - logs:*
76 | - cloudwatch:*
77 | - ec2:*
78 | - cognito-idp:*
79 | - secretsmanager:*
80 | - aws-marketplace:*
81 | - bedrock:*
82 | Effect: Allow
83 | Resource:
84 | - '*'
85 | - Action:
86 | - bedrock:UpdateProvisionedModelThroughput
87 | - bedrock:DeleteProvisionedModelThroughput
88 | - bedrock:CreateProvisionedModelThroughput
89 | Effect: Deny
90 | Resource:
91 | - '*'
92 | PolicyName: gen-ai-builder-access
93 | Type: AWS::IAM::Group
94 |
95 | User01:
96 | Properties:
97 | UserName: User01
98 | Groups:
99 | - !Ref 'IAMGroup'
100 | LoginProfile:
101 | Password: GenAIBuilder!
102 | PasswordResetRequired: false
103 | Type: AWS::IAM::User
104 | User02:
105 | Properties:
106 | UserName: User02
107 | Groups:
108 | - !Ref 'IAMGroup'
109 | LoginProfile:
110 | Password: GenAIBuilder!
111 | PasswordResetRequired: false
112 | Type: AWS::IAM::User
113 | User03:
114 | Properties:
115 | UserName: User03
116 | Groups:
117 | - !Ref 'IAMGroup'
118 | LoginProfile:
119 | Password: GenAIBuilder!
120 | PasswordResetRequired: false
121 | Type: AWS::IAM::User
122 | User04:
123 | Properties:
124 | UserName: User04
125 | Groups:
126 | - !Ref 'IAMGroup'
127 | LoginProfile:
128 | Password: GenAIBuilder!
129 | PasswordResetRequired: false
130 | Type: AWS::IAM::User
131 | User05:
132 | Properties:
133 | UserName: User05
134 | Groups:
135 | - !Ref 'IAMGroup'
136 | LoginProfile:
137 | Password: GenAIBuilder!
138 | PasswordResetRequired: false
139 | Type: AWS::IAM::User
140 |
--------------------------------------------------------------------------------
/tools/resize.sh:
--------------------------------------------------------------------------------
1 |
2 | #!/bin/bash
3 |
4 | # Specify the desired volume size in GiB as a command line argument. If not specified, default to 20 GiB.
5 | SIZE=${1:-40}
6 |
7 | # Get the ID of the environment host Amazon EC2 instance.
8 | TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 60")
9 | INSTANCEID=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/instance-id 2> /dev/null)
10 | REGION=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/placement/region 2> /dev/null)
11 |
12 | # Get the ID of the Amazon EBS volume associated with the instance.
13 | VOLUMEID=$(aws ec2 describe-instances \
14 | --instance-id $INSTANCEID \
15 | --query "Reservations[0].Instances[0].BlockDeviceMappings[0].Ebs.VolumeId" \
16 | --output text \
17 | --region $REGION)
18 |
19 | # Resize the EBS volume.
20 | aws ec2 modify-volume --volume-id $VOLUMEID --size $SIZE
21 |
22 | # Wait for the resize to finish.
23 | while [ \
24 | "$(aws ec2 describe-volumes-modifications \
25 | --volume-id $VOLUMEID \
26 | --filters Name=modification-state,Values="optimizing","completed" \
27 | --query "length(VolumesModifications)"\
28 | --output text)" != "1" ]; do
29 | sleep 1
30 | done
31 |
32 | # Check if we're on an NVMe filesystem
33 | if [[ -e "/dev/xvda" && $(readlink -f /dev/xvda) = "/dev/xvda" ]]
34 | then
35 | # Rewrite the partition table so that the partition takes up all the space that it can.
36 | sudo growpart /dev/xvda 1
37 | # Expand the size of the file system.
38 | # Check if we're on AL2
39 | STR=$(cat /etc/os-release)
40 | SUB="VERSION_ID=\"2\""
41 | if [[ "$STR" == *"$SUB"* ]]
42 | then
43 | sudo xfs_growfs -d /
44 | else
45 | sudo resize2fs /dev/xvda1
46 | fi
47 |
48 | else
49 | # Rewrite the partition table so that the partition takes up all the space that it can.
50 | sudo growpart /dev/nvme0n1 1
51 |
52 | # Expand the size of the file system.
53 | # Check if we're on AL2
54 | STR=$(cat /etc/os-release)
55 | SUB="VERSION_ID=\"2\""
56 | if [[ "$STR" == *"$SUB"* ]]
57 | then
58 | sudo xfs_growfs -d /
59 | else
60 | sudo resize2fs /dev/nvme0n1p1
61 | fi
62 | fi
63 |
--------------------------------------------------------------------------------
/tools/samconf.toml:
--------------------------------------------------------------------------------
1 | version = 0.1
2 | [default]
3 | [default.deploy]
4 | [default.deploy.parameters]
5 | region = ""
6 | stack_name = ""
7 | resolve_s3 = true
8 | s3_prefix = ""
9 | capabilities = "CAPABILITY_IAM"
10 | parameter_overrides = "StackName=\"\" KendraIndexId=\"\" BedrockWSS3Bucket=\"\" ApiGatewayStageName=\"prod\""
11 |
--------------------------------------------------------------------------------
/tools/solutions/kbfunction.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 | import boto3
4 |
5 | import traceback
6 |
7 |
8 | region = boto3.session.Session().region_name
9 | KB_ID = os.environ["KB_ID"]
10 |
11 |
12 | def lambda_handler(event, context):
13 | boto3_version = boto3.__version__
14 | print(f"Boto3 version: {boto3_version}")
15 |
16 | print(f"Event is: {event}")
17 | event_body = json.loads(event["body"])
18 | prompt = event_body["query"]
19 | model_id = event_body["model_id"]
20 |
21 | response = ''
22 | status_code = 200
23 |
24 | try:
25 | model_arn = 'arn:aws:bedrock:'+region+'::foundation-model/'+model_id
26 | print(f"Model arn: {model_arn}")
27 |
28 | response = retrieveAndGenerate(prompt, model_arn)["output"]["text"]
29 | return {
30 | 'statusCode': status_code,
31 | 'headers': {
32 | 'Access-Control-Allow-Origin': '*',
33 | 'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
34 | 'Access-Control-Allow-Methods': 'OPTIONS,POST'
35 | },
36 | 'body': json.dumps({'answer': response})
37 | }
38 |
39 | except Exception as e:
40 | print(f"An unexpected error occurred: {str(e)}")
41 | stack_trace = traceback.format_exc()
42 | print(stack_trace)
43 | return {
44 | 'statusCode': status_code,
45 | 'headers': {
46 | 'Access-Control-Allow-Origin': '*',
47 | 'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
48 | 'Access-Control-Allow-Methods': 'OPTIONS,POST'
49 | },
50 | 'body': json.dumps({'error': str(e)})
51 | }
52 |
53 | def retrieveAndGenerate(prompt, model_arn):
54 | bedrock_agent_runtime = boto3.client(
55 | service_name = "bedrock-agent-runtime")
56 | return bedrock_agent_runtime.retrieve_and_generate(
57 | input={
58 | 'text': prompt
59 | },
60 | retrieveAndGenerateConfiguration={
61 | 'type': 'KNOWLEDGE_BASE',
62 | 'knowledgeBaseConfiguration': {
63 | 'knowledgeBaseId': KB_ID,
64 | 'modelArn': model_arn
65 | }
66 | }
67 | )
68 |
--------------------------------------------------------------------------------
/tools/solutions/llmfunction.py:
--------------------------------------------------------------------------------
1 | import boto3
2 | import json
3 | import traceback
4 |
5 |
6 | region = boto3.session.Session().region_name
7 |
8 | def lambda_handler(event, context):
9 | boto3_version = boto3.__version__
10 | print(f"Boto3 version: {boto3_version}")
11 |
12 | print(f"Event is: {event}")
13 | event_body = json.loads(event["body"])
14 | prompt = event_body["query"]
15 | temperature = event_body["temperature"]
16 | max_tokens = event_body["max_tokens"]
17 | model_id = event_body["model_id"]
18 |
19 | response = ''
20 | status_code = 200
21 |
22 | try:
23 | if model_id == 'mistral.mistral-7b-instruct-v0:2':
24 | response = invoke_mistral_7b(model_id, prompt, temperature, max_tokens)
25 | elif model_id == 'meta.llama3-1-8b-instruct-v1:0':
26 | response = invoke_llama(model_id, prompt, temperature, max_tokens)
27 | else:
28 | response = invoke_claude(model_id, prompt, temperature, max_tokens)
29 |
30 | return {
31 | 'statusCode': status_code,
32 | 'headers': {
33 | 'Access-Control-Allow-Origin': '*',
34 | 'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
35 | 'Access-Control-Allow-Methods': 'OPTIONS,POST'
36 | },
37 | 'body': json.dumps({'answer': response})
38 | }
39 |
40 | except Exception as e:
41 | print(f"An unexpected error occurred: {str(e)}")
42 | stack_trace = traceback.format_exc()
43 | print(stack_trace)
44 | return {
45 | 'statusCode': status_code,
46 | 'headers': {
47 | 'Access-Control-Allow-Origin': '*',
48 | 'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
49 | 'Access-Control-Allow-Methods': 'OPTIONS,POST'
50 | },
51 | 'body': json.dumps({'error': str(e)})
52 | }
53 |
54 |
55 | def invoke_claude(model_id, prompt, temperature, max_tokens):
56 | try:
57 |
58 | instruction = f"Human: {prompt} nAssistant:"
59 | bedrock_runtime_client = boto3.client(service_name="bedrock-runtime", region_name=region)
60 | body= {
61 | "anthropic_version": "bedrock-2023-05-31",
62 | "max_tokens": max_tokens,
63 | "messages": [
64 | {
65 | "role": "user",
66 | "content": [{"type": "text", "text": prompt}],
67 | }
68 | ],
69 | }
70 |
71 | response = bedrock_runtime_client.invoke_model(
72 | modelId=model_id, body=json.dumps(body)
73 | )
74 |
75 | response_body = json.loads(response["body"].read())
76 | outputs = response_body.get("content")
77 | completions = [output["text"] for output in outputs]
78 | print(f"completions: {completions[0]}")
79 |
80 | return completions[0]
81 |
82 | except Exception as e:
83 | raise
84 |
85 | def invoke_mistral_7b(model_id, prompt, temperature, max_tokens):
86 | try:
87 | instruction = f"[INST] {prompt} [/INST]"
88 | bedrock_runtime_client = boto3.client(service_name="bedrock-runtime", region_name=region)
89 |
90 | body = {
91 | "prompt": instruction,
92 | "max_tokens": max_tokens,
93 | "temperature": temperature,
94 | }
95 |
96 | response = bedrock_runtime_client.invoke_model(
97 | modelId=model_id, body=json.dumps(body)
98 | )
99 | response_body = json.loads(response["body"].read())
100 | outputs = response_body.get("outputs")
101 | print(f"response: {outputs}")
102 |
103 | completions = [output["text"] for output in outputs]
104 | return completions[0]
105 | except Exception as e:
106 | raise
107 |
108 | def invoke_llama(model_id, prompt, temperature, max_tokens):
109 | print(f"Invoking llam model {model_id}" )
110 | print(f"max_tokens {max_tokens}" )
111 | try:
112 | instruction = f"[INST]You are a very intelligent bot with exceptional critical thinking, help me answering below question.[/INST]"
113 | total_prompt = f"{instruction}\n{prompt}"
114 |
115 | print(f"Prompt template {total_prompt}" )
116 |
117 | bedrock_runtime_client = boto3.client(service_name="bedrock-runtime", region_name=region)
118 |
119 | body = {
120 | "prompt": total_prompt,
121 | "max_gen_len": max_tokens,
122 | "temperature": temperature,
123 | "top_p": 0.9
124 | }
125 |
126 | response = bedrock_runtime_client.invoke_model(
127 | modelId=model_id, body=json.dumps(body)
128 | )
129 | response_body = json.loads(response["body"].read())
130 | print(f"response: {response_body}")
131 | return response_body ['generation']
132 | except Exception as e:
133 | raise
134 |
--------------------------------------------------------------------------------
/tools/solutions/ragfunction.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 | import boto3
4 | from langchain_community.retrievers import AmazonKendraRetriever
5 | from langchain_aws import ChatBedrock
6 | from langchain.chains import ConversationalRetrievalChain
7 | from langchain.memory import ConversationBufferMemory
8 | from langchain.prompts import PromptTemplate
9 | from langchain.chains import RetrievalQA
10 |
11 | import traceback
12 |
13 | kendra = boto3.client('kendra')
14 | chain_type = 'stuff'
15 |
16 | KENDRA_INDEX_ID = os.getenv('KENDRA_INDEX_ID')
17 | S3_BUCKET_NAME = os.environ["S3_BUCKET_NAME"]
18 |
19 |
20 | refine_prompt_template = (
21 | "Below is an instruction that describes a task. "
22 | "Write a response that appropriately completes the request.\n\n"
23 | "### Instruction:\n"
24 | "This is the original question: {question}\n"
25 | "The existing answer: {existing_answer}\n"
26 | "Now there are some additional texts, (if needed) you can use them to improve your existing answer."
27 | "\n\n"
28 | "{context_str}\n"
29 | "\\nn"
30 | "Please use the new passage to further improve your answer.\n\n"
31 | "### Response: "
32 | )
33 |
34 | initial_qa_template = (
35 | "Below is an instruction that describes a task. "
36 | "Write a response that appropriately completes the request.\n\n"
37 | "### Instruction:\n"
38 | "The following is background knowledge:\n"
39 | "{context_str}"
40 | "\n"
41 | "Please answer this question based on the background knowledge provided above:{question}。\n\n"
42 | "### Response: "
43 | )
44 |
45 | def lambda_handler(event, context):
46 | print(f"Event is: {event}")
47 |
48 | event_body = json.loads(event["body"])
49 | question = event_body["query"]
50 | print(f"Query is: {question}")
51 |
52 | model_id = event_body["model_id"]
53 | temperature = event_body["temperature"]
54 | max_tokens = event_body["max_tokens"]
55 |
56 | response = ''
57 | status_code = 200
58 |
59 | PROMPT_TEMPLATE = 'prompt-engineering/claude-prompt-template.txt'
60 |
61 | try:
62 | if model_id == 'mistral.mistral-7b-instruct-v0:2':
63 | llm = get_mistral_llm(model_id,temperature,max_tokens)
64 | PROMPT_TEMPLATE = 'prompt-engineering/mistral-prompt-template.txt'
65 | elif model_id == 'meta.llama3-1-8b-instruct-v1:0':
66 | llm = get_llama_llm(model_id,temperature,max_tokens)
67 | PROMPT_TEMPLATE = 'prompt-engineering/llama-prompt-template.txt'
68 | else:
69 | llm = get_claude_llm(model_id,temperature,max_tokens)
70 | PROMPT_TEMPLATE = 'prompt-engineering/claude-prompt-template.txt'
71 |
72 | # Read the prompt template from S3 bucket
73 | s3 = boto3.resource('s3')
74 | obj = s3.Object(S3_BUCKET_NAME, PROMPT_TEMPLATE)
75 | prompt_template = obj.get()['Body'].read().decode('utf-8')
76 | print(f"prompt template: {prompt_template}")
77 |
78 | retriever = AmazonKendraRetriever(kendra_client=kendra,index_id=KENDRA_INDEX_ID)
79 |
80 |
81 | if chain_type == "stuff":
82 | PROMPT = PromptTemplate(
83 | template=prompt_template, input_variables=["context", "question"]
84 | )
85 | chain_type_kwargs = {"prompt": PROMPT}
86 | qa = RetrievalQA.from_chain_type(
87 | llm=llm,
88 | chain_type="stuff",
89 | retriever=retriever,
90 | return_source_documents=True,
91 | chain_type_kwargs=chain_type_kwargs)
92 | response = qa(question, return_only_outputs=False)
93 | elif chain_type == "refine":
94 | refine_prompt = PromptTemplate(
95 | input_variables=["question", "existing_answer", "context_str"],
96 | template=refine_prompt_template,
97 | )
98 | initial_qa_prompt = PromptTemplate(
99 | input_variables=["context_str", "question"],
100 | template=prompt_template,
101 | )
102 | chain_type_kwargs = {"question_prompt": initial_qa_prompt, "refine_prompt": refine_prompt}
103 | qa = RetrievalQA.from_chain_type(
104 | llm=llm,
105 | chain_type="refine",
106 | retriever=retriever,
107 | return_source_documents=True,
108 | chain_type_kwargs=chain_type_kwargs)
109 | response = qa(question, return_only_outputs=False)
110 |
111 | print('Response', response)
112 | source_documents = response.get('source_documents')
113 | source_docs = []
114 | previous_source = None
115 | previous_score = None
116 | response_data = []
117 |
118 | #if chain_type == "stuff":
119 | for source_doc in source_documents:
120 | source = source_doc.metadata['source']
121 | score = source_doc.metadata["score"]
122 | if source != previous_source or score != previous_score:
123 | source_data = {
124 | "source": source,
125 | "score": score
126 | }
127 | response_data.append(source_data)
128 | previous_source = source
129 | previous_score = score
130 |
131 | response_with_metadata = {
132 | "answer": response.get('result'),
133 | "source_documents": response_data
134 | }
135 |
136 | return {
137 | 'statusCode': status_code,
138 | 'headers': {
139 | 'Access-Control-Allow-Origin': '*',
140 | 'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
141 | 'Access-Control-Allow-Methods': 'OPTIONS,POST'
142 | },
143 | 'body': json.dumps(response_with_metadata)
144 | }
145 |
146 |
147 | except Exception as e:
148 | print(f"An unexpected error occurred: {str(e)}")
149 | stack_trace = traceback.format_exc()
150 | print(f"stack trace: {stack_trace}")
151 | print(f"error: {str(e)}")
152 |
153 | response = str(e)
154 | return {
155 | 'statusCode': status_code,
156 | 'headers': {
157 | 'Access-Control-Allow-Origin': '*',
158 | 'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
159 | 'Access-Control-Allow-Methods': 'OPTIONS,POST'
160 | },
161 | 'body': json.dumps({'error': response})
162 | }
163 |
164 | def get_claude_llm(model_id, temperature, max_tokens):
165 | model_kwargs = {
166 | "max_tokens": max_tokens,
167 | "temperature": temperature,
168 | "top_k": 50,
169 | "top_p": 0.95
170 | }
171 | llm = ChatBedrock(model_id=model_id, model_kwargs=model_kwargs)
172 | return llm
173 |
174 | def get_llama_llm(model_id, temperature, max_tokens):
175 | model_kwargs = {
176 | "max_gen_len": max_tokens,
177 | "temperature": temperature,
178 | "top_p": 0.9
179 | }
180 | llm = ChatBedrock(model_id=model_id, model_kwargs=model_kwargs)
181 | return llm
182 |
183 | def get_mistral_llm(model_id, temperature, max_tokens):
184 | model_kwargs = {
185 | "max_tokens": max_tokens,
186 | "temperature": temperature,
187 | "top_k": 50,
188 | "top_p": 0.9
189 | }
190 | llm = ChatBedrock(model_id=model_id, model_kwargs=model_kwargs)
191 | return llm
192 |
193 | def get_memory():
194 |
195 | memory = ConversationBufferMemory(
196 | memory_key="chat_history",
197 | input_key="question",
198 | output_key="answer",
199 | return_messages=True
200 | )
201 | return memory
202 |
--------------------------------------------------------------------------------
/tools/troubleshoot.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #### FIRT RUN ALL THESE ###
4 | export CFNStackName=bedrock-ws954
5 | export S3BucketName=$(aws cloudformation describe-stacks --stack-name ${CFNStackName} --query "Stacks[0].Outputs[?OutputKey=='S3BucketName'].OutputValue" --output text)
6 | export AWS_REGION=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region')
7 | export KendraIndexID=$(aws cloudformation describe-stacks --stack-name ${CFNStackName} --query "Stacks[0].Outputs[?OutputKey=='KendraIndexID'].OutputValue" --output text)
8 | export SAMStackName="sam-$CFNStackName"
9 | export BedrockApiUrl=$(aws cloudformation describe-stacks --stack-name ${SAMStackName} --query "Stacks[0].Outputs[?OutputKey=='BedrockApiUrl'].OutputValue" --output text)
10 | export SecretName=$(aws cloudformation describe-stacks --stack-name ${SAMStackName} --query "Stacks[0].Outputs[?OutputKey=='SecretsName'].OutputValue" --output text)
11 |
12 |
13 | #Amplify and sam builds
14 | #nvm use 16
15 | #node --version
16 |
17 |
18 | export PATH=~/.npm-global/bin:$PATH
19 |
20 | #update Ampliyfy and build frontend
21 | cd ~/environment/bedrock-serverless-workshop/frontend
22 | npm run build
23 |
24 | rm -r build
25 | mv dist build
26 | amplify publish --yes
27 |
28 | ### SAM deployments
29 | cd ~/environment/bedrock-serverless-workshop
30 | sam build && sam deploy
31 |
32 | aws secretsmanager get-secret-value --secret-id $SecretName | jq -r .SecretString
33 |
34 | export KB_ID=$(aws bedrock-agent list-knowledge-bases | jq -r '.knowledgeBaseSummaries[0].knowledgeBaseId')
35 | echo "Knowledge Base ID: $KB_ID"
36 | sed -Ei "s|copy_kb_id|${KB_ID}|g" ./template.yaml
37 |
38 |
--------------------------------------------------------------------------------