diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a7aabf98 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# OS and temporary files: +__pycache__ +.ipynb_checkpoints +.venv +.DS_Store + +# Files generated by the workshop: +/dependencies diff --git a/README.md b/README.md index b02d060a..d7f77418 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,55 @@ # Amazon Bedrock Workshop -## Introduction to the Repository and Workshop +This hands-on workshop, aimed at developers and solution builders, introduces how to leverage foundation models (FMs) through [Amazon Bedrock](https://aws.amazon.com/bedrock/). -The goal of this workshop is to give you hands-on experience leveraging foundation models (FMs) through Amazon Bedrock. Amazon Bedrock is a fully managed service that provides access to FMs from third-party providers and Amazon; available via an API. With Bedrock, you can choose from a variety of models to find the one that’s best suited for your use case. +Amazon Bedrock is a fully managed service that provides access to FMs from third-party providers and Amazon; available via an API. With Bedrock, you can choose from a variety of models to find the one that’s best suited for your use case. -Within this series of labs, you will be taken through some of the most common usage patterns we are seeing with our customers for Generative AI. We will explore techniques for generating text and images, creating value for organizations by improving productivity. This is achieved by leveraging foundation models to help in composing emails, summarizing text, answering questions, building chatbots, and creating images. You will gain hands-on experience using Bedrock APIs, SDKs, and open-source software for example LangChain and FAISS to implement these usage patterns. +Within this series of labs, you'll explore some of the most common usage patterns we are seeing with our customers for Generative AI. We will show techniques for generating text and images, creating value for organizations by improving productivity. This is achieved by leveraging foundation models to help in composing emails, summarizing text, answering questions, building chatbots, and creating images. You will gain hands-on experience implementing these patterns via Bedrock APIs and SDKs, as well as open-source software like [LangChain](https://python.langchain.com/docs/get_started/introduction) and [FAISS](https://faiss.ai/index.html). -This workshop is intended for developers and solution builders. +Labs include: -What’s included in this workshop: - -- Text Generation \[Estimated time to complete - 30 mins\] -- Text Summarization \[Estimated time to complete - 30 mins\] -- Questions Answering \[Estimated time to complete - 45 mins\] -- Chatbot \[Estimated time to complete - 45 mins\] -- Image Generation \[Estimated time to complete - 30 mins\] +- **Text Generation** \[Estimated time to complete - 30 mins\] +- **Text Summarization** \[Estimated time to complete - 30 mins\] +- **Questions Answering** \[Estimated time to complete - 45 mins\] +- **Chatbot** \[Estimated time to complete - 45 mins\] +- **Image Generation** \[Estimated time to complete - 30 mins\]
-![10-overview](10-overview.png) +![imgs/10-overview](imgs/10-overview.png "Overview of the different labs in the workshop")
- -Workshop Link: [https://catalog.us-east-1.prod.workshops.aws/workshops/a4bdb007-5600-4368-81c5-ff5b4154f518/en-US/](https://catalog.us-east-1.prod.workshops.aws/workshops/a4bdb007-5600-4368-81c5-ff5b4154f518/en-US) +You can also refer to these [Step-by-step guided instructions on the workshop website](https://catalog.us-east-1.prod.workshops.aws/workshops/a4bdb007-5600-4368-81c5-ff5b4154f518/en-US). + + +## Getting started + +### Choose a notebook environment + +This workshop is presented as a series of **Python notebooks**, which you can run from the environment of your choice: + +- For a fully-managed environment with rich AI/ML features, we'd recommend using [SageMaker Studio](https://aws.amazon.com/sagemaker/studio/). To get started quickly, you can refer to the [instructions for domain quick setup](https://docs.aws.amazon.com/sagemaker/latest/dg/onboard-quick-start.html). +- For a fully-managed but more basic experience, you could instead [create a SageMaker Notebook Instance](https://docs.aws.amazon.com/sagemaker/latest/dg/howitworks-create-ws.html). +- If you prefer to use your existing (local or other) notebook environment, make sure it has [credentials for calling AWS](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html). + + +### Enable AWS IAM permissions for Bedrock + +The AWS identity you assume from your notebook environment (which is the [*Studio/notebook Execution Role*](https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-roles.html) from SageMaker, or could be a role or IAM User for self-managed notebooks), must have sufficient [AWS IAM permissions](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html) to call the Amazon Bedrock service. + +To grant Bedrock access to your identity, you can: + +- Open the [AWS IAM Console](https://us-east-1.console.aws.amazon.com/iam/home?#) +- Find your [Role](https://us-east-1.console.aws.amazon.com/iamv2/home?#/roles) (if using SageMaker or otherwise assuming an IAM Role), or else [User](https://us-east-1.console.aws.amazon.com/iamv2/home?#/users) +- Select *Add Permissions > Create Inline Policy* to attach new inline permissions, open the *JSON* editor and paste in the below example policy: -## IAM Policy for Bedrock -Following IAM policy should be created to grant access on Bedrock APIs: ``` { "Version": "2012-10-17", "Statement": [ { - "Sid": "Statement1", + "Sid": "BedrockFullAccess", "Effect": "Allow", "Action": "bedrock:*", "Resource": "*" @@ -40,100 +57,63 @@ Following IAM policy should be created to grant access on Bedrock APIs: ] } ``` -After the policy created, you need to attach the policy to your **Sagemaker Execution Role**. -## Using these notebooks +> ⚠️ **Note:** With Amazon SageMaker, your notebook execution role will typically be *separate* from the user or role that you log in to the AWS Console with. If you'd like to explore the AWS Console for Amazon Bedrock, you'll need to grant permissions to your Console user/role too. + +For more information on the fine-grained action and resource permissions in Bedrock, check out the Bedrock Developer Guide. + + +### Clone and use the notebooks + +> ℹ️ **Note:** In SageMaker Studio, you can open a "System Terminal" to run these commands by clicking *File > New > Terminal* -Start by cloning the workshop repo +Once your notebook environment is set up, clone this workshop repository into it. ```sh git clone https://github.com/aws-samples/amazon-bedrock-workshop.git cd amazon-bedrock-workshop ``` -The bedrock SDK is not already a part of boto3. To download the additional python wheel run the following script +Because the service is in preview, the Amazon Bedrock SDK is not yet included in standard releases of the [AWS SDK for Python - boto3](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html). Run the following script to download and extract custom SDK wheels for testing Bedrock: + ```sh bash ./download-dependencies.sh ``` -This script will create a `dependencies` folder and download the relevant SDKs needed to use Amazon Bedrock. Which can then be installed as follows: - -```bash -pip install ./dependencies/botocore-1.29.162-py3-none-any.whl --force-reinstall -pip install ./dependencies/boto3-1.26.162-py3-none-any.whl --force-reinstall -pip install ./dependencies/awscli-1.27.162-py3-none-any.whl --force-reinstall -``` -Following this a bedrock client can be created as follows: +This script will create a `dependencies` folder and download the relevant SDKs, but will not `pip install` them just yet. -```python -import boto3 -bedrock = boto3.client("bedrock", region_name="us-east-1") -``` +You're now ready to explore the lab notebooks! Start with [00_Intro/bedrock_boto3_setup.ipynb](00_Intro/bedrock_boto3_setup.ipynb) for details on how to install the Bedrock SDKs, create a client, and start calling the APIs from Python. -If you need to use a specific role to access bedrock, you can do so using a session as follows: - -```python -import boto3 -session = boto3.session.Session(profile_name='bedrock') -boto3_bedrock = session.client("bedrock", region_name="us-east-1") -``` ## Content This repository contains notebook examples for the Bedrock Architecture Patterns workshop. The notebooks are organised by module as follows: -## Intro - -[Simple Bedrock Usage](./00_Intro/bedrock_boto3_setup.ipynb) +### Intro -This notebook shows setting up the boto3 client and some basic usage of bedrock. +- [Simple Bedrock Usage](./00_Intro/bedrock_boto3_setup.ipynb): This notebook shows setting up the boto3 client and some basic usage of bedrock. ### Generation -[Simple use case with boto3](./01_Generation/00_generate_w_bedrock.ipynb) - -In this notebook, you generate text using Amazon Bedrock. We demonstrate consuming the Amazon Titan model directly with boto3 - -[Simple use case with LangChain](./01_Generation/01_zero_shot_generation.ipynb) - -We then perform the same task but using the popular frame LangChain - -[Generation with additional context](./01_Generation/02_contextual_generation.ipynb) - -We then take this further by enhancing the prompt with additional context in order to improve the response. +- [Simple use case with boto3](./01_Generation/00_generate_w_bedrock.ipynb): In this notebook, you generate text using Amazon Bedrock. We demonstrate consuming the Amazon Titan model directly with boto3 +- [Simple use case with LangChain](./01_Generation/01_zero_shot_generation.ipynb): We then perform the same task but using the popular framework LangChain +- [Generation with additional context](./01_Generation/02_contextual_generation.ipynb): We then take this further by enhancing the prompt with additional context in order to improve the response. ### Summarization -[Small text summarization](./02_Summarization/01.small-text-summarization-claude.ipynb) - -In this notebook, you use use Bedrock to perform a simple task of summarising a small piece of text. - -[Long text summarization](./02_Summarization/02.long-text-summarization-titan.ipynb) - -The above approach may not work as the content to be summarized gets larger and exceeds the max tokens of the model. In this notebook we show an approach of breaking the file up into smaller chunks, summarizing each chunk, and then summarizing the summaries. +- [Small text summarization](./02_Summarization/01.small-text-summarization-claude.ipynb): In this notebook, you use use Bedrock to perform a simple task of summarizing a small piece of text. +- [Long text summarization](./02_Summarization/02.long-text-summarization-titan.ipynb): The above approach may not work as the content to be summarized gets larger and exceeds the max tokens of the model. In this notebook we show an approach of breaking the file up into smaller chunks, summarizing each chunk, and then summarizing the summaries. ### Question Answering -[Simple questions with context](./03_QuestionAnswering/00_qa_w_bedrock_titan.ipynb) - -This notebook shows a simple example answerting a question with given context by calling the model directly. - -[Answering questions with Retrieval Augmented Generation](./03_QuestionAnswering/01_qa_w_rag_claude.ipynb) - -We can improve the above process by implementing an architecure called Retreival Augmented Generation (RAG). RAG retrieves data from outside the language model (non-parametric) and augments the prompts by adding the relevant retrieved data in context. +- [Simple questions with context](./03_QuestionAnswering/00_qa_w_bedrock_titan.ipynb): This notebook shows a simple example answering a question with given context by calling the model directly. +- [Answering questions with Retrieval Augmented Generation](./03_QuestionAnswering/01_qa_w_rag_claude.ipynb): We can improve the above process by implementing an architecure called Retreival Augmented Generation (RAG). RAG retrieves data from outside the language model (non-parametric) and augments the prompts by adding the relevant retrieved data in context. ### Chatbot -[Chatbot using Claude](./04_Chatbot/00_Chatbot_Claude.ipynb) - -This notebook shows a chatbot using Claude - -[Chatbot using Titan](./04_Chatbot/00_Chatbot_Titan.ipynb) - -This notebook shows a chatbot using Titan +- [Chatbot using Claude](./04_Chatbot/00_Chatbot_Claude.ipynb): This notebook shows a chatbot using Claude +- [Chatbot using Titan](./04_Chatbot/00_Chatbot_Titan.ipynb): This notebook shows a chatbot using Titan ### Text to Image -[Image Generation with Stable Diffusion](./05_Image/Bedrock%20Stable%20Diffusion%20XL.ipynb) - -This notebook demonstrates image generation with using the Stable Diffusion model +- [Image Generation with Stable Diffusion](./05_Image/Bedrock%20Stable%20Diffusion%20XL.ipynb): This notebook demonstrates image generation with using the Stable Diffusion model diff --git a/download-dependencies.sh b/download-dependencies.sh old mode 100644 new mode 100755 index fb3c313f..e9c5c79f --- a/download-dependencies.sh +++ b/download-dependencies.sh @@ -6,5 +6,11 @@ cd ./dependencies && \ echo "Downloading dependencies" curl -sS https://preview.documentation.bedrock.aws.dev/Documentation/SDK/bedrock-python-sdk.zip > sdk.zip && \ echo "Unpacking dependencies" -unzip sdk.zip && \ -rm sdk.zip \ No newline at end of file +# (SageMaker Studio system terminals don't have `unzip` utility installed) +if command -v unzip &> /dev/null +then + unzip sdk.zip && rm sdk.zip && echo "Done" +else + echo "'unzip' command not found: Trying to unzip via Python" + python -m zipfile -e sdk.zip . && rm sdk.zip && echo "Done" +fi diff --git a/10-overview.png b/imgs/10-overview.png similarity index 100% rename from 10-overview.png rename to imgs/10-overview.png diff --git a/utils/__init__.py b/utils/__init__.py index 177af15b..b03ad2c1 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1,23 +1,21 @@ -import textwrap +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: MIT-0 +"""General helper utilities the workshop notebooks""" +# Python Built-Ins: from io import StringIO import sys +import textwrap + -def print_ww(*args, **kwargs): +def print_ww(*args, width: int = 100, **kwargs): + """Like print(), but wraps output to `width` characters (default 100)""" buffer = StringIO() - try: + try: _stdout = sys.stdout sys.stdout = buffer - width = 100 - if 'width' in kwargs: - width = kwargs['width'] - del kwargs['width'] print(*args, **kwargs) - output = buffer.getvalue() + output = buffer.getvalue() finally: sys.stdout = _stdout for line in output.splitlines(): print("\n".join(textwrap.wrap(line, width=width))) - - - - diff --git a/utils/bedrock.py b/utils/bedrock.py index 482f856a..1069e146 100644 --- a/utils/bedrock.py +++ b/utils/bedrock.py @@ -1,55 +1,81 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: MIT-0 +"""Helper utilities for working with Amazon Bedrock from Python notebooks""" +# Python Built-Ins: +from enum import Enum import json -import boto3 import os -from typing import Any, Dict, List, Optional -from pydantic import root_validator from time import sleep -from enum import Enum +from typing import Dict, Optional + +# External Dependencies: import boto3 from botocore.config import Config +from pydantic import root_validator -def get_bedrock_client(assumed_role=None, region='us-east-1', url_override = None): - boto3_kwargs = {} - session = boto3.Session() - target_region = os.environ.get('AWS_DEFAULT_REGION',region) +def get_bedrock_client( + assumed_role: Optional[str] = None, + region: Optional[str] = None, + url_override: Optional[str] = None, +): + """Create a boto3 client for Amazon Bedrock, with optional configuration overrides + + Parameters + ---------- + assumed_role : + Optional ARN of an AWS IAM role to assume for calling the Bedrock service. If not + specified, the current active credentials will be used. + region : + Optional name of the AWS Region in which the service should be called (e.g. "us-east-1"). + If not specified, AWS_REGION or AWS_DEFAULT_REGION environment variable will be used. + url_override : + Optional override for the Bedrock service API Endpoint. If setting this, it should usually + include the protocol i.e. "https://..." + """ + if region is None: + target_region = os.environ.get("AWS_REGION", os.environ.get("AWS_DEFAULT_REGION")) + else: + target_region = region print(f"Create new client\n Using region: {target_region}") - if 'AWS_PROFILE' in os.environ: - print(f" Using profile: {os.environ['AWS_PROFILE']}") + boto3_kwargs = {"region_name": target_region} + + profile_name = os.environ.get("AWS_PROFILE") + if profile_name: + print(f" Using profile: {profile_name}") + boto3_kwargs["profile_name"] = profile_name retry_config = Config( - region_name = target_region, - retries = { - 'max_attempts': 10, - 'mode': 'standard' - } + region_name=target_region, + retries={ + "max_attempts": 10, + "mode": "standard", + }, ) - - boto3_kwargs = {} + session = boto3.Session(**boto3_kwargs) if assumed_role: print(f" Using role: {assumed_role}", end='') sts = session.client("sts") response = sts.assume_role( - RoleArn=str(assumed_role), # + RoleArn=str(assumed_role), RoleSessionName="langchain-llm-1" ) print(" ... successful!") - boto3_kwargs['aws_access_key_id']=response['Credentials']['AccessKeyId'] - boto3_kwargs['aws_secret_access_key']=response['Credentials']['SecretAccessKey'] - boto3_kwargs['aws_session_token']=response['Credentials']['SessionToken'] + boto3_kwargs["aws_access_key_id"] = response["Credentials"]["AccessKeyId"] + boto3_kwargs["aws_secret_access_key"] = response["Credentials"]["SecretAccessKey"] + boto3_kwargs["aws_session_token"] = response["Credentials"]["SessionToken"] if url_override: - boto3_kwargs['endpoint_url']=url_override + boto3_kwargs["endpoint_url"] = url_override bedrock_client = session.client( - service_name='bedrock', + service_name="bedrock", config=retry_config, - region_name= target_region, **boto3_kwargs - ) - + ) + print("boto3 Bedrock client successfully created!") print(bedrock_client._endpoint) return bedrock_client @@ -82,7 +108,7 @@ def validate_environment(cls, values: Dict) -> Dict: bedrock_client = get_bedrock_client(assumed_role=None) #boto3.client("bedrock") values["client"] = bedrock_client return values - + def generate_image(self, prompt: str, init_image: Optional[str] = None, **kwargs): """ Invoke Bedrock model to generate embeddings. @@ -181,5 +207,3 @@ def _invoke_model(self, model_id: BedrockModel, body_string: str): sleep(self.__RETRY_BACKOFF_SEC) continue return response - -